From 84594111f48874fcf909b669711755ef08013e91 Mon Sep 17 00:00:00 2001 From: Ozy311 Date: Mon, 5 Oct 2026 18:45:00 -0700 Subject: [PATCH 1/9] Rebuild RTL delivery on current squelch contracts. Principle XI. --- .github/workflows/appimage.yml | 15 +- .github/workflows/macos-dmg.yml | 23 +- .github/workflows/static-checks.yml | 3 + .github/workflows/windows-installer.yml | 21 +- .gitignore | 5 + CMakeLists.txt | 57 +- cmake/AetherRequireRtl.cmake | 6 + .../architecture/aetherd-touchpoint-tags.json | 12 + docs/architecture/aetherd-touchpoints.md | 7 +- docs/architecture/radio-capabilities-map.md | 40 +- docs/decoder-pcm-contract.md | 37 +- docs/native-dax-receive.md | 56 ++ docs/rtl-m1-runtime.md | 45 ++ docs/rtl-multirx-analog.md | 156 +++++ docs/rtl-release-packaging.md | 34 ++ docs/rtl-wfm.md | 10 +- hal-plugin/AetherSDRDAX.cpp | 9 +- packaging/macos/build-installer.sh | 12 +- scripts/build-windows-local.ps1 | 9 +- scripts/setup/rtl-dependencies.json | 37 ++ scripts/setup/setup-fftw.ps1 | 22 +- scripts/setup/setup-macos-deps.sh | 12 +- scripts/setup/setup-rtl-deps.ps1 | 46 ++ scripts/setup/setup-rtl-deps.sh | 62 ++ src/core/AutomationServer.cpp | 2 + src/core/PipeWireAudioBridge.cpp | 91 ++- src/core/PipeWireAudioBridge.h | 14 +- src/core/PipeWireNativeRxSource.cpp | 25 +- src/core/PipeWireNativeRxSource.h | 21 +- src/core/RtlDeviceSettings.cpp | 4 + src/core/RtlDeviceSettings.h | 1 + src/core/RtlSliceSettings.cpp | 12 + src/core/RtlSliceSettings.h | 2 + src/core/TciServer.cpp | 47 +- src/core/TciServer.h | 5 + src/core/VirtualAudioBridge.cpp | 102 ++-- src/core/VirtualAudioBridge.h | 8 +- src/core/backends/IRadioBackend.h | 2 + src/core/backends/MeterDef.h | 1 + src/core/backends/RadioCapabilities.h | 7 + src/core/backends/ReceiveCommand.h | 10 +- src/core/backends/SliceDelta.h | 2 + src/core/backends/SquelchLevelScale.h | 7 +- src/core/backends/anan/AnanBackend.cpp | 1 + src/core/backends/flex/FlexBackend.cpp | 4 + src/core/backends/flex/PanadapterStream.cpp | 3 +- src/core/backends/flex/PanadapterStream.h | 1 + src/core/backends/hl2/Hl2Backend.cpp | 4 + src/core/backends/icom/IcomCivBackend.cpp | 4 + .../backends/rtl/RtlCaptureTransaction.cpp | 2 + src/core/backends/rtl/RtlCaptureTransaction.h | 2 + src/core/backends/rtl/RtlReceivePipeline.cpp | 159 ++++- src/core/backends/rtl/RtlReceivePipeline.h | 60 +- src/core/backends/rtl/RtlSdrBackend.cpp | 356 ++++++++++- src/core/backends/rtl/RtlSdrBackend.h | 17 + src/core/backends/rtl/RtlSdrWorker.cpp | 120 +++- src/core/backends/rtl/RtlSdrWorker.h | 32 +- src/core/backends/rtl/RtlSquelchGate.h | 55 +- src/core/backends/rtl/RtlStartupTrace.h | 122 ++++ src/core/backends/sim/SimBackend.cpp | 1 + src/core/dsp/SpectrumTemporalAverage.h | 22 +- src/gui/AppletPanel.cpp | 9 +- src/gui/AppletPanel.h | 2 +- src/gui/Ax25HfPacketDecodeDialog.cpp | 238 +++++--- src/gui/Ax25HfPacketDecodeDialog.h | 19 +- src/gui/DaxApplet.cpp | 26 + src/gui/DaxApplet.h | 3 + src/gui/MainWindow.cpp | 15 +- src/gui/MainWindow_DigitalModes.cpp | 175 +++--- src/gui/MainWindow_Menus.cpp | 13 +- src/gui/MainWindow_Wiring.cpp | 34 +- src/gui/RtlReceiverSettingsWidget.cpp | 41 +- src/gui/RtlReceiverSettingsWidget.h | 2 + src/gui/RxApplet.cpp | 52 +- src/gui/RxApplet.h | 2 + src/gui/SMeterWidget.cpp | 64 +- src/gui/SMeterWidget.h | 12 +- src/gui/SmartMtrConfig.cpp | 12 + src/gui/SmartMtrConfig.h | 4 +- src/gui/SmartMtrWidget.cpp | 25 +- src/gui/SmartMtrWidget.h | 3 +- src/gui/VfoWidget.cpp | 71 ++- src/gui/VfoWidget.h | 6 + src/gui/WfmApplet.cpp | 150 ++++- src/gui/WfmApplet.h | 5 + src/main.cpp | 8 +- src/models/Ax25ReceiveModel.cpp | 396 ++++++++++++ src/models/Ax25ReceiveModel.h | 75 +++ src/models/DaxReceiveModel.cpp | 146 +++++ src/models/DaxReceiveModel.h | 34 ++ src/models/DecoderAudioModel.cpp | 22 +- src/models/DecoderAudioModel.h | 2 +- src/models/MeterModel.cpp | 19 +- src/models/MeterModel.h | 2 + src/models/RadioModel.cpp | 13 +- src/models/SliceModel.cpp | 30 +- src/models/SliceModel.h | 5 + tests/Ax25ReceiveFixture.h | 116 ++++ tests/ax25_libmodem_shim_test.cpp | 22 +- tests/ax25_receive_model_test.cpp | 562 ++++++++++++++++++ tests/control_receive_test.cpp | 6 +- tests/dax_receive_applet_test.cpp | 75 +++ tests/meter_model_test.cpp | 36 ++ tests/pipewire_rx_bridge_test.cpp | 103 ++++ tests/rtl_aprs_receive_test.cpp | 313 ++++++++++ tests/rtl_backend_test.cpp | 28 + tests/rtl_capture_worker_test.cpp | 275 ++++++++- tests/rtl_device_settings_test.cpp | 7 + tests/rtl_model_acceptance_test.cpp | 242 ++++++++ tests/rtl_receive_pipeline_test.cpp | 315 +++++++++- tests/rtl_receiver_settings_widget_test.cpp | 20 +- tests/rtl_runtime_settings_test.cpp | 32 + tests/rtl_spectrum_averaging_test.cpp | 37 ++ tests/rtl_squelch_test.cpp | 43 ++ tests/rtl_wfm_pipeline_test.cpp | 360 ++++++++++- .../rx_applet_squelch_reconciliation_test.cpp | 71 +++ tests/s_meter_geometry_test.cpp | 25 + tests/spectrum_confirmed_geometry_test.cpp | 1 + tests/squelch_level_scale_test.cpp | 12 +- tests/tci_rx_audio_test.cpp | 250 +++++++- tests/tests.cmake | 70 +++ tests/vfo_meter_overlay_invalidation_test.cpp | 189 ++++++ tests/wfm_controls_test.cpp | 198 ++++++ tools/check_rtl_package.py | 121 ++++ tools/test_check_rtl_package.py | 70 +++ tools/test_require_rtl.py | 17 + 126 files changed, 6873 insertions(+), 542 deletions(-) create mode 100644 cmake/AetherRequireRtl.cmake create mode 100644 docs/native-dax-receive.md create mode 100644 docs/rtl-multirx-analog.md create mode 100644 docs/rtl-release-packaging.md create mode 100644 scripts/setup/rtl-dependencies.json create mode 100644 scripts/setup/setup-rtl-deps.ps1 create mode 100644 scripts/setup/setup-rtl-deps.sh create mode 100644 src/core/backends/rtl/RtlStartupTrace.h create mode 100644 src/models/Ax25ReceiveModel.cpp create mode 100644 src/models/Ax25ReceiveModel.h create mode 100644 src/models/DaxReceiveModel.cpp create mode 100644 src/models/DaxReceiveModel.h create mode 100644 tests/Ax25ReceiveFixture.h create mode 100644 tests/ax25_receive_model_test.cpp create mode 100644 tests/dax_receive_applet_test.cpp create mode 100644 tests/pipewire_rx_bridge_test.cpp create mode 100644 tests/rtl_aprs_receive_test.cpp create mode 100644 tests/vfo_meter_overlay_invalidation_test.cpp create mode 100644 tools/check_rtl_package.py create mode 100644 tools/test_check_rtl_package.py create mode 100644 tools/test_require_rtl.py diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 075c776d3..858f28127 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -112,7 +112,7 @@ jobs: libgstreamer-plugins-bad1.0-0 \ libfuse2 file desktop-file-utils imagemagick \ python3-pip autoconf automake libtool libasound2-dev \ - libfftw3-dev libhidapi-dev portaudio19-dev \ + libfftw3-dev libudev-dev libhidapi-dev portaudio19-dev \ libsecret-1-dev # glslc — the SPIR-V shader compiler whisper's Vulkan backend needs at @@ -251,6 +251,9 @@ jobs: # pure Qt-D-Bus backend talks to KDE Wallet / GNOME Keyring at runtime. run: bash scripts/setup/setup-qtkeychain.sh + - name: Build pinned RTL dependencies (including float FFTW) + run: bash scripts/setup/setup-rtl-deps.sh + - name: Configure run: | # Without pipefail the step's exit status is tee's, so a cmake failure @@ -258,6 +261,7 @@ jobs: # (which also implies -eo pipefail) so the guard is self-contained and # survives anyone editing the step's shell. set -o pipefail + export PKG_CONFIG_PATH="$PWD/third_party/rtl-deps/lib/pkgconfig:${PKG_CONFIG_PATH:-}" # -g on top of Release's -O3: GCC's code generation does not depend # on it, so the shipped code is unchanged. The DWARF is split off # before packaging and attached to the release as its own archive @@ -266,6 +270,8 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ "-DCMAKE_C_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ "-DCMAKE_CXX_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ + -DENABLE_HD_FM=OFF \ + -DENABLE_RTL=ON -DREQUIRE_RTL=ON \ -DCMAKE_INSTALL_PREFIX=/usr \ -DREQUIRE_SERIALPORT=ON \ -DREQUIRE_KEYCHAIN=ON \ @@ -275,7 +281,7 @@ jobs: -DAETHER_GPU_SPECTRUM=${{ matrix.gpu_spectrum }} \ -DMQTT_TLS=OFF \ -DENABLE_NVIDIA_AFX=ON \ - -DCMAKE_PREFIX_PATH="$QT_ROOT" 2>&1 | tee configure.log + -DCMAKE_PREFIX_PATH="$PWD/third_party/rtl-deps;$QT_ROOT" 2>&1 | tee configure.log # AETHER_GPU_SPECTRUM=ON is a request, not a guarantee: CMake silently # flips it back OFF if Qt6GuiPrivate can't be located, and CMakeLists.txt @@ -321,6 +327,8 @@ jobs: convert AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png \ -resize 256x256 AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png + mkdir -p AppDir/usr/share/aethersdr + cp -R third_party/rtl-deps/share/aethersdr-rtl-sources AppDir/usr/share/aethersdr/ # Bundle DeepFilterNet3 model for DFNR if [ -f third_party/deepfilter/models/DeepFilterNet3_onnx.tar.gz ]; then cp third_party/deepfilter/models/DeepFilterNet3_onnx.tar.gz \ @@ -423,13 +431,14 @@ jobs: # libsherpa-onnx-c-api.so, and libonnxruntime.so (the app links sherpa's # copy). Qt6 DBus, which the keychain backend needs at runtime, is pulled # in transitively. - export LD_LIBRARY_PATH="${{ github.workspace }}/third_party/qtkeychain/lib:${{ github.workspace }}/third_party/onnxruntime/lib:${{ github.workspace }}/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}" + export LD_LIBRARY_PATH="${{ github.workspace }}/third_party/rtl-deps/lib:${{ github.workspace }}/third_party/qtkeychain/lib:${{ github.workspace }}/third_party/onnxruntime/lib:${{ github.workspace }}/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}" ./linuxdeploy-${{ matrix.arch }}.AppImage \ --appdir AppDir \ --plugin qt \ --desktop-file AppDir/usr/share/applications/AetherSDR.desktop \ --icon-file AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png \ --output appimage + python3 tools/check_rtl_package.py AppDir --platform linux # Nothing else in the build would notice these plugins going missing: Qt # dlopen()s them at runtime rather than linking them, so no dependency walk diff --git a/.github/workflows/macos-dmg.yml b/.github/workflows/macos-dmg.yml index ac7db8831..7c9c161ec 100644 --- a/.github/workflows/macos-dmg.yml +++ b/.github/workflows/macos-dmg.yml @@ -193,7 +193,7 @@ jobs: # bakes in that nothing downstream can correct, so a 14.0 tree must # never restore into a 12.0 build — that would silently recreate the # bug this change exists to remove. - key: macos-deps-${{ matrix.label }}-deploy${{ matrix.deploy_target }}-${{ hashFiles('scripts/setup/setup-macos-deps.sh') }} + key: macos-deps-${{ matrix.label }}-deploy${{ matrix.deploy_target }}-${{ hashFiles('scripts/setup/setup-macos-deps.sh', 'scripts/setup/setup-rtl-deps.sh', 'scripts/setup/rtl-dependencies.json') }} # ~8 min cold, then cached until the script or the target changes — the # same deal third_party/qtkeychain already has. That cost buys the DMG a @@ -371,6 +371,8 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ "-DCMAKE_C_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ "-DCMAKE_CXX_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ + -DENABLE_HD_FM=OFF \ + -DENABLE_RTL=ON -DREQUIRE_RTL=ON \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_OSX_DEPLOYMENT_TARGET="${{ matrix.deploy_target }}" \ @@ -487,6 +489,9 @@ jobs: "$DEPLOYQT" build/AetherSDR.app \ -verbose=1 \ -always-overwrite + mkdir -p build/AetherSDR.app/Contents/Resources + cp -R third_party/macos-deps/share/aethersdr-rtl-sources build/AetherSDR.app/Contents/Resources/ + python3 tools/check_rtl_package.py build/AetherSDR.app --platform macos # The artifact-side half of the pin. Everything upstream of here asserts # what we *asked* for; this asserts what the DMG actually carries, which @@ -667,6 +672,12 @@ jobs: echo "DAX driver PKG v${APP_VERSION} built and signed" + - name: Name DMG artifact + run: | + # Branch dispatches contain slashes; preserve release tag filenames. + SAFE_REF="${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}" + echo "DMG_FILENAME=AetherSDR-${SAFE_REF}-macOS-${{ matrix.label }}.dmg" >> "$GITHUB_ENV" + - name: Create DMG run: | mkdir -p dmg-stage @@ -692,7 +703,7 @@ jobs: --hide-extension "AetherSDR.app" \ --app-drop-link 450 120 \ --icon "AetherSDR DAX Drivers.pkg" 450 290 \ - "AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg" \ + "$DMG_FILENAME" \ "dmg-stage/" \ || true @@ -705,7 +716,7 @@ jobs: # no DMG on disk, and we surface that immediately instead of # letting Sign DMG fail confusingly one step later. See the v26.6.1 # release-build chain for the exact symptom this guard catches. - DMG="AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg" + DMG="$DMG_FILENAME" if [ ! -f "$DMG" ]; then echo "::error::create-dmg did not produce $DMG" exit 1 @@ -717,7 +728,7 @@ jobs: run: | codesign --force --timestamp \ --sign "Developer ID Application" \ - AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg + "$DMG_FILENAME" - name: Notarize DMG env: @@ -726,7 +737,7 @@ jobs: APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: | xcrun notarytool submit \ - AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg \ + "$DMG_FILENAME" \ --apple-id "$APPLE_ID" \ --password "$APPLE_APP_PASSWORD" \ --team-id "$APPLE_TEAM_ID" \ @@ -744,7 +755,7 @@ jobs: fi xcrun stapler staple \ - AetherSDR-${{ github.ref_name }}-macOS-${{ matrix.label }}.dmg + "$DMG_FILENAME" echo "Notarization and stapling complete" # Read the UUIDs back from the signed bundle the DMG was made from: the diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml index ca99577fa..6143b053f 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -177,6 +177,9 @@ jobs: with: python-version: "3.12" + - name: RTL release requirement combinations + run: python tools/test_require_rtl.py + # ── aetherd-migration ratchet (docs/aetherd-headless-engine-design.md §10) # EB1/EB2 — the engine (src/core/ + src/models/) must never depend on # the UI: no gui/ header includes, no QtWidgets. diff --git a/.github/workflows/windows-installer.yml b/.github/workflows/windows-installer.yml index 548c78a56..03654a683 100644 --- a/.github/workflows/windows-installer.yml +++ b/.github/workflows/windows-installer.yml @@ -159,12 +159,23 @@ jobs: id: cache-fftw with: path: third_party/fftw3 - key: fftw-${{ runner.os }}-${{ hashFiles('scripts/setup/setup-fftw.ps1') }} + key: fftw-${{ runner.os }}-${{ hashFiles('scripts/setup/setup-fftw.ps1', 'scripts/setup/rtl-dependencies.json') }} - name: Setup FFTW3 float (NR4 dependency) if: steps.cache-fftw.outputs.cache-hit != 'true' run: .\scripts\setup\setup-fftw.ps1 + - name: Cache RTL-SDR and libusb + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 + id: cache-rtl + with: + path: third_party/rtl-deps + key: rtl-${{ runner.os }}-${{ hashFiles('scripts/setup/setup-rtl-deps.ps1', 'scripts/setup/rtl-dependencies.json') }} + + - name: Setup RTL-SDR and libusb + if: steps.cache-rtl.outputs.cache-hit != 'true' + run: .\scripts\setup\setup-rtl-deps.ps1 + - name: Cache hidapi uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 id: cache-hidapi @@ -279,7 +290,7 @@ jobs: # native command), so a configure failure still fails this step — # the reason the bash legs need `set -o pipefail` and this one does # not. - cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log + cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log # AETHER_GPU_SPECTRUM=ON is a request, not a guarantee: CMake flips it # back OFF without failing when Qt6GuiPrivate can't be located, and @@ -430,6 +441,12 @@ jobs: Write-Host "Bundling ASR runtime DLL: $($_.Name)" Copy-Item $_.FullName deploy\ -Force } + Copy-Item third_party\rtl-deps\bin\*.dll deploy\ + New-Item -ItemType Directory -Force deploy\licenses\rtl | Out-Null + Copy-Item third_party\rtl-deps\share\aethersdr-rtl-sources deploy\licenses\rtl\ -Recurse + Copy-Item third_party\fftw3\share\aethersdr-rtl-sources deploy\licenses\fftw -Recurse + python tools/check_rtl_package.py deploy --platform windows + if ($LASTEXITCODE -ne 0) { throw "RTL payload validation failed" } # Vulkan loader for the whisper GPU backend. ggml-vulkan hard-links # vulkan-1, so the DLL must ship or the exe fails to start even on # CPU-only machines (the loader then enumerates zero devices and ggml diff --git a/.gitignore b/.gitignore index 4b538bf7c..54abc26c1 100644 --- a/.gitignore +++ b/.gitignore @@ -64,5 +64,10 @@ third_party/qtkeychain-build/ # never be tracked or show up as untracked noise in the parent checkout. .worktrees/ +# Built pinned RTL dependency prefix and intermediate files +third_party/rtl-deps/ +third_party/macos-deps/ +.rtl-deps-build/ + # macOS Finder metadata .DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fcaf0508..18cb20eca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,6 +165,7 @@ else() set(_aether_core_library_type STATIC) endif() option(ENABLE_RTL "Enable the experimental receive-only RTL-SDR backend" ON) +option(REQUIRE_RTL "Fail configuration unless the RTL backend and float FFTW are available" OFF) # Which SSB modulator the Hermes-Lite 2 transmit path is BUILT with. There is # no runtime switch and there must not be one: two silent transmit paths are # worse than one, and Hl2TxDsp.cpp records that the prior TXA attempt failed @@ -464,6 +465,10 @@ else() find_package(PkgConfig QUIET) if(PkgConfig_FOUND) pkg_check_modules(PORTAUDIO portaudio-2.0) + if(PORTAUDIO_FOUND) + # Preserve the resolved prefix; bare -l names can select Homebrew first. + set(PORTAUDIO_LIBRARIES ${PORTAUDIO_LINK_LIBRARIES}) + endif() endif() endif() @@ -482,6 +487,10 @@ if(WIN32) else() if(PkgConfig_FOUND) pkg_check_modules(FFTW3 fftw3) + if(FFTW3_FOUND) + # Preserve the resolved prefix; bare -l names can select Homebrew first. + set(FFTW3_LIBRARIES ${FFTW3_LINK_LIBRARIES}) + endif() endif() if(NOT FFTW3_FOUND) find_library(FFTW3_LIB fftw3) @@ -1040,6 +1049,7 @@ set(CORE_SOURCES src/core/TciIoWorker.cpp src/core/TciClient.h src/core/TciServer.cpp + src/models/DaxReceiveModel.cpp src/core/TciRxConverter.cpp src/core/TciPeerProcess.cpp src/core/TciProtocol.cpp @@ -1174,6 +1184,7 @@ endif() set(MODEL_SOURCES src/models/DecoderAudioModel.cpp + src/models/Ax25ReceiveModel.cpp src/models/AprsDigipeaterModel.cpp src/models/Nr2SettingsModel.cpp src/models/Rn2SettingsModel.cpp @@ -2743,6 +2754,9 @@ else() if(NOT HIDAPI_FOUND) pkg_check_modules(HIDAPI hidapi) endif() + if(HIDAPI_FOUND) + set(HIDAPI_LIBRARIES ${HIDAPI_LINK_LIBRARIES}) + endif() endif() endif() @@ -2756,8 +2770,8 @@ endif() # plain header+library probe so the switch below is reachable on every # platform, not only the pkg-config ones. if(ENABLE_RTL AND NOT RTLSDR_FOUND) - find_path(RTLSDR_INCLUDE_DIR rtl-sdr.h) - find_library(RTLSDR_LIBRARY NAMES rtlsdr librtlsdr) + find_path(RTLSDR_INCLUDE_DIR rtl-sdr.h HINTS "${CMAKE_SOURCE_DIR}/third_party/rtl-deps/include") + find_library(RTLSDR_LIBRARY NAMES rtlsdr librtlsdr HINTS "${CMAKE_SOURCE_DIR}/third_party/rtl-deps/lib") if(RTLSDR_INCLUDE_DIR AND RTLSDR_LIBRARY) add_library(RtlSdr::RtlSdr UNKNOWN IMPORTED) set_target_properties(RtlSdr::RtlSdr PROPERTIES @@ -2771,8 +2785,8 @@ else() endif() if(ENABLE_RTL AND NOT RTL_FFTW3F_FOUND) - find_path(RTL_FFTW3F_INCLUDE_DIR fftw3.h) - find_library(RTL_FFTW3F_LIBRARY NAMES fftw3f libfftw3f libfftw3f-3) + find_path(RTL_FFTW3F_INCLUDE_DIR fftw3.h HINTS "${CMAKE_SOURCE_DIR}/third_party/fftw3/include") + find_library(RTL_FFTW3F_LIBRARY NAMES fftw3f libfftw3f libfftw3f-3 HINTS "${CMAKE_SOURCE_DIR}/third_party/fftw3/lib") if(RTL_FFTW3F_INCLUDE_DIR AND RTL_FFTW3F_LIBRARY) add_library(RtlFftw3f::RtlFftw3f UNKNOWN IMPORTED) set_target_properties(RtlFftw3f::RtlFftw3f PROPERTIES @@ -2805,6 +2819,34 @@ else() message(STATUS "RTL-SDR ... disabled — librtlsdr not found") endif() +# The optional decoder is a separate feature proposal (RFC #6079). +# Reserve the explicitly OFF option name for the separate experimental Digital FM PR. +option(ENABLE_HD_FM "Build experimental embedded digital FM" OFF) +if(ENABLE_HD_FM) + message(FATAL_ERROR "Digital FM is not included in the analog RTL submission. See RFC #6079; configure with -DENABLE_HD_FM=OFF.") +endif() + +include(cmake/AetherRequireRtl.cmake) +aether_require_rtl() + +# Stage RTL independently of the optional NR4 and Digital FM features. +if(WIN32 AND AETHER_BACKEND_RTL) + foreach(_rtl_dll + "${CMAKE_SOURCE_DIR}/third_party/rtl-deps/bin/rtlsdr.dll" + "${CMAKE_SOURCE_DIR}/third_party/rtl-deps/bin/libusb-1.0.dll" + "${CMAKE_SOURCE_DIR}/third_party/fftw3/bin/libfftw3f-3.dll") + if(EXISTS "${_rtl_dll}") + foreach(_rtl_app AetherSDR aetherd) + add_custom_command(TARGET ${_rtl_app} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${_rtl_dll}" "$") + endforeach() + elseif(REQUIRE_RTL) + message(FATAL_ERROR "Required RTL runtime missing: ${_rtl_dll}; run scripts/setup/setup-rtl-deps.ps1 and setup-fftw.ps1") + endif() + endforeach() +endif() + # Ulanzi Dial backend — one per platform. All three implementations # expose the same Qt signal contract (see UlanziDialBackend.h); the # mapper dialog and MainWindow dispatcher are unchanged across @@ -3171,7 +3213,12 @@ if(ENABLE_SPECBLEACH) endif() endif() # libspecbleach uses fftwf (float precision FFTW3) - find_library(FFTW3F_LIB fftw3f HINTS /opt/homebrew/lib /usr/local/lib ${CMAKE_SOURCE_DIR}/third_party/fftw3/lib) + if(RTL_FFTW3F_TARGET AND TARGET "${RTL_FFTW3F_TARGET}") + # NR4 and RTL must use the same float FFTW, including its prefix. + set(FFTW3F_LIB "${RTL_FFTW3F_TARGET}") + else() + find_library(FFTW3F_LIB fftw3f HINTS /opt/homebrew/lib /usr/local/lib ${CMAKE_SOURCE_DIR}/third_party/fftw3/lib) + endif() if(FFTW3F_LIB) target_link_libraries(aethercore PRIVATE ${FFTW3F_LIB}) else() diff --git a/cmake/AetherRequireRtl.cmake b/cmake/AetherRequireRtl.cmake new file mode 100644 index 000000000..8a90ed983 --- /dev/null +++ b/cmake/AetherRequireRtl.cmake @@ -0,0 +1,6 @@ +# Release builds must not silently lose hardware support. Developer OFF is valid. +function(aether_require_rtl) + if(REQUIRE_RTL AND (NOT ENABLE_RTL OR NOT RTLSDR_FOUND OR NOT RTL_FFTW3F_FOUND)) + message(FATAL_ERROR "REQUIRE_RTL needs ENABLE_RTL=ON, librtlsdr and float FFTW (fftw3f). Run the platform dependency setup; use REQUIRE_RTL=OFF only for intentional developer builds.") + endif() +endfunction() diff --git a/docs/architecture/aetherd-touchpoint-tags.json b/docs/architecture/aetherd-touchpoint-tags.json index 024943b02..72fa70941 100644 --- a/docs/architecture/aetherd-touchpoint-tags.json +++ b/docs/architecture/aetherd-touchpoint-tags.json @@ -995,6 +995,12 @@ "split": "", "confidence": "high" }, + "models/DaxReceiveModel.h": { + "tag": "universal", + "note": "Owner-thread adapter from typed native receive PCM and stable TCI receiver bindings to the existing 24 kHz virtual audio bridges; no radio wire protocol.", + "split": "", + "confidence": "high" + }, "models/MeterModel.h": { "tag": "mixed(flex)", "note": "Meter registry+values are core-profile; VITA-49 raw scaling, TGXL/PGXL amp routing, COMPPEAK quirks are Flex", @@ -1547,6 +1553,12 @@ "split": "Core: receiver selection, epochs, bounded backlog, rate conversion and decoder resets. Flex compatibility: DAX channel selection and central consumer holds; no wire commands are generated here.", "confidence": "high" }, + "models/Ax25ReceiveModel.h": { + "tag": "universal", + "note": "Selected AX.25 receive model over the existing typed decoder route; continuous mono24 conversion and bounded modem worker results retain source leases and generation retirement. Existing transmit ownership is separate.", + "split": "", + "confidence": "high" + }, "models/ReceiverSlotCount.h": { "tag": "universal", "note": "How many receiver letters (A, B, C, ...) the UI offers — the RX applet's slice tabs and the CAT applet's VFO targets — tracked from the connection/capability/slice edges. Family-agnostic: the backend's declared ceiling, floored by the slots live receivers occupy (#5775, #5776).", diff --git a/docs/architecture/aetherd-touchpoints.md b/docs/architecture/aetherd-touchpoints.md index 671a5b95b..d68694e25 100644 --- a/docs/architecture/aetherd-touchpoints.md +++ b/docs/architecture/aetherd-touchpoints.md @@ -4,7 +4,7 @@ Burndown manifest for the engine/UI decoupling ([RFC](../aetherd-headless-engine-design.md) §2, §10). One row per engine header the UI includes; converting a touchpoint means the UI reaches that surface through the versioned protocol instead of the header. -**Totals:** 251 touchpoint headers (212 core, 39 models) — 251/251 tagged, 0/251 converted. +**Totals:** 252 touchpoint headers (211 core, 41 models) — 252/252 tagged, 0/252 converted. | Header | Includers | Tag | Status | |---|---:|---|---| @@ -160,7 +160,7 @@ Burndown manifest for the engine/UI decoupling ([RFC](../aetherd-headless-engine | `core/TailnetShimTokenStore.h` | 1 | peripheral(tailnet-shim) — OS-keychain storage of the tailnet shim's admin token, keyed by the Flex chassis serial (RFC #6271). Not SmartSDR wire: AetherSDR's own HTTP API to a separate service (the shim) that happens to run in the radio's container runtime, so a direct-transport peripheral, NOT behind the IRadioBackend radio seam (same shape as core/PgxlConnection.h). | unconverted | | `core/TciServer.h` | 3 | mixed(flex) — TCI WebSocket server for WSJT-X et al: protocol surface is canonical radio state, but audio/IQ rides Flex DAX | unconverted | | `core/TgxlConnection.h` | 3 | peripheral(4o3a) — Direct TCP client for the 4O3A Tuner Genius XL (port 9010, relay/autotune), reverse-engineered from the 4O3A management app — a standalone accessory transport, not SmartSDR. Not radio-family wire; a peripheral accessory, NOT behind the IRadioBackend radio seam (reclassified from vendor(flex), #4087 follow-up). | unconverted | -| `core/ThemeManager.h` | 169 | ui-support — Qt token-based theming singleton (colors/fonts/QSS, theme files, editor hooks) — pure client GUI plumbing, no radio state. | unconverted | +| `core/ThemeManager.h` | 170 | ui-support — Qt token-based theming singleton (colors/fonts/QSS, theme files, editor hooks) — pure client GUI plumbing, no radio state. | unconverted | | `core/ThreadCpuRing.h` | 2 | ui-support — Short host-thread CPU history used by Runtime Monitor peak and sparkline presentation. Diagnostic UI support, not radio state. | unconverted | | `core/TimeFrameVoter.h` | 1 | universal — Shared AetherClock time-frame types plus confidence-weighted cross-frame bit voting over a sliding window. Map-agnostic pure DSP/logic — no Qt, no GUI, no vendor ties. | unconverted | | `core/TxKeyingMarker.h` | 15 | ui-support — QWidget property marker guarding TX-keying controls from the automation bridge; GUI-shell plumbing, no radio state. | unconverted | @@ -211,7 +211,6 @@ Burndown manifest for the engine/UI decoupling ([RFC](../aetherd-headless-engine | `core/backends/sim/NoiseMixer.h` | 1 | vendor(sim) — Demo-mode synthetic AUDIO engine (RFC #4288 Phase 2b) — additive HF-noise and signal channels feeding SimBackend's audioFrameReady. Backend-internal generator reached from DemoApplet.cpp; family-specific below-seam code, frozen into the EB3 baseline. Demo-mode controls should reach it through an extension namespace, not the concrete type. | unconverted | | `core/backends/sim/SimBackend.h` | 3 | vendor(sim) — The synthetic demo backend (#4473) — a concrete IRadioBackend implementor that generates its own audio and spectrum, RX-only by construction (Principle VI). Reached today from ConnectionPanel.cpp, MainWindow.cpp and MainWindow_Session.cpp; pre-existing coupling, frozen into the EB3 baseline. The UI should select it through RadioModel::makeBackend()'s family string, never the concrete type. | unconverted | | `core/pms/PmsMailbox.h` | 1 | universal — Packet personal-message-system mailbox store/logic; radio-agnostic operating feature. | unconverted | -| `core/tnc/AetherAx25LibmodemShim.h` | 1 | universal — AX.25 modem shim bridging the client AFSK/libmodem demod to the TNC; radio-agnostic DSP glue. | unconverted | | `core/tnc/Ax25.h` | 1 | universal — AX.25 frame data types/constants; radio-agnostic protocol layer. | unconverted | | `core/tnc/Ax25AudioCapture.h` | 1 | universal — AX.25 Capture 3m diagnostics: capture-id/path naming and the shared float32 WAV writer for the RX, generated-TX and post-resample stages; radio-agnostic diagnostic I/O (#5311). | unconverted | | `core/tnc/Ax25Connection.h` | 1 | universal — Single-peer AX.25 v2.0 connected-mode (LAPB) data-link state machine, mod-8 sequence space, backing the Personal Mailbox System. Radio-agnostic packet layer above whatever modem or backend carries the audio. | unconverted | @@ -224,6 +223,7 @@ Burndown manifest for the engine/UI decoupling ([RFC](../aetherd-headless-engine | `models/AmpModel.h` | 2 | mixed(flex) — Power-amplifier state model (PGXL / any non-TGXL amp the radio proxies), extracted from RadioModel (#4094). Like TunerModel: universal amp state (presence/operate/telemetry) fused with a Flex relay — the radio-proxied 'amplifier set … operate=' command (the only path that works remote/SmartLink; the direct PgxlConnection is telemetry-only). | unconverted | | `models/AntennaGeniusModel.h` | 5 | peripheral(4o3a) — 4O3A Antenna Genius switch client — standalone accessory with its own UDP-broadcast discovery (port 9007) + direct TCP; connects by device IP/port independent of the radio, works with any radio. Not radio-family wire; a peripheral accessory, NOT behind the IRadioBackend radio seam (reclassified from vendor(flex), #4087 follow-up). | unconverted | | `models/AprsDigipeaterModel.h` | 1 | universal — Session-local APRS fill-in policy and bounded modem queue; no backend-specific commands or transport ownership. | unconverted | +| `models/Ax25ReceiveModel.h` | 1 | universal — Selected AX.25 receive model over the existing typed decoder route; continuous mono24 conversion and bounded modem worker results retain source leases and generation retirement. Existing transmit ownership is separate. | unconverted | | `models/BandDefs.h` | 5 | universal — Static ARRL band plan table (edges, default freq/mode, GEN/WWV); canonical band-plan data, no vendor ties. | unconverted | | `models/BandPlanManager.h` | 9 | universal — Band-plan overlay data (segments/spots/license classes, region merge) from JSON; radio-agnostic canon | unconverted | | `models/BandSettings.h` | 6 | universal — Per-band save/restore of canonical state (freq/mode/filter/AGC/WNB/display range) — band memories, no vendor fields | unconverted | @@ -233,6 +233,7 @@ Burndown manifest for the engine/UI decoupling ([RFC](../aetherd-headless-engine | `models/CwxModel.h` | 1 | universal — CW keyer intent: WPM/delay/QSK, 12 macros, send/erase, sent-index progress. Generic despite Flex 'CWX' name. | unconverted | | `models/DStarModel.h` | 1 | universal — D-STAR configuration and session state (callsigns, RPT1/RPT2 routing, modem/serial paths). Digital-voice state any backend carrying DV would present. | unconverted | | `models/DaxIqModel.h` | 1 | vendor(flex) — Flex DAX IQ streams: dax_iq stream create/rate cmds, 4-ch DAX model, pipes to SDR apps — DAX is Flex-only | unconverted | +| `models/DaxReceiveModel.h` | 1 | universal — Owner-thread adapter from typed native receive PCM and stable TCI receiver bindings to the existing 24 kHz virtual audio bridges; no radio wire protocol. | unconverted | | `models/DecoderAudioModel.h` | 1 | mixed(flex) — Selected-receiver fixed24 decoder PCM and bounded delivery, independent of speaker controls. Native ingress uses normalized typed slice frames; the compatibility DAX lane uses RadioModel's existing stream and holder registry. | unconverted | | `models/DigitalVoiceWaveformHistory.h` | 1 | mixed(flex) — Rolling health history for the DV waveform path (sample rate, turnaround, deficits). Generic rate/latency telemetry fused with Flex VITA sequence-gap counters. | unconverted | | `models/DvkModel.h` | 1 | mixed(flex) — Voice keyer slots/commands are core-profile; status parsing + FlexLib SsdrErrors mapping are flex. | unconverted | diff --git a/docs/architecture/radio-capabilities-map.md b/docs/architecture/radio-capabilities-map.md index b96eb75bd..7b450308a 100644 --- a/docs/architecture/radio-capabilities-map.md +++ b/docs/architecture/radio-capabilities-map.md @@ -74,18 +74,36 @@ are in [local receive control](../aetherd-local-receive-control.md#qualified-bac `control_receive_test` pins declarations and action-time admission; the optional RTL declaration check runs only when the RTL backend is built. +### Native receive audio export + +`receiveAudioExport` describes the typed pre-monitor `sliceAudioFrameReady` +formats and independent receiver capacity. RTL declares 24/48 kHz and, when its +Digital decoder is built, 44.1 kHz. The other backends explicitly leave it absent. +`DaxReceiveModel` admits only declared rates and live TCI receiver bindings; +MainWindow offers the existing DAX audio bridge separately from radio-side DAX +selectors and IQ. This record grants no TX or headless-control permission. +Native DAX currently requires the optional WebSockets build component for the +shared receiver map, but neither a listening TCI server nor an attached TCI +client. Builds without it retain their existing Flex bridge and do not offer +native DAX. See [native DAX receive routing](../native-dax-receive.md). + ### Native squelch -`squelchLevelScale` with `modesExclusive` set is the receiver's own squelch, -not only a place for its pan line: SQL exists in its `modes` and nowhere else, -the client keeps each receiver's manual/Auto intent (`ReceiveSquelchIntent-`), -and Auto starts from the absolute threshold. RTL declares FM/FM-N in dBFS/bin -(`unit`); RxApplet and VfoWidget gate unsupported modes with accessible reasons, -and SpectrumWidget draws the line and runs Auto from the same record. The native -`setSliceSquelch` verb applies through RTL's confirmed transaction. Every other -family leaves `modesExclusive` false, so the #2504 mode rule and radio-owned -squelch state are unchanged. The flag grants no headless squelch verb or -calibrated-power claim. +`squelchLevelScale` adds `automaticInEngine` for backend-owned Auto SQL, dispatched +through `requestSliceSquelch` with an Automatic intent. RTL alone sets it; other +families retain manual dispatch and their existing client Auto behavior. RTL +sets `spectrumComparable=false` because its 2048-bin detector and pan display +have different measurement bandwidths, so no display SQL line or display-driven +Auto loop is used. The manual threshold uses dBFS/2048-bin. + +`squelchLevelScale` with `modesExclusive` set is the receiver's own squelch: +SQL exists in its `modes` and nowhere else. RTL declares FM/FM-N and owns +manual/Auto state in its accepted receiver configuration (`RtlSlices`); +`RxApplet` and `VfoWidget` keep unsupported modes dimmed with reasons. The +existing `requestSliceSquelch` path carries both manual and Automatic requests +through the confirmed transaction. Other families leave `modesExclusive` and +`automaticInEngine` false, preserving the #2504 mode rule and radio-owned state. +The record grants no headless squelch verb or calibrated-power claim. ### Receive capture placement @@ -568,7 +586,7 @@ and conflating them would hide one of them. | `squelchLevelScale` | −160 + 1·L, all modes, Auto SQL | −119 + 0.7·L, AM/SAM/DSB/LSB/USB, no Auto SQL | IC-7300MK2: −194.8 + 1.49·L on the pan, USB/LSB/CW/CWU/CWL/AM/DIGU/DIGL, no Auto SQL (measured, #6180); Flex's for every other Icom | Flex's (no measured map) | Where does squelch level L open, on this axis? | **`squelchLevelScale` draws the SQL line and drives Auto SQL** (#6092). -The demo keeps Flex's scale. RTL-SDR publishes its own gate, −120 + 1.2·L dBFS/bin in FM/FM-N with Auto SQL and `modesExclusive` (see Native squelch above): its detector reads the same FFT as the pan, so a pan-derived floor lands on the gate. Absent, or the active slice's mode not listed: no line, and the SQL button +The demo keeps Flex's scale. RTL-SDR publishes its own gate, −120 + 1.2·L dBFS/2048-bin in FM/FM-N, with engine-owned Auto and no display line (see Native squelch above). For display-driven Auto, an absent record or an unlisted mode means no line, and the SQL button skips Auto with the reason on its accessible description. The HL2 offset is amsq's −140 + 0.7·L dBFS map, referred to the LNA at −12 dB (`Hl2DbReference::levelSquelchOffsetDb`), plus the pan's LNA offset, plus the diff --git a/docs/decoder-pcm-contract.md b/docs/decoder-pcm-contract.md index 406772ac4..c0e1a2e24 100644 --- a/docs/decoder-pcm-contract.md +++ b/docs/decoder-pcm-contract.md @@ -41,7 +41,7 @@ slice alone: the central hold requests the radio stream, and speaker mix, gain and mute do not reach it. It does not require the operating-system DAX audio bridge or PC-audio monitoring. -With no channel assigned, CW (both GGMorse and DeepFist) and RTTY fall back to +With no channel assigned, CW (both GGMorse and DeepFist), RTTY and AX.25 fall back to the radio's shared receive stream — the pre-A5 decoder input. That stream mixes every audible slice and follows speaker gain and mute, so other slices can interfere and muting stops decoding; the panel discloses it as `RX: shared @@ -85,6 +85,41 @@ TX sidetone remains a separate fixed24 GGMorse instance. Both decoders' legacy byte inputs retain the existing trim-oldest backlog behavior on overflow; typed RX overflow retires detector state instead. +## AX.25 receive worker + +`Ax25ReceiveModel` binds the same selected-source policy with its own AX.25 DAX +hold and owns the existing modem worker previously hosted by the dialog. Native +RTL FM at 48 kHz is converted continuously to mono24 before the unchanged modem +rate guard. HL2/Icom mono24 conversion and Flex DAX/shared fallback retain their +existing producer contracts. No sound device or PC-audio monitoring is required +for a native or assigned-DAX route. Receive squelch remains upstream of the tap. +A producer-declared gap or discontinuity still resets decoding, including one +associated with a capture revision during monitor-control changes; this does not +promise uninterrupted packet recovery across such a boundary. + +The worker inbox is bounded to 65,536 mono samples and 256 blocks; the owner +outbox is bounded to 256 output blocks, with one pending drain event for each queue. +Overflow retires the generation, drops the backlog and resets detector state. +Input/configuration/reset operations and output publication share generation +retirement. Results retain the original source lease and generation through +worker callbacks; they cannot acquire a newer context after decoding. + +`Ax25ReceiveContext::current()` is checked before every result and each dialog +fanout operation, including display, MQTT, KISS and existing packet consumers. +A synchronous listener that disables, rebinds or destroys the model makes the +remaining consumers reject that result. Identical configuration and diagnostics +logging changes preserve receive continuity. Changed configuration, reset, +selection, disconnect and revoked sources retire queued PCM and decoded results. +The receive model owns no transmit API or external service; existing packet/TX +policy is unchanged. Closing it releases only its own DAX hold and joins its worker. + +The focused model test covers exact independent AX.25/FCS recovery from 24/48 kHz +mono/stereo, awkward chunk boundaries, route isolation, DAX coexistence and +retirement after worker decode but before owner delivery. The RTL test injects +independently generated CU8 FM into real device callbacks and traverses the +production RTL worker, native 48 kHz selected-slice seam, converter and modem. +Both are offline receive proofs, not hardware, antenna or on-air qualification. + ## Clock time mapping Decoder sample indices always count local 24 kHz mono frames. Let `O` be the diff --git a/docs/native-dax-receive.md b/docs/native-dax-receive.md new file mode 100644 index 000000000..ff4dcf202 --- /dev/null +++ b/docs/native-dax-receive.md @@ -0,0 +1,56 @@ +# Native receive audio through the existing DAX bridge + +On Linux and macOS builds with WebSockets support, a backend declaring +`receiveAudioExport` can feed the existing DAX audio inputs. RTL supplies this +record for its actual typed slice formats. Its configured receiver capacity +still applies; the private eight-receiver evaluation does not alter ordinary +release capacity. Other backends retain their existing behavior. + +Enable the DAX audio applet. Input 1 follows TCI receiver 0, input 2 follows +receiver 1, and so on through input 8/receiver 7. The live TCI receiver map supplies +both paths; sparse internal slice IDs are never interpreted as channel numbers. +Closing/recreating a slice preserves surviving assignments. The applet shows +the slice attached to each input. The TCI listener can be disabled or stopped; +DAX does not require a network client. Builds without the optional WebSockets +component do not offer native DAX because they lack that shared map. + +The adapter accepts owning pre-monitor slice frames, so speaker mute and +speaker gain do not change exported audio. Per-receiver squelch still gates the +slice tap. DAX and TCI gains and consumer lifetimes are independent. DAX stop +never stops TCI or tears down a channel held by another consumer. + +The existing macOS shared-memory ABI is float32 stereo at 24 kHz. Native 24, 44.1 +and 48 kHz are converted continuously to that format; their sample rates are +never relabeled. Linux's existing bridge deliberately downmixes that stereo +signal and emits float32 mono at 48 kHz. Linux DAX therefore does not provide +stereo WFM, while macOS DAX and stereo TCI retain separate left/right samples. +44.1 kHz is an internal Digital decoder rate; TCI negotiation remains 8/12/24/48 kHz. + +Each route has independent converter history, an epoch witness, a replay +cursor, and a revocable receiver binding. The adapter has eight channel slots, +a 32-producer bound, output blocks at most 1024 stereo frames, and no PCM queue. +A live old producer cannot seize a newly created receiver's route. Epoch/rate +changes, discontinuities, forward gaps, removal, stop and backend replacement +reset conversion and retained bridge output. The Linux RX-only bridge buffers +at most 65536 bytes (~341 ms at mono 48 kHz) to accommodate decoder bursts; the +legacy packet bridge retains its smaller capacity. Writes stay nonblocking. +The macOS bridge retains its existing bounded live-edge clamp. + +Receive-only startup creates only RX endpoints: no TX sink/shared-memory +segment or TX poller, no TX producer, and no mic-selection/DAX-TX command. +The TX control is dimmed with an accessible explanation. Native audio export +does not advertise radio-side DAX stream selectors or DAX-IQ support. + +A reset can discard samples retained by AetherSDR or its source FIFO/ring. +Samples already accepted by an external audio client cannot be recalled. +The updated macOS HAL reader uses a cursor compare/exchange so a simultaneous +route reset cannot be overwritten by an older callback. Older installed +four-input drivers must be updated separately before all-eight device testing; +application compilation alone does not validate that installed driver. + +Socket-free checks exercise eight sparse receivers with eight concurrent TCI +clients, independent rates and stereo signatures, mute, stop/restart, listener +stop, epoch changes, live-producer refusal, removal/recreate and callback +revocation. The Linux bridge test injects anonymous output pipes and exercises +all eight downmix/gain paths plus queue/interpolator reset. Hardware, operating +system devices, timing and installed-driver qualification are separate runs. diff --git a/docs/rtl-m1-runtime.md b/docs/rtl-m1-runtime.md index 60a3ab6d8..7768778f5 100644 --- a/docs/rtl-m1-runtime.md +++ b/docs/rtl-m1-runtime.md @@ -633,3 +633,48 @@ two close peaks, asymmetric/outside capture limits, queued stale deliveries, reentrant disconnect, history movement/wrapping, and per-retune RF frames. Native GPU presentation, bounded performance observations and receive liveness are recorded separately for the exact delivered revision in the stage report. + +### Relative receive metering + +RTL publishes one `SLC:LEVEL` meter per configured receiver, with +unit `dBFS`. It reports the strongest windowed FFT bin intersecting that +receiver's accepted passband, using the existing unaveraged 2048-bin ADC +spectrum detector (about 30 observations/second). This is an uncalibrated +relative RF measurement, not dBm, S-units, integrated channel power, or audio +peak/RMS. Tuner gain and DC content affect it; no guessed RF calibration offset +is applied. Display FFT averaging, speaker mute/gain and SQL do not change its +measurement path. + +The acquisition mailbox is bounded and carries the accepted session/revision; +observations older than 500 ms are discarded at delivery. Every accepted +capture revision withdraws the prior samples before defining current meters, +including parked receivers. Parked/removed/disconnected or unfed receivers +therefore cannot retain a live reading. The UI checks the model's existing +freshness window at 100 ms intervals and displays unavailable when unfed or +stale. Relative readings never enter the calibrated `sLevelChanged` signal or +`sLevelForSlice` accessor. Slice bars, SmartMTR and the selected analog meter +use a labeled -120..0 dBFS scale; other radios retain their calibrated scale. + + +### Private evaluation: optional receive meters + +RTL Receiver settings exposes **Enable receive meters**, default enabled. +Its accepted boolean is stored as `receiveMetersEnabled` in the same per-serial +`RtlDevice` schema-1 document; missing fields in older documents mean enabled. +The `rtl/receive_meters.set` extension requires a boolean and a connected, +accepted receiver state. `receiveMetersControl` advertises that verb. + +Disabling stops the independent RF-passband measurement/publication, clears +cached readings, and stops relative-meter animation. The existing typed meter +catalogue carries an unavailable reason so both the VFO and selected analog +meter identify the disabled state instead of freezing a reading. A generation +stamp rejects queued/in-flight readings across disable/re-enable. The shared +spectrum needed for squelch and the receiver/audio paths remain active. This +control does not retune or rebuild the capture graph. Enabling requires a fresh +observation before any meter becomes live. Settings are unchanged on other +radio families, and anonymous or mismatched serials remain session-only. + +This is an interim operator choice for the private eight-receiver evaluation. +The enabled display still has measurable CPU overhead; bounded redraw fixes +alone did not recover the earlier pre-meter baseline. Exact performance and +hardware-validation evidence belongs to the sealed evaluation package. diff --git a/docs/rtl-multirx-analog.md b/docs/rtl-multirx-analog.md new file mode 100644 index 000000000..eb6e79109 --- /dev/null +++ b/docs/rtl-multirx-analog.md @@ -0,0 +1,156 @@ +# RTL analog multi-receiver evaluation + +This checkpoint implements the analog multi-receiver path from RFC #5468. +It does not raise the qualified default capacity, enable multiple digital decoders, +or establish release qualification. The free-pan amendment remains subject to maintainer review. Optional +Digital FM is separate RFC #6079 and is absent from this analog branch. Native qualification results belong to the exact +tested revision; component or injected-USB tests do not establish live RF proof. + +## Evaluation admission + +On Linux x86_64, Windows x64 and macOS arm64, start an explicitly isolated evaluation process with +`AETHER_AUTOMATION=1` and `AETHER_RTL_EVALUATION_RECEIVERS=2`, `4` or `8`. +This chooses the same capacity for the transaction and private worker at +construction. It cannot resize a running session, is never persisted, and +does not change `kQualifiedReceiverCapacity` (one). Every other value or +platform retains one. The automation bridge retains its independent receive +authorization and TX exclusion. Use a separate `AETHER_SETTINGS_DIR` and +preserve the normal launcher/profile before hardware testing. + +FM, FM-N and native analog WFM can coexist. Each receiver owns its exact RF +filter, Mono/Stereo choice, deemphasis and monitor gain/pan/mute. AM/SAM/SSB/CW remain singleton legacy configurations, including when a +configured sibling is parked. A refusal reports the unsupported combination +without adopting a partial recipe. Digital FM is outside this branch. + +## Selection and presentation + +RTL owns the selected stable ID separately from receiving membership. Focus +does not prepare DSP, retune capture or change any audio setting. An accepted +capture update retains a surviving selected receiver, including when parked. +Removal chooses the first surviving configured receiver; slot reuse does not +inherit the retired object's selection. Confirmed-control models do not emit +optimistic focus before the owner accepts their live-object identity. + +One WFM applet shows the selected receiver's letter and accepted frequency. +Queued status callbacks and controls retain their originating model binding; +an old popup or held button cannot command a new selection or reused slot. +Parking clears reception observations while retaining configured controls. +Digital station/program metadata and overlays are deferred with RFC #6079. + +## Measurements and interpretation + +The existing health snapshot exposes the qualified default and evaluation +capacities separately. Bounded lifetime counters add USB read starts/cancels, +valid/malformed callback counts, IQ sample counts, callback duration histogram +and exact maximum, queue occupancy/high-water, and per-slot processing-fault +withdrawals. Duration covers callback adoption/DDC/FFT/DSP work, excluding its +own counter update and USB transit time. A callback exceeding its actual +sample duration increments a separate deadline counter. Histogram percentile +bounds are conservative; the final bucket is unbounded. + +`rtlReceiverRecipes` reports each last-published accepted receiver recipe, +including its `stableId`, current `selected` identity, `receiving`/`parked` +membership, `carrierHz`, `mode`, `filterLowHz`/`filterHighHz`, `wfmForceMono`, +`wfmDeemphasisUs`, and `audioGain`/`audioPan`/`audioMute`. +An observer can freeze every receiver's accepted settings without moving +selection to inspect an applet. These are accepted control recipes, not an +independent measurement of the demodulated signal or observed stereo lock. + +`rtlPcmStreams` contains eight stable receiver slots followed by the speaker +mix. Frames, nonzero samples, nonfinite input samples, delivered discontinuities, +left/right energy, peak, queue residence and delivery gaps are cumulative +owner-thread observations. Queue residence uses a separate enqueue clock. Counts and +energies can be differenced across a frozen workload. Lifetime maxima and +delivery gaps can include intentional parking/reconfiguration; they must not +be called steady-state dropouts without checking that interval. Finite, +nonzero PCM establishes processing, not intelligibility or acoustic fidelity. + +Counters are sampled independently, not one atomic instant. An empty +application audio queue is not a hardware XRUN counter. The RTL driver supplies +no sequence counter proving that every RF sample reached the host. Distinct +FM/WFM paths align their sample labels for mixing but do not claim phase-coherent +inter-station acoustic group delay. + +## Qualification boundary + +Use socket-free injected device/model tests plus real generated-IQ demodulation +for selection, stable-ID reuse, restore, stereo/deemphasis/filter independence, +monitor isolation, parking, shared capture refusal/rollback and stale delivery. +Retain the existing mixer deadlines and bounded queues. Before claiming live +capacity, freeze representative one-, two- and four-receiver workloads and +measure actual USB, FFT/waterfall and nonzero audio for at least 30 minutes +each, including CPU/thermal headroom, callback latency, queue growth, saturated +history RSS, planning and teardown. Report failures without changing their +thresholds after observing the result. Linux x86_64 evidence does not qualify +Linux aarch64, macOS, Windows, mixed legacy/native rates or multiple digital workers. + +## Startup delivery headroom + +The packet ring is allocated once from the immutable process receiver capacity. +Its usable slots are 127, 170, 254 and 424 for one, two, four and eight receivers; +two, four and eight are exposed by the process evaluation setting. Native WFM +produces one 256-frame tap per receiver plus a shared 128-frame speaker stream +at 48 kHz. Scaling by that packet rate preserves at least the original one-RX +nominal 225.8 ms buffering time. The qualified one-RX queue stays at 128 allocated +slots, including its empty sentinel. No allocation occurs during acquisition. + +A four-WFM startup trace showed 151 ms spent synchronously publishing the bank, +with all 53 observed packet drops inside that span. The former shared 127-slot +usable ring covered only 112.9 ms at four WFM receivers. The scaled ring retains +publication order and the existing 128-packet per-service drain bound; it does +not shorten UI work or tolerate sustained overload. A process admitted for four +receivers retains its larger ring even with fewer receivers currently active, +so it can hold more queued latency in that state. `rtlPacketQueueCapacity` +reports actual usable slots. Occupancy is sampled current backlog; drops and +high-water remain lifetime observations. +Old 127-packet high-water screening results remain historical comparisons and +must not be described as the current ring's full condition. + +## Eight-receiver requirement and SQL correction + +The operator requested eight on Nobara after observing approximately 3% laptop +CPU with four. That observation is not an eight-receiver capacity result. +This is an isolated evaluation amendment for maintainer review under RFC #5468, +not approval to raise the qualified one-receiver default or other radio limits. +The existing eight slots, PCM routes, mixer membership, queue sizing and stable +IDs are reused; no thread or dependency is added. Evaluate the representative +operator workload and report actual continuity faults and timing/thermal data +separately; a historical zero-deadline-miss screen is not an evaluation admission +rule. Public release qualification remains separate. + +RTL FM/FM-N manual SQL retains its absolute -120 + 1.2 * level scale, in dBFS +per 2048-point Blackman-Harris detector bin. For example, levels 51 and 54 mean +-58.8 and -55.2 dBFS/detector-bin. It is neither calibrated antenna dBm nor a +noise-relative percentage. Tuner gain, bandwidth, in-band interference and +peak/hang behavior influence the threshold required to close audio. There is +no guessed RF offset or changed default/manual threshold in this correction. +The temporary SQL overlay is suppressed when detector/display bandwidths differ; +the visible SQL controls describe the detector units instead of plotting a +misleading absolute threshold on a different scale. + +The display changed to 65536 bins while SQL detection stayed at 2048. Both +normalize by FFT length; white-noise bin power therefore differs by about +15.05 dB, so display Auto thresholds are not detector thresholds. Additionally, +a trimmed logarithmic floor is biased low relative to noise peaks. RTL Auto now +runs inside each acquisition-owned gate on the same detector spectrum as its +peak measurement. It excludes the selected receiver passband plus window leakage, +uses the median of neighboring bin powers (with the exponential-noise median to +mean correction), and smooths locally. It retains the existing ramp, hang and +stale-data closure. It remains a coarse signal-level gate, not a discriminator +noise squelch or calibrated channel-power measurement. + +The backend accepts a typed Auto intent and publishes its adopted per-slice +state. The engine owns Auto/margin alongside the manual threshold in RtlSlices; +manual/Off intent exits Auto. Selection, display zoom, FFT averaging and a hidden +applet do not control adaptation. WFM retains its separate decoder behavior; +the FM/FM-N manual/Auto gate is unavailable there. Schema-1 SQL objects now have +optional `automatic` (default false) and `marginDb` (default 10, range 5–20). +Legacy display-Auto intent rows are preserved but do not silently enable the +new engine policy; explicitly choose Auto after upgrading. The prior manual +threshold is never replaced with a computed Auto threshold by the new engine. + +TCI already consumes the typed per-slice 48 kHz PCM route for RTL. This is a +source-level route audit, not eight-client throughput or RadioReference service +qualification. RTL does not advertise Flex DAX streams; its virtual DAX and +DAX-IQ paths are not supplied by this change. RadioReference/Broadcastify +publishing still requires a separately validated external streaming workflow. diff --git a/docs/rtl-release-packaging.md b/docs/rtl-release-packaging.md new file mode 100644 index 000000000..624fa71e2 --- /dev/null +++ b/docs/rtl-release-packaging.md @@ -0,0 +1,34 @@ +# RTL release dependency contract + +Official AppImage (both architectures), macOS DMG (both architectures), and +Windows installer/portable/MSIX pipelines configure `ENABLE_RTL=ON` and +`REQUIRE_RTL=ON`. Missing librtlsdr or float FFTW is a configure error. An +intentional developer-only build can still set `ENABLE_RTL=OFF` with the +requirement left OFF. `ENABLE_HD_FM` remains OFF by default and in official +release workflows. This analog-only branch rejects ON explicitly; the separate +Digital evaluation branch and RFC #6079 own opt-in decoder builds. + +`scripts/setup/rtl-dependencies.json` pins RTL-SDR Blog (the existing qualified +Blog V4 driver variant), libusb, and FFTW sources by URL and SHA256. Unix setup +builds shared RTL/libusb; AppImage also builds float FFTW. macOS builds double +and float FFTW at its declared deployment floor. Windows builds RTL/libusb +with MSVC and prepares the official double/float FFTW DLLs and import libs. +The pthreads source/build is a Windows upstream configure prerequisite; it is +not an imported runtime of the library-only RTL target. + +Setup retains corresponding archives, copyright notices and the actual build +recipes in `share/aethersdr-rtl-sources`. Those travel inside each payload. +Source archives include their original license/copyright files. Separate Digital evaluation packages carry their own decoder source and notices; +those dependencies are not part of this analog delivery. +No setup script installs a USB driver, changes udev rules, or opens a radio. + +`tools/check_rtl_package.py` checks that the app imports RTL and float FFTW, +that RTL imports libusb, and that those libraries exist in the payload. Mac +checks reject external non-system load paths; Windows requires these DLLs +beside the executable and recursively resolves their normal and delay DLL +imports from that same directory or Windows system libraries. A dependency +hidden elsewhere in the staging tree or available only on a build-tool PATH +does not satisfy the check. The existing complete deployment audit also runs. +This is packaging evidence, not physical USB or RF qualification. CI builds +and tests that only upload logs are not release producers and keep optional +RTL behavior. Dependency-only build workflows do not build AetherSDR. diff --git a/docs/rtl-wfm.md b/docs/rtl-wfm.md index 98cf5beb5..eb726434b 100644 --- a/docs/rtl-wfm.md +++ b/docs/rtl-wfm.md @@ -97,11 +97,11 @@ second plus actual status changes. Unknown or stale measurements are unavailable no quality percentage is invented. Configuration and processing-health readouts are distinct from reception measurements. -Native NRSC-5/HD FM is a separately requested next checkpoint and RFC amendment, +Native Digital FM is a separate proposal in RFC #6079, not covered by the existing analog architecture approval or the qualification -below. The analog checkpoint exposes no ineffective HD state or empty metadata -controls. Future HD program/ID3 metadata and analog RDS have different decoder -sources; RDS remains separate from the requested native HD implementation. +below. The analog checkpoint exposes no ineffective digital state or empty metadata +controls. Future digital program/ID3 metadata and analog RDS have different decoder +sources; RDS remains separate from the requested native digital implementation. The stereo indicator reports the last completed decoder block carried by a current-session/current-revision independent audio packet. It is unavailable @@ -210,7 +210,7 @@ architecture-specific and operator acceptance work remains separate. ## Future digital reception -HD Radio/nrsc5 implementation is deferred until the current RTL sprint finishes. +Digital FM implementation is deferred to standalone RFC #6079. Its future branch needs wide capture IQ before this analog filter, its own station-centered extraction and 744187.5 Hz conversion, and explicit paired 44.1→48 kHz PCM conversion. RADE's demodulated 24 kHz speech input is unsuitable. diff --git a/hal-plugin/AetherSDRDAX.cpp b/hal-plugin/AetherSDRDAX.cpp index d8a148d8d..a9ce1e690 100644 --- a/hal-plugin/AetherSDRDAX.cpp +++ b/hal-plugin/AetherSDRDAX.cpp @@ -75,6 +75,7 @@ class DaxRxHandler : public aspl::IORequestHandler { } uint32_t rp = block->readPos.load(std::memory_order_acquire); + uint32_t originalRp = rp; uint32_t wp = block->writePos.load(std::memory_order_acquire); uint32_t available = wp - rp; @@ -89,7 +90,13 @@ class DaxRxHandler : public aspl::IORequestHandler { std::memset(dst + toRead, 0, (totalSamples - toRead) * sizeof(float)); } - block->readPos.store(rp, std::memory_order_release); + // The app can retire a route or trim backlog while this callback copies. + // Do not overwrite its newer cursor or return an old owner's samples. + // Equality cannot detect an ABA reset to the same cursor; this only + // protects a concurrently changed cursor value. + if (!block->readPos.compare_exchange_strong(originalRp,rp,std::memory_order_acq_rel)) { + std::memset(dst,0,bytesCount); + } } private: diff --git a/packaging/macos/build-installer.sh b/packaging/macos/build-installer.sh index dc617db67..0f51183d7 100755 --- a/packaging/macos/build-installer.sh +++ b/packaging/macos/build-installer.sh @@ -11,8 +11,11 @@ VERSION=$(grep 'project(AetherSDR' CMakeLists.txt | grep -oE '[0-9]+\.[0-9]+\.[0 echo "=== Building AetherSDR macOS installer v${VERSION} ===" -# 1. Build app -cmake -B "${BUILD_DIR}" -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo +# 1. Build app with the same pinned native dependencies as the release DMG. +export MACOS_DEPLOYMENT_TARGET="${MACOS_DEPLOYMENT_TARGET:-14.0}" +bash scripts/setup/setup-macos-deps.sh +export PKG_CONFIG_PATH="$PWD/third_party/macos-deps/lib/pkgconfig:${PKG_CONFIG_PATH:-}" +cmake -B "${BUILD_DIR}" -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOS_DEPLOYMENT_TARGET" -DCMAKE_PREFIX_PATH="$PWD/third_party/macos-deps;${CMAKE_PREFIX_PATH:-}" cmake --build "${BUILD_DIR}" -j$(sysctl -n hw.ncpu) # 1b. Build HAL plugin (separate build because libASPL FetchContent conflicts with main Ninja build) @@ -29,6 +32,11 @@ mkdir -p "${PKG_DIR}/app" "${PKG_DIR}/hal" "${PKG_DIR}/scripts" # Copy app bundle cp -R "${BUILD_DIR}/AetherSDR.app" "${PKG_DIR}/app/" +QT_DEPLOY="$(sed -n 's|^Qt6_DIR:PATH=\(.*\)/lib/cmake/Qt6$|\1/bin/macdeployqt|p' "${BUILD_DIR}/CMakeCache.txt")" +[ -x "$QT_DEPLOY" ] || { echo "Cannot locate matching macdeployqt" >&2; exit 1; } +"$QT_DEPLOY" "${PKG_DIR}/app/AetherSDR.app" -always-overwrite +cp -R third_party/macos-deps/share/aethersdr-rtl-sources "${PKG_DIR}/app/AetherSDR.app/Contents/Resources/" +python3 tools/check_rtl_package.py "${PKG_DIR}/app/AetherSDR.app" --platform macos # Copy HAL plugin (check both possible build locations) if [ -d "${HAL_BUILD}/AetherSDRDAX.driver" ]; then diff --git a/scripts/build-windows-local.ps1 b/scripts/build-windows-local.ps1 index 4186c35e5..7dfec50c4 100644 --- a/scripts/build-windows-local.ps1 +++ b/scripts/build-windows-local.ps1 @@ -89,7 +89,7 @@ Write-Host " MSVC + CMake + Ninja OK; Qt = $QtDir; jobs = $Jobs" -ForegroundCol # --------------------------------------------------------------------------- Write-Host "== Staging third_party deps ==" -ForegroundColor Cyan foreach ($s in @( - "setup-opus.ps1", "setup-fftw.ps1", "setup-hidapi.ps1", + "setup-opus.ps1", "setup-fftw.ps1", "setup-rtl-deps.ps1", "setup-hidapi.ps1", "setup-deepfilter.ps1", "setup-qtkeychain.ps1", "setup-onnxruntime.ps1", "setup-sherpa-onnx.ps1", "setup-vulkan-sdk.ps1")) { Write-Host " -> $s" -ForegroundColor DarkCyan @@ -113,6 +113,7 @@ Write-Host " VULKAN_SDK = $env:VULKAN_SDK" -ForegroundColor Green Write-Host "== Configure ==" -ForegroundColor Cyan cmake -B build -G "Ninja" ` -DCMAKE_BUILD_TYPE=RelWithDebInfo ` + -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON ` -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON ` -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON ` -DCMAKE_PREFIX_PATH="$QtDir;$env:VULKAN_SDK" ` @@ -159,6 +160,12 @@ Get-ChildItem build\*.dll -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " bundling ASR runtime DLL: $($_.Name)" -ForegroundColor DarkCyan Copy-Item $_.FullName $deploy\ -Force } +Copy-Item third_party\rtl-deps\bin\*.dll $deploy\ -Force +New-Item -ItemType Directory -Force "$deploy\licenses" | Out-Null +Copy-Item third_party\rtl-deps\share\aethersdr-rtl-sources "$deploy\licenses\rtl" -Recurse -Force +Copy-Item third_party\fftw3\share\aethersdr-rtl-sources "$deploy\licenses\fftw" -Recurse -Force +python tools/check_rtl_package.py $deploy --platform windows +if ($LASTEXITCODE -ne 0) { throw "RTL payload validation failed" } # Vulkan loader (whisper GPU): the exe hard-links it, so it must ship. Its # location varies by SDK layout (runtime\x64 on newer SDKs, Bin on older) with a # driver copy in System32 -- take the first that exists. diff --git a/scripts/setup/rtl-dependencies.json b/scripts/setup/rtl-dependencies.json new file mode 100644 index 000000000..56bb834cd --- /dev/null +++ b/scripts/setup/rtl-dependencies.json @@ -0,0 +1,37 @@ +{ + "rtl": { + "version": "aed0ea19f3a273370a13c9009b96313c75d54c7b", + "url": "https://github.com/rtlsdrblog/rtl-sdr-blog/archive/aed0ea19f3a273370a13c9009b96313c75d54c7b.tar.gz", + "sha256": "53d070b234507bd0eaa25ee2ec7b4e69d93a038ccf970c1390ad84162d3da5e1", + "archive": "rtl-sdr-blog.tar.gz", + "directory": "rtl-sdr-blog-aed0ea19f3a273370a13c9009b96313c75d54c7b" + }, + "libusb": { + "version": "1.0.30", + "url": "https://github.com/libusb/libusb/releases/download/v1.0.30/libusb-1.0.30.tar.bz2", + "sha256": "fea36f34f9156400209595e300840767ab1a385ede1dc7ee893015aea9c6dbaf", + "archive": "libusb-1.0.30.tar.bz2", + "directory": "libusb-1.0.30" + }, + "pthreads": { + "version": "2.9.1", + "url": "https://sourceware.org/pub/pthreads-win32/pthreads-w32-2-9-1-release.tar.gz", + "sha256": "e6aca7aea8de33d9c8580bcb3a0ea3ec0a7ace4ba3f4e263ac7c7b66bc95fb4d", + "archive": "pthreads-w32-2-9-1-release.tar.gz", + "directory": "pthreads-w32-2-9-1-release" + }, + "fftw": { + "version": "3.3.11", + "url": "https://fftw.org/fftw-3.3.11.tar.gz", + "sha256": "5630c24cdeb33b131612f7eb4b1a9934234754f9f388ff8617458d0be6f239a1", + "archive": "fftw-3.3.11.tar.gz", + "directory": "fftw-3.3.11" + }, + "fftwWindowsSource": { + "version": "3.3.5", + "url": "https://fftw.org/fftw-3.3.5.tar.gz", + "sha256": "8ecfe1b04732ec3f5b7d279fdb8efcad536d555f9d1e8fabd027037d45ea8bcf", + "archive": "fftw-3.3.5.tar.gz", + "directory": "fftw-3.3.5" + } +} diff --git a/scripts/setup/setup-fftw.ps1 b/scripts/setup/setup-fftw.ps1 index d56000ebf..1dafaf271 100644 --- a/scripts/setup/setup-fftw.ps1 +++ b/scripts/setup/setup-fftw.ps1 @@ -49,8 +49,18 @@ if (-not $VsVars) { } } +# Matching FFTW source travels with packages, including cache hits. +$pins = Get-Content "$PSScriptRoot\rtl-dependencies.json" -Raw | ConvertFrom-Json +$sourcePin = $pins.fftwWindowsSource +$sourceDir = "$OutDir\share\aethersdr-rtl-sources" +New-Item -ItemType Directory -Force $sourceDir | Out-Null +$sourceArchive = Join-Path $sourceDir $sourcePin.archive +if (-not (Test-Path $sourceArchive)) { Invoke-WebRequest $sourcePin.url -OutFile $sourceArchive } +Confirm-Sha256 $sourceArchive $sourcePin.sha256 +Copy-Item "$PSScriptRoot\setup-fftw.ps1","$PSScriptRoot\rtl-dependencies.json","$PSScriptRoot\_verify_sha256.ps1" "$sourceDir\" + # ── Check if already set up ────────────────────────────────────────────── -if ((Test-Path "$OutDir\lib\fftw3.lib") -and (Test-Path "$OutDir\lib\fftw3f.lib")) { +if ((Test-Path "$OutDir\lib\fftw3.lib") -and (Test-Path "$OutDir\lib\fftw3f.lib") -and (Test-Path "$OutDir\bin\libfftw3f-3.dll") -and (Test-Path "$OutDir\bin\libfftw3-3.dll") -and (Test-Path "$OutDir\include\fftw3.h")) { Write-Host "FFTW3 already set up in $OutDir (double + float)" -ForegroundColor Green exit 0 } @@ -66,8 +76,8 @@ New-Item -ItemType Directory -Force -Path "$OutDir\bin" | Out-Null if (-not (Test-Path $ZipFile)) { Write-Host "Downloading FFTW3 prebuilt DLLs..." -ForegroundColor Cyan Invoke-WebRequest -Uri $FftwUrl -OutFile $ZipFile - Confirm-Sha256 -Path $ZipFile -Expected $FftwSha256 } +Confirm-Sha256 -Path $ZipFile -Expected $FftwSha256 # ── Extract ────────────────────────────────────────────────────────────── Write-Host "Extracting..." -ForegroundColor Cyan @@ -80,8 +90,8 @@ Copy-Item "$tempDir\fftw3.h" "$OutDir\include\" Copy-Item "$tempDir\libfftw3-3.dll" "$OutDir\bin\" Copy-Item "$tempDir\libfftw3-3.def" "$OutDir\lib\" # Float precision (fftw3f) — needed by libspecbleach (NR4) -Copy-Item "$tempDir\libfftw3f-3.dll" "$OutDir\bin\" -ErrorAction SilentlyContinue -Copy-Item "$tempDir\libfftw3f-3.def" "$OutDir\lib\" -ErrorAction SilentlyContinue +Copy-Item "$tempDir\libfftw3f-3.dll" "$OutDir\bin\" +Copy-Item "$tempDir\libfftw3f-3.def" "$OutDir\lib\" # ── Ensure the MSVC environment, then generate .lib ────────────────────── Write-Host "Generating MSVC import library..." -ForegroundColor Cyan @@ -105,12 +115,14 @@ if (-not (Get-Command lib.exe -ErrorAction SilentlyContinue)) { # Generate .lib from .def (double and float precision) Push-Location "$OutDir\lib" & lib.exe /machine:x64 /def:libfftw3-3.def /out:fftw3.lib 2>&1 | Out-Null +if ($LASTEXITCODE -ne 0) { throw "FFTW double import library failed" } if (Test-Path "libfftw3f-3.def") { & lib.exe /machine:x64 /def:libfftw3f-3.def /out:fftw3f.lib 2>&1 | Out-Null } +if ($LASTEXITCODE -ne 0) { throw "FFTW float import library failed" } Pop-Location -if (-not (Test-Path "$OutDir\lib\fftw3.lib")) { +if (-not (Test-Path "$OutDir\lib\fftw3.lib") -or -not (Test-Path "$OutDir\lib\fftw3f.lib")) { Write-Error "Failed to generate fftw3.lib" exit 1 } diff --git a/scripts/setup/setup-macos-deps.sh b/scripts/setup/setup-macos-deps.sh index 5341718d9..24e339bba 100755 --- a/scripts/setup/setup-macos-deps.sh +++ b/scripts/setup/setup-macos-deps.sh @@ -81,7 +81,7 @@ OUT_DIR="third_party/macos-deps" PREFIX="$(pwd)/$OUT_DIR" WORK_DIR="$(pwd)/.macos-deps-build" STAMP="$OUT_DIR/.build-stamp" -STAMP_CONTENT="target=$TARGET fftw=$FFTW_VERSION portaudio=$PORTAUDIO_VERSION hidapi=$HIDAPI_VERSION arch=$(uname -m)" +STAMP_CONTENT="target=$TARGET fftw=$FFTW_VERSION portaudio=$PORTAUDIO_VERSION hidapi=$HIDAPI_VERSION arch=$(uname -m) rtl=$(shasum -a 256 "$SCRIPT_DIR/rtl-dependencies.json" "$SCRIPT_DIR/setup-rtl-deps.sh" | shasum -a 256 | cut -d' ' -f1)" # ── Already set up? (lets CI cache third_party/macos-deps) ─────────────── # The stamp carries the deployment target, so a local rebuild at a different @@ -154,7 +154,9 @@ done # target we just exported. Homebrew disables it for the same reason. tar xzf "$WORK_DIR/portaudio.tgz" -C "$WORK_DIR" echo "Building PortAudio $PORTAUDIO_VERSION for macOS $TARGET..." -( cd "$WORK_DIR/portaudio" && ./configure \ +# AppleClang 17 diagnoses the pinned upstream float scaling constants. Keep +# the warning visible without treating that dependency-only warning as fatal. +( cd "$WORK_DIR/portaudio" && CFLAGS="${CFLAGS:-} -Wno-error=implicit-const-int-float-conversion" ./configure \ --prefix="$PREFIX" \ --enable-shared --disable-static \ --disable-mac-universal >/dev/null ) @@ -179,6 +181,12 @@ cmake -B "$WORK_DIR/hidapi-build" -S "$WORK_DIR/hidapi-hidapi-$HIDAPI_VERSION" - cmake --build "$WORK_DIR/hidapi-build" -j"$JOBS" >/dev/null cmake --install "$WORK_DIR/hidapi-build" >/dev/null +RTL_DEPS_PREFIX="$PREFIX" bash "$SCRIPT_DIR/setup-rtl-deps.sh" +SOURCES="$PREFIX/share/aethersdr-rtl-sources" +cp "$WORK_DIR/fftw.tar.gz" "$SOURCES/fftw-$FFTW_VERSION.tar.gz" +cp "$WORK_DIR/fftw-$FFTW_VERSION/COPYING" "$SOURCES/FFTW-COPYING" +cp "$SCRIPT_DIR/setup-macos-deps.sh" "$SOURCES/" + # ── Verify the whole point of the exercise ────────────────────────────── # A build that silently ignored MACOSX_DEPLOYMENT_TARGET would look exactly like # a successful one until a user on the older OS tried to launch the DMG, which is diff --git a/scripts/setup/setup-rtl-deps.ps1 b/scripts/setup/setup-rtl-deps.ps1 new file mode 100644 index 000000000..499bba380 --- /dev/null +++ b/scripts/setup/setup-rtl-deps.ps1 @@ -0,0 +1,46 @@ +<# Pinned x64 MSVC RTL-SDR Blog build. Run from a VS developer environment. #> +param([string]$OutDir = "third_party\rtl-deps") +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\_verify_sha256.ps1" +$pins = Get-Content "$PSScriptRoot\rtl-dependencies.json" -Raw | ConvertFrom-Json +$prefix = [IO.Path]::GetFullPath($OutDir) +$work = Join-Path (Get-Location) '.rtl-deps-build' +$sources = Join-Path $prefix 'share\aethersdr-rtl-sources' +New-Item -ItemType Directory -Force $work,$sources,"$prefix\bin","$prefix\lib","$prefix\include" | Out-Null +function Check-Native([string]$step) { if ($LASTEXITCODE -ne 0) { throw "$step failed: $LASTEXITCODE" } } +foreach ($name in @('libusb','rtl','pthreads')) { + $pin = $pins.$name + $archive = Join-Path $sources $pin.archive + if (-not (Test-Path $archive)) { Invoke-WebRequest $pin.url -OutFile $archive } + Confirm-Sha256 $archive $pin.sha256 + tar -xf $archive -C $work + Check-Native "extract $name" +} +$usb = Join-Path $work $pins.libusb.directory +$rtl = Join-Path $work $pins.rtl.directory +$pthreads = Join-Path $work $pins.pthreads.directory +msbuild "$usb\msvc\libusb_dll.vcxproj" /m:4 /p:Configuration=Release /p:Platform=x64 /p:PlatformToolset=v143 +Check-Native 'libusb build' +Push-Location $pthreads +try { + $savedCl = $env:CL + $env:CL = "$savedCl /FS /D_TIMESPEC_DEFINED" + nmake /nologo VC + Check-Native 'pthreads build' +} finally { $env:CL = $savedCl; Pop-Location } +$usbOutput = "$usb\build\v143\x64\Release\dll" +cmake -S $rtl -B "$work\rtl-build" -G Ninja -DCMAKE_BUILD_TYPE=Release ` + -DCMAKE_DISABLE_FIND_PACKAGE_PkgConfig=ON -DCMAKE_DISABLE_FIND_PACKAGE_Git=ON ` + "-DLIBUSB_LIBRARIES=$usbOutput/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=$usb/libusb" ` + "-DTHREADS_PTHREADS_LIBRARY=$pthreads/pthreadVC2.lib" "-DTHREADS_PTHREADS_INCLUDE_DIR=$pthreads" ` + -DCMAKE_C_FLAGS=/D_TIMESPEC_DEFINED +Check-Native 'RTL configure' +cmake --build "$work\rtl-build" --target rtlsdr -j4 +Check-Native 'RTL build' +Copy-Item "$work\rtl-build\src\rtlsdr.dll","$usbOutput\libusb-1.0.dll" "$prefix\bin\" +Copy-Item "$work\rtl-build\src\rtlsdr.lib" "$prefix\lib\" +Copy-Item "$rtl\include\rtl-sdr.h","$rtl\include\rtl-sdr_export.h" "$prefix\include\" +Copy-Item "$rtl\COPYING" "$sources\RTL-SDR-COPYING" +Copy-Item "$usb\COPYING" "$sources\libusb-COPYING" +Copy-Item "$PSScriptRoot\rtl-dependencies.json","$PSScriptRoot\setup-rtl-deps.ps1","$PSScriptRoot\_verify_sha256.ps1" "$sources\" +Write-Host "RTL libraries and corresponding sources ready at $prefix" diff --git a/scripts/setup/setup-rtl-deps.sh b/scripts/setup/setup-rtl-deps.sh new file mode 100644 index 000000000..fee794f73 --- /dev/null +++ b/scripts/setup/setup-rtl-deps.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Pinned RTL-SDR Blog + libusb, with float FFTW on Linux. No device access. +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. "$SCRIPT_DIR/_verify_sha256.sh" +PREFIX="${RTL_DEPS_PREFIX:-$(pwd)/third_party/rtl-deps}" +WORK="$(pwd)/.rtl-deps-build" +SOURCES="$PREFIX/share/aethersdr-rtl-sources" +mkdir -p "$WORK" "$SOURCES" +JOBS="${CMAKE_BUILD_PARALLEL_LEVEL:-4}" +field() { python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))[sys.argv[2]][sys.argv[3]])' "$SCRIPT_DIR/rtl-dependencies.json" "$1" "$2"; } +fetch() { + local name="$1" archive + archive="$(field "$name" archive)" + if [ ! -f "$SOURCES/$archive" ]; then + curl -fL --retry 3 --connect-timeout 30 "$(field "$name" url)" -o "$SOURCES/$archive" + fi + verify_sha256 "$SOURCES/$archive" "$(field "$name" sha256)" + tar xf "$SOURCES/$archive" -C "$WORK" +} +fetch libusb +fetch rtl +USB="$WORK/$(field libusb directory)" +RTL="$WORK/$(field rtl directory)" +cmake_args=() +if [ "$(uname -s)" = Darwin ]; then + : "${MACOS_DEPLOYMENT_TARGET:?Set the deployment target explicitly}" + export MACOSX_DEPLOYMENT_TARGET="$MACOS_DEPLOYMENT_TARGET" + # New Apple SDKs can link pipe2 as a weak import even when the target + # macOS lacks it. Autoconf's link-only probe then selects a null call. + # libusb's pipe + fcntl fallback works across our supported macOS floor. + export ac_cv_func_pipe2=no + cmake_args=(-DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOS_DEPLOYMENT_TARGET" -DCMAKE_INSTALL_NAME_DIR="$PREFIX/lib") +fi +(cd "$USB" && ./configure --prefix="$PREFIX" --libdir="$PREFIX/lib" --enable-shared --disable-static --disable-examples-build --disable-tests-build) +make -C "$USB" -j"$JOBS" +make -C "$USB" install +export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}" +USB_LIBRARY="$PREFIX/lib/libusb-1.0.so" +[ "$(uname -s)" != Darwin ] || USB_LIBRARY="$PREFIX/lib/libusb-1.0.dylib" +cmake -S "$RTL" -B "$WORK/rtl-build" -G Ninja \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$PREFIX" -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_DISABLE_FIND_PACKAGE_Git=ON -DCMAKE_DISABLE_FIND_PACKAGE_PkgConfig=ON \ + -DLIBUSB_LIBRARIES="$USB_LIBRARY" -DLIBUSB_INCLUDE_DIRS="$PREFIX/include/libusb-1.0" \ + -DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=ON -DINSTALL_UDEV_RULES=OFF -DDETACH_KERNEL_DRIVER=ON \ + ${cmake_args[@]+"${cmake_args[@]}"} +cmake --build "$WORK/rtl-build" -j"$JOBS" +cmake --install "$WORK/rtl-build" +# macOS setup already builds both FFTW precisions at the pinned floor. +if [ "$(uname -s)" = Linux ]; then + fetch fftw + mkdir -p "$WORK/fftw-build" + (cd "$WORK/fftw-build" && "$WORK/$(field fftw directory)/configure" \ + --prefix="$PREFIX" --enable-float --enable-shared --disable-static --disable-fortran --disable-doc) + make -C "$WORK/fftw-build" -j"$JOBS" + make -C "$WORK/fftw-build" install + cp "$WORK/$(field fftw directory)/COPYING" "$SOURCES/FFTW-COPYING" +fi +cp "$RTL/COPYING" "$SOURCES/RTL-SDR-COPYING" +cp "$USB/COPYING" "$SOURCES/libusb-COPYING" +cp "$SCRIPT_DIR/rtl-dependencies.json" "$SCRIPT_DIR/setup-rtl-deps.sh" "$SCRIPT_DIR/_verify_sha256.sh" "$SOURCES/" +echo "RTL dependency sources and recipes: $SOURCES" diff --git a/src/core/AutomationServer.cpp b/src/core/AutomationServer.cpp index c884a45c8..b4489991b 100644 --- a/src/core/AutomationServer.cpp +++ b/src/core/AutomationServer.cpp @@ -1619,6 +1619,8 @@ QJsonObject sliceSnapshot(const SliceModel* s, int linkedTo, s->externalReceiveReplacementActive()}, {QStringLiteral("squelch"), s->squelchOn()}, {QStringLiteral("squelchLevel"), s->squelchLevel()}, + {QStringLiteral("automaticSquelch"), s->automaticSquelch()}, + {QStringLiteral("automaticSquelchMarginDb"), s->automaticSquelchMarginDb()}, {QStringLiteral("receiveSquelch"), s->receiveSquelchOn()}, {QStringLiteral("receiveSquelchLevel"), s->receiveSquelchLevel()}, {QStringLiteral("flexSquelch"), s->flexSquelchOn()}, diff --git a/src/core/PipeWireAudioBridge.cpp b/src/core/PipeWireAudioBridge.cpp index f32b8f980..a20eb844d 100644 --- a/src/core/PipeWireAudioBridge.cpp +++ b/src/core/PipeWireAudioBridge.cpp @@ -111,10 +111,11 @@ static void cleanupStaleModules() } } -bool PipeWireAudioBridge::open(int activeChannels) +bool PipeWireAudioBridge::open(int activeChannels, bool receiveOnly) { if (m_open) return true; + m_receiveOnly = receiveOnly; cleanupStaleModules(); // Open only as many RX sources as the radio has slices (the device list @@ -134,7 +135,7 @@ bool PipeWireAudioBridge::open(int activeChannels) // fails (e.g. PipeWire not running, only PulseAudio). for (int i = 0; i < m_activeChannels; ++i) { #ifdef HAVE_PIPEWIRE_NATIVE - auto native = std::make_unique(i + 1); + auto native = std::make_unique(i + 1, receiveOnly); if (native->open()) { m_nativeRx[i] = std::move(native); continue; @@ -149,22 +150,24 @@ bool PipeWireAudioBridge::open(int activeChannels) } } - // Create TX pipe sink (apps → radio) - if (!loadPipeSink()) { - qCWarning(lcDax) << "PipeWireAudioBridge: failed to create TX pipe"; - close(); - return false; - } + if (!receiveOnly) { + // Create TX pipe sink (apps → radio) + if (!loadPipeSink()) { + qCWarning(lcDax) << "PipeWireAudioBridge: failed to create TX pipe"; + close(); + return false; + } - // Poll TX pipe for incoming audio from apps - m_txReadTimer = new QTimer(this); - m_txReadTimer->setInterval(5); - m_txReadTimer->setTimerType(Qt::PreciseTimer); - connect(m_txReadTimer, &QTimer::timeout, this, &PipeWireAudioBridge::readTxPipe); - m_txReadTimer->start(); + // Poll TX pipe for incoming audio from apps + m_txReadTimer = new QTimer(this); + m_txReadTimer->setInterval(5); + m_txReadTimer->setTimerType(Qt::PreciseTimer); + connect(m_txReadTimer, &QTimer::timeout, this, &PipeWireAudioBridge::readTxPipe); + m_txReadTimer->start(); + } m_open.store(true, std::memory_order_release); - qCInfo(lcDax) << "PipeWireAudioBridge: opened —" << m_activeChannels << "RX sources + 1 TX sink"; + qCInfo(lcDax) << "PipeWireAudioBridge: opened —" << m_activeChannels << "RX sources, TX sink:" << !receiveOnly; return true; } @@ -198,6 +201,7 @@ void PipeWireAudioBridge::close() // Close pipe file descriptors for (auto& rx : m_rx) { if (rx.fd >= 0) { ::close(rx.fd); rx.fd = -1; } + if (rx.drainFd >= 0) { ::close(rx.drainFd); rx.drainFd = -1; } } if (m_tx.fd >= 0) { ::close(m_tx.fd); m_tx.fd = -1; } @@ -280,7 +284,7 @@ bool PipeWireAudioBridge::loadPipeSource(int index) } // Open the pipe for writing (non-blocking to avoid hanging if no reader) - int fd = ::open(pipePath.toUtf8().constData(), O_WRONLY | O_NONBLOCK); + int fd = ::open(pipePath.toUtf8().constData(), O_WRONLY | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { qCWarning(lcDax) << "PipeWireAudioBridge: open pipe failed:" << strerror(errno); runPactl({"unload-module", QString::number(modIdx)}); @@ -288,12 +292,27 @@ bool PipeWireAudioBridge::loadPipeSource(int index) return false; } - // Cap kernel FIFO capacity so back-pressure surfaces quickly instead of - // accumulating ~1.3 s of audio in the default 64 KB pipe buffer. - if (::fcntl(fd, F_SETPIPE_SZ, PIPE_KERNEL_BUF) < 0) { - qCDebug(lcDax) << "PipeWireAudioBridge: F_SETPIPE_SZ failed (non-fatal):" << strerror(errno); + // Native decoded PCM can arrive in bursts (Digital prebuffer is ~186 ms). + // Bound that RX-only backlog to ~341 ms rather than truncating a valid burst + // at the legacy Flex packet-sized 21 ms capacity. No blocking writes. + const int capacity = m_receiveOnly ? NATIVE_RX_KERNEL_BUF : PIPE_KERNEL_BUF; + const int actual = ::fcntl(fd,F_SETPIPE_SZ,capacity); + if (actual < 0) { + qCDebug(lcDax) << "PipeWireAudioBridge: F_SETPIPE_SZ failed:" << strerror(errno); + } + if (actual < capacity && m_receiveOnly) { + qCWarning(lcDax) << "PipeWireAudioBridge: insufficient native RX FIFO capacity"; + ::close(fd); + runPactl({"unload-module",QString::number(modIdx)}); + ::unlink(pipePath.toUtf8().constData()); + return false; + } + if (!openRxDrain(index, pipePath)) { + ::close(fd); + runPactl({"unload-module",QString::number(modIdx)}); + ::unlink(pipePath.toUtf8().constData()); + return false; } - m_rx[index].fd = fd; m_rx[index].moduleIndex = modIdx; m_rx[index].pipePath = pipePath; @@ -302,6 +321,15 @@ bool PipeWireAudioBridge::loadPipeSource(int index) return true; } +bool PipeWireAudioBridge::openRxDrain(int index, const QString& pipePath) +{ + // Receive-only route changes discard retained FIFO bytes. Flex has no + // route-reset consumer and must retain its sole-reader/EPIPE behavior. + if (!m_receiveOnly) { return true; } + m_rx[index].drainFd = ::open(pipePath.toUtf8().constData(), O_RDONLY|O_NONBLOCK|O_CLOEXEC); + return m_rx[index].drainFd >= 0; +} + bool PipeWireAudioBridge::loadPipeSink() { // FIFO lives in the per-user XDG runtime directory with mode 0600. @@ -448,8 +476,29 @@ void PipeWireAudioBridge::feedDaxAudio(int channel, const QByteArray& pcm) } } +void PipeWireAudioBridge::resetRxChannel(int channel) +{ + if (channel < 1 || channel > NUM_CHANNELS) { return; } + m_rxLastSample[channel-1] = 0; +#ifdef HAVE_PIPEWIRE_NATIVE + if (m_nativeRx[channel-1]) { m_nativeRx[channel-1]->reset(); } +#endif + const int fd = m_rx[channel-1].drainFd; + if (fd >= 0) { + std::array discarded{}; + int total = 0; + while (total < NATIVE_RX_KERNEL_BUF) { + const ssize_t count = ::read(fd,discarded.data(),discarded.size()); + if (count <= 0) { break; } + total += int(count); + } + } + emit daxRxLevel(channel,0); +} + void PipeWireAudioBridge::setTransmitting(bool tx) { + if (m_receiveOnly) { return; } m_transmitting.store(tx, std::memory_order_release); if (tx) { diff --git a/src/core/PipeWireAudioBridge.h b/src/core/PipeWireAudioBridge.h index 72db3c3be..360809035 100644 --- a/src/core/PipeWireAudioBridge.h +++ b/src/core/PipeWireAudioBridge.h @@ -25,6 +25,7 @@ class PipeWireNativeRxSource; // Works with both PulseAudio and PipeWire (via pipewire-pulse). class PipeWireAudioBridge : public QObject { Q_OBJECT + friend class PipeWireRxBridgeTest; public: static constexpr int NUM_CHANNELS = 8; @@ -32,6 +33,7 @@ class PipeWireAudioBridge : public QObject { static constexpr int PIPE_RATE = 48000; // matches PipeWire graph rate — avoids in-graph resampler static constexpr int PIPE_CHANNELS = 1; // mono — ham radio DAX is single-channel static constexpr int PIPE_KERNEL_BUF = 4096; // ~21 ms at 48 kHz mono float32 — bounds kernel FIFO buffering + static constexpr int NATIVE_RX_KERNEL_BUF = 65536; // ~341 ms: bounded decoded-audio bursts static constexpr int TX_RATE = 24000; // TX sink stays at radio-native rate (s16le mono) static constexpr int TX_CHANNELS = 1; @@ -43,12 +45,14 @@ class PipeWireAudioBridge : public QObject { // NUM_CHANNELS stays the compile-time array bound; this only bounds the // open loop so the device list follows the radio (#4854). Dynamic resize // after connect is a follow-up (issue #4935). - bool open(int activeChannels = NUM_CHANNELS); + // receiveOnly opens no TX endpoint and never polls application TX audio. + bool open(int activeChannels = NUM_CHANNELS, bool receiveOnly = false); + bool isReceiveOnly() const { return m_receiveOnly; } void close(); bool isOpen() const { return m_open; } void setGain(float g); // global gain (all RX channels) - void setChannelGain(int channel, float g); // per-channel RX gain (1-4) + void setChannelGain(int channel, float g); // per-channel RX gain (1-8) void setTxGain(float g); // TX gain float gain() const { return m_gain; } void setTxContext(const TxCoordinator::Context& context); @@ -56,6 +60,7 @@ class PipeWireAudioBridge : public QObject { public slots: void feedDaxAudio(int channel, const QByteArray& pcm); void setTransmitting(bool tx); + void resetRxChannel(int channel); signals: void txAudioReady(const QByteArray& pcm, const AetherSDR::TxCoordinator::Context& context); @@ -64,12 +69,14 @@ public slots: private: bool loadPipeSource(int index); + bool openRxDrain(int index, const QString& pipePath); bool loadPipeSink(); void unloadModules(); - // RX: we write int16 mono PCM to these pipes → PulseAudio reads them + // RX: we write float32 mono PCM to these pipes → PulseAudio reads them struct RxPipe { int fd{-1}; + int drainFd{-1}; uint32_t moduleIndex{0}; QString pipePath; }; @@ -98,6 +105,7 @@ public slots: // Teardown loops still walk the full NUM_CHANNELS bound — closing an // unopened slot is a no-op. (#4854) int m_activeChannels{NUM_CHANNELS}; + bool m_receiveOnly{false}; float m_gain{0.5f}; // m_channelGain is read on PanadapterStream's network thread (DirectConnection // fast path) and written from the main thread (DaxApplet slider). Float diff --git a/src/core/PipeWireNativeRxSource.cpp b/src/core/PipeWireNativeRxSource.cpp index 3f08aebd4..d380b362b 100644 --- a/src/core/PipeWireNativeRxSource.cpp +++ b/src/core/PipeWireNativeRxSource.cpp @@ -55,8 +55,8 @@ const pw_stream_events kStreamEvents = { } // namespace -PipeWireNativeRxSource::PipeWireNativeRxSource(int channel) - : m_channel(channel) +PipeWireNativeRxSource::PipeWireNativeRxSource(int channel, bool nativePcm) + : m_ringSize(nativePcm ? kMaximumRingSize : 2048), m_ringMask(m_ringSize-1), m_channel(channel) {} PipeWireNativeRxSource::~PipeWireNativeRxSource() @@ -176,6 +176,17 @@ void PipeWireNativeRxSource::close() } } +void PipeWireNativeRxSource::reset() +{ + if (!m_stream) { return; } + auto& context = PipeWireNativeContext::instance(); + context.lock(); + // The RT callback is excluded here; no lock is acquired inside it. + m_readIdx.store(m_writeIdx.load(std::memory_order_acquire),std::memory_order_release); + pw_stream_flush(m_stream,false); + context.unlock(); +} + void PipeWireNativeRxSource::feedAudio(const float* samples, uint32_t count) { if (!m_stream || count == 0) { @@ -185,7 +196,7 @@ void PipeWireNativeRxSource::feedAudio(const float* samples, uint32_t count) const uint32_t writeIdx = m_writeIdx.load(std::memory_order_relaxed); const uint32_t readIdx = m_readIdx.load(std::memory_order_acquire); const uint32_t pending = writeIdx - readIdx; - const uint32_t space = RING_SIZE - pending; + const uint32_t space = m_ringSize - pending; // SPSC invariant: only the producer touches m_writeIdx, only the consumer // touches m_readIdx. On overflow, drop the *newest* incoming samples that @@ -201,8 +212,8 @@ void PipeWireNativeRxSource::feedAudio(const float* samples, uint32_t count) } // Copy in up to two contiguous spans (handles wraparound). - const uint32_t startMasked = writeIdx & RING_MASK; - const uint32_t firstSpan = std::min(toCopy, RING_SIZE - startMasked); + const uint32_t startMasked = writeIdx & m_ringMask; + const uint32_t firstSpan = std::min(toCopy, m_ringSize - startMasked); std::memcpy(&m_ring[startMasked], samples, firstSpan * sizeof(float)); if (toCopy > firstSpan) { std::memcpy(&m_ring[0], samples + firstSpan, (toCopy - firstSpan) * sizeof(float)); @@ -248,8 +259,8 @@ void PipeWireNativeRxSource::onProcess(void* userdata) const uint32_t toRead = std::min(frames, pending); if (toRead > 0) { - const uint32_t startMasked = readIdx & RING_MASK; - const uint32_t firstSpan = std::min(toRead, RING_SIZE - startMasked); + const uint32_t startMasked = readIdx & self->m_ringMask; + const uint32_t firstSpan = std::min(toRead, self->m_ringSize - startMasked); std::memcpy(dst, &self->m_ring[startMasked], firstSpan * sizeof(float)); if (toRead > firstSpan) { std::memcpy(dst + firstSpan, &self->m_ring[0], (toRead - firstSpan) * sizeof(float)); diff --git a/src/core/PipeWireNativeRxSource.h b/src/core/PipeWireNativeRxSource.h index 2cdc4ab7e..2ee74e4e1 100644 --- a/src/core/PipeWireNativeRxSource.h +++ b/src/core/PipeWireNativeRxSource.h @@ -15,7 +15,7 @@ namespace AetherSDR { // allocation-free SPSC float ring with atomic head/tail. class PipeWireNativeRxSource { public: - explicit PipeWireNativeRxSource(int channel); + explicit PipeWireNativeRxSource(int channel, bool nativePcm = false); ~PipeWireNativeRxSource(); PipeWireNativeRxSource(const PipeWireNativeRxSource&) = delete; @@ -28,10 +28,12 @@ class PipeWireNativeRxSource { // Disconnects and destroys the stream, releases the shared context. void close(); + // Owner-thread boundary: discard retained samples under the existing loop lock. + void reset(); // Push 48 kHz mono float32 samples into the ring buffer. Drops the // newest incoming samples that wouldn't fit if the consumer (PipeWire) - // is too slow — this caps backlog at the ring size (~42 ms) so latency + // is too slow — this caps backlog at the configured ring size (42 or 341 ms) so latency // cannot grow unboundedly even under stalls, and it keeps the SPSC // invariant intact (only the producer touches m_writeIdx, only the // consumer touches m_readIdx). @@ -44,14 +46,11 @@ class PipeWireNativeRxSource { private: - // Ring buffer is a power of two so wraparound is a mask op. - // 2048 samples = ~42 ms @ 48 kHz mono float32. In steady state the ring - // is near-empty; the cap matters only on transient stalls — at which - // point we *want* to drop oldest samples rather than let latency grow. - // Earlier 8192 sizing absorbed up to 170 ms of stalls, which directly - // showed up as DT growth under load. - static constexpr uint32_t RING_SIZE = 2048; - static constexpr uint32_t RING_MASK = RING_SIZE - 1; + // Legacy packet stream: 2048 samples (~42 ms). Native decoded bursts: + // 16384 samples (~341 ms), matching the bounded RX-only FIFO path. + static constexpr uint32_t kMaximumRingSize = 16384; + const uint32_t m_ringSize; + const uint32_t m_ringMask; int m_channel; pw_stream* m_stream{nullptr}; @@ -65,7 +64,7 @@ class PipeWireNativeRxSource { // SPSC ring. Producer (Qt thread) writes m_writeIdx; consumer // (PipeWire thread) writes m_readIdx. Indices are free-running and // masked at access time. - float m_ring[RING_SIZE]{}; + float m_ring[kMaximumRingSize]{}; std::atomic m_writeIdx{0}; std::atomic m_readIdx{0}; }; diff --git a/src/core/RtlDeviceSettings.cpp b/src/core/RtlDeviceSettings.cpp index 9ad3a3e78..034443e53 100644 --- a/src/core/RtlDeviceSettings.cpp +++ b/src/core/RtlDeviceSettings.cpp @@ -30,6 +30,9 @@ bool RtlDeviceSettings::decode(const QJsonObject& object, Values& values) } parsed.ppm = static_cast(number); parsed.dcSuppression = dc.toBool(); + const QJsonValue meters = object.value(QStringLiteral("receiveMetersEnabled")); + if (!meters.isUndefined() && !meters.isBool()) { return false; } + parsed.receiveMetersEnabled = meters.toBool(true); values = parsed; return true; } @@ -85,6 +88,7 @@ bool RtlDeviceSettings::saveAccepted(const Values& values, QString& reason) cons } object.insert(QStringLiteral("ppm"), values.ppm); object.insert(QStringLiteral("dcSuppression"), values.dcSuppression); + object.insert(QStringLiteral("receiveMetersEnabled"), values.receiveMetersEnabled); if (!m_scope.setFeature(featureName(), kSchemaVersion, object)) { reason = QStringLiteral("Applied for this session; saving device settings failed."); qCWarning(lcRtlDeviceSettings) << reason; diff --git a/src/core/RtlDeviceSettings.h b/src/core/RtlDeviceSettings.h index 3e3ca5e64..a530f318e 100644 --- a/src/core/RtlDeviceSettings.h +++ b/src/core/RtlDeviceSettings.h @@ -16,6 +16,7 @@ class RtlDeviceSettings final { struct Values { int ppm = 0; bool dcSuppression = false; + bool receiveMetersEnabled = true; bool operator==(const Values&) const = default; }; enum class ReadStatus { Missing, Ready, Refused }; diff --git a/src/core/RtlSliceSettings.cpp b/src/core/RtlSliceSettings.cpp index 416b4ea82..939008efd 100644 --- a/src/core/RtlSliceSettings.cpp +++ b/src/core/RtlSliceSettings.cpp @@ -49,6 +49,8 @@ QJsonObject encodeSlice(const RtlSliceSettings::Slice& slice, QJsonObject previo QJsonObject squelch = previous.value(QStringLiteral("squelch")).toObject(); squelch.insert(QStringLiteral("enabled"), slice.squelchEnabled); squelch.insert(QStringLiteral("level"), slice.squelchLevel); + squelch.insert(QStringLiteral("automatic"), slice.automaticSquelch); + squelch.insert(QStringLiteral("marginDb"), slice.automaticSquelchMarginDb); previous.insert(QStringLiteral("squelch"), squelch); previous.insert(QStringLiteral("audioGain"), slice.audioGain); previous.insert(QStringLiteral("audioMute"), slice.audioMute); @@ -130,6 +132,16 @@ bool RtlSliceSettings::decode(const QJsonObject& object, Document& output, QStri return false; } slice.squelchEnabled = squelch.value(QStringLiteral("enabled")).toBool(); + if (squelch.contains(QStringLiteral("automatic"))) { + if (!squelch.value(QStringLiteral("automatic")).isBool()) { return false; } + slice.automaticSquelch = squelch.value(QStringLiteral("automatic")).toBool(); + } + if (squelch.contains(QStringLiteral("marginDb"))) { + double margin = 0; + if (!number(squelch, QStringLiteral("marginDb"), 5, 20, margin, true)) { return false; } + slice.automaticSquelchMarginDb = static_cast(margin); + } + if (slice.automaticSquelch && !slice.squelchEnabled) { return false; } slice.audioMute = entry.value(QStringLiteral("audioMute")).toBool(); parsed.slices.insert(slice.id, slice); } diff --git a/src/core/RtlSliceSettings.h b/src/core/RtlSliceSettings.h index f8a5d3049..206e1ee42 100644 --- a/src/core/RtlSliceSettings.h +++ b/src/core/RtlSliceSettings.h @@ -34,6 +34,8 @@ class RtlSliceSettings { // Optional in schema 1; old documents retain the legacy 75 us default. int wfmDeemphasisUs = 75; bool wfmForceMono = false; + bool automaticSquelch = false; + int automaticSquelchMarginDb = 10; }; struct Document { double captureCenterHz = 0; diff --git a/src/core/TciServer.cpp b/src/core/TciServer.cpp index 5e4f82181..851785a4f 100644 --- a/src/core/TciServer.cpp +++ b/src/core/TciServer.cpp @@ -550,6 +550,12 @@ TciServer::TciServer(RadioModel* model, QObject* parent) connect(m_model, &RadioModel::sliceAdded, this, bindSlice); connect(m_model, &RadioModel::sliceRemoved, this, [this] { refreshRxBindings(); }); + // A session can create TCI after the backend restored its slices. + // Seed the sticky map before publishing any audio bindings; positional + // fallback would silently renumber survivors on the first removal. + for (SliceModel* slice : m_model->slices()) { + m_trxMap.acquire(slice->sliceId()); + } for (SliceModel* slice : m_model->slices()) { bindSlice(slice); } } for (int ch = 1; ch <= 8; ++ch) { m_io->setRxGain(ch, m_rxChannelGain[ch - 1]); } @@ -1137,14 +1143,12 @@ QJsonObject TciServer::routingSnapshot() const QJsonArray endpoints; if (m_model) { - const auto slices = m_model->slices(); - for (int trx = 0; trx < slices.size(); ++trx) { - const SliceModel* slice = slices.at(trx); + for (const SliceModel* slice : m_model->slices()) { if (!slice) { continue; } endpoints.append(QJsonObject{ - {QStringLiteral("trx"), trx}, + {QStringLiteral("trx"), m_trxMap.trxForSlice(m_model, slice)}, {QStringLiteral("sliceId"), slice->sliceId()}, {QStringLiteral("panId"), slice->panId()}, {QStringLiteral("frequencyHz"), @@ -2687,6 +2691,15 @@ void TciServer::resetClientRx(ClientState& client) syncClient(client); } +TciRxBinding TciServer::sliceRxBinding(int sliceId) const +{ + const quint64 key = TciIoWorker::rxRouteKey(false,sliceId); + const TciRxBinding binding = m_rxBindings.value(key); + const QPointer owner = m_rxBindingOwners.value(key); + if (!m_model || !owner || m_model->slice(sliceId) != owner || !binding.current()) { return {}; } + return binding; +} + void TciServer::refreshRxBindings() { QHash next; @@ -2729,6 +2742,7 @@ void TciServer::refreshRxBindings() m_rxBindings = next; m_rxBindingOwners = owners; m_io->post([io = m_io.get(), next] { io->setRxBindings(next); }); + emit rxBindingsChanged(); } void TciServer::retireAllRxRoutes() @@ -3117,18 +3131,31 @@ void TciServer::sendInitBurst(TciClient* client) << "TCI: receiver map" << (receiverMap.isEmpty() ? QStringLiteral("(none)") : receiverMap.join(QLatin1Char(' '))); - // TCI protocol requires one command per WebSocket message. - // Split the concatenated burst into individual messages. - QString burst = protocol->generateInitBurst(); - const auto commands = burst.split(';', Qt::SkipEmptyParts); - for (const auto& cmd : commands) { + // Preserve one command per WebSocket message, but admit the bounded init + // snapshot as one worker job. Eight receivers times eight simultaneous + // clients otherwise exceed the 256-job mailbox during their handshakes. + const QString burst = protocol->generateInitBurst(); + const QStringList commands = burst.split(';', Qt::SkipEmptyParts); + QStringList messages; + qsizetype bytes = 0; + for (const QString& cmd : commands) { // DIAG: log each init-burst command — the startup vfo:/dds: here is what // WSJT-X reconciles against on connect; a wrong/late one explains the // "TCI failed set rxfreq" some users hit right at WSJT-X startup. qCDebug(lcCat).noquote() << "TCI tx→init:" << (cmd + QLatin1Char(';')); const QString message = cmd + QLatin1Char(';'); - sendClientText(client, message); + noteClientTextTx(client, message); + messages.append(message); + bytes += message.size() * sizeof(QChar); } + TciIoWorker* io = m_io.get(); + const quint64 id = client->id(); + io->post([io, id, messages = std::move(messages)] { + for (const QString& message : messages) { + // sendText rechecks client lifetime and socket backlog each time. + io->sendText(id, message); + } + }, bytes); qCDebug(lcCat) << "TCI: sent init burst," << commands.size() << "commands"; } diff --git a/src/core/TciServer.h b/src/core/TciServer.h index 99f7f57a0..a600cba9f 100644 --- a/src/core/TciServer.h +++ b/src/core/TciServer.h @@ -92,6 +92,10 @@ class TciServer : public QObject { // local automation bridge's `tci routes` action. QJsonObject routingSnapshot() const; + // Owner-thread projection of the live native receive route. Valid while + // its revocation token is current, independently of listener state. + TciRxBinding sliceRxBinding(int sliceId) const; + // (ownsDaxChannel() and the cross-consumer peeking it existed for were // replaced by per-consumer holds in PanadapterStream's centralized DAX // channel manager — see acquireDaxChannel/releaseDaxChannel. #3305) @@ -162,6 +166,7 @@ public slots: signals: void clientCountChanged(int count); + void rxBindingsChanged(); // Fired whenever the client list or any client's subscriptions change // (connect, disconnect, audio start/stop). The TCI tab repopulates on // this signal. diff --git a/src/core/VirtualAudioBridge.cpp b/src/core/VirtualAudioBridge.cpp index d2bb6f9a0..bfbbe25b0 100644 --- a/src/core/VirtualAudioBridge.cpp +++ b/src/core/VirtualAudioBridge.cpp @@ -86,10 +86,12 @@ static bool openShmSegment(const char* name, int& fd, DaxShmBlock*& block) return true; } -bool VirtualAudioBridge::open(int activeChannels) +bool VirtualAudioBridge::open(int activeChannels, bool receiveOnly) { if (m_open.load(std::memory_order_acquire)) return true; + m_receiveOnly = receiveOnly; + // Open only as many RX shm segments as the radio has slices (the device // list follows the radio — product decision, #4854). open() is reached // from MainWindow::startDax(), which runs on a 3 s post-connect timer, so @@ -109,52 +111,54 @@ bool VirtualAudioBridge::open(int activeChannels) m_blocks[i]->active = 1; } - // Open TX shared memory segment - if (!openShmSegment("/aethersdr-dax-tx", m_txShmFd, m_txBlock)) { - close(); - return false; - } - m_txBlock->active = 0; // HAL plugin sets this to 1 when apps write - - // Poll TX shared memory for incoming audio. - // Use a short interval and fixed-size reads so TX stays close to real-time. - m_txPollTimer = new QTimer(this); - m_txPollTimer->setInterval(2); - connect(m_txPollTimer, &QTimer::timeout, this, [this]() { - static int pollNum = 0; - ++pollNum; - - // Diagnostic: log shm state every second regardless of data - if (m_txBlock && pollNum % 500 == 0) { - uint32_t wp = m_txBlock->writePos.load(std::memory_order_relaxed); - uint32_t rp = m_txBlock->readPos.load(std::memory_order_relaxed); - qCDebug(lcDax) << "TX shm poll#" << pollNum - << "wp=" << wp << "rp=" << rp - << "avail=" << (wp - rp) << "active=" << m_txBlock->active; + if (!receiveOnly) { + // Open TX shared memory segment + if (!openShmSegment("/aethersdr-dax-tx", m_txShmFd, m_txBlock)) { + close(); + return false; } + m_txBlock->active = 0; // HAL plugin sets this to 1 when apps write + + // Poll TX shared memory for incoming audio. + // Use a short interval and fixed-size reads so TX stays close to real-time. + m_txPollTimer = new QTimer(this); + m_txPollTimer->setInterval(2); + connect(m_txPollTimer, &QTimer::timeout, this, [this]() { + static int pollNum = 0; + ++pollNum; + + // Diagnostic: log shm state every second regardless of data + if (m_txBlock && pollNum % 500 == 0) { + uint32_t wp = m_txBlock->writePos.load(std::memory_order_relaxed); + uint32_t rp = m_txBlock->readPos.load(std::memory_order_relaxed); + qCDebug(lcDax) << "TX shm poll#" << pollNum + << "wp=" << wp << "rp=" << rp + << "avail=" << (wp - rp) << "active=" << m_txBlock->active; + } - // Drain multiple small chunks each tick to avoid stale backlog. - constexpr int FRAMES_PER_READ = 128; // ~5.3ms @ 24kHz - constexpr int MAX_CHUNKS_PER_TICK = 8; // keep UI responsive under load - for (int i = 0; i < MAX_CHUNKS_PER_TICK; ++i) { - const TxCoordinator::Context context = m_txContext; - QByteArray audio = readTxAudio(FRAMES_PER_READ); - if (audio.isEmpty()) break; - - static int txPollCount = 0; - ++txPollCount; - if (txPollCount <= 10 || txPollCount % 200 == 0) - qCDebug(lcDax) << "VirtualAudioBridge: TX audio from shm, bytes=" << audio.size() - << "(chunk #" << txPollCount << ")" - << "wp=" << m_txBlock->writePos.load(std::memory_order_relaxed) - << "active=" << m_txBlock->active; - emit txAudioReady(audio, context); - } - }); - m_txPollTimer->start(); + // Drain multiple small chunks each tick to avoid stale backlog. + constexpr int FRAMES_PER_READ = 128; // ~5.3ms @ 24kHz + constexpr int MAX_CHUNKS_PER_TICK = 8; // keep UI responsive under load + for (int i = 0; i < MAX_CHUNKS_PER_TICK; ++i) { + const TxCoordinator::Context context = m_txContext; + QByteArray audio = readTxAudio(FRAMES_PER_READ); + if (audio.isEmpty()) break; + + static int txPollCount = 0; + ++txPollCount; + if (txPollCount <= 10 || txPollCount % 200 == 0) + qCDebug(lcDax) << "VirtualAudioBridge: TX audio from shm, bytes=" << audio.size() + << "(chunk #" << txPollCount << ")" + << "wp=" << m_txBlock->writePos.load(std::memory_order_relaxed) + << "active=" << m_txBlock->active; + emit txAudioReady(audio, context); + } + }); + m_txPollTimer->start(); + } m_open.store(true, std::memory_order_release); - qCInfo(lcDax) << "VirtualAudioBridge: opened" << NUM_CHANNELS << "RX + 1 TX shared memory segments"; + qCInfo(lcDax) << "VirtualAudioBridge: opened" << m_activeChannels << "RX shared memory segments, TX:" << !receiveOnly; return true; } @@ -214,6 +218,7 @@ void VirtualAudioBridge::close() void VirtualAudioBridge::setTransmitting(bool tx) { + if (m_receiveOnly) { return; } m_transmitting.store(tx, std::memory_order_release); if (tx) { @@ -287,8 +292,19 @@ void VirtualAudioBridge::feedSilenceToAllChannels() } } +void VirtualAudioBridge::resetRxChannel(int channel) +{ + if (channel < 1 || channel > NUM_CHANNELS) { return; } + DaxShmBlock* block = m_blocks[channel-1]; + if (block) { + block->readPos.store(block->writePos.load(std::memory_order_acquire),std::memory_order_release); + } + emit daxRxLevel(channel,0); +} + void VirtualAudioBridge::feedDaxAudio(int channel, const QByteArray& pcm) { + if (!m_open.load(std::memory_order_acquire)) { return; } // This slot may run on PanadapterStream's network thread via // Qt::DirectConnection — keep it lock-free and free of Qt-thread-affine // calls. The silence-timer stop and ring-buffer readPos snap that used diff --git a/src/core/VirtualAudioBridge.h b/src/core/VirtualAudioBridge.h index 86e0ca108..7261a97e7 100644 --- a/src/core/VirtualAudioBridge.h +++ b/src/core/VirtualAudioBridge.h @@ -45,7 +45,9 @@ class VirtualAudioBridge : public QObject { // NUM_CHANNELS stays the compile-time array bound; this only bounds the // open loop so the device list follows the radio (#4854). Dynamic resize // after connect is a follow-up (issue #4935). - bool open(int activeChannels = NUM_CHANNELS); + // receiveOnly opens no TX endpoint and never polls application TX audio. + bool open(int activeChannels = NUM_CHANNELS, bool receiveOnly = false); + bool isReceiveOnly() const { return m_receiveOnly; } void close(); bool isOpen() const { return m_open.load(std::memory_order_acquire); } @@ -67,11 +69,12 @@ class VirtualAudioBridge : public QObject { public slots: // Feed decoded DAX audio for a channel (1-8). - // pcm format: int16 stereo, 24 kHz, little-endian. + // pcm format: native float32 stereo, 24 kHz. void feedDaxAudio(int channel, const QByteArray& pcm); // When transmitting, feed silence to all RX channels so apps don't starve. void setTransmitting(bool tx); + void resetRxChannel(int channel); signals: void txAudioReady(const QByteArray& pcm, const AetherSDR::TxCoordinator::Context& context); @@ -98,6 +101,7 @@ public slots: // Teardown loops still walk the full NUM_CHANNELS bound — closing an // unopened slot is a no-op. (#4854) int m_activeChannels{NUM_CHANNELS}; + bool m_receiveOnly{false}; float m_gain{0.5f}; // -6 dB default — GUI-only std::atomic m_channelGain[NUM_CHANNELS]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f}; float m_txGain{0.5f}; diff --git a/src/core/backends/IRadioBackend.h b/src/core/backends/IRadioBackend.h index 7014d6f6c..977bb1b6a 100644 --- a/src/core/backends/IRadioBackend.h +++ b/src/core/backends/IRadioBackend.h @@ -685,6 +685,8 @@ class IRadioBackend : public QObject { { Q_UNUSED(sliceId); Q_UNUSED(on); Q_UNUSED(level); } + virtual void setSliceAutoSquelch(int sliceId, bool enabled, int marginDb) + { Q_UNUSED(sliceId); Q_UNUSED(enabled); Q_UNUSED(marginDb); } // BroadcastFmReceive declares supported values; accepted state returns in // SliceDelta only after the receiver adopts the requested configuration. virtual void setSliceWfmForceMono(int sliceId, bool forceMono) diff --git a/src/core/backends/MeterDef.h b/src/core/backends/MeterDef.h index af80e8116..471bb94d3 100644 --- a/src/core/backends/MeterDef.h +++ b/src/core/backends/MeterDef.h @@ -23,6 +23,7 @@ struct MeterDef { double low{0.0}; double high{0.0}; QString description; + QString unavailableReason; // Empty unless measurement is explicitly unavailable. }; } // namespace AetherSDR diff --git a/src/core/backends/RadioCapabilities.h b/src/core/backends/RadioCapabilities.h index a2add30c0..8efb3aadd 100644 --- a/src/core/backends/RadioCapabilities.h +++ b/src/core/backends/RadioCapabilities.h @@ -63,6 +63,12 @@ struct ReceiveAudioControl { SliceFrequencyControl::Authority authority{SliceFrequencyControl::Authority::Unknown}; // Both gain (0..100) and mute must act on the shared slice's RX audio. }; +// Typed pre-monitor sliceAudioFrameReady export; grants no TX, IQ or control verb. +struct ReceiveAudioExport { + QVector sampleRatesHz; + int maximumReceivers = 0; +}; + // Broadcast FM controls are available only when their typed request is supported. struct BroadcastFmReceive { QVector deemphasisUs; @@ -327,6 +333,7 @@ struct RadioCapabilities { std::optional receiveModeControl; std::optional receiveFilterControl; std::optional receiveAudioControl; + std::optional receiveAudioExport; std::optional broadcastFmReceive; std::optional receivePanCenterControl; std::optional receivePanBandwidthControl; diff --git a/src/core/backends/ReceiveCommand.h b/src/core/backends/ReceiveCommand.h index fa563d915..c7411af6d 100644 --- a/src/core/backends/ReceiveCommand.h +++ b/src/core/backends/ReceiveCommand.h @@ -116,8 +116,16 @@ struct SliceSquelchRequest { int level{0}; bool enabledChanged{false}; bool levelChanged{false}; + enum class Kind { Manual, Automatic }; + Kind kind{Kind::Manual}; + int marginDb{10}; - bool valid() const { return level >= 0 && level <= 100; } + bool valid() const + { + return level >= 0 && level <= 100 + && (kind == Kind::Manual + || (kind == Kind::Automatic && marginDb >= 5 && marginDb <= 20)); + } }; } // namespace AetherSDR diff --git a/src/core/backends/SliceDelta.h b/src/core/backends/SliceDelta.h index 3cc870b9a..4b7d8c116 100644 --- a/src/core/backends/SliceDelta.h +++ b/src/core/backends/SliceDelta.h @@ -107,6 +107,8 @@ struct SliceDelta { std::optional agcOffLevel; std::optional squelchOn; std::optional squelchLevel; + std::optional automaticSquelch; + std::optional automaticSquelchMarginDb; std::optional wfmDeemphasisUs; std::optional wfmForceMono; std::optional wfmReceptionDiagnostics; diff --git a/src/core/backends/SquelchLevelScale.h b/src/core/backends/SquelchLevelScale.h index 8666bd7d6..455e4e386 100644 --- a/src/core/backends/SquelchLevelScale.h +++ b/src/core/backends/SquelchLevelScale.h @@ -30,6 +30,11 @@ struct SquelchLevelScale { // threshold rather than sending a margin. False keeps the #2504 mode rule // and the radio-owned squelch state every other family has. bool modesExclusive = false; + // Auto is computed by the receiver, through requestSliceSquelch, rather + // than from the display FFT. Observed state returns in SliceDelta. + bool automaticInEngine = false; + // False when detector and display use different measurement bandwidths. + bool spectrumComparable = true; bool operator==(const SquelchLevelScale&) const = default; @@ -68,7 +73,7 @@ struct SquelchLevelScale { [[nodiscard]] inline std::optional squelchScaleForMode( const std::optional& published, const QString& mode) { - if (!published || !published->appliesTo(mode)) { + if (!published || !published->spectrumComparable || !published->appliesTo(mode)) { return std::nullopt; } return published; diff --git a/src/core/backends/anan/AnanBackend.cpp b/src/core/backends/anan/AnanBackend.cpp index 0ba18d691..30b7f4d86 100644 --- a/src/core/backends/anan/AnanBackend.cpp +++ b/src/core/backends/anan/AnanBackend.cpp @@ -468,6 +468,7 @@ RadioCapabilities AnanBackend::capabilities() const // backend, which is what the record promises. Engine authority: the state is // ours, the radio echoes nothing back, and there is no register to read. c.receiveAudioControl = ReceiveAudioControl{SliceFrequencyControl::Authority::Engine}; + c.receiveAudioExport = std::nullopt; // native DAX export not qualified for this backend c.receivePanCenterControl = std::nullopt; // center also retunes the slice c.receivePanBandwidthControl = ReceivePanRangeControl{SliceFrequencyControl::Authority::Engine, 48'000, 1'536'000}; diff --git a/src/core/backends/flex/FlexBackend.cpp b/src/core/backends/flex/FlexBackend.cpp index fe00972ca..ecab38915 100644 --- a/src/core/backends/flex/FlexBackend.cpp +++ b/src/core/backends/flex/FlexBackend.cpp @@ -214,6 +214,7 @@ RadioCapabilities FlexBackend::capabilities() const {QStringLiteral("SAM"), -12000, -10, 10, 12000, 20, 24000}, {QStringLiteral("DSB"), -12000, -10, 10, 12000, 20, 24000}}}; caps.receiveAudioControl = std::nullopt; // legacy wire mixer path has not migrated + caps.receiveAudioExport = std::nullopt; // native DAX export not qualified for this backend caps.receivePanCenterControl = std::nullopt; // unknown coverage including transverters caps.receivePanBandwidthControl = std::nullopt; // legacy coupled geometry path caps.txPowerBands = {}; @@ -542,6 +543,9 @@ ReceiveDispatch FlexBackend::requestSliceAudio(int sliceId, const SliceAudioRequ ReceiveDispatch FlexBackend::requestSliceSquelch(int sliceId, const SliceSquelchRequest& request) { + if (request.kind != SliceSquelchRequest::Kind::Manual) { + return ReceiveDispatch::Unsupported; + } if (sliceId < 0 || !request.valid()) { return ReceiveDispatch::Unsupported; } diff --git a/src/core/backends/flex/PanadapterStream.cpp b/src/core/backends/flex/PanadapterStream.cpp index e4018ff68..9b8e4f488 100644 --- a/src/core/backends/flex/PanadapterStream.cpp +++ b/src/core/backends/flex/PanadapterStream.cpp @@ -1680,6 +1680,7 @@ const char* PanadapterStream::daxConsumerName(DaxConsumer who) case DaxConsumer::Clock: return "clock"; case DaxConsumer::CwDecoder: return "cw-decoder"; case DaxConsumer::RttyDecoder: return "rtty-decoder"; + case DaxConsumer::Ax25Decoder: return "ax25-decoder"; } return "?"; } @@ -1815,7 +1816,7 @@ QVector PanadapterStream::daxChannelSnapsh s.createPending = it->createPending; for (DaxConsumer who : {DaxConsumer::Bridge, DaxConsumer::Tci, DaxConsumer::Rade, DaxConsumer::Clock, DaxConsumer::CwDecoder, - DaxConsumer::RttyDecoder}) { + DaxConsumer::RttyDecoder, DaxConsumer::Ax25Decoder}) { if (it->holders & daxHolderBit(who)) s.holders << QString::fromLatin1(daxConsumerName(who)); } diff --git a/src/core/backends/flex/PanadapterStream.h b/src/core/backends/flex/PanadapterStream.h index a1a5c3551..ebc006d26 100644 --- a/src/core/backends/flex/PanadapterStream.h +++ b/src/core/backends/flex/PanadapterStream.h @@ -133,6 +133,7 @@ class PanadapterStream : public QObject { Clock = 3, // AetherClock time-signal decode engine CwDecoder = 4, // selected receiver CW decoder (pre-monitor DAX) RttyDecoder = 5, // selected receiver RTTY decoder (pre-monitor DAX) + Ax25Decoder = 6, // selected receiver AX.25/APRS decoder (pre-monitor DAX) }; static const char* daxConsumerName(DaxConsumer who); diff --git a/src/core/backends/hl2/Hl2Backend.cpp b/src/core/backends/hl2/Hl2Backend.cpp index 7817f1f1d..37ce63332 100644 --- a/src/core/backends/hl2/Hl2Backend.cpp +++ b/src/core/backends/hl2/Hl2Backend.cpp @@ -1837,6 +1837,7 @@ RadioCapabilities Hl2Backend::capabilities() const {QStringLiteral("AM"), -12000, -10, 10, 12000, 20, 24000}, {QStringLiteral("SAM"), -12000, -10, 10, 12000, 20, 24000}}}; c.receiveAudioControl = ReceiveAudioControl{SliceFrequencyControl::Authority::Engine}; + c.receiveAudioExport = std::nullopt; // native DAX export not qualified for this backend c.receivePanCenterControl = ReceivePanRangeControl{SliceFrequencyControl::Authority::Engine, 100'000, 38'400'000}; c.receivePanBandwidthControl = std::nullopt; // radio-wide rate can retire other receivers @@ -3154,6 +3155,9 @@ ReceiveDispatch Hl2Backend::requestSliceDsp(int sliceId, const SliceDspRequest& ReceiveDispatch Hl2Backend::requestSliceSquelch(int sliceId, const SliceSquelchRequest& request) { + if (request.kind != SliceSquelchRequest::Kind::Manual) { + return ReceiveDispatch::Unsupported; + } if (!request.valid() || !rx(ddcForSlice(sliceId))) { return ReceiveDispatch::Unsupported; } diff --git a/src/core/backends/icom/IcomCivBackend.cpp b/src/core/backends/icom/IcomCivBackend.cpp index c8298f689..9e8738863 100644 --- a/src/core/backends/icom/IcomCivBackend.cpp +++ b/src/core/backends/icom/IcomCivBackend.cpp @@ -311,6 +311,7 @@ RadioCapabilities IcomCivBackend::capabilities() const c.receiveModeControl = std::nullopt; c.receiveFilterControl = std::nullopt; c.receiveAudioControl = std::nullopt; + c.receiveAudioExport = std::nullopt; // native DAX export not qualified for this backend c.receivePanCenterControl = std::nullopt; c.receivePanBandwidthControl = std::nullopt; @@ -4405,6 +4406,9 @@ ReceiveDispatch IcomCivBackend::requestSliceAudio(int sliceId, const SliceAudioR ReceiveDispatch IcomCivBackend::requestSliceSquelch(int sliceId, const SliceSquelchRequest& request) { + if (request.kind != SliceSquelchRequest::Kind::Manual) { + return ReceiveDispatch::Unsupported; + } if (!m_connected || !m_session || !m_model || sliceId != 0 || !request.valid()) { return ReceiveDispatch::Unsupported; } diff --git a/src/core/backends/rtl/RtlCaptureTransaction.cpp b/src/core/backends/rtl/RtlCaptureTransaction.cpp index 70a83339e..760658e0c 100644 --- a/src/core/backends/rtl/RtlCaptureTransaction.cpp +++ b/src/core/backends/rtl/RtlCaptureTransaction.cpp @@ -242,6 +242,8 @@ RtlCaptureTransaction::Submission RtlCaptureTransaction::submit(const Desired& d || receiver.audioGain < 0 || receiver.audioGain > 100 || receiver.audioPan < 0 || receiver.audioPan > 100 || receiver.squelchLevel < 0 || receiver.squelchLevel > 100 + || receiver.automaticSquelchMarginDb < 5 || receiver.automaticSquelchMarginDb > 20 + || (receiver.automaticSquelch && !receiver.squelchEnabled) || (receiver.wfmDeemphasisUs != 50 && receiver.wfmDeemphasisUs != 75) || (receiver.squelchEnabled && receiver.mode != Mode::Fm && receiver.mode != Mode::Fmn)) { return {{}, Policy::Error::InvalidNumber}; diff --git a/src/core/backends/rtl/RtlCaptureTransaction.h b/src/core/backends/rtl/RtlCaptureTransaction.h index 81b669748..79d1ccf6f 100644 --- a/src/core/backends/rtl/RtlCaptureTransaction.h +++ b/src/core/backends/rtl/RtlCaptureTransaction.h @@ -33,6 +33,8 @@ class RtlCaptureTransaction final { int squelchLevel = 20; int wfmDeemphasisUs = 75; // broadcast standard; validated 50 or 75 only bool wfmForceMono = false; // selected decoder matrix policy, not observed pilot + bool automaticSquelch = false; + int automaticSquelchMarginDb = 10; bool operator==(const Receiver&) const = default; }; struct Desired { diff --git a/src/core/backends/rtl/RtlReceivePipeline.cpp b/src/core/backends/rtl/RtlReceivePipeline.cpp index c9096a069..9bb3c6300 100644 --- a/src/core/backends/rtl/RtlReceivePipeline.cpp +++ b/src/core/backends/rtl/RtlReceivePipeline.cpp @@ -1,11 +1,25 @@ #include "RtlReceivePipeline.h" #include +#include #include #include #include namespace AetherSDR::rtl { namespace { +unsigned packetSlots(std::size_t capacity) +{ + // Native WFM is the highest analog packet rate: one 256-frame tap per + // receiver plus two 128-frame speaker packets, all at 48 kHz. The old + // 127 usable packets cover ~225 ms for one receiver but only ~113 ms for + // four. Startup UI publication measured 151 ms with no owner drain. + // Scale the same nominal headroom, rounded up, once before acquisition; + // the qualified singleton remains exactly 128 allocated slots. This is + // finite jitter tolerance, not permission to block the owner indefinitely. + constexpr unsigned kSingleReceiverUsablePackets = 127; + const unsigned receivers = static_cast(std::clamp(capacity, 1, 8)); + return 1 + (kSingleReceiverUsablePackets * (receivers + 2) + 2) / 3; +} std::optional mixerOrigin(std::uint64_t firstSample, double rateHz) noexcept { // The extractor accepts only integral, bounded achieved rates. Validate @@ -40,7 +54,8 @@ WdspChannel::Mode dspMode(RtlCaptureTransaction::Mode mode) } } RtlReceivePipeline::RtlReceivePipeline(std::size_t capacity, bool enableWfm) - : m_enableWfm(enableWfm), m_registry({8, capacity, std::min(32, capacity * 2)}) + : m_enableWfm(enableWfm), m_registry({8, capacity, std::min(32, capacity * 2)}), + m_packetSlots(packetSlots(capacity)), m_packets(std::make_unique(m_packetSlots)) { for (auto& monitor : m_monitor) { monitor.store(100 | (50 << 8)); } } @@ -59,8 +74,17 @@ RtlReceivePipeline::Submission RtlReceivePipeline::prepareDetailed( || receiver.audioGain < 0 || receiver.audioGain > 100 || receiver.audioPan < 0 || receiver.audioPan > 100 || receiver.squelchLevel < 0 || receiver.squelchLevel > 100 + || receiver.automaticSquelchMarginDb < 5 || receiver.automaticSquelchMarginDb > 20 + || (receiver.automaticSquelch && !receiver.squelchEnabled) || (receiver.wfmDeemphasisUs != 50 && receiver.wfmDeemphasisUs != 75) || (receiver.mode == Transaction::Mode::Wfm && receiver.squelchEnabled)) { return Submission::Failed; } + // Parking changes reception membership, not the configured graph's + // admission contract. A legacy recipe cannot hide outside the capture + // and then resume alongside a native 48 kHz receiver. + const bool native = receiver.mode == Transaction::Mode::Fm + || receiver.mode == Transaction::Mode::Fmn + || (m_enableWfm && receiver.mode == Transaction::Mode::Wfm); + if (!native && state.receivers.size() != 1) { return Submission::Failed; } validated[id] = true; } const unsigned faults = m_faults.load(std::memory_order_acquire); @@ -167,7 +191,9 @@ RtlReceivePipeline::Submission RtlReceivePipeline::prepareDetailed( m_specs = specs; m_prepared = m_registry.service().requested; for (const auto& receiver : state.receivers) { - m_nextSquelch[receiver.passband.stableId] = {receiver.squelchEnabled, receiver.squelchLevel}; + m_nextMeterPassbands[receiver.passband.stableId] = receiver.passband; + m_nextSquelch[receiver.passband.stableId] = {receiver.squelchEnabled, receiver.squelchLevel, + receiver.automaticSquelch, receiver.automaticSquelchMarginDb}; m_nextMonitor[receiver.passband.stableId] = static_cast(receiver.audioGain | (receiver.audioPan << 8) | (receiver.audioMute ? 1 << 16 : 0)); } @@ -192,12 +218,14 @@ bool RtlReceivePipeline::adopt() noexcept for (std::size_t slot = 0; slot < m_monitor.size(); ++slot) { m_monitor[slot].store(m_nextMonitor[slot], std::memory_order_relaxed); m_squelchConfig[slot] = m_nextSquelch[slot]; - m_squelch[slot].configure(m_nextSquelch[slot].enabled, m_nextSquelch[slot].level, resetSquelch); + m_squelch[slot].configure(m_nextSquelch[slot].enabled, m_nextSquelch[slot].level, resetSquelch, + m_nextSquelch[slot].automatic, m_nextSquelch[slot].marginDb); } if (resetSquelch) { m_spectrumFresh = false; m_squelchEpoch.fill(0); } m_token = m_nextToken; m_capture = m_nextCapture; m_captureEpoch = m_nextEpoch; m_legacy = m_nextLegacy; m_receivingMask = m_nextReceivingMask; + m_meterPassbands = m_nextMeterPassbands; m_faults.store(0, std::memory_order_release); // old bank faults cannot request a second reset return true; } @@ -213,6 +241,54 @@ void RtlReceivePipeline::observeSpectrum(std::span bins, std::uint6 if (bins.size() != m_spectrum.size()) { return; } std::copy(bins.begin(), bins.end(), m_spectrum.begin()); m_spectrumFirstSample = firstSample; m_spectrumFresh = true; + const std::uint64_t generation = m_rfGeneration.load(std::memory_order_acquire); + if (!(generation & 1)) { return; } // Keep the independent SQL spectrum above. + if (!m_token.session || m_capture.achievedSampleRateHz <= 0) { return; } + RfObservation observation; + observation.token = m_token; + observation.generation = generation; + observation.producedMs = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); + const double binHz = m_capture.achievedSampleRateHz / bins.size(); + for (int id = 0; id < 8; ++id) { + if (!(m_receivingMask & (1u << id))) { continue; } + const auto& band = m_meterPassbands[id]; + const double offset = band.carrierHz - m_capture.centerHz; + const int low = std::clamp(int(std::floor((offset + band.filterLowHz) / binHz + 1024)), 0, 2047); + const int high = std::clamp(int(std::ceil((offset + band.filterHighHz) / binHz + 1024)), low, 2047); + float peak = -120.0f; + bool valid = true; + for (int bin = low; bin <= high; ++bin) { + if (!std::isfinite(bins[bin])) { valid = false; break; } + peak = std::max(peak, bins[bin]); + } + if (valid) { observation.mask |= 1u << id; observation.dbfs[id] = peak; } + } + const unsigned write = m_rfWrite.load(std::memory_order_relaxed); + const unsigned next = (write + 1) % m_rfObservations.size(); + if (next != m_rfRead.load(std::memory_order_acquire)) { + m_rfObservations[write] = observation; + m_rfWrite.store(next, std::memory_order_release); + } +} +bool RtlReceivePipeline::takeRfObservation(RfObservation& output) noexcept +{ + for (unsigned count = 0; count < m_rfObservations.size(); ++count) { + const unsigned read = m_rfRead.load(std::memory_order_relaxed); + if (read == m_rfWrite.load(std::memory_order_acquire)) { return false; } + output = m_rfObservations[read]; + m_rfRead.store((read + 1) % m_rfObservations.size(), std::memory_order_release); + const std::uint64_t generation = m_rfGeneration.load(std::memory_order_acquire); + if ((generation & 1) && output.generation == generation) { return true; } + } + return false; +} +void RtlReceivePipeline::setReceiveMetersEnabled(bool enabled) noexcept +{ + const std::uint64_t generation = m_rfGeneration.load(std::memory_order_relaxed); + if (bool(generation & 1) != enabled) { + m_rfGeneration.store(generation + 1, std::memory_order_release); + } } void RtlReceivePipeline::setMonitor(int slot, int gain, int pan, bool mute) noexcept { @@ -224,6 +300,10 @@ void RtlReceivePipeline::process(const RtlReceiverRegistry::SampleBlock& block, std::span views) noexcept { if (m_legacy) { return; } + if (m_startupTrace && !m_startupTrace->nativeFirstNs) { + m_startupTrace->markProducer(RtlStartupTrace::FirstNativeBlock, m_token.session, + m_token.revision, block.firstSample, -1, 0, block.samples.size()); + } std::array inputs; for (std::size_t i = 0; i < views.size(); ++i) { const auto& spec = *views[i].spec; @@ -255,7 +335,8 @@ void RtlReceivePipeline::process(const RtlReceiverRegistry::SampleBlock& block, const int slot = view.spec->handle.slot; auto& gate = m_squelch[slot]; if (m_squelchEpoch[slot] != view.spec->epoch) { - gate.configure(m_squelchConfig[slot].enabled, m_squelchConfig[slot].level, true); + gate.configure(m_squelchConfig[slot].enabled, m_squelchConfig[slot].level, true, + m_squelchConfig[slot].automatic, m_squelchConfig[slot].marginDb); m_squelchEpoch[slot] = view.spec->epoch; } if (m_spectrumFresh && !view.spec->dsp.wbfmReceive) { @@ -268,16 +349,12 @@ void RtlReceivePipeline::process(const RtlReceiverRegistry::SampleBlock& block, (offset + passband.filterLowHz) / binHz + 1024)), 0, 2047); const int high = std::clamp(static_cast(std::ceil( (offset + passband.filterHighHz) / binHz + 1024)), low, 2047); - float peak = -120.0f; - for (int bin = low; bin <= high; ++bin) { - if (!std::isfinite(m_spectrum[bin])) { peak = std::numeric_limits::quiet_NaN(); break; } - peak = std::max(peak, m_spectrum[bin]); - } - gate.observe(peak, clock(m_spectrumFirstSample)); + gate.observeSpectrum(m_spectrum, low, high, clock(m_spectrumFirstSample)); } if (!view.receiver->processCapture(block, *this)) { const unsigned bit = 1u << slot; if (!(m_faults.fetch_or(bit, std::memory_order_release) & bit)) { + m_receiverWithdrawals[slot].fetch_add(1, std::memory_order_relaxed); TraceEvent event = traceContext(); event.slot = slot; event.stableId = view.spec->passband.stableId; event.instance = view.spec->handle.instance; @@ -295,11 +372,19 @@ void RtlReceivePipeline::process(const RtlReceiverRegistry::SampleBlock& block, } RtlReceivePipeline::Diagnostics RtlReceivePipeline::diagnostics() const noexcept { - return {m_observed.load(std::memory_order_acquire), + Diagnostics result{m_observed.load(std::memory_order_acquire), m_drops.load(std::memory_order_relaxed), m_mixerLate.load(std::memory_order_relaxed), m_mixerRejected.load(std::memory_order_relaxed), m_mixerConfigurationFailures.load(std::memory_order_relaxed), m_traceDrops.load(std::memory_order_relaxed)}; + result.queuedPackets = (m_write.load(std::memory_order_acquire) + m_packetSlots + - m_read.load(std::memory_order_acquire)) % m_packetSlots; + result.packetQueueHighWater = m_packetQueueHighWater.load(std::memory_order_relaxed); + result.packetQueueCapacity = m_packetSlots - 1; + for (std::size_t slot = 0; slot < m_receiverWithdrawals.size(); ++slot) { + result.receiverWithdrawals[slot] = m_receiverWithdrawals[slot].load(std::memory_order_relaxed); + } + return result; } void RtlReceivePipeline::audioBlock(const RtlReceiverRegistry::ReceiverSpec& spec, std::uint64_t first, std::span left, std::span right, bool discontinuity) noexcept @@ -330,6 +415,18 @@ void RtlReceivePipeline::audioBlockWithStatus(const RtlReceiverRegistry::Receive packet.samples[2 * i] = gatedLeft[i]; packet.samples[2 * i + 1] = gatedRight[i]; } enqueue(packet); // independent tap, before gain/mute/pan + // A retained receiver can finish buffered PCM from before a new speaker + // epoch. Keep its tap continuous, but do not offer that retired prefix to + // the new mix. Use the fixed origin, never the advancing playback cursor: + // genuinely late current-epoch blocks must still fail mixer validation. + if (m_mixerOrigin && first < *m_mixerOrigin && !left.empty() + && left.size() <= *m_mixerOrigin - first + && std::ranges::all_of(std::span(gatedLeft).first(left.size()), + [](float value) { return std::isfinite(value); }) + && std::ranges::all_of(std::span(gatedRight).first(right.size()), + [](float value) { return std::isfinite(value); })) { + return; + } m_mixer.push(packet.slot, packet.instance, packet.receiverEpoch, first, std::span(gatedLeft).first(left.size()), std::span(gatedRight).first(right.size())); } @@ -382,9 +479,43 @@ bool RtlReceivePipeline::takeTraceEvent(TraceEvent& output) noexcept bool RtlReceivePipeline::enqueue(const Packet& packet) noexcept { const unsigned write = m_write.load(std::memory_order_relaxed); - const unsigned next = (write + 1) % kPackets; - if (next == m_read.load(std::memory_order_acquire)) { m_drops.fetch_add(1, std::memory_order_relaxed); return false; } + const unsigned next = (write + 1) % m_packetSlots; + const unsigned read = m_read.load(std::memory_order_acquire); + if (m_startupTrace) { ++m_startupTrace->packetAttempts; } + if (next == read) { + m_drops.fetch_add(1, std::memory_order_relaxed); + if (m_startupTrace) { + ++m_startupTrace->packetDrops; + if (!m_startupTrace->firstDrop) { + m_startupTrace->firstDrop = true; + m_startupTrace->markProducer(RtlStartupTrace::FirstDrop, packet.token.session, + packet.token.revision, m_traceCaptureFirst, packet.slot, packet.firstSample, + packet.frames, m_packetSlots - 1); + } + } + return false; + } + if (m_startupTrace && m_startupTrace->firstDrop && !m_startupTrace->firstRecovery) { + m_startupTrace->firstRecovery = true; + m_startupTrace->markProducer(RtlStartupTrace::FirstRecovery, packet.token.session, + packet.token.revision, m_traceCaptureFirst, packet.slot, packet.firstSample, + packet.frames, (write + m_packetSlots - read) % m_packetSlots); + } m_packets[write] = packet; + m_packets[write].enqueuedMonotonicNs = static_cast( + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); + const std::uint64_t queued = (next + m_packetSlots - read) % m_packetSlots; + if (m_startupTrace && queued == m_packetSlots - 1 && !m_startupTrace->firstFull) { + m_startupTrace->firstFull = true; + m_startupTrace->markProducer(RtlStartupTrace::FirstQueueFull, packet.token.session, + packet.token.revision, m_traceCaptureFirst, packet.slot, packet.firstSample, + packet.frames, queued); + } + // Sole producer: no compare/exchange loop or callback contention needed. + if (queued > m_packetQueueHighWater.load(std::memory_order_relaxed)) { + m_packetQueueHighWater.store(queued, std::memory_order_relaxed); + } m_write.store(next, std::memory_order_release); return true; } @@ -393,7 +524,7 @@ bool RtlReceivePipeline::takePacket(Packet& output) noexcept const unsigned read = m_read.load(std::memory_order_relaxed); if (read == m_write.load(std::memory_order_acquire)) { return false; } output = m_packets[read]; - m_read.store((read + 1) % kPackets, std::memory_order_release); + m_read.store((read + 1) % m_packetSlots, std::memory_order_release); return true; } } // namespace AetherSDR::rtl diff --git a/src/core/backends/rtl/RtlReceivePipeline.h b/src/core/backends/rtl/RtlReceivePipeline.h index 976f9173c..bee15d03b 100644 --- a/src/core/backends/rtl/RtlReceivePipeline.h +++ b/src/core/backends/rtl/RtlReceivePipeline.h @@ -3,10 +3,12 @@ #include "RtlCaptureTransaction.h" #include "RtlReceiverRegistry.h" #include "RtlSquelchGate.h" +#include "RtlStartupTrace.h" #include #include #include #include +#include namespace AetherSDR::rtl { // One acquisition context and one serialized control owner. Preparation, @@ -24,6 +26,21 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, std::uint64_t mixerRejectedBlocks = 0; std::uint64_t mixerConfigurationFailures = 0; std::uint64_t droppedTraceEvents = 0; + std::uint64_t queuedPackets = 0; + std::uint64_t packetQueueHighWater = 0; + std::uint64_t packetQueueCapacity = 0; // usable packets, excluding the SPSC sentinel + std::array receiverWithdrawals{}; + // The worker fills these acquisition-lifetime observations in its + // snapshot. A pipeline-only fixture truthfully leaves them at zero. + std::uint64_t callbackCount = 0; + std::uint64_t callbackIqSamples = 0; + std::uint64_t malformedCallbacks = 0; + std::uint64_t callbackTotalNs = 0; + std::uint64_t callbackMaxNs = 0; + std::uint64_t callbackDeadlineMisses = 0; + std::uint64_t usbReadStarts = 0; + std::uint64_t usbCancelRequests = 0; + std::array callbackDurationBuckets{}; }; // Independently sampled lifetime counters, not one atomic point-in-time // transaction. The control owner caches these for healthSnapshot(). @@ -33,6 +50,7 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, std::uint64_t captureEpoch = 0; std::uint64_t instance = 0; std::uint64_t receiverEpoch = 0; + std::uint64_t enqueuedMonotonicNs = 0; // mailbox residence instrumentation std::uint64_t firstSample = 0; int slot = -1; std::size_t frames = 0; @@ -45,6 +63,22 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, std::optional wfmReception; std::array samples{}; }; + // Windowed peak FFT bin in each accepted passband, relative to ADC full + // scale. This is neither calibrated dBm nor integrated channel power. + struct RfObservation { + Transaction::Token token; + std::uint64_t producedMs = 0; + std::uint64_t generation = 0; + std::uint8_t mask = 0; + std::array dbfs{}; + }; + bool takeRfObservation(RfObservation& output) noexcept; + void setReceiveMetersEnabled(bool enabled) noexcept; + bool rfObservationIsCurrent(const RfObservation& observation) const noexcept + { + const auto generation = m_rfGeneration.load(std::memory_order_acquire); + return (generation & 1) && observation.generation == generation; + } // Bounded acquisition observations; no formatting, clock reads, allocation, // or logging on the sample callback. The owner drains across bank adoption // so repair cannot erase the fault that requested it. @@ -83,7 +117,7 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, bool adopt() noexcept; bool process(std::uint64_t firstSample, std::span> samples) noexcept; // Acquisition only, before process() for this capture block. Shares the - // display's existing FFT; the caller supplies capture-sample identity. + // fixed 2048-bin detector FFT; caller supplies capture-sample identity. void observeSpectrum(std::span bins, std::uint64_t firstSample) noexcept; void stop(); // after acquisition joined bool takePacket(Packet& output) noexcept; @@ -91,6 +125,9 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, bool needsRepair() const noexcept { return m_faults.load(std::memory_order_acquire) != 0; } std::uint64_t droppedPackets() const noexcept { return m_drops.load(std::memory_order_relaxed); } bool legacy() const noexcept { return m_legacy; } + void setStartupTrace(RtlStartupTrace* trace) noexcept { m_startupTrace = trace; } + std::uint64_t startupQueuedPackets() const noexcept + { return (m_write.load(std::memory_order_acquire) + m_packetSlots - m_read.load(std::memory_order_acquire)) % m_packetSlots; } private: friend struct RtlReceivePipelineTestAccess; void process(const RtlReceiverRegistry::SampleBlock&, std::span) noexcept override; @@ -104,6 +141,7 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, const RtlAudioMixer::MissingMask&) noexcept override; void enqueueTrace(const TraceEvent&) noexcept; TraceEvent traceContext() const noexcept; + RtlStartupTrace* m_startupTrace = nullptr; // fixed before acquisition; worker retains ownership const bool m_enableWfm; RtlReceiverRegistry m_registry; RtlReceiverRegistry::SampleReader m_reader; @@ -139,21 +177,35 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, std::atomic m_faults{0}; std::array, 8> m_monitor; std::array m_nextMonitor{}; - struct SquelchConfig { bool enabled = false; int level = 20; }; + struct SquelchConfig { + bool enabled = false; int level = 20; + bool automatic = false; int marginDb = 10; + }; std::array m_nextSquelch{}; std::array m_squelchConfig{}; std::array m_squelch; std::array m_squelchEpoch{}; + std::array m_nextMeterPassbands{}, m_meterPassbands{}; + std::array m_rfObservations{}; + // Main-thread writer; callback snapshots the epoch. Odd means enabled. + // Epoch tagging rejects queued/in-flight samples across disable/re-enable. + std::atomic m_rfGeneration{1}; + alignas(64) std::atomic m_rfWrite{0}; + alignas(64) std::atomic m_rfRead{0}; std::array m_spectrum{}; std::uint64_t m_spectrumFirstSample = 0; bool m_spectrumFresh = false; // SPSC queue. Overflow drops the new packet; the consumer observes the // sample-position gap and marks its next typed frame discontinuous. - static constexpr unsigned kPackets = 128; - std::array m_packets; + // Fixed before acquisition. Preserve the singleton's nominal buffering + // time as independent analog taps share the existing speaker queue. + const unsigned m_packetSlots; + std::unique_ptr m_packets; alignas(64) std::atomic m_write{0}; alignas(64) std::atomic m_read{0}; std::atomic m_drops{0}; + std::atomic m_packetQueueHighWater{0}; + std::array, 8> m_receiverWithdrawals{}; std::atomic m_mixerLate{0}; std::atomic m_mixerRejected{0}; std::atomic m_mixerConfigurationFailures{0}; diff --git a/src/core/backends/rtl/RtlSdrBackend.cpp b/src/core/backends/rtl/RtlSdrBackend.cpp index 6538b7e7e..bc5dc8e9e 100644 --- a/src/core/backends/rtl/RtlSdrBackend.cpp +++ b/src/core/backends/rtl/RtlSdrBackend.cpp @@ -15,6 +15,7 @@ #include #include +#include #include namespace AetherSDR::rtl { @@ -26,6 +27,14 @@ Q_LOGGING_CATEGORY(lcRtlReceive, "aether.rtl.receive", QtWarningMsg) constexpr double kMinTuneHz = 24'000.0; constexpr double kMaxTuneHz = 1'766'000'000.0; +bool supportsAnalogBank(const RtlCaptureTransaction::Receiver& receiver) +{ + return receiver.mode == RtlCaptureTransaction::Mode::Fm + || receiver.mode == RtlCaptureTransaction::Mode::Fmn + || (RtlReceivePipeline::kQualifiedWfmEnabled + && receiver.mode == RtlCaptureTransaction::Mode::Wfm); +} + bool hasReceivingNarrowFm(const RtlCaptureTransaction::State& state) { return std::ranges::any_of(state.receivers, [&state](const auto& receiver) { @@ -114,6 +123,18 @@ uint32_t RtlSdrBackend::clampSampleRate(uint32_t requestedHz) RtlSdrBackend::RtlSdrBackend(QObject* parent) : IRadioBackend(parent) { + // Process-only, opt-in qualification profile. This does not raise the + // shipped capacity or persist an unqualified limit in a radio profile. +#if (defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_64)) \ + || (defined(Q_OS_MAC) && defined(Q_PROCESSOR_ARM_64)) \ + || (defined(Q_OS_WIN) && defined(Q_PROCESSOR_X86_64)) + const QByteArray evaluation = qgetenv("AETHER_RTL_EVALUATION_RECEIVERS"); + if (qgetenv("AETHER_AUTOMATION") == "1" && (evaluation == "2" || evaluation == "4" || evaluation == "8")) { + m_receiverCapacity = evaluation.toInt(); + m_capture = RtlCaptureTransaction({8, static_cast(m_receiverCapacity)}); + qCWarning(lcRtlReceive) << "Unqualified RTL analog evaluation capacity:" << m_receiverCapacity; + } +#endif m_captureTimer.setInterval(10); connect(&m_captureTimer, &QTimer::timeout, this, &RtlSdrBackend::serviceCapture); } @@ -177,12 +198,12 @@ RadioCapabilities RtlSdrBackend::capabilities() const c.txMonitorControl = std::nullopt; c.hasAgcThreshold = false; c.hasModeIndependentSquelch = false; - // RtlSquelchGate is the receiver's own squelch, FM/FMN only. The gate and - // the pan read the same FFT in dBFS/bin, so the pan floor plus a margin - // lands on the gate and Auto SQL may use it. + // RtlSquelchGate is the receiver's own squelch, FM/FMN only. Its + // detector now uses its own 2048-bin FFT; the display has a different + // bandwidth, so Auto runs in the engine and no display SQL line is drawn. c.squelchLevelScale = SquelchLevelScale{RtlSquelchGate::kReferenceDb, - RtlSquelchGate::kStepDb, {QStringLiteral("FM"), QStringLiteral("FMN")}, true, - QStringLiteral("dBFS/bin"), true}; + RtlSquelchGate::kStepDb, {QStringLiteral("FM"), QStringLiteral("FMN")}, false, + QStringLiteral("dBFS/2048-bin"), true, true, false}; c.agcModes = {QStringLiteral("off"), QStringLiteral("slow"), QStringLiteral("med"), QStringLiteral("fast")}; // Unused TX presentation retains the shared legacy shape; canTransmit @@ -219,6 +240,7 @@ RadioCapabilities RtlSdrBackend::capabilities() const c.broadcastFmReceive = BroadcastFmReceive{{50, 75}, true, true}; } c.receiveAudioControl = ReceiveAudioControl{SliceFrequencyControl::Authority::Engine}; + c.receiveAudioExport = ReceiveAudioExport{{24000, 48000}, m_receiverCapacity}; c.panSpanModel = PanSpanModel{false, false}; if (m_lastPublished && hasReceivingNarrowFm(*m_lastPublished)) { c.receiveCapturePlacement = ReceiveCapturePlacement{ @@ -254,7 +276,7 @@ RadioCapabilities RtlSdrBackend::capabilities() const | RadioCapabilities::ClientSettingsDomain::RfGain | RadioCapabilities::ClientSettingsDomain::Memories; } // Vendor extensions - c.extensions["rtl"] = QVariantMap{{"serial", m_serial}, {"settingsVersion", 1}}; + c.extensions["rtl"] = QVariantMap{{"serial", m_serial}, {"settingsVersion", 1}, {"receiveMetersControl", true}}; c.extensionNamespaces = {"rtl"}; return c; @@ -410,13 +432,19 @@ RtlCaptureTransaction::Receiver RtlSdrBackend::initialReceiver() const void RtlSdrBackend::startCapture(std::unique_ptr worker) { m_worker = std::move(worker); + m_worker->setReceiveMetersEnabled(m_receiveMetersEnabled); + m_startupTrace = qgetenv("AETHER_AUTOMATION") == "1" && qgetenv("AETHER_RTL_STARTUP_TRACE") == "1" + ? std::make_shared() : nullptr; + m_worker->setStartupTrace(m_startupTrace); m_diagnostics = {}; + m_pcmDiagnostics.fill({}); m_wfmStatus.fill(WfmStereoStatus::Unavailable); m_wfmReception.fill({}); m_wfmLastObservation.fill(std::nullopt); for (auto& age : m_wfmPublicationAge) { age.invalidate(); } for (auto& age : m_wfmObservationAge) { age.invalidate(); } m_capture.beginSession(); + m_activeSliceId = -1; m_published = {}; m_lastPublished.reset(); m_viewport.reset(); @@ -494,6 +522,7 @@ void RtlSdrBackend::disconnectRadio() for (auto& age : m_wfmObservationAge) { age.invalidate(); } m_captureTimer.stop(); m_capture.endSession(); + m_activeSliceId = -1; m_published = {}; m_pendingViewport = {}; m_pendingDrag = false; @@ -522,6 +551,9 @@ void RtlSdrBackend::disconnectRadio() connect(worker.get(), &QThread::finished, worker.get(), &QObject::deleteLater); if (!worker->stopReading()) { worker.release(); + } else if (m_startupTrace) { + qCWarning(lcRtlReceive).noquote().nospace() << "RtlStartupTrace " + << QString::fromUtf8(m_startupTrace->jsonAfterJoin()); } } worker.reset(); @@ -541,6 +573,9 @@ IRadioBackend::HealthSnapshot RtlSdrBackend::healthSnapshot() const { if (!m_connected) { return {}; } HealthSnapshot snapshot; + snapshot.values.insert(QStringLiteral("rtlReceiverCapacity"), m_receiverCapacity); + snapshot.values.insert(QStringLiteral("rtlQualifiedReceiverCapacity"), kQualifiedReceiverCapacity); + snapshot.values.insert(QStringLiteral("rtlEvaluationProfile"), m_receiverCapacity > kQualifiedReceiverCapacity); snapshot.order = {QStringLiteral("rtlQueueDrops"), QStringLiteral("rtlMixerLateFrames"), QStringLiteral("rtlMixerRejectedBlocks"), QStringLiteral("rtlMixerConfigurationFailures"), QStringLiteral("rtlReceiveTraceDrops")}; @@ -559,6 +594,101 @@ IRadioBackend::HealthSnapshot RtlSdrBackend::healthSnapshot() const for (int i = 0; i < snapshot.order.size(); ++i) { snapshot.values.insert(snapshot.order[i], QVariant::fromValue(counters[i])); } + const auto value = [&](const char* key, std::uint64_t count) { + snapshot.values.insert(QString::fromLatin1(key), QVariant::fromValue(count)); + }; + value("rtlQueuedPackets", m_diagnostics.queuedPackets); + value("rtlPacketQueueHighWater", m_diagnostics.packetQueueHighWater); + value("rtlPacketQueueCapacity", m_diagnostics.packetQueueCapacity); + value("rtlCallbackCount", m_diagnostics.callbackCount); + value("rtlCallbackIqSamples", m_diagnostics.callbackIqSamples); + value("rtlMalformedCallbacks", m_diagnostics.malformedCallbacks); + value("rtlCallbackTotalNs", m_diagnostics.callbackTotalNs); + value("rtlCallbackMaxNs", m_diagnostics.callbackMaxNs); + value("rtlCallbackDeadlineMisses", m_diagnostics.callbackDeadlineMisses); + value("rtlUsbReadStarts", m_diagnostics.usbReadStarts); + value("rtlUsbCancelRequests", m_diagnostics.usbCancelRequests); + QVariantList histogram, bounds, withdrawals, pcm; + for (std::size_t i = 0; i < m_diagnostics.callbackDurationBuckets.size(); ++i) { + histogram.append(QVariant::fromValue(m_diagnostics.callbackDurationBuckets[i])); + bounds.append(QVariant::fromValue(RtlSdrWorker::kCallbackDurationUpperBoundsNs[i])); + } + for (const auto count : m_diagnostics.receiverWithdrawals) { + withdrawals.append(QVariant::fromValue(count)); + } + for (const auto& stream : m_pcmDiagnostics) { + QVariantMap row; + row.insert(QStringLiteral("packets"), QVariant::fromValue(stream.packets)); + row.insert(QStringLiteral("frames"), QVariant::fromValue(stream.frames)); + row.insert(QStringLiteral("nonzeroSamples"), QVariant::fromValue(stream.nonzeroSamples)); + row.insert(QStringLiteral("nonfiniteSamples"), QVariant::fromValue(stream.nonfiniteSamples)); + row.insert(QStringLiteral("discontinuities"), QVariant::fromValue(stream.discontinuities)); + row.insert(QStringLiteral("queueAgeTotalNs"), QVariant::fromValue(stream.queueAgeTotalNs)); + row.insert(QStringLiteral("queueAgeMaxNs"), QVariant::fromValue(stream.queueAgeMaxNs)); + row.insert(QStringLiteral("maxDeliveryGapMs"), QVariant::fromValue(stream.maxDeliveryGapMs)); + row.insert(QStringLiteral("lastDeliveryMs"), QVariant::fromValue(stream.lastDeliveryMs)); + row.insert(QStringLiteral("peak"), stream.peak); + row.insert(QStringLiteral("sumSquaresLeft"), stream.sumSquaresLeft); + row.insert(QStringLiteral("sumSquaresRight"), stream.sumSquaresRight); + pcm.append(row); + } + snapshot.values.insert(QStringLiteral("rtlCallbackDurationBuckets"), histogram); + snapshot.values.insert(QStringLiteral("rtlCallbackDurationUpperBoundsNs"), bounds); + snapshot.values.insert(QStringLiteral("rtlReceiverWithdrawals"), withdrawals); + snapshot.values.insert(QStringLiteral("rtlPcmStreams"), pcm); + } + if (m_lastPublished) { + QVariantList recipes; + for (const auto& receiver : m_lastPublished->receivers) { + const int stableId = receiver.passband.stableId; + const bool receiving = std::ranges::find(m_lastPublished->receivingIds, stableId) + != m_lastPublished->receivingIds.end(); + QVariantMap recipe; + recipe.insert(QStringLiteral("stableId"), stableId); + recipe.insert(QStringLiteral("selected"), stableId == m_activeSliceId); + recipe.insert(QStringLiteral("receiving"), receiving); + recipe.insert(QStringLiteral("parked"), !receiving); + recipe.insert(QStringLiteral("carrierHz"), receiver.passband.carrierHz); + recipe.insert(QStringLiteral("mode"), modeName(receiver.mode)); + recipe.insert(QStringLiteral("filterLowHz"), receiver.passband.filterLowHz); + recipe.insert(QStringLiteral("filterHighHz"), receiver.passband.filterHighHz); + recipe.insert(QStringLiteral("wfmForceMono"), receiver.wfmForceMono); + recipe.insert(QStringLiteral("wfmDeemphasisUs"), receiver.wfmDeemphasisUs); + recipe.insert(QStringLiteral("audioGain"), receiver.audioGain); + recipe.insert(QStringLiteral("audioPan"), receiver.audioPan); + recipe.insert(QStringLiteral("audioMute"), receiver.audioMute); + recipe.insert(QStringLiteral("squelchEnabled"), receiver.squelchEnabled); + recipe.insert(QStringLiteral("squelchLevel"), receiver.squelchLevel); + recipe.insert(QStringLiteral("automaticSquelch"), receiver.automaticSquelch); + recipe.insert(QStringLiteral("automaticSquelchMarginDb"), receiver.automaticSquelchMarginDb); + recipes.append(recipe); + } + snapshot.values.insert(QStringLiteral("rtlReceiverRecipes"), recipes); + } + const QList> diagnosticRows{ + {QStringLiteral("rtlReceiverCapacity"), tr("Configured receiver capacity")}, + {QStringLiteral("rtlQualifiedReceiverCapacity"), tr("Qualified default receiver capacity")}, + {QStringLiteral("rtlEvaluationProfile"), tr("Unqualified evaluation profile enabled")}, + {QStringLiteral("rtlQueuedPackets"), tr("Audio packets currently queued")}, + {QStringLiteral("rtlPacketQueueHighWater"), tr("Audio packet queue high-water mark")}, + {QStringLiteral("rtlPacketQueueCapacity"), tr("Audio packet queue usable capacity")}, + {QStringLiteral("rtlCallbackCount"), tr("USB callbacks processed")}, + {QStringLiteral("rtlCallbackIqSamples"), tr("IQ samples delivered by USB callbacks")}, + {QStringLiteral("rtlMalformedCallbacks"), tr("Malformed USB callbacks")}, + {QStringLiteral("rtlCallbackTotalNs"), tr("Total callback processing time (ns)")}, + {QStringLiteral("rtlCallbackMaxNs"), tr("Maximum callback processing time (ns)")}, + {QStringLiteral("rtlCallbackDeadlineMisses"), tr("Callbacks exceeding their sample duration")}, + {QStringLiteral("rtlUsbReadStarts"), tr("USB asynchronous read starts")}, + {QStringLiteral("rtlUsbCancelRequests"), tr("USB asynchronous read cancellations")}, + {QStringLiteral("rtlCallbackDurationBuckets"), tr("Callback duration histogram")}, + {QStringLiteral("rtlCallbackDurationUpperBoundsNs"), tr("Callback histogram upper bounds (ns)")}, + {QStringLiteral("rtlReceiverWithdrawals"), tr("Processing-fault withdrawals by stable receiver")}, + {QStringLiteral("rtlPcmStreams"), tr("PCM observations for receivers A-H, then speaker")}, + {QStringLiteral("rtlReceiverRecipes"), tr("Accepted recipes by stable receiver")}}; + snapshot.sections.insert(diagnosticRows.front().first, tr("RTL evaluation measurements (since connect)")); + for (const auto& row : diagnosticRows) { + snapshot.order.append(row.first); + snapshot.labels.insert(row.first, row.second); } if (m_lastPublished) { const auto& capture = m_lastPublished->capture; @@ -603,8 +733,11 @@ bool RtlSdrBackend::createSlice(const QString& panId, double frequencyHz) { if (!m_connected || !m_capture.confirmed() || !std::isfinite(frequencyHz) || m_capture.busy() || (panId != QStringLiteral("0xe1000000") && !panId.isEmpty()) - || m_requested.receivers.size() >= static_cast(m_receiverCapacity) - || std::ranges::any_of(m_requested.receivers, [](const auto& receiver) { return receiver.mode != RtlCaptureTransaction::Mode::Fm && receiver.mode != RtlCaptureTransaction::Mode::Fmn; })) { return false; } + || m_requested.receivers.size() >= static_cast(m_receiverCapacity)) { return false; } + if (!std::ranges::all_of(m_requested.receivers, supportsAnalogBank)) { + emit configurationWarning(tr("Additional RTL slices support FM, FM-N and analog WFM only. AM/SSB/CW require a single configured receiver.")); + return false; + } auto desired = m_requested; for (int id = 0; id < 8; ++id) { if (std::ranges::any_of(desired.receivers, [id](const auto& value) { return value.passband.stableId == id; })) { continue; } @@ -636,6 +769,27 @@ bool RtlSdrBackend::hasAcceptedSlice(int sliceId) const }); } +void RtlSdrBackend::setActiveSlice(int sliceId) +{ + if (!hasAcceptedSlice(sliceId)) { return; } + m_activeSliceId = sliceId; + const auto token = m_published; + const auto receivers = m_lastPublished->receivers; + // Copy membership before signalling: a receiver may be removed or a new + // selection made reentrantly by a model/UI observer. + for (const auto& receiver : receivers) { + if (!acceptsFrame(token.session, token.revision) || m_activeSliceId != sliceId) { return; } + if (receiver.passband.stableId == sliceId) { continue; } + SliceDelta delta; + delta.active = false; + emit sliceChanged(receiver.passband.stableId, delta); + } + if (!acceptsFrame(token.session, token.revision) || m_activeSliceId != sliceId) { return; } + SliceDelta delta; + delta.active = true; + emit sliceChanged(sliceId, delta); +} + void RtlSdrBackend::setSliceFrequency(int sliceId, double hz) { requestReceiveTune(sliceId, hz, ReceiveTuneView::Preserve); @@ -701,7 +855,12 @@ void RtlSdrBackend::setSliceMode(int sliceId, const QString& mode) if (receiver == desired.receivers.end()) { return; } const auto modeValue = captureMode(canonical); const bool narrowFm = modeValue == RtlCaptureTransaction::Mode::Fm || modeValue == RtlCaptureTransaction::Mode::Fmn; - if (!narrowFm && desired.receivers.size() != 1) { return; } + auto candidate = *receiver; + candidate.mode = modeValue; + if (desired.receivers.size() != 1 && !supportsAnalogBank(candidate)) { + emit configurationWarning(tr("This RTL mode requires a single configured receiver. Multiple receivers support FM, FM-N and analog WFM.")); + return; + } if (narrowFm && (receiver->passband.filterLowHz < -21600 || receiver->passband.filterHighHz > 21600 || receiver->passband.filterLowHz >= 0 || receiver->passband.filterHighHz <= 0)) { // The existing wide/sideband passband cannot describe the FM graph. @@ -721,7 +880,7 @@ void RtlSdrBackend::setSliceMode(int sliceId, const QString& mode) receiver->mode = modeValue; desired.followReceiverId = sliceId; desired.avoidDc = narrowFm; - if (!narrowFm) { receiver->squelchEnabled = false; } + if (!narrowFm) { receiver->squelchEnabled = false; receiver->automaticSquelch = false; } receiver->passband.guardLowHz = (narrowFm || nativeWfm) ? 3000 : 0; receiver->passband.guardHighHz = (narrowFm || nativeWfm) ? 3000 : 0; requestCapture(desired); @@ -789,11 +948,29 @@ void RtlSdrBackend::setSliceSquelch(int sliceId, bool enabled, int level) if (receiver == desired.receivers.end() || (receiver->mode != RtlCaptureTransaction::Mode::Fm && receiver->mode != RtlCaptureTransaction::Mode::Fmn)) { return; } - if (receiver->squelchEnabled == enabled && receiver->squelchLevel == level) { return; } + if (receiver->squelchEnabled == enabled && receiver->squelchLevel == level + && !receiver->automaticSquelch) { return; } + receiver->automaticSquelch = false; receiver->squelchEnabled = enabled; receiver->squelchLevel = level; requestCapture(desired); } +void RtlSdrBackend::setSliceAutoSquelch(int sliceId, bool enabled, int marginDb) +{ + if (!hasAcceptedSlice(sliceId) || marginDb < 5 || marginDb > 20) { return; } + auto desired = m_requested; + const auto receiver = std::ranges::find_if(desired.receivers, + [sliceId](const auto& value) { return value.passband.stableId == sliceId; }); + if (receiver == desired.receivers.end() + || (receiver->mode != RtlCaptureTransaction::Mode::Fm + && receiver->mode != RtlCaptureTransaction::Mode::Fmn)) { return; } + if (receiver->automaticSquelch == enabled && receiver->automaticSquelchMarginDb == marginDb) { return; } + receiver->automaticSquelch = enabled; + receiver->automaticSquelchMarginDb = marginDb; + if (enabled) { receiver->squelchEnabled = true; } + requestCapture(desired); +} + // ────────────────────────────────────────────────────────────────────────────── // IRadioBackend — pan control // ────────────────────────────────────────────────────────────────────────────── @@ -949,7 +1126,11 @@ ReceiveDispatch RtlSdrBackend::requestSliceSquelch(int sliceId, const SliceSquel && receiver->mode != RtlCaptureTransaction::Mode::Fmn)) { return ReceiveDispatch::Unsupported; } - setSliceSquelch(sliceId, request.enabled, request.level); + if (request.kind == SliceSquelchRequest::Kind::Automatic) { + setSliceAutoSquelch(sliceId, request.enabled, request.marginDb); + } else { + setSliceSquelch(sliceId, request.enabled, request.level); + } return ReceiveDispatch::Dispatched; } @@ -1035,6 +1216,22 @@ void RtlSdrBackend::invokeExtension(const QString& ns, const QString& verb, if (verb == QLatin1String("settings.get")) { emit extensionResult(requestId, deviceSettingsStatus()); return; } + if (verb == QLatin1String("receive_meters.set")) { + if (arg.metaType().id() != QMetaType::Bool || !m_lastPublished) { + emit extensionError(requestId, tr("Receive meters require a boolean and an accepted receiver state")); + return; + } + const QPointer producer(m_worker.get()); + m_receiveMetersEnabled = arg.toBool(); + m_worker->setReceiveMetersEnabled(m_receiveMetersEnabled); + saveAcceptedDeviceSettings(); + publishRfMeterDefinitions(true); + if (!producer || producer.data() != m_worker.get() || !m_connected) { return; } + emit extensionStatus(QStringLiteral("rtl"), QStringLiteral("settings"), deviceSettingsStatus()); + if (!producer || producer.data() != m_worker.get() || !m_connected) { return; } + emit extensionResult(requestId, m_receiveMetersEnabled); + return; + } bool ok = false; const qint64 value = arg.toLongLong(&ok); const int type = arg.metaType().id(); @@ -1098,6 +1295,8 @@ QVector RtlSdrBackend::acceptedSettings() const } slice.audioGain = m_monitors[id].gain; slice.audioMute = m_monitors[id].mute; slice.audioPan = m_monitors[id].pan; slice.squelchEnabled = receiver.squelchEnabled; slice.squelchLevel = receiver.squelchLevel; + slice.automaticSquelch = receiver.automaticSquelch; + slice.automaticSquelchMarginDb = receiver.automaticSquelchMarginDb; slice.wfmDeemphasisUs = receiver.wfmDeemphasisUs; slice.wfmForceMono = receiver.wfmForceMono; output.append(slice); @@ -1163,13 +1362,14 @@ void RtlSdrBackend::restoreAcceptedSlices() unsupportedWfmFilter = true; continue; } + const bool bankCompatible = !wide || nativeWfm; if ((!wide && (slice.filterLowHz < -21600 || slice.filterHighHz > 21600 || slice.filterLowHz >= 0 || slice.filterHighHz <= 0)) - || (wide && !desired.receivers.empty()) - || (!desired.receivers.empty() && desired.receivers.front().mode != RtlCaptureTransaction::Mode::Fm - && desired.receivers.front().mode != RtlCaptureTransaction::Mode::Fmn)) { continue; } + || (!desired.receivers.empty() && (!bankCompatible + || !std::ranges::all_of(desired.receivers, supportsAnalogBank)))) { continue; } desired.receivers.push_back({descriptor, captureMode(slice.mode), slice.audioGain, slice.audioPan, - slice.audioMute, !wide && slice.squelchEnabled, slice.squelchLevel, slice.wfmDeemphasisUs, slice.wfmForceMono}); + slice.audioMute, !wide && slice.squelchEnabled, slice.squelchLevel, slice.wfmDeemphasisUs, slice.wfmForceMono, + !wide && slice.automaticSquelch, slice.automaticSquelchMarginDb}); } // Preserve a valid configured receiver even when it is parked. The // transaction derives DSP membership from the confirmed capture; a @@ -1203,6 +1403,7 @@ void RtlSdrBackend::applyRestoredState(const RestoredRadioState& state) m_panRfGainDb = kDefaultRfGainDb; m_ppmCorrection = m_savedDeviceSettings.values.ppm; m_dcSuppression = m_savedDeviceSettings.values.dcSuppression; + m_receiveMetersEnabled = m_savedDeviceSettings.values.receiveMetersEnabled; m_directSampling = 0; if (state.rfFrequencyHz > 0) { @@ -1350,13 +1551,24 @@ bool RtlSdrBackend::acceptsFrame(quint64 session, quint64 revision) const return m_connected && m_published == RtlCaptureTransaction::Token{session, revision}; } +void RtlSdrBackend::markStartup(RtlStartupTrace::Kind kind, int slot) +{ + if (!m_startupTrace || m_startupTrace->ownerExpired) { return; } + m_startupTrace->markOwner(kind, m_published.session, m_published.revision, + m_capture.requested().revision, m_worker ? m_worker->startupDroppedPackets() : 0, + m_worker ? m_worker->startupQueuedPackets() : 0, slot); +} + void RtlSdrBackend::serviceCapture() { if (!m_worker) { return; } + markStartup(RtlStartupTrace::ServiceBegin); const QPointer producer(m_worker.get()); m_worker->serviceCancellation(); + markStartup(RtlStartupTrace::ControlDone); const std::uint64_t previousTraceDrops = m_diagnostics.droppedTraceEvents; m_diagnostics = m_worker->diagnostics(); + markStartup(RtlStartupTrace::SnapshotDone); if (m_diagnostics.droppedTraceEvents != previousTraceDrops) { qCDebug(lcRtlReceive).nospace() << "RtlReceive ms=" << QDateTime::currentMSecsSinceEpoch() << " kind=trace_overflow trace_drops=" << m_diagnostics.droppedTraceEvents; @@ -1397,7 +1609,11 @@ void RtlSdrBackend::serviceCapture() return; } if (completion == RtlCaptureTransaction::Completion::Published) { + markStartup(RtlStartupTrace::PublishBegin); publishCapture(); + if (producer && producer.data() == m_worker.get()) { + markStartup(RtlStartupTrace::PublishEnd); + } finishExtensions(true, result->token); } else if (completion == RtlCaptureTransaction::Completion::Failed && !m_capture.busy()) { const auto& state = *m_capture.confirmed(); @@ -1423,8 +1639,12 @@ void RtlSdrBackend::serviceCapture() } } if (m_worker) { + if (producer && producer.data() == m_worker.get()) { + markStartup(RtlStartupTrace::DrainBegin); + } drainAudio(); if (!producer || producer.data() != m_worker.get()) { return; } + markStartup(RtlStartupTrace::DrainEnd); expireWfmObservations(); if (!producer || producer.data() != m_worker.get()) { return; } if (!m_capture.busy() && m_worker->needsRepair()) { requestCapture(m_requested); } @@ -1436,6 +1656,9 @@ void RtlSdrBackend::serviceCapture() requestViewport(true); } } + if (producer && producer.data() == m_worker.get()) { + markStartup(RtlStartupTrace::ServiceEnd); + } } void RtlSdrBackend::retireNativeAudio() @@ -1473,6 +1696,21 @@ void RtlSdrBackend::publishLegacyPcm(const QByteArray& pcm, const QByteArray& pr void RtlSdrBackend::drainAudio() { const QPointer worker(m_worker.get()); + RtlReceivePipeline::RfObservation rf; + while (worker && worker.data() == m_worker.get() && worker->takeRfObservation(rf)) { + const auto now = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + if (!m_receiveMetersEnabled || !acceptsFrame(rf.token.session, rf.token.revision) || !rf.producedMs + || rf.producedMs > now || now - rf.producedMs > 500) { continue; } + for (int id = 0; id < 8; ++id) { + if (rf.mask & (1u << id)) { + emit meterUpdate(QStringLiteral("SLC%1:LEVEL").arg(id), rf.dbfs[id]); + if (!worker || worker.data() != m_worker.get() + || !acceptsFrame(rf.token.session, rf.token.revision) + || !worker->rfObservationIsCurrent(rf)) { return; } + } + } + } + if (!worker || worker.data() != m_worker.get()) { return; } RtlReceivePipeline::Packet packet; for (int count = 0; count < 128 && worker && worker.data() == m_worker.get() && worker->takeAudio(packet); ++count) { @@ -1495,6 +1733,10 @@ void RtlSdrBackend::drainAudio() } QVector samples(static_cast(2 * packet.frames)); std::copy_n(packet.samples.begin(), samples.size(), samples.begin()); + auto& statistics = m_pcmDiagnostics[packet.slot < 0 ? 8 : packet.slot]; + for (const float sample : samples) { + statistics.nonfiniteSamples += !std::isfinite(sample); + } const auto frame = output.producer->produce(std::move(samples), packet.firstSample, packet.discontinuity || packet.firstSample != output.nextSample); output.nextSample = packet.firstSample + packet.frames; @@ -1502,6 +1744,29 @@ void RtlSdrBackend::drainAudio() observeWfm(packet); if (!worker || worker.data() != m_worker.get() || !acceptsFrame(packet.token.session, packet.token.revision)) { return; } + ++statistics.packets; + statistics.frames += packet.frames; + statistics.discontinuities += frame->discontinuity(); + const auto deliveredNs = static_cast(std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); + if (packet.enqueuedMonotonicNs && packet.enqueuedMonotonicNs <= deliveredNs) { + const auto age = deliveredNs - packet.enqueuedMonotonicNs; + statistics.queueAgeTotalNs += age; + statistics.queueAgeMaxNs = std::max(statistics.queueAgeMaxNs, age); + } + const quint64 deliveredMs = deliveredNs / 1'000'000; + if (statistics.lastDeliveryMs) { + statistics.maxDeliveryGapMs = std::max(statistics.maxDeliveryGapMs, + deliveredMs - statistics.lastDeliveryMs); + } + statistics.lastDeliveryMs = deliveredMs; + for (std::size_t sample = 0; sample < 2 * packet.frames; ++sample) { + const double amplitude = packet.samples[sample]; + statistics.nonzeroSamples += std::abs(amplitude) > 1e-9; + statistics.peak = std::max(statistics.peak, std::abs(amplitude)); + if (sample % 2 == 0) { statistics.sumSquaresLeft += amplitude * amplitude; } + else { statistics.sumSquaresRight += amplitude * amplitude; } + } if (packet.slot < 0) { emit audioFrameReady(*frame); } else { emit sliceAudioFrameReady(packet.slot, *frame); } } @@ -1578,7 +1843,9 @@ void RtlSdrBackend::emitSliceState(const RtlCaptureTransaction::Receiver& receiv delta.filterHigh = static_cast(receiver.passband.filterHighHz); delta.audioGain = m_monitors[id].gain; delta.audioMute = m_monitors[id].mute; delta.audioPan = m_monitors[id].pan; delta.squelchOn = receiver.squelchEnabled; delta.squelchLevel = receiver.squelchLevel; - delta.panId = QStringLiteral("0xe1000000"); delta.active = id == m_requested.receivers.front().passband.stableId; + delta.automaticSquelch = receiver.automaticSquelch; + delta.automaticSquelchMarginDb = receiver.automaticSquelchMarginDb; + delta.panId = QStringLiteral("0xe1000000"); delta.active = id == m_activeSliceId; delta.inCapture = m_lastPublished && std::ranges::find(m_lastPublished->receivingIds, id) != m_lastPublished->receivingIds.end(); delta.wfmDeemphasisUs = receiver.wfmDeemphasisUs; @@ -1586,7 +1853,14 @@ void RtlSdrBackend::emitSliceState(const RtlCaptureTransaction::Receiver& receiv delta.wfmReceptionDiagnostics = m_wfmReception[id]; delta.wfmStereoStatus = m_wfmStatus[id]; delta.modeList = capabilities().receiveModeControl->modes; + const QPointer traceWorker(m_worker.get()); + markStartup(RtlStartupTrace::SliceBegin, id); emit sliceChanged(id, delta); + // A synchronous observer may replace the session. Its trace cannot own + // an end marker from this retired publication. + if (traceWorker && traceWorker.data() == m_worker.get()) { + markStartup(RtlStartupTrace::SliceEnd, id); + } } void RtlSdrBackend::publishCapture() @@ -1625,6 +1899,11 @@ void RtlSdrBackend::publishCapture() else { retirePcmStreams(); } } const auto prior = m_lastPublished; + if (std::ranges::none_of(state.receivers, [this](const auto& receiver) { + return receiver.passband.stableId == m_activeSliceId; + })) { + m_activeSliceId = state.receivers.empty() ? -1 : state.receivers.front().passband.stableId; + } const bool restoring = m_restoreToken == state.token; if (restoring) { for (const auto& receiver : state.receivers) { @@ -1668,7 +1947,17 @@ void RtlSdrBackend::publishCapture() m_wfmPublicationAge[id].invalidate(); m_wfmObservationAge[id].invalidate(); } + // Retire samples across every accepted capture revision, including parking, + // removal, retune and ID reuse. A definition alone is never a live sample. + if (prior) { + for (const auto& receiver : prior->receivers) { + emit meterRemoved(700 + receiver.passband.stableId); + if (!m_capture.confirmed() || m_capture.confirmed()->token != state.token) { return; } + } + } m_lastPublished = state; + publishRfMeterDefinitions(); + if (!m_capture.confirmed() || m_capture.confirmed()->token != state.token) { return; } for (const auto& receiver : state.receivers) { m_monitors[receiver.passband.stableId] = {receiver.audioGain, receiver.audioPan, receiver.audioMute}; updateMonitor(receiver.passband.stableId); @@ -1806,6 +2095,7 @@ void RtlSdrBackend::verifyDeviceSettingsIdentity(const QString& serial) m_deviceSettingsReason = tr("Session only: opened device serial does not match the settings identity."); m_ppmCorrection = 0; m_dcSuppression = false; + m_receiveMetersEnabled = true; } } @@ -1814,7 +2104,36 @@ void RtlSdrBackend::saveAcceptedDeviceSettings() m_deviceSettingsSaved = false; if (m_deviceSettingsAllowed) { m_deviceSettingsSaved = RtlDeviceSettings(m_settingsScope).saveAccepted( - {m_ppmCorrection, m_dcSuppression}, m_deviceSettingsReason); + {m_ppmCorrection, m_dcSuppression, m_receiveMetersEnabled}, m_deviceSettingsReason); + } +} + +void RtlSdrBackend::publishRfMeterDefinitions(bool retireValues) +{ + if (!m_lastPublished) { return; } + const auto state = *m_lastPublished; + const bool enabled = m_receiveMetersEnabled; + const auto current = [this, &state, enabled] { + return m_lastPublished && m_lastPublished->token == state.token + && m_capture.confirmed() && m_capture.confirmed()->token == state.token + && m_receiveMetersEnabled == enabled; + }; + for (const auto& receiver : state.receivers) { + MeterDef def; + def.index = 700 + receiver.passband.stableId; + def.source = QStringLiteral("SLC"); def.sourceIndex = receiver.passband.stableId; + def.name = QStringLiteral("LEVEL"); def.unit = QStringLiteral("dBFS"); + def.low = -120; def.high = 0; + def.description = tr("Peak windowed FFT bin in the receive passband, relative to ADC full scale; uncalibrated, not dBm or channel power"); + if (!enabled) { def.unavailableReason = tr("Receive meters disabled"); } + // Retire cached values before either enabling or disabling. A definition + // never stands in for a fresh observation after re-enabling. + if (retireValues) { + emit meterRemoved(def.index); + if (!current()) { return; } + } + emit meterDefined(def); + if (!current()) { return; } } } @@ -1824,6 +2143,7 @@ QVariantMap RtlSdrBackend::deviceSettingsStatus() const {QStringLiteral("applied"), m_connected && m_lastPublished.has_value()}, {QStringLiteral("ppm"), m_ppmCorrection}, {QStringLiteral("dcSuppression"), m_dcSuppression}, + {QStringLiteral("receiveMetersEnabled"), m_receiveMetersEnabled}, {QStringLiteral("pending"), m_capture.busy()}, {QStringLiteral("requestedPpm"), m_requested.hardware.ppm}, {QStringLiteral("requestedDcSuppression"), m_requested.dcSuppression}, diff --git a/src/core/backends/rtl/RtlSdrBackend.h b/src/core/backends/rtl/RtlSdrBackend.h index ae2c5782a..a653e946f 100644 --- a/src/core/backends/rtl/RtlSdrBackend.h +++ b/src/core/backends/rtl/RtlSdrBackend.h @@ -61,6 +61,7 @@ class RtlSdrBackend : public IRadioBackend { bool createSlice(const QString& panId, double frequencyHz) override; bool removeSlice(int sliceId) override; + void setActiveSlice(int sliceId) override; void setSliceFrequency(int sliceId, double hz) override; bool requestReceiveTune(int sliceId, double hz, ReceiveTuneView view) override; bool recenterReceiveCapture(const QString& panId) override; @@ -80,6 +81,7 @@ class RtlSdrBackend : public IRadioBackend { void setSliceAudioGain(int sliceId, int gainPercent) override; void setSliceAudioPan(int sliceId, int panPercent) override; void setSliceSquelch(int sliceId, bool enabled, int level) override; + void setSliceAutoSquelch(int sliceId, bool enabled, int marginDb) override; void setSliceWfmDeemphasis(int sliceId, int microseconds) override; void setSliceWfmForceMono(int sliceId, bool forceMono) override; void setKeying(bool key, const AetherSDR::TxCoordinator::Operation& operation, const AetherSDR::TxCoordinator::Completion& completion = {}) override; @@ -137,6 +139,7 @@ class RtlSdrBackend : public IRadioBackend { QVector acceptedSettings() const; void finishExtensions(bool success, RtlCaptureTransaction::Token token = {}); QVariantMap deviceSettingsStatus() const; + void publishRfMeterDefinitions(bool retireValues = false); void saveAcceptedDeviceSettings(); void verifyDeviceSettingsIdentity(const QString& serial); bool acceptsFrame(quint64 session, quint64 revision) const; @@ -178,6 +181,7 @@ class RtlSdrBackend : public IRadioBackend { int m_panRfGainDb{kDefaultRfGainDb}; int m_ppmCorrection{0}; bool m_dcSuppression = false; + bool m_receiveMetersEnabled = true; RtlDeviceSettings::ReadResult m_savedDeviceSettings; bool m_deviceSettingsAllowed = false; bool m_deviceSettingsSaved = false; @@ -194,6 +198,9 @@ class RtlSdrBackend : public IRadioBackend { int m_receiverCapacity = kQualifiedReceiverCapacity; RtlCaptureTransaction m_capture{{8, kQualifiedReceiverCapacity}}; std::optional m_lastPublished; + // Focus belongs to a configured stable identity, including parked receivers. + // It does not change the capture or demodulator bank. + int m_activeSliceId = -1; struct Monitor { int gain = 100; int pan = 50; bool mute = false; }; std::array m_monitors; struct NativeAudio { @@ -225,6 +232,8 @@ class RtlSdrBackend : public IRadioBackend { // Worker thread (owns async USB reader & RtlSdrDdc engine) std::unique_ptr m_worker; + std::shared_ptr m_startupTrace; + void markStartup(RtlStartupTrace::Kind kind, int slot = -1); std::array m_wfmStatus{}; std::array m_wfmObservationAge; std::array m_wfmPublicationAge; @@ -237,6 +246,14 @@ class RtlSdrBackend : public IRadioBackend { }; std::array, 8> m_wfmLastObservation; RtlReceivePipeline::Diagnostics m_diagnostics; // owner-thread health cache + struct PcmDiagnostics { + quint64 packets = 0, frames = 0, nonzeroSamples = 0, nonfiniteSamples = 0; + quint64 discontinuities = 0, queueAgeTotalNs = 0, queueAgeMaxNs = 0; + quint64 lastDeliveryMs = 0, maxDeliveryGapMs = 0; + double peak = 0, sumSquaresLeft = 0, sumSquaresRight = 0; + }; + // Slots 0..7 are independent pre-monitor taps; slot 8 is the speaker mix. + std::array m_pcmDiagnostics{}; RtlSdrDdc* ddc(); }; diff --git a/src/core/backends/rtl/RtlSdrWorker.cpp b/src/core/backends/rtl/RtlSdrWorker.cpp index ee7109a3b..79c13901f 100644 --- a/src/core/backends/rtl/RtlSdrWorker.cpp +++ b/src/core/backends/rtl/RtlSdrWorker.cpp @@ -6,12 +6,31 @@ #include "core/LogManager.h" #include "core/backends/rtl/RtlSdrUsbDevice.h" #include +#include namespace AetherSDR::rtl { namespace { constexpr std::uint32_t kRtlBufLength = 16384; using T = RtlCaptureTransaction; +bool onlyMonitorDiffers(const T::State& before, const T::State& after) +{ + if (before.token.session != after.token.session || before.hardware != after.hardware + || before.capture != after.capture || before.dcSuppression != after.dcSuppression + || before.automaticDirectSampling != after.automaticDirectSampling + || before.receivingIds != after.receivingIds + || before.receivers.size() != after.receivers.size()) { return false; } + for (std::size_t i = 0; i < before.receivers.size(); ++i) { + T::Receiver previous = before.receivers[i]; + const T::Receiver& next = after.receivers[i]; + previous.audioGain = next.audioGain; + previous.audioPan = next.audioPan; + previous.audioMute = next.audioMute; + if (previous != next) { return false; } + } + return true; +} + } // namespace RtlSdrWorker::RtlSdrWorker(struct rtlsdr_dev* dev, QObject* parent, std::size_t capacity) @@ -51,7 +70,7 @@ bool RtlSdrWorker::stopReading() { m_stopRequested = true; for (int attempt = 0; isRunning() && attempt < 50; ++attempt) { - if (m_device) { m_device->cancelAsync(); } + cancelReading(); if (wait(100)) { break; } } if (isRunning()) { @@ -62,6 +81,54 @@ bool RtlSdrWorker::stopReading() return true; } +void RtlSdrWorker::cancelReading() +{ + if (m_device) { + // Count API requests, including retries; this is not a USB restart or + // a claim that the driver acknowledged cancellation. + m_usbCancelRequests.fetch_add(1, std::memory_order_relaxed); + m_device->cancelAsync(); + } +} + +RtlReceivePipeline::Diagnostics RtlSdrWorker::diagnostics() const +{ + RtlReceivePipeline::Diagnostics result = m_pipeline->diagnostics(); + result.callbackCount = m_callbackCount.load(std::memory_order_relaxed); + result.callbackIqSamples = m_callbackIqSamples.load(std::memory_order_relaxed); + result.malformedCallbacks = m_malformedCallbacks.load(std::memory_order_relaxed); + result.callbackTotalNs = m_callbackTotalNs.load(std::memory_order_relaxed); + result.callbackMaxNs = m_callbackMaxNs.load(std::memory_order_relaxed); + result.callbackDeadlineMisses = m_callbackDeadlineMisses.load(std::memory_order_relaxed); + result.usbReadStarts = m_usbReadStarts.load(std::memory_order_relaxed); + result.usbCancelRequests = m_usbCancelRequests.load(std::memory_order_relaxed); + for (std::size_t i = 0; i < m_callbackDurationBuckets.size(); ++i) { + result.callbackDurationBuckets[i] = m_callbackDurationBuckets[i].load(std::memory_order_relaxed); + } + return result; +} + +void RtlSdrWorker::recordCallback(std::uint64_t durationNs, std::uint32_t iqSamples) noexcept +{ + m_callbackCount.fetch_add(1, std::memory_order_relaxed); + m_callbackIqSamples.fetch_add(iqSamples, std::memory_order_relaxed); + m_callbackTotalNs.fetch_add(durationNs, std::memory_order_relaxed); + // Only the acquisition context writes this maximum; readers use the mirror. + if (durationNs > m_callbackMaxNs.load(std::memory_order_relaxed)) { + m_callbackMaxNs.store(durationNs, std::memory_order_relaxed); + } + if (m_captureRateHz != 0 + && durationNs > std::uint64_t(iqSamples) * 1'000'000'000 / m_captureRateHz) { + m_callbackDeadlineMisses.fetch_add(1, std::memory_order_relaxed); + } + for (std::size_t i = 0; i < kCallbackDurationUpperBoundsNs.size(); ++i) { + if (durationNs <= kCallbackDurationUpperBoundsNs[i]) { + m_callbackDurationBuckets[i].fetch_add(1, std::memory_order_relaxed); + break; + } + } +} + bool RtlSdrWorker::submit(const Transaction::Work& work) { if (work.target.receivers.empty() @@ -121,12 +188,28 @@ void RtlSdrWorker::serviceCancellation() // race, including a device that never delivers its first callback. if (m_command.load(std::memory_order_acquire) == Command::Hardware && m_readerRunning.load(std::memory_order_acquire) && m_device) { - m_device->cancelAsync(); + cancelReading(); } } void RtlSdrWorker::applyDdc(const Transaction::State& state) { + if (m_work->before && m_applied == m_work->before->token && m_applied.revision != 0 + && !m_work->hardwareChanged && m_result + && m_result->code == Transaction::ResultCode::Applied + && onlyMonitorDiffers(*m_work->before, state)) { + // Includes compensation for a superseded audio-only request. The RF + // observation never changed, so retain the partial FFT, cadence and + // estimator. Still stamp every new frame with the adopted token; the + // backend's publication fences continue rejecting obsolete revisions. + const Transaction::Receiver& receiver = state.receivers.front(); + m_ddc.applyMonitor(receiver.audioGain, receiver.audioPan, receiver.audioMute); + m_applied = state.token; + qCDebug(lcPerf).nospace() << "RtlCapture phase=monitor_adopt ms=" << QDateTime::currentMSecsSinceEpoch() + << " session=" << state.token.session << " revision=" << state.token.revision + << " generation=" << state.capture.generation << " compensation=" << m_work->compensation; + return; + } bool compatible = m_work->before && m_applied.session == state.token.session && m_applied.revision != 0 && !m_work->compensation && m_result && m_result->code == Transaction::ResultCode::Applied @@ -164,6 +247,7 @@ void RtlSdrWorker::applyDdc(const Transaction::State& state) m_legacyFilterHighHz = highHz; m_ddc.applyMonitor(receiver.audioGain, receiver.audioPan, receiver.audioMute); m_applied = state.token; + m_captureRateHz = state.hardware.sampleRateHz; qCDebug(lcPerf).nospace() << "RtlCapture phase=adopt ms=" << QDateTime::currentMSecsSinceEpoch() << " session=" << state.token.session << " revision=" << state.token.revision << " centerHz=" << state.hardware.centerHz << " generation=" << state.capture.generation; @@ -247,6 +331,7 @@ void RtlSdrWorker::run() } m_readerRunning.store(true, std::memory_order_release); if (m_stopRequested.load(std::memory_order_acquire)) { break; } + m_usbReadStarts.fetch_add(1, std::memory_order_relaxed); const int rc = m_device->readAsync(&RtlSdrWorker::rtlsdrCallback, this); m_readerRunning.store(false, std::memory_order_release); if (m_stopRequested.load(std::memory_order_acquire)) { break; } @@ -263,20 +348,47 @@ void RtlSdrWorker::rtlsdrCallback(unsigned char* buf, std::uint32_t len, void* c if (!worker) { return; } const Command command = worker->m_command.load(std::memory_order_acquire); if (worker->m_stopRequested.load(std::memory_order_acquire) || command == Command::Hardware) { - worker->m_device->cancelAsync(); + worker->cancelReading(); return; } if (!buf || len == 0 || len % 2 != 0 || len > kRtlBufLength) { + worker->m_malformedCallbacks.fetch_add(1, std::memory_order_relaxed); worker->m_ddc.resetSpectrum(); worker->m_dcBlocker.reset(); return; } + const std::chrono::steady_clock::time_point started = std::chrono::steady_clock::now(); + RtlStartupTrace* const trace = worker->m_startupTrace.get(); + const std::uint64_t attemptsBefore = trace ? trace->packetAttempts : 0; + const std::uint64_t dropsBefore = trace ? trace->packetDrops : 0; + if (trace) { + trace->callbackStartNs = static_cast(std::chrono::duration_cast(started.time_since_epoch()).count()); + trace->callbackIndex = worker->m_callbackCount.load(std::memory_order_relaxed) + 1; + if (command == Command::Receiver) { + trace->markProducer(RtlStartupTrace::WorkerAdoptBegin, worker->m_work->token.session, + worker->m_work->token.revision, worker->m_firstSample); + } + } const bool adopting = command == Command::Receiver && worker->m_pipeline->adopt(); - if (adopting) { worker->applyDdc(worker->m_work->target); } + if (adopting) { + worker->applyDdc(worker->m_work->target); + if (trace) { + trace->markProducer(RtlStartupTrace::WorkerAdoptEnd, worker->m_applied.session, + worker->m_applied.revision, worker->m_firstSample); + } + } worker->handleCallback(buf, len); // Complete is the release of ALL references into m_work, including DSP // output during this block. The control side may destroy it immediately. if (adopting) { worker->m_command.store(Command::Complete, std::memory_order_release); } + const auto ended = std::chrono::steady_clock::now(); + const std::uint64_t durationNs = static_cast( + std::chrono::duration_cast(ended - started).count()); + worker->recordCallback(durationNs, len / 2); + if (trace) { + trace->finishCallback(static_cast(std::chrono::duration_cast( + ended.time_since_epoch()).count()), len / 2, attemptsBefore, dropsBefore); + } } void RtlSdrWorker::handleCallback(unsigned char* buf, std::uint32_t len) diff --git a/src/core/backends/rtl/RtlSdrWorker.h b/src/core/backends/rtl/RtlSdrWorker.h index d7dbad3e9..d9d39ae64 100644 --- a/src/core/backends/rtl/RtlSdrWorker.h +++ b/src/core/backends/rtl/RtlSdrWorker.h @@ -7,10 +7,13 @@ #include #include +#include #include #include +#include #include #include +#include struct rtlsdr_dev; @@ -44,10 +47,24 @@ class RtlSdrWorker : public QThread { bool submit(const Transaction::Work& work); std::optional takeResult(); void serviceCancellation(); + bool takeRfObservation(RtlReceivePipeline::RfObservation& value) { return m_pipeline->takeRfObservation(value); } + bool rfObservationIsCurrent(const RtlReceivePipeline::RfObservation& value) const { return m_pipeline->rfObservationIsCurrent(value); } + void setReceiveMetersEnabled(bool enabled) { m_pipeline->setReceiveMetersEnabled(enabled); } bool takeAudio(RtlReceivePipeline::Packet& packet) { return m_pipeline->takePacket(packet); } - RtlReceivePipeline::Diagnostics diagnostics() const { return m_pipeline->diagnostics(); } + // Independently sampled counters since construction. Timing covers each + // valid callback's adoption and processing, not USB transport latency. + // The final bucket is unbounded; finite percentile bounds are conservative. + static constexpr std::array kCallbackDurationUpperBoundsNs{ + 50'000, 100'000, 200'000, 400'000, 800'000, 1'200'000, 1'600'000, 2'000'000, + 2'400'000, 2'800'000, 3'400'000, 5'000'000, 10'000'000, 20'000'000, + 50'000'000, std::numeric_limits::max()}; + RtlReceivePipeline::Diagnostics diagnostics() const; bool takeTraceEvent(RtlReceivePipeline::TraceEvent& event) { return m_pipeline->takeTraceEvent(event); } bool needsRepair() const { return m_pipeline->needsRepair(); } + void setStartupTrace(std::shared_ptr trace) + { m_startupTrace = std::move(trace); m_pipeline->setStartupTrace(m_startupTrace.get()); } + std::uint64_t startupDroppedPackets() const { return m_pipeline->droppedPackets(); } + std::uint64_t startupQueuedPackets() const { return m_pipeline->startupQueuedPackets(); } void setMonitor(int slot, int gain, int pan, bool mute) { m_pipeline->setMonitor(slot, gain, pan, mute); } signals: @@ -66,7 +83,10 @@ class RtlSdrWorker : public QThread { void applyDdc(const Transaction::State& state); void applyHardware(); bool prepareHardwareResult(); + void cancelReading(); + void recordCallback(std::uint64_t durationNs, std::uint32_t iqSamples) noexcept; + std::shared_ptr m_startupTrace; std::unique_ptr m_device; std::atomic m_readerRunning{false}; std::atomic m_stopRequested{false}; @@ -83,6 +103,16 @@ class RtlSdrWorker : public QThread { int m_legacyFilterHighHz = 0; std::unique_ptr m_pipeline; std::uint64_t m_firstSample = 0; + std::uint32_t m_captureRateHz = 0; // acquisition-context only + std::atomic m_callbackCount{0}; + std::atomic m_callbackIqSamples{0}; + std::atomic m_malformedCallbacks{0}; + std::atomic m_callbackTotalNs{0}; + std::atomic m_callbackMaxNs{0}; + std::atomic m_callbackDeadlineMisses{0}; + std::array, kCallbackDurationUpperBoundsNs.size()> m_callbackDurationBuckets{}; + std::atomic m_usbReadStarts{0}; + std::atomic m_usbCancelRequests{0}; RtlSdrDdc m_ddc; RtlDcBlocker m_dcBlocker; bool m_dcSuppression = false; diff --git a/src/core/backends/rtl/RtlSquelchGate.h b/src/core/backends/rtl/RtlSquelchGate.h index 47bcc58e2..656aaac61 100644 --- a/src/core/backends/rtl/RtlSquelchGate.h +++ b/src/core/backends/rtl/RtlSquelchGate.h @@ -1,13 +1,17 @@ #pragma once #include +#include #include #include +#include +#include namespace AetherSDR::rtl { -// Acquisition-owned envelope. Detector and display both consume the existing -// 2048-point Blackman-Harris FFT, normalized by FFT size: dBFS/bin, not dBm -// or integrated channel power. No planner, locks or allocations run here. +// Acquisition-owned envelope using the fixed 2048-point Blackman-Harris FFT. +// Thresholds are dBFS per detector bin, not dBm or integrated channel power. +// The display has a different FFT size and must never drive this estimator. +// No planner, locks or allocations run here. class RtlSquelchGate final { public: static constexpr double kReferenceDb = -120.0; @@ -16,14 +20,52 @@ class RtlSquelchGate final { static constexpr std::uint64_t kStaleFrames = 4800; // missing detector: close static constexpr float kRampStep = 1.0f / 240.0f; // 5 ms - void configure(bool enabled, int level, bool reset = false) noexcept + void configure(bool enabled, int level, bool reset = false, + bool automatic = false, int marginDb = 10) noexcept { if (reset || enabled != m_enabled) { m_open = false; m_observed = false; m_lastAbove = 0; if (reset) { m_gain = enabled ? 0.0f : 1.0f; } } + if (reset || automatic != m_automatic || enabled != m_enabled) { m_floor = std::numeric_limits::quiet_NaN(); } + m_automatic = automatic; + m_marginDb = std::clamp(marginDb, 5, 20); m_enabled = enabled; - m_threshold = kReferenceDb + kStepDb * std::clamp(level, 0, 100); + if (!automatic) { m_threshold = kReferenceDb + kStepDb * std::clamp(level, 0, 100); } + } + void observeSpectrum(std::span bins, int low, int high, + std::uint64_t frame) noexcept + { + if (low < 0 || high < low || high >= static_cast(bins.size())) { + observe(std::numeric_limits::quiet_NaN(), frame); return; + } + double peak = kReferenceDb; + for (int i = low; i <= high; ++i) { + if (!std::isfinite(bins[i])) { observe(std::numeric_limits::quiet_NaN(), frame); return; } + peak = std::max(peak, double(bins[i])); + } + if (m_automatic) { + // Exclude the desired passband and window leakage. A median of + // neighboring bin powers rejects isolated carriers. For complex + // Gaussian noise, power is exponential: median / ln(2) estimates + // mean power. A trimmed mean of log power is systematically low + // and makes a peak detector hold noise open at the normal margin. + const int first = std::max(0, low - 64); + const int end = std::min(static_cast(bins.size()), high + 65); + std::array neighbors{}; + std::size_t count = 0; + for (int i = first; i < end; ++i) { + if (i >= low - 4 && i <= high + 4) { continue; } + if (std::isfinite(bins[i])) { neighbors[count++] = bins[i]; } + } + if (!count) { observe(std::numeric_limits::quiet_NaN(), frame); return; } + std::sort(neighbors.begin(), neighbors.begin() + count); + const double medianDb = (neighbors[(count - 1) / 2] + neighbors[count / 2]) * 0.5; + const double floor = medianDb + 10.0 * std::log10(1.0 / std::log(2.0)); + m_floor = std::isfinite(m_floor) ? 0.9 * m_floor + 0.1 * floor : floor; + m_threshold = m_floor + m_marginDb; + } + observe(peak, frame); } void observe(double peakDb, std::uint64_t frame) noexcept { @@ -46,6 +88,9 @@ class RtlSquelchGate final { return m_gain; } private: + bool m_automatic = false; + int m_marginDb = 10; + double m_floor = std::numeric_limits::quiet_NaN(); bool m_enabled = false; bool m_open = false; bool m_observed = false; diff --git a/src/core/backends/rtl/RtlStartupTrace.h b/src/core/backends/rtl/RtlStartupTrace.h new file mode 100644 index 000000000..6cca7c6ec --- /dev/null +++ b/src/core/backends/rtl/RtlStartupTrace.h @@ -0,0 +1,122 @@ +#pragma once + +// Temporary opt-in startup investigation. Each buffer has one writer; inspect +// only after the acquisition thread has joined. No logging/I/O in capture. +#include +#include +#include +#include +#include +#include +#include + +namespace AetherSDR::rtl { +struct RtlStartupTrace { + enum Kind { ServiceBegin, ControlDone, SnapshotDone, PublishBegin, PublishEnd, + DrainBegin, DrainEnd, ServiceEnd, SliceBegin, SliceEnd, WorkerAdoptBegin, + WorkerAdoptEnd, FirstNativeBlock, FirstQueueFull, FirstDrop, FirstRecovery }; + static std::uint64_t nowNs() noexcept + { + return static_cast(std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); + } + struct Event { + int kind = 0, slot = -1; + std::uint64_t ns = 0, session = 0, revision = 0, requestedRevision = 0; + std::uint64_t captureFirst = 0, packetFirst = 0, callback = 0; + std::uint64_t drops = 0, queued = 0, frames = 0; + }; + template struct Buffer { + std::array events{}; + std::size_t size = 0, omitted = 0; + void add(const Event& event) noexcept + { + if (size < N) { events[size++] = event; } + else { ++omitted; } + } + }; + struct Bin { + std::uint64_t callbacks = 0, iqFrames = 0, attempts = 0, drops = 0; + std::uint64_t firstNs = 0, lastNs = 0, maxCallbackNs = 0, maxAttempts = 0; + }; + const std::uint64_t startedNs = nowNs(); + Buffer<16384> owner; // first 20 seconds, bounded even under timer reentry + Buffer<64> producer; + std::array bins{}; // 20 ms aggregates: first 2.56 s of native PCM + bool ownerExpired = false; + std::uint64_t nativeFirstNs = 0, callbackStartNs = 0, callbackIndex = 0; + std::uint64_t packetAttempts = 0, packetDrops = 0; + bool firstFull = false, firstDrop = false, firstRecovery = false; + + void markOwner(Kind kind, std::uint64_t session, std::uint64_t revision, + std::uint64_t requested, std::uint64_t drops, std::uint64_t queued, int slot = -1) noexcept + { + if (ownerExpired) { return; } + const std::uint64_t ns = nowNs(); + if (ns - startedNs > 20'000'000'000ULL) { ownerExpired = true; return; } + Event event; event.kind = kind; event.slot = slot; event.ns = ns; + event.session = session; event.revision = revision; event.requestedRevision = requested; + event.drops = drops; event.queued = queued; owner.add(event); + } + void markProducer(Kind kind, std::uint64_t session, std::uint64_t revision, + std::uint64_t captureFirst, int slot = -1, std::uint64_t packetFirst = 0, + std::uint64_t frames = 0, std::uint64_t queued = 0) noexcept + { + Event event; event.kind = kind; event.slot = slot; event.ns = nowNs(); + event.session = session; event.revision = revision; event.captureFirst = captureFirst; + event.packetFirst = packetFirst; event.callback = callbackIndex; + event.drops = packetDrops; event.queued = queued; event.frames = frames; + producer.add(event); + if (kind == FirstNativeBlock) { nativeFirstNs = event.ns; } + } + void finishCallback(std::uint64_t endedNs, std::uint64_t iqFrames, + std::uint64_t attemptsBefore, std::uint64_t dropsBefore) noexcept + { + if (!nativeFirstNs || endedNs < nativeFirstNs) { return; } + const std::uint64_t index = (endedNs - nativeFirstNs) / 20'000'000; + if (index >= bins.size()) { return; } + Bin& bin = bins[index]; + ++bin.callbacks; bin.iqFrames += iqFrames; + const std::uint64_t attempts = packetAttempts - attemptsBefore; + bin.attempts += attempts; bin.drops += packetDrops - dropsBefore; + if (!bin.firstNs) { bin.firstNs = callbackStartNs; } + bin.lastNs = endedNs; + bin.maxCallbackNs = std::max(bin.maxCallbackNs, endedNs - callbackStartNs); + bin.maxAttempts = std::max(bin.maxAttempts, attempts); + } + QByteArray jsonAfterJoin() const + { + const auto number = [](std::uint64_t n) { return QString::number(static_cast(n)); }; + const auto serialize = [&number](const auto& buffer) { + QJsonArray result; + for (std::size_t i = 0; i < buffer.size; ++i) { + const Event& event = buffer.events[i]; + result.append(QJsonObject{{"kind", event.kind}, {"slot", event.slot}, + {"ns", number(event.ns)}, {"session", number(event.session)}, + {"revision", number(event.revision)}, {"requestedRevision", number(event.requestedRevision)}, + {"captureFirst", number(event.captureFirst)}, {"packetFirst", number(event.packetFirst)}, + {"callback", number(event.callback)}, {"drops", number(event.drops)}, + {"queued", number(event.queued)}, {"frames", number(event.frames)}}); + } + return result; + }; + QJsonArray aggregate; + for (std::size_t i = 0; i < bins.size(); ++i) { + const Bin& bin = bins[i]; + if (!bin.callbacks) { continue; } + aggregate.append(QJsonObject{{"bin", int(i)}, {"callbacks", number(bin.callbacks)}, + {"iqFrames", number(bin.iqFrames)}, {"attempts", number(bin.attempts)}, + {"drops", number(bin.drops)}, {"firstNs", number(bin.firstNs)}, + {"lastNs", number(bin.lastNs)}, {"maxCallbackNs", number(bin.maxCallbackNs)}, + {"maxAttempts", number(bin.maxAttempts)}}); + } + const QJsonObject document{{"schema", 1}, {"startedNs", number(startedNs)}, + {"nativeFirstNs", number(nativeFirstNs)}, {"ownerEvents", serialize(owner)}, + {"producer", serialize(producer)}, {"producerBins20ms", aggregate}, + {"ownerOmitted", number(owner.omitted)}, {"producerOmitted", number(producer.omitted)}, + {"ownerExpired", ownerExpired}, {"writtenAfterJoin", true}, + {"packetAttemptsAtJoin", number(packetAttempts)}, {"packetDropsAtJoin", number(packetDrops)}}; + return QJsonDocument(document).toJson(QJsonDocument::Compact); + } +}; +} // namespace AetherSDR::rtl diff --git a/src/core/backends/sim/SimBackend.cpp b/src/core/backends/sim/SimBackend.cpp index 873114324..78eaf41ca 100644 --- a/src/core/backends/sim/SimBackend.cpp +++ b/src/core/backends/sim/SimBackend.cpp @@ -257,6 +257,7 @@ RadioCapabilities SimBackend::capabilities() const {QStringLiteral("USB"), QStringLiteral("LSB")}}; caps.receiveFilterControl = std::nullopt; // demo passband setters only echo state caps.receiveAudioControl = std::nullopt; // no independently controlled RX mixer yet + caps.receiveAudioExport = std::nullopt; // native DAX export not qualified for this backend caps.receivePanCenterControl = std::nullopt; // spectrum remains anchored to the VFO caps.receivePanBandwidthControl = std::nullopt; // fixed synthetic span caps.canReboot = false; diff --git a/src/core/dsp/SpectrumTemporalAverage.h b/src/core/dsp/SpectrumTemporalAverage.h index 2a968bb8c..bd8bcbea9 100644 --- a/src/core/dsp/SpectrumTemporalAverage.h +++ b/src/core/dsp/SpectrumTemporalAverage.h @@ -73,12 +73,32 @@ class SpectrumTemporalAverage final { m_originHz = originHz; m_stepHz = stepHz; } - std::copy(values.begin(), values.end(), m_observation.begin()); const double exponent = -elapsedSeconds / (average * kMsPerStep / 1000.0); const double retained = std::exp(exponent); const double alpha = -std::expm1(exponent); const double offset = (originHz - m_originHz) / stepHz; std::size_t reused = 0; + if (offset == 0.0) { + // Aligned observations only read their own old RF bin, so display + // and history can advance together without copying or resampling. + // Use old validity for output before retiring newly excluded bins. + for (std::size_t i = 0; i < values.size(); ++i) { + const float observed = values[i]; + const bool previous = m_valid[i]; + const float estimate = previous ? float(retained * m_state[i] + alpha * observed) + : observed; + values[i] = logarithmic ? estimate : decibels(estimate); + const bool usable = i >= firstUsable && i < endUsable; + if (usable) { + m_state[i] = estimate; + if (previous) { ++reused; } + } + m_valid[i] = usable; + } + m_haveAverage = true; + return reused; + } + std::copy(values.begin(), values.end(), m_observation.begin()); for (std::size_t i = 0; i < values.size(); ++i) { const auto previous = sample(m_state, double(i) + offset, 0, values.size(), true); const float estimate = previous ? float(retained * *previous + alpha * m_observation[i]) diff --git a/src/gui/AppletPanel.cpp b/src/gui/AppletPanel.cpp index da0e5be9c..87adab93e 100644 --- a/src/gui/AppletPanel.cpp +++ b/src/gui/AppletPanel.cpp @@ -1803,13 +1803,12 @@ void AppletPanel::setProfilesVisible(bool visible) QStringLiteral("Applet_PROF"), visible); } -void AppletPanel::setDaxStreamsVisible(bool visible) +void AppletPanel::setDaxStreamsVisible(bool visible, bool nativeReceive) { - // Both tiles are the same capability: "DAX" is per-slice receive audio, - // "IQ" is per-panadapter IQ, and a radio produces both stream kinds or - // neither. + // A native slice PCM export provides audio only; IQ and radio-side DAX + // selectors retain their separate radio stream capability. applyCapabilityVisibility(QStringLiteral("DAX"), - QStringLiteral("Applet_DAX"), visible); + QStringLiteral("Applet_DAX"), visible || nativeReceive); applyCapabilityVisibility(QStringLiteral("IQ"), QStringLiteral("Applet_IQ"), visible); // The AetherClock applet is NOT a DAX tile — it stays visible on every diff --git a/src/gui/AppletPanel.h b/src/gui/AppletPanel.h index b9a2a744c..8b817e912 100644 --- a/src/gui/AppletPanel.h +++ b/src/gui/AppletPanel.h @@ -235,7 +235,7 @@ class AppletPanel : public QWidget { // connected radio produces per-slice audio / per-pan IQ streams // (RadioCapabilities::hasDaxStreams). Same not-markHardwareConditional // reasoning as setProfilesVisible above. - void setDaxStreamsVisible(bool visible); + void setDaxStreamsVisible(bool visible, bool nativeReceive = false); // Show/hide the EQ button and applet — the radio's own 8-band hardware // equalizer (RadioCapabilities::hasRadioSideDsp). Deliberately does NOT diff --git a/src/gui/Ax25HfPacketDecodeDialog.cpp b/src/gui/Ax25HfPacketDecodeDialog.cpp index 4f6c8a2a5..952985a8e 100644 --- a/src/gui/Ax25HfPacketDecodeDialog.cpp +++ b/src/gui/Ax25HfPacketDecodeDialog.cpp @@ -845,10 +845,9 @@ Ax25HfPacketDecodeDialog::Ax25HfPacketDecodeDialog(AudioEngine* audio, theme::setContainer(this, QStringLiteral("dialog/ax25Decode")); setMinimumSize(1080, 680); - m_shim = new AetherAx25LibmodemShim(); - m_shim->moveToThread(&m_shimThread); - connect(&m_shimThread, &QThread::finished, m_shim, &QObject::deleteLater); - m_shimThread.start(); + if (m_radio) { + m_receive = std::make_unique(*m_radio); + } m_kissServer = new KissTncServer(this); configureTncAuthority(true); m_heard = new HeardList(this); @@ -1163,51 +1162,35 @@ Ax25HfPacketDecodeDialog::Ax25HfPacketDecodeDialog(AudioEngine* audio, this, &Ax25HfPacketDecodeDialog::startTransmitFromUi); connect(m_txPaceTimer, &QTimer::timeout, this, &Ax25HfPacketDecodeDialog::paceTransmitAudio); - connect(m_shim, &AetherAx25LibmodemShim::frameDecoded, - this, &Ax25HfPacketDecodeDialog::appendFrame); -#ifdef HAVE_MQTT - connect(m_shim, &AetherAx25LibmodemShim::frameDecoded, - this, &Ax25HfPacketDecodeDialog::publishFrameMqtt); -#endif - // RX -> KISS clients: forward every decoded frame to connected hosts. - connect(m_shim, &AetherAx25LibmodemShim::frameDecoded, this, - [this](const Ax25DecodedFrame& frame) { - if (m_kissServer && m_kissServer->isListening() && !frame.ax25FrameNoFcs.isEmpty()) { - m_kissServer->broadcastAx25Frame(frame.ax25FrameNoFcs); - ++m_kissRxCount; - refreshTncStatus(); - } - }); - // RX -> Mailbox: feed every decoded frame to the PMS (heard list always; - // connected-mode handling only for frames addressed to our PMS callsign). - connect(m_shim, &AetherAx25LibmodemShim::frameDecoded, this, - [this](const Ax25DecodedFrame& frame) { - if (frame.ax25FrameNoFcs.isEmpty()) - return; - // Record into the shared heard log once (drives MHEARD + quick-connect), - // then through the APRS parser into the station roster + messenger. - if (auto decoded = ax25::Frame::decode(frame.ax25FrameNoFcs)) { - if (m_heard) - m_heard->record(*decoded); - if (auto packet = aprs::parseFrame(*decoded)) { - if (m_aprsStations) - m_aprsStations->record(*packet); - if (m_aprsMessenger) - m_aprsMessenger->onPacket(*packet); - } - if (frame.fcsOk && m_enableDecode && m_enableDecode->isChecked()) { - m_digi->receiveFrame(frame.ax25FrameNoFcs); - } - } - if (m_pms) - m_pms->onAirFrame(frame.ax25FrameNoFcs); - if (m_terminal) - m_terminal->onAirFrame(frame.ax25FrameNoFcs); - }); - connect(m_shim, &AetherAx25LibmodemShim::diagnosticsUpdated, - this, &Ax25HfPacketDecodeDialog::updateDiagnostics); - connect(m_shim, &AetherAx25LibmodemShim::statusChanged, - this, &Ax25HfPacketDecodeDialog::refreshStatus); + if (m_receive) { + connect(m_receive.get(), &Ax25ReceiveModel::frameDecoded, + this, &Ax25HfPacketDecodeDialog::handleDecodedFrame); + connect(m_receive.get(), &Ax25ReceiveModel::diagnosticsUpdated, this, + [this](const Ax25DecoderDiagnostics& diagnostics, const Ax25ReceiveContext& context) { + updateDiagnostics(diagnostics, context); + }); + connect(m_receive.get(), &Ax25ReceiveModel::statusChanged, + this, &Ax25HfPacketDecodeDialog::refreshStatus); + connect(m_receive.get(), &Ax25ReceiveModel::routeStatusChanged, + this, &Ax25HfPacketDecodeDialog::refreshStatus); + connect(m_receive.get(), &Ax25ReceiveModel::sourceReset, this, [this] { + const QPointer guard(this); + m_lastDiagnostics = {}; + m_lastDiagnosticsUtc = {}; + m_lastActivityHdlc = 0; + m_lastActivityAccepted = 0; + if (m_captureActive) { finishAudioCapture(false); } + if (!guard) { return; } + refreshStatus(); + }); + connect(m_receive.get(), &Ax25ReceiveModel::pcmReady, this, + [this](const DecoderPcmBlock& block, const Ax25ReceiveContext& context) { + if (!context.current()) { return; } + handleRxAudio(QByteArray(reinterpret_cast(block.samples.constData()), + block.samples.size() * static_cast(sizeof(float))), + DecoderPcmBlock::kSampleRateHz, context); + }); + } connect(m_heartbeatTimer, &QTimer::timeout, this, &Ax25HfPacketDecodeDialog::updateHeartbeat); @@ -1228,12 +1211,6 @@ Ax25HfPacketDecodeDialog::Ax25HfPacketDecodeDialog(AudioEngine* audio, }); } - if (m_audio) { - connect(m_audio, &AudioEngine::tncRxAudioReady, - this, &Ax25HfPacketDecodeDialog::handleRxAudio, - Qt::QueuedConnection); - } - // KISS TNC server wiring. connect(m_kissServer, &KissTncServer::ax25FrameFromClient, this, &Ax25HfPacketDecodeDialog::handleKissFrameFromClient); @@ -1457,14 +1434,14 @@ Ax25HfPacketDecodeDialog::~Ax25HfPacketDecodeDialog() finishAudioCapture(false); if (m_kissServer) m_kissServer->stop(); - if (m_audio) - m_audio->setTncRxTapEnabled(false); - m_shimThread.quit(); - m_shimThread.wait(); + m_receive.reset(); } void Ax25HfPacketDecodeDialog::setAttachedSlice(SliceModel* slice) { + const QPointer guard(this); + if (m_receive) { m_receive->setSlice(slice); } + if (!guard) { return; } if (m_attachedSlice == slice) { logAttachedSliceState(QStringLiteral("slice state refresh")); refreshStatus(); @@ -1505,6 +1482,7 @@ void Ax25HfPacketDecodeDialog::setAttachedSlice(SliceModel* slice) void Ax25HfPacketDecodeDialog::setModemProfile(Ax25ModemProfile profile, bool persist) { + const QPointer guard(this); m_shimConfig = ax25DemodConfigForProfile(profile, Ax25TonePolarity::Normal); if (m_digi) { m_digi->setBaud(m_shimConfig.baud); } if (m_digiEnable) { m_digiEnable->setEnabled(m_shimConfig.baud == 1200); } @@ -1513,9 +1491,8 @@ void Ax25HfPacketDecodeDialog::setModemProfile(Ax25ModemProfile profile, bool pe // mid-sweep — and the sweep would be measuring the wrong preamble. if (m_terminalTxPreamble) m_shimConfig.txPreambleFlags = m_terminalTxPreamble->value(); - QMetaObject::invokeMethod(m_shim, [shim = m_shim, cfg = m_shimConfig]() { - shim->configure(cfg); - }, Qt::QueuedConnection); + if (m_receive) { m_receive->configure(m_shimConfig); } + if (!guard) { return; } m_lastDiagnostics = {}; m_lastDiagnosticsUtc = {}; @@ -2066,6 +2043,10 @@ void Ax25HfPacketDecodeDialog::enableDecodeForProgram(TxProgram kind) void Ax25HfPacketDecodeDialog::applyDecodeEnabled(bool enabled) { + const QPointer guard(this); + // Retire receive work before any callback from shutdown/control cleanup. + if (m_receive) { m_receive->setEnabled(enabled); } + if (!guard) { return; } if (!enabled) { for (std::size_t i = 0; i < m_txPrograms.size(); ++i) { (void)setTxProgram(static_cast(i), false); @@ -2074,17 +2055,14 @@ void Ax25HfPacketDecodeDialog::applyDecodeEnabled(bool enabled) } if (!enabled && m_digi) { m_digi->setEnabled(false); } if (enabled) { - QMetaObject::invokeMethod(m_shim, &AetherAx25LibmodemShim::reset, Qt::QueuedConnection); m_lastDiagnostics = {}; m_enabledUtc = QDateTime::currentDateTimeUtc(); m_lastDiagnosticsUtc = {}; m_lastNoAudioNoticeUtc = {}; m_lastActivityHdlc = 0; m_lastActivityAccepted = 0; - if (m_audio) - m_audio->setTncRxTapEnabled(true); appendSystemLine(QStringLiteral( - "Modem enabled. RX tap requested; waiting for 24 kHz PC RX audio.")); + "Modem enabled. Waiting for selected receive audio converted to 24 kHz mono.")); } else { if (m_captureActive) finishAudioCapture(false); @@ -2115,9 +2093,6 @@ void Ax25HfPacketDecodeDialog::applyDecodeEnabled(bool enabled) if (m_pms) m_pms->dropLink(); - if (m_audio) - m_audio->setTncRxTapEnabled(false); - QMetaObject::invokeMethod(m_shim, &AetherAx25LibmodemShim::reset, Qt::QueuedConnection); m_lastDiagnostics = {}; m_lastDiagnosticsUtc = {}; m_lastActivityHdlc = 0; @@ -2130,8 +2105,51 @@ void Ax25HfPacketDecodeDialog::applyDecodeEnabled(bool enabled) refreshPmsStatus(); } -void Ax25HfPacketDecodeDialog::handleRxAudio(const QByteArray& monoFloat32Pcm, int sampleRate) +void Ax25HfPacketDecodeDialog::handleDecodedFrame( + const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context) { + const QPointer guard(this); + const auto current = [&] { return guard && context.current(); }; + if (!current()) { return; } + appendFrame(frame, context); + if (!current()) { return; } +#ifdef HAVE_MQTT + publishFrameMqtt(frame); + if (!current()) { return; } +#endif + if (m_kissServer && m_kissServer->isListening() && !frame.ax25FrameNoFcs.isEmpty()) { + m_kissServer->broadcastAx25Frame(frame.ax25FrameNoFcs); + if (!current()) { return; } + ++m_kissRxCount; + refreshTncStatus(); + if (!current()) { return; } + } + if (frame.ax25FrameNoFcs.isEmpty()) { return; } + if (auto decoded = ax25::Frame::decode(frame.ax25FrameNoFcs)) { + if (m_heard) { m_heard->record(*decoded); } + if (!current()) { return; } + if (auto packet = aprs::parseFrame(*decoded)) { + if (m_aprsStations) { m_aprsStations->record(*packet); } + if (!current()) { return; } + if (m_aprsMessenger) { m_aprsMessenger->onPacket(*packet); } + if (!current()) { return; } + } + if (frame.fcsOk && m_enableDecode && m_enableDecode->isChecked()) { + m_digi->receiveFrame(frame.ax25FrameNoFcs); + if (!current()) { return; } + } + } + if (m_pms) { m_pms->onAirFrame(frame.ax25FrameNoFcs); } + if (!current()) { return; } + if (m_terminal) { m_terminal->onAirFrame(frame.ax25FrameNoFcs); } +} + +void Ax25HfPacketDecodeDialog::handleRxAudio( + const QByteArray& monoFloat32Pcm, int sampleRate, const Ax25ReceiveContext& context) +{ + const QPointer guard(this); + const auto current = [&] { return guard && context.current(); }; + if (!current()) { return; } if (m_captureActive && !monoFloat32Pcm.isEmpty()) { if (m_captureSampleRate == 0) { m_captureSampleRate = sampleRate; @@ -2141,12 +2159,14 @@ void Ax25HfPacketDecodeDialog::handleRxAudio(const QByteArray& monoFloat32Pcm, i appendSystemLine(QStringLiteral("Audio capture armed: %1 seconds at %2 Hz.") .arg(kAudioCaptureSeconds) .arg(sampleRate)); + if (!current() || !m_captureActive) { return; } } if (sampleRate != m_captureSampleRate) { appendSystemLine(QStringLiteral("Audio capture cancelled: sample rate changed from %1 to %2 Hz.") .arg(m_captureSampleRate) .arg(sampleRate)); + if (!current() || !m_captureActive) { return; } finishAudioCapture(false); } else { const qsizetype remaining = m_captureTargetBytes - m_capturePcm.size(); @@ -2158,9 +2178,6 @@ void Ax25HfPacketDecodeDialog::handleRxAudio(const QByteArray& monoFloat32Pcm, i } } - QMetaObject::invokeMethod(m_shim, [shim = m_shim, pcm = monoFloat32Pcm, sr = sampleRate]() { - shim->feedAudio(pcm, sr); - }, Qt::QueuedConnection); } void Ax25HfPacketDecodeDialog::startAudioCapture() @@ -2170,6 +2187,9 @@ void Ax25HfPacketDecodeDialog::startAudioCapture() return; } + const QPointer guard(this); + if (m_receive) { m_receive->reset(); } + if (!guard) { return; } m_capturePcm.clear(); m_captureId = makeAx25AudioCaptureId(); m_captureSampleRate = 0; @@ -2177,7 +2197,6 @@ void Ax25HfPacketDecodeDialog::startAudioCapture() m_captureTxSequence = 0; m_captureIcomPostResampleActive = false; m_captureActive = true; - QMetaObject::invokeMethod(m_shim, &AetherAx25LibmodemShim::reset, Qt::QueuedConnection); m_lastDiagnostics = {}; m_lastDiagnosticsUtc = {}; m_lastActivityHdlc = 0; @@ -2187,6 +2206,7 @@ void Ax25HfPacketDecodeDialog::startAudioCapture() if (m_captureButton) m_captureButton->setText(QStringLiteral("Cancel Capture")); appendSystemLine(QStringLiteral("Decoder state reset for RX audio capture.")); + if (!guard) { return; } appendSystemLine(QStringLiteral( "Starting %1 second RX/TX audio capture (%2); transmit several packets now.") .arg(kAudioCaptureSeconds) @@ -2195,7 +2215,9 @@ void Ax25HfPacketDecodeDialog::startAudioCapture() void Ax25HfPacketDecodeDialog::finishAudioCapture(bool save) { + const QPointer guard(this); finishIcomPostResampleCapture(); + if (!guard) { return; } const QByteArray capture = m_capturePcm; const QString captureId = m_captureId; const int sampleRate = m_captureSampleRate; @@ -3099,21 +3121,32 @@ void Ax25HfPacketDecodeDialog::finishTransmit(bool aborted, const QString& reaso } } -void Ax25HfPacketDecodeDialog::appendFrame(const Ax25DecodedFrame& frame) +void Ax25HfPacketDecodeDialog::appendFrame( + const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context) { - if (!frame.fcsOk) + const QPointer guard(this); + const auto current = [&] { return guard && context.current(); }; + if (!current() || !frame.fcsOk) return; ++m_frameCount; m_lastDecodeUtc = frame.timestampUtc; m_log->append(formatTerminalLine(frame)); + if (!current()) { return; } m_log->verticalScrollBar()->setValue(m_log->verticalScrollBar()->maximum()); - if (m_packetActivity) + if (!current()) { return; } + if (m_packetActivity) { m_packetActivity->recordFrame(); + } + if (!current()) { return; } refreshStatus(); } -void Ax25HfPacketDecodeDialog::updateDiagnostics(const Ax25DecoderDiagnostics& diagnostics) +void Ax25HfPacketDecodeDialog::updateDiagnostics( + const Ax25DecoderDiagnostics& diagnostics, const Ax25ReceiveContext& context) { + const QPointer guard(this); + const auto current = [&] { return guard && context.current(); }; + if (!current()) { return; } const bool firstAudio = !m_lastDiagnosticsUtc.isValid(); m_lastDiagnostics = diagnostics; m_lastDiagnosticsUtc = QDateTime::currentDateTimeUtc(); @@ -3121,9 +3154,12 @@ void Ax25HfPacketDecodeDialog::updateDiagnostics(const Ax25DecoderDiagnostics& d appendSystemLine(QStringLiteral("RX audio stream detected: %1 Hz, %2 samples/window.") .arg(diagnostics.sampleRate) .arg(diagnostics.audioSamples)); + if (!current()) { return; } + } + if (m_diagnosticsDebugEnabled) { + appendDiagnosticsLine(diagnostics, context); + if (!current()) { return; } } - if (m_diagnosticsDebugEnabled) - appendDiagnosticsLine(diagnostics); refreshStatus(); } @@ -3165,13 +3201,13 @@ void Ax25HfPacketDecodeDialog::updateHeartbeat() if (waited >= 2 && (!m_lastNoAudioNoticeUtc.isValid() || m_lastNoAudioNoticeUtc.secsTo(now) >= 5)) { appendSystemLine(QStringLiteral( - "Waiting for RX audio blocks. Confirm PC Audio is enabled, a slice is active, and AetherSDR is receiving the packet audio stream.")); + "Waiting for RX audio blocks. Check the selected receiver and receive source shown in modem status.")); m_lastNoAudioNoticeUtc = now; } } else if (m_lastDiagnosticsUtc.secsTo(now) >= 4 && (!m_lastNoAudioNoticeUtc.isValid() || m_lastNoAudioNoticeUtc.secsTo(now) >= 5)) { appendSystemLine(QStringLiteral( - "No fresh RX audio diagnostics for %1 s. The tap is enabled, but audio may be paused or PC Audio may be off.") + "No fresh RX audio diagnostics for %1 s. The selected receive source may be paused or unavailable.") .arg(m_lastDiagnosticsUtc.secsTo(now))); m_lastNoAudioNoticeUtc = now; } @@ -3200,7 +3236,31 @@ void Ax25HfPacketDecodeDialog::refreshStatus() status = m_attachedSliceId >= 0 ? QStringLiteral("Standby") : QStringLiteral("No slice attached"); } + QString sourceText = QStringLiteral("RX: unavailable"); + if (m_receive) { + switch (m_receive->routeStatus()) { + case DecoderAudioModel::RouteStatus::Bound: + sourceText = QStringLiteral("RX: selected slice"); + break; + case DecoderAudioModel::RouteStatus::SharedRxAudio: + sourceText = QStringLiteral("RX: shared audio (audible slice mix; gain and mute apply)"); + break; + case DecoderAudioModel::RouteStatus::DaxTransportUnavailable: + sourceText = QStringLiteral("RX: assigned DAX transport unavailable"); + break; + case DecoderAudioModel::RouteStatus::Inactive: + sourceText = QStringLiteral("RX: inactive"); + break; + } + } + status += QStringLiteral(" | ") + sourceText; if (m_modemStatusValue) { + QString sourceDescription = sourceText; + if (m_receive && (m_receive->routeStatus() == DecoderAudioModel::RouteStatus::SharedRxAudio + || m_receive->routeStatus() == DecoderAudioModel::RouteStatus::DaxTransportUnavailable)) { + sourceDescription += QStringLiteral(". Assign a DAX RX channel to isolate a Flex slice; no channel is assigned automatically."); + } + m_modemStatusValue->setAccessibleDescription(sourceDescription); const QString stateText = m_lastDiagnostics.inFrame ? QStringLiteral("frame") : m_lastDiagnostics.inPreamble ? QStringLiteral("preamble") : QStringLiteral("search"); @@ -3288,10 +3348,7 @@ void Ax25HfPacketDecodeDialog::setDiagnosticsDebugEnabled(bool enabled, bool per return; m_diagnosticsDebugEnabled = enabled; - if (m_shim) - QMetaObject::invokeMethod(m_shim, [shim = m_shim, enabled]() { - shim->setDiagnosticsLoggingEnabled(enabled); - }, Qt::QueuedConnection); + if (m_receive) { m_receive->setDiagnosticsLoggingEnabled(enabled); } if (m_terminal) m_terminal->setVerbose(enabled); // echo protocol detail inline in the terminal if (m_packetActivity) @@ -3341,6 +3398,7 @@ void Ax25HfPacketDecodeDialog::logAttachedSliceState(const QString& reason) void Ax25HfPacketDecodeDialog::appendSystemLine(const QString& text) { + const QPointer guard(this); if (!m_log) return; qCDebug(lcAx25).noquote() << text; @@ -3349,6 +3407,7 @@ void Ax25HfPacketDecodeDialog::appendSystemLine(const QString& text) "MODEM  " "%2") .arg(utcClock().toHtmlEscaped(), text.toHtmlEscaped())); + if (!guard) { return; } m_log->verticalScrollBar()->setValue(m_log->verticalScrollBar()->maximum()); } @@ -3375,9 +3434,11 @@ void Ax25HfPacketDecodeDialog::appendTransmitLine(const Ax25TransmitFrame& frame m_log->verticalScrollBar()->setValue(m_log->verticalScrollBar()->maximum()); } -void Ax25HfPacketDecodeDialog::appendDiagnosticsLine(const Ax25DecoderDiagnostics& diagnostics) +void Ax25HfPacketDecodeDialog::appendDiagnosticsLine( + const Ax25DecoderDiagnostics& diagnostics, const Ax25ReceiveContext& context) { - if (!m_log || !m_diagnosticsDebugEnabled) + const QPointer guard(this); + if (!context.current() || !m_log || !m_diagnosticsDebugEnabled) return; const QString state = diagnostics.inFrame @@ -3434,6 +3495,7 @@ void Ax25HfPacketDecodeDialog::appendDiagnosticsLine(const Ax25DecoderDiagnostic "DIAG  " "%2") .arg(utcClock().toHtmlEscaped(), line.toHtmlEscaped())); + if (!guard || !context.current()) { return; } m_log->verticalScrollBar()->setValue(m_log->verticalScrollBar()->maximum()); } diff --git a/src/gui/Ax25HfPacketDecodeDialog.h b/src/gui/Ax25HfPacketDecodeDialog.h index 5c9de0408..d68078c72 100644 --- a/src/gui/Ax25HfPacketDecodeDialog.h +++ b/src/gui/Ax25HfPacketDecodeDialog.h @@ -4,7 +4,7 @@ #include "models/RadioModel.h" #include "models/TxController.h" #include -#include "core/tnc/AetherAx25LibmodemShim.h" +#include "models/Ax25ReceiveModel.h" #include #include @@ -15,7 +15,7 @@ #include #include #include -#include +#include class QAbstractButton; class QCheckBox; @@ -200,7 +200,9 @@ class Ax25HfPacketDecodeDialog : public PersistentDialog { // without this a backend that drops the command leaves the TX — and every // frame queued behind it — hung until the window closes. void armTxStreamWaitTimeout(); - void handleRxAudio(const QByteArray& monoFloat32Pcm, int sampleRate); + void handleDecodedFrame(const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context); + void handleRxAudio(const QByteArray& monoFloat32Pcm, int sampleRate, + const Ax25ReceiveContext& context); void startAudioCapture(); void finishAudioCapture(bool save); void captureGeneratedTxAudio(const Ax25TransmitResult& tx); @@ -279,8 +281,9 @@ class Ax25HfPacketDecodeDialog : public PersistentDialog { const TxCoordinator::Request& input); void maybeStartNextKissTx(); void refreshTncStatus(); - void appendFrame(const Ax25DecodedFrame& frame); - void updateDiagnostics(const Ax25DecoderDiagnostics& diagnostics); + void appendFrame(const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context); + void updateDiagnostics(const Ax25DecoderDiagnostics& diagnostics, + const Ax25ReceiveContext& context); void updateHeartbeat(); void refreshStatus(); void refreshTransmitControls(); @@ -288,7 +291,8 @@ class Ax25HfPacketDecodeDialog : public PersistentDialog { void logAttachedSliceState(const QString& reason); void appendSystemLine(const QString& text); void appendTransmitLine(const Ax25TransmitFrame& frame); - void appendDiagnosticsLine(const Ax25DecoderDiagnostics& diagnostics); + void appendDiagnosticsLine(const Ax25DecoderDiagnostics& diagnostics, + const Ax25ReceiveContext& context); QString formatTerminalLine(const Ax25DecodedFrame& frame) const; QString defaultTransmitSource() const; QString transmitSliceSummary() const; @@ -299,8 +303,7 @@ class Ax25HfPacketDecodeDialog : public PersistentDialog { AudioEngine* m_audio{nullptr}; RadioModel* m_radio{nullptr}; - AetherAx25LibmodemShim* m_shim{nullptr}; - QThread m_shimThread; + std::unique_ptr m_receive; Ax25DemodConfig m_shimConfig; QStackedWidget* m_tabStack{nullptr}; QAbstractButton* m_ax25Tab{nullptr}; diff --git a/src/gui/DaxApplet.cpp b/src/gui/DaxApplet.cpp index fc9e6664b..228394909 100644 --- a/src/gui/DaxApplet.cpp +++ b/src/gui/DaxApplet.cpp @@ -1,5 +1,6 @@ #include "DaxApplet.h" #include "MeterSlider.h" +#include "ControlAvailabilityRegistry.h" #include "SliceLabel.h" #include "core/AppSettings.h" #include "core/ThemeManager.h" @@ -117,6 +118,7 @@ void DaxApplet::buildUI() row->addWidget(chLabel); m_daxRxStatus[i] = new QLabel(QStringLiteral("\u2014")); + m_daxRxStatus[i]->setObjectName(QStringLiteral("daxRxStatus%1").arg(i+1)); m_daxRxStatus[i]->setStyleSheet(kStatusLabel); m_daxRxStatus[i]->setFixedWidth(40); m_daxRxStatus[i]->setTextFormat(Qt::RichText); // slice letter may be HTML (#2606) @@ -202,9 +204,16 @@ void DaxApplet::setRadioModel(RadioModel* model) return; } + auto* availability = new ControlAvailabilityRegistry(*model,this); + availability->registerWidget(m_daxTxMeter,tr("DAX transmit audio is unavailable on this receive-only radio."), + [](bool connected,const RadioCapabilities& caps) { + return !connected || (caps.canTransmit && caps.hasDaxStreams); + }); + // Wire slice add/remove for DAX channel tracking connect(model, &RadioModel::sliceAdded, this, [this](SliceModel* s) { connect(s, &SliceModel::daxChannelChanged, this, [this]() { + if (m_nativeReceiveRouting) { return; } // Update DAX status labels for (int i = 0; i < kChannels; ++i) { m_daxRxStatus[i]->setText(QStringLiteral("\u2014")); @@ -246,6 +255,23 @@ void DaxApplet::setRadioModel(RadioModel* model) updateTxLabel(); } +void DaxApplet::setNativeReceiveRouting(bool native) +{ + if (m_nativeReceiveRouting == native) { return; } + m_nativeReceiveRouting = native; + for (int channel = 1; channel <= kChannels; ++channel) { + setReceiveChannelSlice(channel,nullptr); + } +} + +void DaxApplet::setReceiveChannelSlice(int channel, SliceModel* slice) +{ + if (channel < 1 || channel > kChannels || !m_daxRxStatus[channel-1]) { return; } + m_daxRxStatus[channel-1]->setText(slice + ? tr("Slice %1").arg(SliceLabel::richText(slice->sliceId(),slice->letter())) + : QStringLiteral("—")); +} + void DaxApplet::setDaxEnabled(bool on) { if (!m_daxEnable) { // note-only on Windows (#4112) diff --git a/src/gui/DaxApplet.h b/src/gui/DaxApplet.h index deb08354e..934bff929 100644 --- a/src/gui/DaxApplet.h +++ b/src/gui/DaxApplet.h @@ -22,6 +22,8 @@ class DaxApplet : public QWidget { explicit DaxApplet(QWidget* parent = nullptr); void setRadioModel(RadioModel* model); + void setNativeReceiveRouting(bool native); + void setReceiveChannelSlice(int channel, SliceModel* slice); // Bound the operator-facing RX row count to the radio's slice capacity // (Principle I): a 6300 exposes 2, a 6700 eight. Rows above n are HIDDEN, @@ -43,6 +45,7 @@ class DaxApplet : public QWidget { void buildUI(); RadioModel* m_model{nullptr}; + bool m_nativeReceiveRouting{false}; int m_maxDaxChannels{kChannels}; // radio slice capacity (FlexLib table) QPushButton* m_daxEnable{nullptr}; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 47d57fe0a..31c2f7569 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -7956,18 +7956,17 @@ void MainWindow::applyCapabilitiesToUi(bool connected, const RadioCapabilities& // ── DAX: the DAX and DAX-IQ applets, and the autostart toggle ────────── // - // VISIBILITY ONLY. startDax()'s null-check on panStream() is a separate - // crash guard and stays exactly where it is — the two are not merged. This - // stops the operator being offered the controls; that stops a session that - // somehow reaches the bridge anyway from segfaulting on a null stream. const bool dax = !connected || caps.hasDaxStreams; + bool nativeDax = false; +#if defined(HAVE_WEBSOCKETS) && (defined(Q_OS_MAC) || defined(HAVE_PIPEWIRE)) + nativeDax = connected && caps.receiveAudioExport.has_value(); +#endif if (m_appletPanel) { - m_appletPanel->setDaxStreamsVisible(dax); + m_appletPanel->setDaxStreamsVisible(dax,nativeDax); + m_appletPanel->daxApplet()->setNativeReceiveRouting(nativeDax); } - for (VfoWidget* vfo : findChildren()) + for (VfoWidget* vfo : findChildren()) { vfo->setDaxVisible(dax); - if (m_autoDaxAction) { - m_autoDaxAction->setVisible(dax); } // Extended DSP (NRS / RNN / NRF): read via hasExtendedDspFilters(), which diff --git a/src/gui/MainWindow_DigitalModes.cpp b/src/gui/MainWindow_DigitalModes.cpp index 442cc0fb9..8d77cd672 100644 --- a/src/gui/MainWindow_DigitalModes.cpp +++ b/src/gui/MainWindow_DigitalModes.cpp @@ -42,6 +42,7 @@ #include "core/WfmSettings.h" #include "models/BandPlanManager.h" #include "models/RadioModel.h" +#include "models/DaxReceiveModel.h" #include "models/SliceModel.h" #include @@ -1213,14 +1214,12 @@ bool MainWindow::startDax() { if (m_daxBridge) return true; - // DAX rides PanadapterStream's VITA-49 audio, which only a Flex backend - // owns — RadioModel leaves panStream() null for every other family (see - // its makeBackend/Flex-adapter step). Bail before creating the bridge so a - // non-Flex session can't reach the acquireDaxChannel() calls below on a - // null stream. Without this, connecting to an HL2 with AutoStartDAX=True - // segfaults ~3 s later from the auto-start timer in onConnectionStateChanged. - if (!m_radioModel.panStream()) { - qCDebug(lcDax) << "MainWindow: DAX unavailable — backend has no PanadapterStream"; + bool nativeReceive = false; +#ifdef HAVE_WEBSOCKETS + nativeReceive = m_radioModel.backendCapabilities().receiveAudioExport.has_value() && tciServer(); +#endif + if (!nativeReceive && !m_radioModel.panStream()) { + qCDebug(lcDax) << "MainWindow: DAX unavailable for this backend/build"; return false; } @@ -1240,7 +1239,7 @@ bool MainWindow::startDax() // Open only as many DAX RX devices as the radio has slices — the audio // device list follows the radio (#4854). maxSlices() is known here: this // runs on the 3 s post-connect timer, well after the radio reported it. - if (!m_daxBridge->open(m_radioModel.maxSlices())) { + if (!m_daxBridge->open(m_radioModel.maxSlices(), nativeReceive)) { qWarning() << "MainWindow: failed to open DAX audio bridge"; QMessageBox::warning(this, "DAX Audio Bridge Error", "AetherSDR could not open the DAX audio bridge.\n\n" @@ -1254,68 +1253,70 @@ bool MainWindow::startDax() // RadioModel::handleDaxRxStreamRegistry (#3305) — instead of per-consumer // statusReceived hooks with divergent filtering. - // Acquire DAX channels only for slices with a channel assigned. - // FlexLib creates streams on demand, not all 8 unconditionally. - // Creating unused streams causes the radio to round-robin audio - // across all of them, starving the active channels. - m_daxSliceLastCh.clear(); - for (auto* s : m_radioModel.slices()) { - int ch = s->daxChannel(); - m_daxSliceLastCh[s->sliceId()] = ch; - if (ch >= 1 && ch <= 8) { - m_radioModel.acquireDaxChannel( - ch, PanadapterStream::DaxConsumer::Bridge); - } - } - - // #2895: the one-shot loop above only covers slices that ALREADY have a - // DAX channel at bridge startup (typically just slice 0 / DAX 1). When the - // user later assigns DAX 2-4 to another slice via the UI, SliceModel only - // sends `slice set dax=` — it never sends `stream create - // type=dax_rx`, so the radio never registers a DAX client (dax_clients - // stays 0) and sends silence. React to per-slice channel changes here and - // create/remove the DAX RX stream on demand, mirroring the TCI path - // (TciServer::ensureDaxForTci, #1331/#1439) and FlexLib - // RequestDAXRXAudioStream(channel). - for (auto* s : m_radioModel.slices()) { - wireDaxSlice(s); - } - m_daxSliceConns.append(connect(&m_radioModel, &RadioModel::sliceAdded, - this, [this](SliceModel* s) { - if (!m_daxBridge || !s) return; - // Let onDaxChannelChanged() see a 0 -> channel transition for slices - // restored with DAX already assigned. - m_daxSliceLastCh[s->sliceId()] = 0; - wireDaxSlice(s); - // A slice can arrive already carrying a DAX channel (radio profile - // restore); make sure its stream exists too. - if (s->daxChannel() >= 1 && s->daxChannel() <= 8) { - onDaxChannelChanged(s, s->daxChannel()); - } - })); - // A slice removed out from under us (pan close, foreign client, profile - // load) never fires daxChannelChanged — release its channel here or the - // bridge holds it until stopDax (a silent radio-side orphan, #3305). - m_daxSliceConns.append(connect(&m_radioModel, &RadioModel::sliceRemoved, - this, [this](int sliceId) { - if (!m_daxBridge) return; - const int ch = m_daxSliceLastCh.take(sliceId); - if (ch < 1 || ch > 4) return; + if (!nativeReceive) { + // Acquire DAX channels only for slices with a channel assigned. + // FlexLib creates streams on demand, not all 8 unconditionally. + // Creating unused streams causes the radio to round-robin audio + // across all of them, starving the active channels. + m_daxSliceLastCh.clear(); for (auto* s : m_radioModel.slices()) { - if (s && s->daxChannel() == ch) return; // channel hopped slices + int ch = s->daxChannel(); + m_daxSliceLastCh[s->sliceId()] = ch; + if (ch >= 1 && ch <= 8) { + m_radioModel.acquireDaxChannel( + ch, PanadapterStream::DaxConsumer::Bridge); + } } - m_radioModel.releaseDaxChannel( - ch, PanadapterStream::DaxConsumer::Bridge); - })); - // Wire DAX RX: PanadapterStream routes registered DAX streams here - connect(m_radioModel.panStream(), &PanadapterStream::daxPcmReady, - m_daxBridge, [bridge = m_daxBridge](int channel, const PcmFrame& frame) { - const QByteArray pcm = frame.legacyStereo24(); - if (!pcm.isEmpty()) { - bridge->feedDaxAudio(channel, pcm); + // #2895: the one-shot loop above only covers slices that ALREADY have a + // DAX channel at bridge startup (typically just slice 0 / DAX 1). When the + // user later assigns DAX 2-4 to another slice via the UI, SliceModel only + // sends `slice set dax=` — it never sends `stream create + // type=dax_rx`, so the radio never registers a DAX client (dax_clients + // stays 0) and sends silence. React to per-slice channel changes here and + // create/remove the DAX RX stream on demand, mirroring the TCI path + // (TciServer::ensureDaxForTci, #1331/#1439) and FlexLib + // RequestDAXRXAudioStream(channel). + for (auto* s : m_radioModel.slices()) { + wireDaxSlice(s); } - }); + m_daxSliceConns.append(connect(&m_radioModel, &RadioModel::sliceAdded, + this, [this](SliceModel* s) { + if (!m_daxBridge || !s) return; + // Let onDaxChannelChanged() see a 0 -> channel transition for slices + // restored with DAX already assigned. + m_daxSliceLastCh[s->sliceId()] = 0; + wireDaxSlice(s); + // A slice can arrive already carrying a DAX channel (radio profile + // restore); make sure its stream exists too. + if (s->daxChannel() >= 1 && s->daxChannel() <= 8) { + onDaxChannelChanged(s, s->daxChannel()); + } + })); + // A slice removed out from under us (pan close, foreign client, profile + // load) never fires daxChannelChanged — release its channel here or the + // bridge holds it until stopDax (a silent radio-side orphan, #3305). + m_daxSliceConns.append(connect(&m_radioModel, &RadioModel::sliceRemoved, + this, [this](int sliceId) { + if (!m_daxBridge) return; + const int ch = m_daxSliceLastCh.take(sliceId); + if (ch < 1 || ch > DaxBridge::NUM_CHANNELS) { return; } + for (auto* s : m_radioModel.slices()) { + if (s && s->daxChannel() == ch) return; // channel hopped slices + } + m_radioModel.releaseDaxChannel( + ch, PanadapterStream::DaxConsumer::Bridge); + })); + + // Wire DAX RX: PanadapterStream routes registered DAX streams here + connect(m_radioModel.panStream(), &PanadapterStream::daxPcmReady, + m_daxBridge, [bridge = m_daxBridge](int channel, const PcmFrame& frame) { + const QByteArray pcm = frame.legacyStereo24(); + if (!pcm.isEmpty()) { + bridge->feedDaxAudio(channel, pcm); + } + }); + } // DAX-IQ stream-status routing, the VITA-49 IQ feed, level meter, and the // enable/disable/rate connections are wired ONCE at construction (see the @@ -1343,6 +1344,21 @@ bool MainWindow::startDax() m_daxBridge->setChannelGain(i, ss.value(QStringLiteral("DaxRxGain%1").arg(i), "0.5").toString().toFloat()); m_daxBridge->setTxGain(ss.value("DaxTxGain", "0.5").toString().toFloat()); +#ifdef HAVE_WEBSOCKETS + if (nativeReceive) { + auto* receive = new DaxReceiveModel(m_radioModel, *tciServer(), m_daxBridge); + connect(receive, &DaxReceiveModel::audioReady, m_daxBridge, &DaxBridge::feedDaxAudio); + connect(receive, &DaxReceiveModel::channelReset, m_daxBridge, &DaxBridge::resetRxChannel); + connect(receive, &DaxReceiveModel::channelSliceChanged, m_appletPanel->daxApplet(), + [this](int channel, int sliceId) { + m_appletPanel->daxApplet()->setReceiveChannelSlice(channel,m_radioModel.slice(sliceId)); + }); + receive->setEnabled(true); + qCInfo(lcDax) << "MainWindow: starting receive-only DAX audio bridge"; + return true; + } +#endif + // Wire DAX TX: apps → bridge → AudioEngine → VITA-49. // AudioEngine chooses packet format/routing based on DaxTxLowLatency. // This is one configured shared endpoint, not an inferred process identity. @@ -1380,8 +1396,11 @@ void MainWindow::stopDax() { if (!m_daxBridge) return; - m_audio->setDaxTxMode(false); - m_audio->clearTxAccumulators(); // self-marshals + const bool receiveOnly = m_daxBridge->isReceiveOnly(); + if (!receiveOnly) { + m_audio->setDaxTxMode(false); + m_audio->clearTxAccumulators(); // self-marshals + } // #2895: drop the per-slice daxChannelChanged / sliceAdded reactions wired // in startDax() so they don't fire against a torn-down bridge. @@ -1391,8 +1410,9 @@ void MainWindow::stopDax() m_daxSliceConns.clear(); m_daxSliceLastCh.clear(); - disconnect(m_radioModel.panStream(), &PanadapterStream::daxPcmReady, - m_daxBridge, nullptr); + if (m_radioModel.panStream()) { + disconnect(m_radioModel.panStream(), &PanadapterStream::daxPcmReady, m_daxBridge, nullptr); + } disconnect(m_daxBridge, &DaxBridge::txAudioReady, this, nullptr); @@ -1401,13 +1421,20 @@ void MainWindow::stopDax() // or RADE still uses survives a bridge teardown — the #3363/#2886 failure // class, now enforced structurally instead of by cross-consumer peeking // (#3305). - m_radioModel.releaseAllDaxChannels( - PanadapterStream::DaxConsumer::Bridge); + if (!receiveOnly) { + m_radioModel.releaseAllDaxChannels(PanadapterStream::DaxConsumer::Bridge); + } // Restore original mic selection - if (!m_savedMicSelection.isEmpty() && m_savedMicSelection != "PC") + if (!receiveOnly && !m_savedMicSelection.isEmpty() && m_savedMicSelection != "PC") { m_radioModel.sendCommand(QString("transmit set mic_selection=%1").arg(m_savedMicSelection)); + } + if (receiveOnly) { + for (int channel = 1; channel <= DaxBridge::NUM_CHANNELS; ++channel) { + m_appletPanel->daxApplet()->setReceiveChannelSlice(channel,nullptr); + } + } m_daxBridge->close(); delete m_daxBridge; m_daxBridge = nullptr; diff --git a/src/gui/MainWindow_Menus.cpp b/src/gui/MainWindow_Menus.cpp index c0d1d6f91..4d7f66399 100644 --- a/src/gui/MainWindow_Menus.cpp +++ b/src/gui/MainWindow_Menus.cpp @@ -21,6 +21,7 @@ #include "AppletPanel.h" #include "DaxApplet.h" #include "DocsLinks.h" +#include "ControlAvailabilityRegistry.h" #include "PanadapterApplet.h" #include "PanadapterStack.h" #include "RadioSetupDialog.h" @@ -985,8 +986,16 @@ void MainWindow::buildMenuBar() } #else auto* autoDaxAction = settingsMenu->addAction("Autostart DAX with AetherSDR"); - m_autoDaxAction = autoDaxAction; // hidden by applyCapabilitiesToUi() on a - // radio that reports no DAX streams + m_autoDaxAction = autoDaxAction; + auto* daxAvailability = new ControlAvailabilityRegistry(m_radioModel, this); + daxAvailability->registerAction(autoDaxAction, tr("DAX audio export is unavailable for this receiver in this build."), + [](bool connected, const RadioCapabilities& caps) { + bool nativeReceive = false; +#ifdef HAVE_WEBSOCKETS + nativeReceive = caps.receiveAudioExport.has_value(); +#endif + return !connected || caps.hasDaxStreams || nativeReceive; + }); autoDaxAction->setCheckable(true); autoDaxAction->setChecked( AppSettings::instance().value("AutoStartDAX", "False").toString() == "True"); diff --git a/src/gui/MainWindow_Wiring.cpp b/src/gui/MainWindow_Wiring.cpp index 3251dceba..4f83e8c8f 100644 --- a/src/gui/MainWindow_Wiring.cpp +++ b/src/gui/MainWindow_Wiring.cpp @@ -1373,7 +1373,10 @@ void MainWindow::syncSquelchScaleToUi() // capability gate: there is no mode yet to judge the scale against. QString reason; const SliceModel* s = activeSlice(); - if (m_radioModel.isConnected() && s && !autoSquelchAvailable(scale)) { + const auto published = m_radioModel.backendCapabilities().squelchLevelScale; + const bool engineAuto = published && published->automaticInEngine && s + && published->appliesTo(s->mode()); + if (m_radioModel.isConnected() && s && !engineAuto && !autoSquelchAvailable(scale)) { const auto& published = m_radioModel.backendCapabilities().squelchLevelScale; if (!published) { reason = tr("this radio does not publish where its squelch level " @@ -1415,6 +1418,9 @@ bool MainWindow::autoSquelchShouldRunOnSpectrum( return panId == s->panId(); } + const auto sql = m_radioModel.backendCapabilities().squelchLevelScale; + if (sql && sql->automaticInEngine) { return false; } + // The receiver's own gate runs Auto only on its slice's pan. if (exclusiveSquelchScale(m_radioModel.backendCapabilities().squelchLevelScale) && panId != s->panId()) { @@ -1540,6 +1546,16 @@ void MainWindow::wireVfoTelemetry(VfoWidget* vfo, SliceModel* s) return; } + auto* relativeTimer = new QTimer(vfo); + relativeTimer->setInterval(100); + connect(relativeTimer, &QTimer::timeout, vfo, [this, vfo, id = s->sliceId()] { + const auto& meters = m_radioModel.meterModel(); + const MeterDef* def = meters.meterDef(meters.findMeter("SLC", "LEVEL", id)); + if ((def && def->unit == "dBFS") || vfo->relativeSignalLevel()) { + vfo->setRelativeSignalLevel(meters.relativeLevelForSlice(id), def ? def->unavailableReason : QString()); + } + }); + relativeTimer->start(); // Feed S-meter per-slice — only this VFO's slice level const int sid = s->sliceId(); const QPointer vfoPtr(vfo); @@ -1871,6 +1887,7 @@ void MainWindow::onSliceAdded(SliceModel* s) // toggle that flag on slice-mode transitions (e.g. band-stack restore) // because doing so parks DAX2's TX Stream in Busy. (#2315) auto updateDaxTxMode = [this]() { + if (!m_radioModel.backendCapabilities().canTransmit) { return; } bool isDigital = false; int txSliceId = -1; for (auto* sl : m_radioModel.slices()) { @@ -3075,7 +3092,7 @@ void MainWindow::runProfileLoadRecoveryPass(const QString& profileType, #endif #if defined(Q_OS_MAC) || defined(HAVE_PIPEWIRE) - if (m_daxBridge) { + if (m_daxBridge && m_radioModel.backendCapabilities().hasDaxStreams) { auto* panStream = m_radioModel.panStream(); bool txSliceIsDigital = false; @@ -6390,6 +6407,19 @@ void MainWindow::applyAmpTxMeters(float watts, float swr, bool fromRelay) void MainWindow::wireMeters() { + auto* relativeTimer = new QTimer(this); + relativeTimer->setInterval(100); + connect(relativeTimer, &QTimer::timeout, this, [this] { + if (!m_appletPanel) { return; } + auto* meter = m_appletPanel->sMeterWidget(); + const auto& meters = m_radioModel.meterModel(); + const MeterDef* def = meters.meterDef(meters.findMeter("SLC", "LEVEL", m_activeSliceId)); + if ((def && def->unit == "dBFS") || meter->relativeLevel()) { + meter->setRelativeLevel(meters.relativeLevelForSlice(m_activeSliceId), m_activeSliceId, + def ? def->unavailableReason : QString()); + } + }); + relativeTimer->start(); // ── S-Meter: MeterModel → SMeterWidget (active slice only) ───────────── connect(&m_radioModel.meterModel(), &MeterModel::sLevelChanged, this, [this](int sliceIndex, float dbm) { diff --git a/src/gui/RtlReceiverSettingsWidget.cpp b/src/gui/RtlReceiverSettingsWidget.cpp index 3b96f8c98..a59d6251f 100644 --- a/src/gui/RtlReceiverSettingsWidget.cpp +++ b/src/gui/RtlReceiverSettingsWidget.cpp @@ -47,8 +47,9 @@ bool supportsSettings(const RadioCapabilities& caps) RtlReceiverSettingsWidget::RtlReceiverSettingsWidget(RadioModel& model, QWidget* parent) : QWidget(parent), m_model(model), m_availability(new ControlAvailabilityRegistry(model, this)), - m_controls(new QGroupBox(tr("Receiver corrections"), this)), + m_controls(new QGroupBox(tr("Receiver settings"), this)), m_ppm(new RtlPpmSpinBox(m_controls)), m_ppmTimer(new QTimer(this)), m_dc(new QCheckBox(tr("Suppress IQ DC"), m_controls)), + m_meters(new QCheckBox(tr("Enable receive meters"), m_controls)), m_applied(new QLabel(this)), m_status(new QLabel(this)), m_identity(new QLabel(this)) { setObjectName(QStringLiteral("rtlReceiverSettings")); @@ -69,6 +70,9 @@ RtlReceiverSettingsWidget::RtlReceiverSettingsWidget(RadioModel& model, QWidget* form->addRow(apply); m_dc->setObjectName(QStringLiteral("rtlDcSuppression")); form->addRow(m_dc); + m_meters->setObjectName(QStringLiteral("rtlReceiveMeters")); + m_meters->setAccessibleDescription(tr("Turn off to reduce display CPU use. Reception, squelch and audio continue.")); + form->addRow(m_meters); layout->addWidget(m_controls); m_applied->setObjectName(QStringLiteral("rtlCorrectionsApplied")); m_status->setObjectName(QStringLiteral("rtlCorrectionsStatus")); @@ -90,8 +94,9 @@ RtlReceiverSettingsWidget::RtlReceiverSettingsWidget(RadioModel& model, QWidget* "with a 5 Hz high-pass response. Signals at capture center are also affected, especially an AM or CW carrier. " "Keep wanted signals away from capture center. After a capture change or enable, allow about 0.3 seconds to settle. " "The squelch measurement stays unchanged.\n\n" - "Accepted corrections are saved per reported device serial. Dongles reporting the same serial share settings; " - "devices without a serial use corrections for this session only."), this); + "Receive meters can be disabled to reduce CPU use; RF levels then show unavailable. Reception, squelch and audio continue.\n\n" + "Accepted settings are saved per reported device serial. Dongles reporting the same serial share settings; " + "devices without a serial use settings for this session only."), this); explanation->setWordWrap(true); layout->addWidget(explanation); layout->addStretch(); @@ -105,6 +110,17 @@ RtlReceiverSettingsWidget::RtlReceiverSettingsWidget(RadioModel& model, QWidget* m_availability->registerWidget(m_dc, tr("The connected radio does not provide RTL receiver corrections."), available, [this] { return m_haveState && m_confirmed.value(QStringLiteral("dcSuppression")).toBool(); }); + m_availability->registerWidget(m_meters, + tr("The connected radio does not provide a receive-meter switch."), + [available](bool connected, const RadioCapabilities& caps) { + return available(connected, caps) && caps.extensions.value(QStringLiteral("rtl")).toMap() + .value(QStringLiteral("receiveMetersControl")).toBool(); + }, [this] { return m_haveState && m_confirmed.value(QStringLiteral("receiveMetersEnabled"), true).toBool(); }); + connect(m_meters, &QCheckBox::clicked, this, [this](bool requested) { + const QSignalBlocker block(m_meters); + m_meters->setChecked(m_confirmed.value(QStringLiteral("receiveMetersEnabled"), true).toBool()); + submit(QStringLiteral("receive_meters.set"), requested, m_metersRequest); + }); connect(m_ppm, &QSpinBox::valueChanged, this, [this] { if (!isVisible() || !m_haveState) { return; } m_ppmEdited = true; @@ -132,12 +148,12 @@ void RtlReceiverSettingsWidget::bindBackend() disconnect(m_statusConnection); disconnect(m_resultConnection); disconnect(m_errorConnection); m_backend = m_model.backend(); const quint64 generation = ++m_bindingGeneration; - m_queryRequest = m_ppmRequest = m_dcRequest = 0; + m_queryRequest = m_ppmRequest = m_dcRequest = m_metersRequest = 0; m_haveState = false; m_ppmEdited = false; m_confirmed.clear(); m_error.clear(); { - const QSignalBlocker ppmBlock(m_ppm), dcBlock(m_dc); - m_ppm->setValue(0); m_dc->setChecked(false); + const QSignalBlocker ppmBlock(m_ppm), dcBlock(m_dc), metersBlock(m_meters); + m_ppm->setValue(0); m_dc->setChecked(false); m_meters->setChecked(true); } renderStatus(); if (!m_model.isConnected() || !m_backend || !supportsSettings(m_model.backendCapabilities())) { return; } @@ -153,20 +169,21 @@ void RtlReceiverSettingsWidget::bindBackend() if (generation != m_bindingGeneration || !source || source != m_model.backend()) { return; } if (id == m_queryRequest && id) { m_queryRequest = 0; acceptStatus(value.toMap()); - } else if (id && (id == m_ppmRequest || id == m_dcRequest)) { + } else if (id && (id == m_ppmRequest || id == m_dcRequest || id == m_metersRequest)) { if (id == m_ppmRequest) { m_ppmRequest = 0; // Preserve a newer edit that the operator has not submitted. m_ppmEdited = m_ppm->value() != value.toInt(); } if (id == m_dcRequest) { m_dcRequest = 0; } + if (id == m_metersRequest) { m_metersRequest = 0; } queryStatus(); } }); m_errorConnection = connect(source, &IRadioBackend::extensionError, this, [this, source, generation](quint64 id, const QString& reason) { if (generation != m_bindingGeneration || !source || source != m_model.backend() || !id - || (id != m_queryRequest && id != m_ppmRequest && id != m_dcRequest)) { return; } + || (id != m_queryRequest && id != m_ppmRequest && id != m_dcRequest && id != m_metersRequest)) { return; } const bool queryFailed = id == m_queryRequest; if (queryFailed) { m_queryRequest = 0; } if (id == m_ppmRequest) { @@ -176,6 +193,7 @@ void RtlReceiverSettingsWidget::bindBackend() if (m_ppm->value() == m_submittedPpm) { m_ppmTimer->stop(); } } if (id == m_dcRequest) { m_dcRequest = 0; } + if (id == m_metersRequest) { m_metersRequest = 0; } m_error = tr("Request refused: %1").arg(reason); if (!queryFailed) { queryStatus(); } renderStatus(); @@ -196,9 +214,10 @@ void RtlReceiverSettingsWidget::acceptStatus(const QVariantMap& status) if (!status.value(QStringLiteral("applied")).toBool()) { return; } m_haveState = true; m_confirmed = status; - const QSignalBlocker ppmBlock(m_ppm), dcBlock(m_dc); + const QSignalBlocker ppmBlock(m_ppm), dcBlock(m_dc), metersBlock(m_meters); if (!m_ppmEdited) { m_ppm->setValue(status.value(QStringLiteral("ppm")).toInt()); } m_dc->setChecked(status.value(QStringLiteral("dcSuppression")).toBool()); + m_meters->setChecked(status.value(QStringLiteral("receiveMetersEnabled"), true).toBool()); m_availability->refreshEngaged(); renderStatus(); } @@ -257,11 +276,11 @@ void RtlReceiverSettingsWidget::renderStatus() if (!usable) { lines << tr("Waiting for a connected receiver's confirmed settings."); } else { if (m_ppmTimer->isActive()) { lines << tr("Frequency correction will apply after this adjustment."); } - if (m_ppmRequest || m_dcRequest || m_confirmed.value(QStringLiteral("pending")).toBool()) { + if (m_ppmRequest || m_dcRequest || m_metersRequest || m_confirmed.value(QStringLiteral("pending")).toBool()) { lines << tr("Applying request; the applied values above remain in use until confirmation."); } lines << (m_confirmed.value(QStringLiteral("saved")).toBool() - ? tr("Applied corrections saved for this device.") + ? tr("Applied settings saved for this device.") : m_confirmed.value(QStringLiteral("saveReason"), tr("Applied for this session; not saved.")).toString()); } if (!m_error.isEmpty()) { lines << m_error; } diff --git a/src/gui/RtlReceiverSettingsWidget.h b/src/gui/RtlReceiverSettingsWidget.h index f2dfe6dc7..d2e2f793f 100644 --- a/src/gui/RtlReceiverSettingsWidget.h +++ b/src/gui/RtlReceiverSettingsWidget.h @@ -44,6 +44,7 @@ class RtlReceiverSettingsWidget final : public QWidget { QSpinBox* m_ppm; QTimer* m_ppmTimer; QCheckBox* m_dc; + QCheckBox* m_meters; QLabel* m_applied; QLabel* m_status; QLabel* m_identity; @@ -56,5 +57,6 @@ class RtlReceiverSettingsWidget final : public QWidget { quint64 m_queryRequest = 0; quint64 m_ppmRequest = 0; quint64 m_dcRequest = 0; + quint64 m_metersRequest = 0; }; } // namespace AetherSDR diff --git a/src/gui/RxApplet.cpp b/src/gui/RxApplet.cpp index 3e2ace0c2..b61b779a9 100644 --- a/src/gui/RxApplet.cpp +++ b/src/gui/RxApplet.cpp @@ -1061,9 +1061,13 @@ void RxApplet::buildUI() // pan's auto-squelch algorithm picks up the new margin // and the suggested level recomputes on the next FFT. const int margin = std::clamp(v, 5, 20); - auto& s = AppSettings::instance(); - s.setValue("AutoSqlMarginDb", QString::number(margin)); - s.save(); + if (usingEngineAutoSquelch() && m_slice) { + m_slice->setAutomaticSquelch(true, margin); + } else { + auto& s = AppSettings::instance(); + s.setValue("AutoSqlMarginDb", QString::number(margin)); + s.save(); + } emit autoSqlMarginDbChanged(margin); } }); @@ -1412,8 +1416,7 @@ void RxApplet::applySqlModeVisuals() case SqlMode::Auto: { m_sqlSlider->setRange(5, 20); const int margin = std::clamp( - AppSettings::instance().value("AutoSqlMarginDb", "10").toInt(), - 5, 20); + autoSqlMarginDb(), 5, 20); m_sqlSlider->setValue(margin); m_sqlSlider->setEnabled(m_sqlBtn->isEnabled()); m_sqlSlider->setToolTip( @@ -1473,7 +1476,7 @@ QString RxApplet::sqlButtonAccessibleDescription() const bool RxApplet::autoSqlAvailable() const { - return usingExternalReceiveSquelch() || m_autoSqlAvailable; + return usingExternalReceiveSquelch() || usingEngineAutoSquelch() || m_autoSqlAvailable; } QString RxApplet::autoSqlUnavailableReason() const @@ -1524,10 +1527,17 @@ void RxApplet::cycleSqlModeExternal() int RxApplet::autoSqlMarginDb() const { + if (usingEngineAutoSquelch() && m_slice) { return m_slice->automaticSquelchMarginDb(); } return std::clamp( AppSettings::instance().value("AutoSqlMarginDb", "10").toInt(), 5, 20); } +bool RxApplet::usingEngineAutoSquelch() const +{ + return m_exclusiveSquelch && m_exclusiveSquelch->automaticInEngine + && !usingExternalReceiveSquelch(); +} + bool RxApplet::usingExternalReceiveSquelch() const { return m_slice && m_slice->externalReceiveReplacementActive(); @@ -1596,9 +1606,13 @@ void RxApplet::setSqlSliderValueExternal(int v) } } else if (m_sqlMode == SqlMode::Auto) { const int margin = std::clamp(v, 5, 20); - auto& s = AppSettings::instance(); - s.setValue("AutoSqlMarginDb", QString::number(margin)); - s.save(); + if (usingEngineAutoSquelch() && m_slice) { + m_slice->setAutomaticSquelch(true, margin); + } else { + auto& s = AppSettings::instance(); + s.setValue("AutoSqlMarginDb", QString::number(margin)); + s.save(); + } emit autoSqlMarginDbChanged(margin); if (m_sqlSlider) { QSignalBlocker b(m_sqlSlider); @@ -1615,7 +1629,7 @@ void RxApplet::loadClientSquelchIntent() m_clientManualSqlLevel.reset(); m_restoreAutoSql = false; m_clientSqlAwaitingReport = false; - if (!m_slice || !m_radioModel || usingExternalReceiveSquelch()) { + if (!m_slice || !m_radioModel || usingExternalReceiveSquelch() || usingEngineAutoSquelch()) { return; } const RadioSettingsScope scope = m_radioModel->settingsScope(); @@ -1652,7 +1666,7 @@ void RxApplet::loadClientSquelchIntent() void RxApplet::saveClientSquelchIntent() { if (!m_clientSquelchScope.hasRadioIdentity() || !m_slice - || usingExternalReceiveSquelch()) { + || usingExternalReceiveSquelch() || usingEngineAutoSquelch()) { return; } const RadioSettingsScope scope = m_clientSquelchScope; @@ -1742,7 +1756,11 @@ void RxApplet::setSqlMode(SqlMode m, bool propagateToRadio) // For an absolute detector Auto starts from the accepted/manual // threshold. A dB margin is not an absolute threshold; the next FFT // supplies that through the spectrum's declared scale. - m_slice->setSquelch(sqOn, level); + if (m == SqlMode::Auto && usingEngineAutoSquelch()) { + m_slice->setAutomaticSquelch(true, autoSqlMarginDb()); + } else { + m_slice->setSquelch(sqOn, level); + } } } @@ -2755,6 +2773,14 @@ void RxApplet::connectSlice(SliceModel* s) }); auto applySquelchState = [this](bool on, int level, bool externalReceive) { + if (!externalReceive && usingEngineAutoSquelch()) { + if (on && !m_slice->automaticSquelch()) { setManualSqlLevelForCurrentSurface(level); } + const SqlMode mode = !on ? SqlMode::Off + : (m_slice->automaticSquelch() ? SqlMode::Auto : SqlMode::Manual); + setSqlMode(mode, /*propagateToRadio=*/false); + emit squelchStateChanged(on, level); + return; + } if (!externalReceive && m_clientSqlAwaitingReport) { if (!m_slice->squelchStateKnown()) { return; @@ -2836,6 +2862,8 @@ void RxApplet::connectSlice(SliceModel* s) if (s->externalReceiveAutoSquelchOn()) { mode = SqlMode::Auto; } + } else if (usingEngineAutoSquelch()) { + mode = s->squelchOn() && s->automaticSquelch() ? SqlMode::Auto : mode; } else if (m_clientSqlAwaitingReport) { // A previous radio/slice's Auto mode is not this radio's intent. // Wait for its first SQL report instead of starting on defaults. diff --git a/src/gui/RxApplet.h b/src/gui/RxApplet.h index ad54b1dda..c9a1c045f 100644 --- a/src/gui/RxApplet.h +++ b/src/gui/RxApplet.h @@ -35,6 +35,7 @@ class KiwiSdrManager; // RX Applet: controls for one receive slice (antenna, filter presets, AGC, // AF/RF gain, squelch, NB/NR/ANF, RIT/XIT). class RxApplet : public QWidget { + friend struct RxAppletSquelchTestAccess; Q_OBJECT public: @@ -353,6 +354,7 @@ class RxApplet : public QWidget { void cycleSqlMode(); void setSqlMode(SqlMode m, bool propagateToRadio); bool usingExternalReceiveSquelch() const; + bool usingEngineAutoSquelch() const; int clampManualSqlLevelForCurrentSurface(int level) const; void setManualSqlLevelForCurrentSurface(int level); int agcThresholdMinimum() const; diff --git a/src/gui/SMeterWidget.cpp b/src/gui/SMeterWidget.cpp index ab53faafe..1fd608667 100644 --- a/src/gui/SMeterWidget.cpp +++ b/src/gui/SMeterWidget.cpp @@ -161,6 +161,7 @@ QString SMeterWidget::accessibleValueText() const } const float displayDbm = m_rxMode == RxMode::SMeterPeak ? m_peakDbm : m_levelDbm; + if (m_relativeLevel) { return tr("Relative RF, %1 dBFS, uncalibrated").arg(displayDbm, 0, 'f', 0); } return tr("%1, %2 dBm") .arg(sUnitsTextFor(displayDbm)) .arg(static_cast(displayDbm)); @@ -179,7 +180,17 @@ void SMeterWidget::setFaceTheme(AnalogMeterFaceTheme theme) void SMeterWidget::setLevel(float dbm) // a11y-check: skip -- settled update is timer-throttled { + if (m_relativeLevel) { + setAccessibleDescription(tr("Signal strength meter, shows S-units or TX power")); + } + m_relativeLevel = false; + if (!m_peakReset.isActive()) { m_peakReset.start(); } m_receiveMeterReadingActive = false; + updateReceiveLevel(dbm); +} + +void SMeterWidget::updateReceiveLevel(float dbm) +{ m_levelDbm = dbm; // Peak hold (existing needle/triangle behavior) @@ -207,9 +218,38 @@ void SMeterWidget::setLevel(float dbm) // a11y-check: skip -- settled update is } } +void SMeterWidget::setRelativeLevel(std::optional dbfs, int receiverId, const QString& unavailableReason) +{ + const bool valid = unavailableReason.isEmpty() && dbfs && std::isfinite(*dbfs); + if (m_relativeLevel && !m_relativeValid && !valid && receiverId == m_relativeReceiver + && unavailableReason == m_relativeUnavailableReason) { return; } + m_relativeUnavailableReason = unavailableReason; + const bool reset = !m_relativeLevel || receiverId != m_relativeReceiver || !valid; + const float value = valid ? *dbfs : -120.0f; + if (reset) { m_peakDbm = value; m_peakHoldDbm = value; m_peakHoldTimerRunning = false; } + setAccessibleDescription(unavailableReason.isEmpty() + ? tr("Relative RF peak FFT bin in dBFS; uncalibrated, not dBm or audio level") : unavailableReason); + m_relativeLevel = true; m_relativeValid = valid; m_relativeReceiver = receiverId; + m_receiveMeterReadingActive = false; + if (!valid) { + m_levelDbm = value; + m_needleFraction = m_targetNeedleFraction = 0; + m_needleAnimation.stop(); m_peakDecay.stop(); m_peakReset.stop(); + update(); scheduleAccessibleValue(); + return; + } + if (!m_peakReset.isActive()) { m_peakReset.start(); } + updateReceiveLevel(value); +} + void SMeterWidget::setReceiveMeterReading( const KiwiSdrProtocol::MeterReading& reading) { + if (m_relativeLevel) { + setAccessibleDescription(tr("Signal strength meter, shows S-units or TX power")); + } + m_relativeLevel = false; + if (!m_peakReset.isActive()) { m_peakReset.start(); } m_receiveMeterReading = reading; m_receiveMeterReadingActive = true; @@ -436,6 +476,7 @@ void SMeterWidget::animateNeedle() bool SMeterWidget::usesUnavailableRxMeter() const { + if (m_relativeLevel) { return !m_relativeValid; } return m_receiveMeterReadingActive && !(m_receiveMeterReading.valid && (m_receiveMeterReading.capability @@ -447,6 +488,8 @@ bool SMeterWidget::usesUnavailableRxMeter() const QString SMeterWidget::unavailableRxMeterLabel() const { + if (m_relativeLevel) { return m_relativeUnavailableReason.isEmpty() + ? tr("Relative RF — unavailable") : m_relativeUnavailableReason; } if (!m_receiveMeterReadingActive) { return QStringLiteral("Meter unavailable"); } @@ -509,6 +552,7 @@ QString SMeterWidget::sUnitsText() const QString SMeterWidget::sUnitsTextFor(float dbm) const { + if (m_relativeLevel) { return tr("Relative RF"); } const SMeterGeometry::RxScale& scale = m_geometry.rxScale; if (dbm <= scale.minimumDbm) { return QStringLiteral("S0"); @@ -524,6 +568,7 @@ QString SMeterWidget::sUnitsTextFor(float dbm) const float SMeterWidget::dbmToFraction(float dbm) const { + if (m_relativeLevel) { return std::clamp((dbm + 120.0f) / 120.0f, 0.0f, 1.0f); } return static_cast(m_geometry.rxFraction(dbm)); } @@ -843,9 +888,15 @@ void SMeterWidget::paintEvent(QPaintEvent*) const QColor labelColor = physicalTheme ? physicalPalette->text : whiteColor; // -- Outside ticks (RX) --------------------------------------------------- - for (const SMeterGeometry::Tick& tick : m_geometry.rxScale.ticks) { - const QColor& color = (tick.value > m_geometry.rxScale.s9Dbm) ? redColor : whiteColor; - drawOutsideTick(dbmToFraction(static_cast(tick.value)), tick.label, color, true); + if (m_relativeLevel) { + for (int db = -120; db <= 0; db += 20) { + drawOutsideTick((db + 120.0f) / 120.0f, QString::number(db), whiteColor, true); + } + } else { + for (const SMeterGeometry::Tick& tick : m_geometry.rxScale.ticks) { + const QColor& color = (tick.value > m_geometry.rxScale.s9Dbm) ? redColor : whiteColor; + drawOutsideTick(dbmToFraction(static_cast(tick.value)), tick.label, color, true); + } } // -- Inside ticks (TX): scale depends on TX mode -------------------------- @@ -1105,8 +1156,9 @@ void SMeterWidget::paintEvent(QPaintEvent*) p.setFont(srcFont); p.setPen(sourceColor); - p.drawText(qRound(faceCenterX - sfm.horizontalAdvance(m_source) / 2.0), - sourceBaseline, m_source); + const QString source = m_relativeLevel ? tr("RF peak bin") : m_source; + p.drawText(qRound(faceCenterX - sfm.horizontalAdvance(source) / 2.0), + sourceBaseline, source); const float displayDbm = (m_rxMode == RxMode::SMeterPeak) ? m_peakDbm : m_levelDbm; @@ -1117,7 +1169,7 @@ void SMeterWidget::paintEvent(QPaintEvent*) p.drawText(qRound(faceLeft) + m_geometry.readout.sideMarginPixels, valueBaseline, sText); - const QString dbmText = QString("%1 dBm").arg(displayDbm, 0, 'f', 0); + const QString dbmText = QString(m_relativeLevel ? "%1 dBFS" : "%1 dBm").arg(displayDbm, 0, 'f', 0); p.setPen(valueColor); p.drawText(qRound(faceRight) - vfm.horizontalAdvance(dbmText) - m_geometry.readout.sideMarginPixels, diff --git a/src/gui/SMeterWidget.h b/src/gui/SMeterWidget.h index fdc097c33..877723c53 100644 --- a/src/gui/SMeterWidget.h +++ b/src/gui/SMeterWidget.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -31,8 +32,8 @@ class SMeterWidget : public QWidget { QSize sizeHint() const override { return m_geometry.sizing.preferred; } QSize minimumSizeHint() const override { return m_geometry.sizing.minimum; } - // Current reading in dBm. - float levelDbm() const { return m_levelDbm; } + // Calibrated reading only; a relative RF reading has no dBm equivalent. + float levelDbm() const { return m_relativeLevel ? std::numeric_limits::quiet_NaN() : m_levelDbm; } // Reading as S-units string (e.g. "S7", "S9+20"). QString sUnitsText() const; @@ -54,6 +55,8 @@ class SMeterWidget : public QWidget { public slots: // Update the displayed RX level (S-meter dBm). void setLevel(float dbm); + void setRelativeLevel(std::optional dbfs, int receiverId, const QString& unavailableReason = {}); + bool relativeLevel() const { return m_relativeLevel; } void setReceiveMeterReading( const AetherSDR::KiwiSdrProtocol::MeterReading& reading); @@ -87,6 +90,7 @@ public slots: void paintEvent(QPaintEvent* event) override; private: + void updateReceiveLevel(float value); void updateNeedleTarget(); void animateNeedle(); void updatePeakHoldValue(); @@ -125,6 +129,10 @@ public slots: bool m_backgroundCacheValid{false}; QTimer m_accessibilityTimer; QString m_lastAccessibleValue; + bool m_relativeLevel = false; + bool m_relativeValid = false; + QString m_relativeUnavailableReason; + int m_relativeReceiver = -1; float m_levelDbm{0.0f}; // current RX reading; initialized from geometry float m_peakDbm{0.0f}; // RX peak hold; initialized from geometry QString m_source{"S-Meter Peak"}; diff --git a/src/gui/SmartMtrConfig.cpp b/src/gui/SmartMtrConfig.cpp index 643d92241..6c92e5e76 100644 --- a/src/gui/SmartMtrConfig.cpp +++ b/src/gui/SmartMtrConfig.cpp @@ -251,6 +251,16 @@ QString formatWatts(double w) const MeterConfig& meterConfig(MeterKind kind) { + static const MeterConfig relativeCfg = [] { + MeterConfig cfg; cfg.valueToPosition = mapLinear; + for (int db = -120; db <= 0; db += 20) { + ScaleMarker marker; + marker.position = mapLinear(db, -120, 0); + marker.size = MarkerSize::Large; marker.label = QString::number(db); + cfg.markers.push_back(marker); + } + return cfg; + }(); static const MeterConfig signalCfg = buildSignalConfig(); static const MeterConfig micCfg = buildMicConfig(); static const MeterConfig swrCfg = buildSwrConfig(); @@ -260,6 +270,8 @@ const MeterConfig& meterConfig(MeterKind kind) // valueToPosition for indicatorPosition(). A barefoot default scale is fine. static const MeterConfig powerCfg = buildPowerConfig(120.0); switch (kind) { + case MeterKind::RelativeSignal: + return relativeCfg; case MeterKind::MicLevel: return micCfg; case MeterKind::SWR: diff --git a/src/gui/SmartMtrConfig.h b/src/gui/SmartMtrConfig.h index 90dbba144..33c401a6c 100644 --- a/src/gui/SmartMtrConfig.h +++ b/src/gui/SmartMtrConfig.h @@ -13,8 +13,8 @@ namespace AetherSDR { // value plus a registry entry in SmartMtrConfig.cpp; no widget changes. // The measurement the control currently shows. Extend here for new kinds. -// Signal is the RX scale; the rest are TX scales (see DisplaySettings::TxMeter). -enum class MeterKind { Signal, MicLevel, SWR, Power, Compression }; +// Signal (dBm) and RelativeSignal (dBFS) are RX scales; the rest are TX scales. +enum class MeterKind { RelativeSignal, Signal, MicLevel, SWR, Power, Compression }; // Forward-power scale headroom: the scale top sits this far above the radio's // rated power so a rig pushing slightly past rated doesn't peg the bar, and the diff --git a/src/gui/SmartMtrWidget.cpp b/src/gui/SmartMtrWidget.cpp index 35ee83325..f4b6faf2c 100644 --- a/src/gui/SmartMtrWidget.cpp +++ b/src/gui/SmartMtrWidget.cpp @@ -1,6 +1,7 @@ #include "SmartMtrWidget.h" #include "SmartMtrStyle.h" +#include "core/ThemeManager.h" #include #include @@ -111,8 +112,10 @@ void SmartMtrWidget::applyBallistics(MeterKind kind) m_smooth.setBallistics(b); } -void SmartMtrWidget::setMeterInput(const MeterInput& input) +void SmartMtrWidget::setMeterInput(const MeterInput& input, const QString& unavailableReason) { + m_unavailableReason = unavailableReason; + setAccessibleDescription(unavailableReason); // A non-finite value (NaN/Inf) would survive std::clamp (both comparisons // false), stick in the bar smoother so needsAnimation() never clears, and // poison the extremes running sum permanently. Drop it; the last good frame @@ -168,6 +171,13 @@ void SmartMtrWidget::setMeterInput(const MeterInput& input) span > 0.0 ? float((posUnits - kScaleMin) / span) : 0.0f; m_smooth.setTarget(targetFrac); + if (!input.hasValue && input.kind == MeterKind::RelativeSignal) { + m_animTimer.stop(); + m_extremes.reset(); + m_smooth.snapToTarget(); + update(); + return; + } if (kindChanged) m_smooth.snapToTarget(); // snap across the discontinuity, don't glide @@ -254,6 +264,13 @@ void SmartMtrWidget::paintEvent(QPaintEvent*) drawIndicator(p, g); p.drawPixmap(0, 0, m_aboveBar); drawExtremes(p, g); + if (!m_unavailableReason.isEmpty()) { + p.setPen(ThemeManager::instance().color(this, "color.text.secondary")); + QFont labelFont = font(); + labelFont.setPixelSize(10); + p.setFont(labelFont); + p.drawText(rect(), Qt::AlignCenter, tr("Meters off")); + } // Let the parent's value-label overlay repaint in lockstep with the markers. emit repainted(); @@ -502,7 +519,7 @@ void SmartMtrWidget::drawMarkers(QPainter& p, const SmartMtrGeometry& g) const void SmartMtrWidget::drawTypeLabel(QPainter& p, const SmartMtrGeometry& g) const { // TX meters only — the RX signal meter shows no type label. - if (!m_showTypeLabel || m_kind == MeterKind::Signal) + if ((!m_showTypeLabel && m_kind != MeterKind::RelativeSignal) || m_kind == MeterKind::Signal) return; QString text; @@ -511,6 +528,7 @@ void SmartMtrWidget::drawTypeLabel(QPainter& p, const SmartMtrGeometry& g) const case MeterKind::SWR: text = QStringLiteral("SWR"); break; case MeterKind::Power: text = QStringLiteral("PWR"); break; case MeterKind::Compression: text = QStringLiteral("COMP"); break; + case MeterKind::RelativeSignal: text = QStringLiteral("dBFS"); break; case MeterKind::Signal: return; } @@ -641,6 +659,7 @@ QString SmartMtrWidget::extremeSUnit(double raw) const QString SmartMtrWidget::extremeDbm(double raw) const { const int v = qRound(raw); + if (m_kind == MeterKind::RelativeSignal) { return QStringLiteral("%1dBFS").arg(v); } return m_kind == MeterKind::MicLevel ? QStringLiteral("%1dB").arg(v) : QStringLiteral("%1dBm").arg(v); } @@ -693,7 +712,7 @@ QVector SmartMtrWidget::extremeLabels() const // Numeric value labels are a signal-meter feature only; other kinds (mic/TX) // show no value text (the peak marker itself still draws). - if (m_kind != MeterKind::Signal) + if (m_kind != MeterKind::Signal && m_kind != MeterKind::RelativeSignal) return out; // Two lines for signal (S-unit over dBm); a single top line for mic (no diff --git a/src/gui/SmartMtrWidget.h b/src/gui/SmartMtrWidget.h index b3b1fe5a5..b9e879c60 100644 --- a/src/gui/SmartMtrWidget.h +++ b/src/gui/SmartMtrWidget.h @@ -27,7 +27,7 @@ class SmartMtrWidget : public QWidget { // Push what to display. The parent owns the data (kind + value + range); // the widget just renders it. Triggers a repaint. - void setMeterInput(const MeterInput& input); + void setMeterInput(const MeterInput& input, const QString& unavailableReason = {}); // Extremes (min/max peak-hold markers) options. Widget-local mirrors of the // DisplaySettings enums so this widget stays free of the settings/AppSettings @@ -77,6 +77,7 @@ class SmartMtrWidget : public QWidget { void changeEvent(QEvent*) override; private: + QString m_unavailableReason; // One element per method; all draw in UNITS via SmartMtrGeometry. Called // from paintEvent in back-to-front order. void drawControl(QPainter& p, const SmartMtrGeometry& g) const; diff --git a/src/gui/VfoWidget.cpp b/src/gui/VfoWidget.cpp index 872ba781d..b7e153660 100644 --- a/src/gui/VfoWidget.cpp +++ b/src/gui/VfoWidget.cpp @@ -4055,6 +4055,17 @@ void VfoWidget::paintEvent(QPaintEvent* event) const int scaleY = barY + barH + 2; const int tickH = 3; + if (m_relativeSignal) { + p.setPen(ThemeManager::instance().color(this, "color.text.secondary")); + p.drawLine(barX, scaleY, barX + barW, scaleY); + QFont font = p.font(); font.setPixelSize(7); p.setFont(font); + for (int db = -120; db <= 0; db += 30) { + const int x = barX + (db + 120) * barW / 120; + p.drawLine(x, scaleY, x, scaleY + tickH); + p.drawText(x - (db == 0 ? 4 : 8), scaleY + tickH + 7, QString::number(db)); + } + return; + } // Horizontal line: blue from start to S9, red from S9 to end. p.setPen(QColor(0x30, 0x80, 0xff)); p.drawLine(barX, scaleY, s9X, scaleY); @@ -4131,6 +4142,7 @@ QRect VfoWidget::meterBarRect() const void VfoWidget::applyMeterView(bool smartMtr) { m_smartMtr = smartMtr; + updateSignalMeterTarget(); if (m_meterStack) { m_meterStack->setCurrentIndex(smartMtr ? 1 : 0); } @@ -4280,6 +4292,8 @@ void VfoWidget::syncSmartMtrSettingsControls() void VfoWidget::setSignalLevel(float dbm) { + if (m_relativeSignal) { m_dbmLabel->setAccessibleDescription({}); } + m_relativeSignal = false; m_receiveMeterReadingActive = false; m_signalDbm = dbm; m_signalHasDbm = true; // FLEX always delivers a calibrated dBm reading @@ -4289,6 +4303,26 @@ void VfoWidget::setSignalLevel(float dbm) pushSmartMtrInput(); } +void VfoWidget::setRelativeSignalLevel(std::optional dbfs, const QString& unavailableReason) +{ + const bool valid = unavailableReason.isEmpty() && dbfs && std::isfinite(*dbfs); + if (m_relativeSignal && !m_relativeSignalValid && !valid + && m_relativeUnavailableReason == unavailableReason) { return; } + m_relativeUnavailableReason = unavailableReason; + m_relativeSignal = true; + m_relativeSignalValid = valid; + m_receiveMeterReadingActive = false; + m_signalHasDbm = false; + m_signalDbm = m_relativeSignalValid ? *dbfs : -120.0f; + m_dbmLabel->setText(m_relativeSignalValid + ? tr("%1 dBFS").arg(m_signalDbm, 0, 'f', 0) : (unavailableReason.isEmpty() ? tr("— dBFS") : tr("Meters off"))); + m_dbmLabel->setAccessibleName(tr("Relative RF level dBFS")); + m_dbmLabel->setAccessibleDescription(unavailableReason.isEmpty() + ? tr("Peak FFT bin in the receive passband, uncalibrated; not dBm or audio level") : unavailableReason); + updateSignalMeterTarget(); + pushSmartMtrInput(); +} + // SmartMTR feed: choose signal vs mic by TX state and push the input. The // canonical ranges match the per-kind configs in SmartMtrConfig.cpp. void VfoWidget::pushSmartMtrInput() @@ -4356,7 +4390,7 @@ void VfoWidget::pushSmartMtrInput() } in.hasValue = true; } else { - in.kind = MeterKind::Signal; + in.kind = m_relativeSignal ? MeterKind::RelativeSignal : MeterKind::Signal; in.value = m_signalDbm; in.min = -127.0; // dBm: S0 in.max = -13.0; // dBm: S9+60 @@ -4365,8 +4399,11 @@ void VfoWidget::pushSmartMtrInput() // The widget parks/fades the indicator and suppresses the value labels // when hasValue is false, matching the "Meter ---" dBm label. in.hasValue = m_signalHasDbm; + if (m_relativeSignal) { + in.min = -120; in.max = 0; in.hasValue = m_relativeSignalValid; + } } - m_smartMtrWidget->setMeterInput(in); + m_smartMtrWidget->setMeterInput(in, m_relativeSignal ? m_relativeUnavailableReason : QString()); } void VfoWidget::pushSmartMtrOptions() @@ -4407,6 +4444,19 @@ void VfoWidget::pushSmartMtrOptions() void VfoWidget::onSmartMtrRepainted() { + // The animated bar is painted inside the flag, independently of the + // spectrum's optional value-label overlay. An empty overlay needs no + // rebuild on each bar frame. Do erase its last labels immediately when a + // reading disappears, even inside the normal animation throttle window. + if (m_smartMtrWidget->extremeLabels().isEmpty()) { + if (m_smartMtrLabelsVisible) { + m_smartMtrLabelsVisible = false; + m_lastLabelDirtyMs = -1; + emit smartMtrLabelsChanged(); + } + return; + } + // The meter repaints up to ~120 Hz while markers move; the spectrum's // static-overlay redraw (which draws the value labels) is comparatively // costly, so throttle the refresh requests to ~20 Hz. @@ -4414,6 +4464,7 @@ void VfoWidget::onSmartMtrRepainted() if (m_lastLabelDirtyMs >= 0 && now - m_lastLabelDirtyMs < 50) return; m_lastLabelDirtyMs = now; + m_smartMtrLabelsVisible = true; emit smartMtrLabelsChanged(); } @@ -4584,6 +4635,8 @@ void VfoWidget::setTransmitting(bool tx) void VfoWidget::setReceiveMeterReading( const KiwiSdrProtocol::MeterReading& reading) { + if (m_relativeSignal) { m_dbmLabel->setAccessibleDescription({}); } + m_relativeSignal = false; m_receiveMeterReading = reading; m_receiveMeterReadingActive = true; const bool hasDisplayDbm = @@ -4629,12 +4682,24 @@ float VfoWidget::signalDbmToMeterFraction(float dbm) void VfoWidget::updateSignalMeterTarget() { - if (usesUnavailableSignalMeter()) { + if (m_relativeSignal) { + m_targetSignalMeterFraction = m_relativeSignalValid + ? std::clamp((m_signalDbm + 120.0f) / 120.0f, 0.0f, 1.0f) : 0.0f; + } else if (usesUnavailableSignalMeter()) { m_targetSignalMeterFraction = 0.0f; } else { m_targetSignalMeterFraction = signalDbmToMeterFraction(m_signalDbm); } + // Only the selected meter needs animation. Keep the standard bar seeded + // for switching back, without repainting the whole flag behind SmartMTR. + if (m_smartMtr || (m_relativeSignal && !m_relativeSignalValid)) { + m_signalMeterAnimation.stop(); + m_signalMeterFraction = m_targetSignalMeterFraction; + if (!m_smartMtr) { update(); } + return; + } + if (qAbs(m_targetSignalMeterFraction - m_signalMeterFraction) <= kSignalMeterSnapEpsilon) { m_signalMeterFraction = m_targetSignalMeterFraction; if (m_signalMeterAnimation.isActive()) { diff --git a/src/gui/VfoWidget.h b/src/gui/VfoWidget.h index 3b896e87d..598e1cc76 100644 --- a/src/gui/VfoWidget.h +++ b/src/gui/VfoWidget.h @@ -70,6 +70,8 @@ class VfoWidget : public QWidget { // 2-state on/off mode against the slice's squelchLevel only. void setRxApplet(RxApplet* rx); void setSignalLevel(float dbm); + void setRelativeSignalLevel(std::optional dbfs, const QString& unavailableReason = {}); + bool relativeSignalLevel() const { return m_relativeSignal; } void setReceiveMeterReading( const AetherSDR::KiwiSdrProtocol::MeterReading& reading); // SmartMTR feeds: live mic level + separately-measured mic peak (both dBFS) @@ -444,6 +446,9 @@ class VfoWidget : public QWidget { QStringList m_antList; bool m_updatingFromModel{false}; bool m_lastOnLeft{true}; + bool m_relativeSignal = false; + bool m_relativeSignalValid = false; + QString m_relativeUnavailableReason; float m_signalDbm{-130.0f}; // Whether m_signalDbm is a real calibrated reading. FLEX always is; a // KiwiSDR slice without a calibrated meter is not, in which case the @@ -503,6 +508,7 @@ class VfoWidget : public QWidget { // meter's repaint asks the spectrum overlay to refresh. QElapsedTimer m_labelDirtyClock; qint64 m_lastLabelDirtyMs{-1}; + bool m_smartMtrLabelsVisible{false}; float m_micDbfs{-40.0f}; // latest mic level (dBFS); SmartMTR TX scale float m_micPeakDbfs{-40.0f}; // latest mic peak (dBFS, radio MICPEAK stat) // Latest TX-meter values, cached for the SmartMTR TX scales (see the setters). diff --git a/src/gui/WfmApplet.cpp b/src/gui/WfmApplet.cpp index e29e6c6aa..ff3f3499c 100644 --- a/src/gui/WfmApplet.cpp +++ b/src/gui/WfmApplet.cpp @@ -11,6 +11,7 @@ #include "models/SliceModel.h" #include +#include #include #include #include @@ -20,6 +21,9 @@ #include #include #include +#include +#include +#include #include #include @@ -31,6 +35,55 @@ QJsonObject preferences() .value(QStringLiteral("WfmApplet"), QString{}).toString().toUtf8()).object(); } +// A popup may outlive its receiver binding. Preserve the binding witness +// after hidePopup(): Qt can deliver activated later. +class WfmReceiverComboBox final : public GuardedComboBox { +public: + using GuardedComboBox::GuardedComboBox; + void setBinding(quint64 binding) + { + m_binding = binding; + invalidateInteraction(); + } + void invalidateInteraction() + { + ++m_generation; + hidePopup(); + } + quint64 takeActivationBinding() + { + const Origin origin = m_origin.value_or(Origin{m_binding, m_generation}); + m_origin.reset(); + // Bound controls use nonzero slice generations. Zero cannot authorize + // an activation whose receiver binding was retired. + return origin.generation == m_generation ? origin.binding : 0; + } + void showPopup() override + { + m_origin = Origin{m_binding, m_generation}; + GuardedComboBox::showPopup(); + } +protected: + void keyPressEvent(QKeyEvent* event) override + { + if (!view()->isVisible()) { m_origin = Origin{m_binding, m_generation}; } + GuardedComboBox::keyPressEvent(event); + } + void wheelEvent(QWheelEvent* event) override + { + if (!view()->isVisible()) { m_origin = Origin{m_binding, m_generation}; } + GuardedComboBox::wheelEvent(event); + } +private: + struct Origin { + quint64 binding; + quint64 generation; + }; + quint64 m_binding{0}; + quint64 m_generation{0}; + std::optional m_origin; +}; + void setReadout(QLabel* label, const QString& text, const QString& name) { if (label->text() == text && label->accessibleName() == name) { return; } @@ -67,6 +120,13 @@ WfmApplet::WfmApplet(QWidget* parent) : QWidget(parent) auto* root = new QVBoxLayout(this); root->setContentsMargins(2, 2, 2, 2); root->setSpacing(3); + m_identity = new QLabel(this); + m_identity->setObjectName(QStringLiteral("wfmReceiverIdentity")); + m_identity->setTextFormat(Qt::PlainText); + m_identity->setWordWrap(true); + ThemeManager::instance().applyStyleSheet(m_identity, + "QLabel { color: {{color.text.primary}}; font-size: 11px; }"); + root->addWidget(m_identity); m_scope = new WfmLockScope(this); m_scope->setObjectName(QStringLiteral("wfmLockScope")); root->addWidget(m_scope); @@ -90,13 +150,13 @@ WfmApplet::WfmApplet(QWidget* parent) : QWidget(parent) auto* settings = new QGridLayout; settings->setHorizontalSpacing(5); settings->setVerticalSpacing(3); - m_deemphasis = new GuardedComboBox(this); + m_deemphasis = new WfmReceiverComboBox(this); m_deemphasis->setObjectName(QStringLiteral("wfmDeemphasis")); m_deemphasis->setAccessibleName(tr("Broadcast FM deemphasis")); m_deemphasis->setPlaceholderText(tr("Unknown")); m_deemphasis->setFocusPolicy(Qt::StrongFocus); applyComboStyle(m_deemphasis); - m_bandwidth = new GuardedComboBox(this); + m_bandwidth = new WfmReceiverComboBox(this); m_bandwidth->setObjectName(QStringLiteral("wfmBandwidth")); m_bandwidth->setAccessibleName(tr("Broadcast FM bandwidth")); m_bandwidth->setPlaceholderText(tr("Unavailable")); @@ -151,20 +211,25 @@ WfmApplet::WfmApplet(QWidget* parent) : QWidget(parent) " border-radius: 3px; padding: 4px; }"); root->addWidget(m_diagnostics); + connect(m_audioMode, &QPushButton::pressed, this, [this] { + m_audioModeBinding = m_sliceBinding; + }); connect(m_audioMode, &QPushButton::clicked, this, [this] { - if (m_available && m_slice && m_audioMode->isEnabled()) { + if (acceptsControl(m_audioMode, m_audioModeBinding)) { m_slice->setWfmForceMono(!m_slice->wfmForceMono()); } - refresh(); + refresh(); // The displayed selection remains backend-confirmed. }); connect(m_deemphasis, &QComboBox::activated, this, [this](int index) { - if (m_available && m_slice && m_deemphasis->isEnabled()) { + const quint64 binding = static_cast(m_deemphasis)->takeActivationBinding(); + if (acceptsControl(m_deemphasis, binding) && index >= 0 && index < m_deemphasis->count()) { m_slice->setWfmDeemphasis(m_deemphasis->itemData(index).toInt()); } refresh(); }); connect(m_bandwidth, &QComboBox::activated, this, [this](int index) { - if (m_available && m_slice && m_bandwidth->isEnabled()) { + const quint64 binding = static_cast(m_bandwidth)->takeActivationBinding(); + if (acceptsControl(m_bandwidth, binding) && index >= 0 && index < m_bandwidth->count()) { const int width = m_bandwidth->itemData(index).toInt(); const ModeFilters::Edges edges = ModeFilters::edgesForWidth(QStringLiteral("WFM"), width, {}); const RadioCapabilities caps = m_model->backendCapabilities(); @@ -197,8 +262,15 @@ bool WfmApplet::ownsWfmSlice() const && m_slice->mode() == QLatin1String("WFM"); } +bool WfmApplet::acceptsControl(const QWidget* control, quint64 binding) const +{ + return binding == m_sliceBinding && m_connected && m_available && ownsWfmSlice() + && !m_slice->externalReceiveReplacementActive() && control->isEnabled(); +} + void WfmApplet::setRadioModel(RadioModel* model) { + const quint64 binding = ++m_modelBinding; for (const auto& connection : m_modelConnections) { disconnect(connection); } m_modelConnections.clear(); m_availability.reset(); @@ -208,58 +280,81 @@ void WfmApplet::setRadioModel(RadioModel* model) m_caps = model ? model->backendCapabilities() : RadioCapabilities{}; if (model) { m_modelConnections.append(connect(model, &RadioModel::capabilitiesChanged, this, - [this](bool connected, const RadioCapabilities& caps) { + [this, binding](bool connected, const RadioCapabilities& caps) { + if (binding != m_modelBinding) { return; } + const bool connectionChanged = connected != m_connected; m_connected = connected; m_caps = caps; + if (connectionChanged) { setSlice(m_slice); } refresh(); })); m_modelConnections.append(connect(model, &RadioModel::connectionStateChanged, this, - [this](bool connected) { + [this, binding](bool connected) { + if (binding != m_modelBinding) { return; } + const bool connectionChanged = connected != m_connected; m_connected = connected; m_caps = m_model ? m_model->backendCapabilities() : RadioCapabilities{}; + if (connectionChanged) { setSlice(m_slice); } refresh(); })); m_modelConnections.append(connect(model, &RadioModel::sliceRemoved, this, - [this](int id) { - if (m_slice && m_slice->sliceId() == id) { setSlice(nullptr); } + [this, binding](int id) { + if (binding != m_modelBinding) { return; } + if (m_slice && m_slice->sliceId() == id + && (!m_model || m_model->slice(id) != m_slice)) { setSlice(nullptr); } })); - m_modelConnections.append(connect(model, &QObject::destroyed, this, [this] { + m_modelConnections.append(connect(model, &QObject::destroyed, this, [this, binding] { + if (binding != m_modelBinding) { return; } m_availability.reset(); m_model = nullptr; m_connected = false; m_caps = {}; - refresh(); + setSlice(nullptr); })); registerControls(); } - refresh(); + setSlice(m_slice); } void WfmApplet::setSlice(SliceModel* slice) { + const quint64 binding = ++m_sliceBinding; + for (QComboBox* combo : {m_deemphasis, m_bandwidth}) { + static_cast(combo)->setBinding(binding); + } + m_audioMode->setDown(false); for (const auto& connection : m_sliceConnections) { disconnect(connection); } m_sliceConnections.clear(); m_scope->clear(); m_slice = slice; if (slice) { - m_sliceConnections.append(connect(slice, &SliceModel::modeChanged, this, &WfmApplet::refresh)); - m_sliceConnections.append(connect(slice, &SliceModel::filterChanged, this, &WfmApplet::refresh)); - m_sliceConnections.append(connect(slice, &SliceModel::wfmDeemphasisChanged, this, &WfmApplet::refresh)); - m_sliceConnections.append(connect(slice, &SliceModel::wfmForceMonoChanged, this, &WfmApplet::refresh)); - m_sliceConnections.append(connect(slice, &SliceModel::wfmStereoStatusChanged, this, &WfmApplet::refresh)); - m_sliceConnections.append(connect(slice, &SliceModel::wfmReceptionDiagnosticsChanged, this, [this] { + const auto current = [this, binding] { + return binding == m_sliceBinding && m_slice && m_model + && m_model->slice(m_slice->sliceId()) == m_slice; + }; + const auto update = [this, current] { if (current()) { refresh(); } }; + m_sliceConnections.append(connect(slice, &SliceModel::modeChanged, this, update)); + m_sliceConnections.append(connect(slice, &SliceModel::filterChanged, this, update)); + m_sliceConnections.append(connect(slice, &SliceModel::letterChanged, this, update)); + m_sliceConnections.append(connect(slice, &SliceModel::frequencyReported, this, update)); + m_sliceConnections.append(connect(slice, &SliceModel::wfmDeemphasisChanged, this, update)); + m_sliceConnections.append(connect(slice, &SliceModel::wfmForceMonoChanged, this, update)); + m_sliceConnections.append(connect(slice, &SliceModel::wfmStereoStatusChanged, this, update)); + m_sliceConnections.append(connect(slice, &SliceModel::wfmReceptionDiagnosticsChanged, this, [this, current] { + if (!current()) { return; } refreshDiagnostics(); appendScopeSample(); })); - m_sliceConnections.append(connect(slice, &SliceModel::frequencyChanged, this, [this] { + m_sliceConnections.append(connect(slice, &SliceModel::frequencyChanged, this, [this, current] { + if (!current()) { return; } m_scope->clear(); refreshDiagnostics(); })); - m_sliceConnections.append(connect(slice, &SliceModel::inCaptureChanged, this, &WfmApplet::refresh)); - m_sliceConnections.append(connect(slice, &SliceModel::externalReceiveReplacementChanged, this, &WfmApplet::refresh)); - m_sliceConnections.append(connect(slice, &QObject::destroyed, this, [this] { - m_slice = nullptr; - refresh(); + m_sliceConnections.append(connect(slice, &SliceModel::inCaptureChanged, this, update)); + m_sliceConnections.append(connect(slice, &SliceModel::externalReceiveReplacementChanged, this, update)); + m_sliceConnections.append(connect(slice, &QObject::destroyed, this, [this, binding] { + if (binding != m_sliceBinding) { return; } + setSlice(nullptr); })); } refresh(); @@ -305,6 +400,11 @@ void WfmApplet::refresh() if (!available) { m_scope->clear(); } emit availabilityChanged(available); } + const QString identity = m_available + ? tr("Slice %1 · %2").arg(m_slice->letter(), m_slice->frequencyReportedKnown() + ? tr("%1 MHz").arg(m_slice->reportedFrequency(), 0, 'f', 6) : tr("Frequency unavailable")) + : tr("No selected WFM receiver"); + setReadout(m_identity, identity, tr("Broadcast FM receiver: %1").arg(identity)); if (m_availability) { m_availability->refreshEngaged(); } else { diff --git a/src/gui/WfmApplet.h b/src/gui/WfmApplet.h index 71a391006..98a5f5c59 100644 --- a/src/gui/WfmApplet.h +++ b/src/gui/WfmApplet.h @@ -34,6 +34,7 @@ class WfmApplet : public QWidget { void availabilityChanged(bool available); private: bool ownsWfmSlice() const; + bool acceptsControl(const QWidget* control, quint64 binding) const; void refresh(); void refreshDiagnostics(); void appendScopeSample(); @@ -44,12 +45,16 @@ class WfmApplet : public QWidget { QPointer m_model; QPointer m_slice; + quint64 m_modelBinding{0}; + quint64 m_sliceBinding{0}; + quint64 m_audioModeBinding{0}; QVector m_modelConnections; QVector m_sliceConnections; std::unique_ptr m_availability; RadioCapabilities m_caps; bool m_connected{false}; bool m_available{false}; + QLabel* m_identity{nullptr}; WfmLockScope* m_scope{nullptr}; QPushButton* m_audioMode{nullptr}; QLabel* m_status{nullptr}; diff --git a/src/main.cpp b/src/main.cpp index 2e09c98d6..69e451b67 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -504,7 +504,8 @@ int main(int argc, char* argv[]) // (~/.config/AetherSDR/AetherSDR/, because org and app name are both // "AetherSDR") to GenericConfigLocation + "/AetherSDR", where AppSettings lives. // Idempotent: skips existing targets and removes the old dir once empty. - { + // An isolated profile must not migrate the operator's ordinary profile. + if (qEnvironmentVariableIsEmpty("AETHER_SETTINGS_DIR")) { const QString newDir = QStandardPaths::writableLocation( QStandardPaths::GenericConfigLocation) + QStringLiteral("/AetherSDR"); @@ -555,8 +556,9 @@ int main(int argc, char* argv[]) // listing (which was getting crowded with 50+ aethersdr-*.log files // sitting alongside the actual settings). GenericConfigLocation + // app name still avoids the AppConfigLocation double-nest. - const QString configRoot = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) - + "/AetherSDR"; + // Honour the same explicit profile as the settings store. An isolated + // qualification launch must not migrate or prune the operator's logs. + const QString configRoot = AetherSDR::SettingsPaths::configDir(); const QString logDir = configRoot + "/logs"; QDir().mkpath(logDir); // Source-feed logs (dxcluster.log, rbn.log, wsjtx.log, …) live in diff --git a/src/models/Ax25ReceiveModel.cpp b/src/models/Ax25ReceiveModel.cpp new file mode 100644 index 000000000..182f3e02b --- /dev/null +++ b/src/models/Ax25ReceiveModel.cpp @@ -0,0 +1,396 @@ +#include "Ax25ReceiveModel.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace AetherSDR { + +struct Ax25ReceiveModel::Impl { + struct Input { + DecoderPcmBlock block; + quint64 generation = 0; + }; + struct Output { + PcmEpochLease source; + quint64 generation = 0; + QVector frames; + std::optional diagnostics; + }; + struct State { + QMutex mutex; + std::shared_ptr> generation = + std::make_shared>(1); + Ax25ReceiveModel* owner = nullptr; + QObject* worker = nullptr; + std::deque input; + std::deque output; + qsizetype frames = 0; + bool workerScheduled = false; + bool outputScheduled = false; + bool resetNotification = false; + bool diagnosticsLogging = false; + Ax25DemodConfig config; + quint64 configuration = 1; + static constexpr qsizetype kMaxFrames = 65536; + static constexpr std::size_t kMaxBlocks = 256; + + // Called under mutex. Retire BEFORE any callbacks or worker operation; + // a result already queued to the owner must become stale immediately. + void retire() + { + ++*generation; + input.clear(); + output.clear(); + frames = 0; + } + }; + + class Worker final : public QObject { + public: + explicit Worker(std::shared_ptr state) + : m_state(std::move(state)), m_shim(this) + { + connect(&m_shim, &AetherAx25LibmodemShim::frameDecoded, this, + [this](const Ax25DecodedFrame& frame) { + if (m_active && current(*m_active)) { + m_active->frames.append(frame); + } + }); + connect(&m_shim, &AetherAx25LibmodemShim::diagnosticsUpdated, this, + [this](const Ax25DecoderDiagnostics& diagnostics) { + if (m_active && current(*m_active)) { + m_active->diagnostics = diagnostics; + } + }); + } + + void drain() + { + // Yield regularly to shutdown/configuration work. The owner and + // producer cannot create one queued event per incoming PCM block. + for (int count = 0; count < 8; ++count) { + Input input; + Ax25DemodConfig config; + quint64 generation = 0; + quint64 configuration = 0; + bool logging = false; + { + QMutexLocker lock(&m_state->mutex); + if (!m_state->owner) { + m_state->workerScheduled = false; + return; + } + generation = m_state->generation->load(); + configuration = m_state->configuration; + config = m_state->config; + logging = m_state->diagnosticsLogging; + if (!m_state->input.empty()) { + input = std::move(m_state->input.front()); + m_state->input.pop_front(); + m_state->frames -= input.block.samples.size(); + } + } + // Configure/reset signals have no active PCM context and cannot + // borrow a new generation for an older result. + m_active.reset(); + if (configuration != m_configuration) { + m_shim.configure(config); + m_configuration = configuration; + m_generation = generation; + } else if (generation != m_generation) { + m_shim.reset(); + m_generation = generation; + } + m_shim.setDiagnosticsLoggingEnabled(logging); + if (input.generation && input.generation == generation + && input.block.current() + && generation == m_state->generation->load()) { + // Preserve the feed's original context throughout synchronous + // shim callbacks, even if the owner resets concurrently. + m_active = Output{input.block.source, input.generation, {}, {}}; + m_shim.feedAudio(QByteArray( + reinterpret_cast(input.block.samples.constData()), + input.block.samples.size() * static_cast(sizeof(float))), + DecoderPcmBlock::kSampleRateHz); + if (current(*m_active) + && (!m_active->frames.isEmpty() || m_active->diagnostics)) { + QMutexLocker lock(&m_state->mutex); + if (m_state->owner && current(*m_active)) { + if (m_state->output.size() == State::kMaxBlocks) { + m_state->retire(); + m_state->resetNotification = true; + } else { + m_state->output.push_back(std::move(*m_active)); + } + scheduleOutput(m_state); + } + } + m_active.reset(); + } + { + QMutexLocker lock(&m_state->mutex); + if (m_state->input.empty() + && generation == m_state->generation->load() + && configuration == m_state->configuration + && logging == m_state->diagnosticsLogging) { + m_state->workerScheduled = false; + return; + } + } + } + QMetaObject::invokeMethod(this, [this] { drain(); }, Qt::QueuedConnection); + } + + private: + bool current(const Output& output) const + { + return output.generation == m_state->generation->load() + && output.source.current(); + } + std::shared_ptr m_state; + AetherAx25LibmodemShim m_shim; + quint64 m_generation = 0; + quint64 m_configuration = 0; + std::optional m_active; + }; + + Ax25ReceiveModel* owner; + std::unique_ptr audio; + std::shared_ptr state = std::make_shared(); + QThread thread; + bool enabled = false; + + // Caller holds State::mutex, fencing invocation against owner destruction. + static void scheduleOutput(const std::shared_ptr& state) + { + if (!state->owner || state->outputScheduled) { + return; + } + state->outputScheduled = true; + Ax25ReceiveModel* target = state->owner; + QMetaObject::invokeMethod(target, [target, state] { + target->m_impl->publish(state); + }, Qt::QueuedConnection); + } + + void scheduleWorker() + { + if (!state->worker || state->workerScheduled) { + return; + } + state->workerScheduled = true; + auto* worker = static_cast(state->worker); + QMetaObject::invokeMethod(worker, [worker] { worker->drain(); }, Qt::QueuedConnection); + } + + void retire() + { + { + QMutexLocker lock(&state->mutex); + state->retire(); + state->resetNotification = false; + scheduleWorker(); + } + const QPointer guard(owner); + emit owner->sourceReset(); + if (guard) { + emit owner->statusChanged(); + } + } + + void enqueue(const DecoderPcmBlock& block) + { + if (!enabled || !block.current()) { + return; + } + const QPointer guard(owner); + bool overflow = false; + quint64 generation = 0; + { + QMutexLocker lock(&state->mutex); + if (block.samples.size() > State::kMaxFrames) { + state->retire(); + overflow = true; + } else { + if (state->input.size() == State::kMaxBlocks + || state->frames + block.samples.size() > State::kMaxFrames) { + state->retire(); + overflow = true; + } + generation = state->generation->load(); + state->input.push_back({block, generation}); + state->frames += block.samples.size(); + } + scheduleWorker(); + } + if (overflow) { + emit owner->sourceReset(); + if (!guard) { + return; + } + } + if (generation && enabled && block.current() + && generation == state->generation->load()) { + emit owner->pcmReady(block, {block.source, generation, state->generation}); + } + } + + void publish(const std::shared_ptr& box) + { + std::deque output; + bool reset = false; + { + QMutexLocker lock(&box->mutex); + output.swap(box->output); + box->outputScheduled = false; + reset = std::exchange(box->resetNotification, false); + } + const QPointer guard(owner); + if (reset) { + emit owner->sourceReset(); + if (!guard) { + return; + } + } + for (const Output& item : output) { + const auto current = [&] { + return enabled && item.source.current() + && item.generation == box->generation->load(); + }; + for (const Ax25DecodedFrame& frame : item.frames) { + if (!current()) { + break; + } + emit owner->frameDecoded(frame, {item.source, item.generation, box->generation}); + if (!guard) { + return; + } + } + if (item.diagnostics && current()) { + emit owner->diagnosticsUpdated(*item.diagnostics, + {item.source, item.generation, box->generation}); + if (!guard) { + return; + } + } + } + } +}; + +Ax25ReceiveModel::Ax25ReceiveModel(RadioModel& radio, QObject* parent) + : QObject(parent), m_impl(std::make_unique()) +{ + Impl& d = *m_impl; + d.owner = this; + d.state->owner = this; + auto* worker = new Impl::Worker(d.state); + d.state->worker = worker; + worker->moveToThread(&d.thread); + connect(&d.thread, &QThread::finished, worker, &QObject::deleteLater); + d.thread.setObjectName(QStringLiteral("Ax25Receive")); + d.thread.start(); + d.audio = std::make_unique(radio, DecoderAudioModel::Consumer::Ax25); + connect(d.audio.get(), &DecoderAudioModel::sourceReset, this, [this] { m_impl->retire(); }); + connect(d.audio.get(), &DecoderAudioModel::pcmReady, this, + [this](const DecoderPcmBlock& block) { m_impl->enqueue(block); }); + connect(d.audio.get(), &DecoderAudioModel::routeStatusChanged, + this, &Ax25ReceiveModel::routeStatusChanged); +} + +Ax25ReceiveModel::~Ax25ReceiveModel() +{ + Impl& d = *m_impl; + { + QMutexLocker lock(&d.state->mutex); + d.state->owner = nullptr; + d.state->retire(); + } + d.audio.reset(); + d.thread.quit(); + d.thread.wait(); +} + +void Ax25ReceiveModel::setSlice(SliceModel* slice) +{ + m_impl->audio->setSlice(slice); +} + +void Ax25ReceiveModel::setEnabled(bool enabled) +{ + if (m_impl->enabled == enabled) { + return; + } + m_impl->enabled = enabled; + m_impl->audio->setEnabled(enabled); +} + +void Ax25ReceiveModel::configure(const Ax25DemodConfig& config) +{ + { + QMutexLocker lock(&m_impl->state->mutex); + const Ax25DemodConfig& previous = m_impl->state->config; + if (std::tie(previous.profile, previous.sampleRate, previous.baud, + previous.markHz, previous.spaceHz, previous.polarity, + previous.vhfMode, previous.txPreambleFlags) + == std::tie(config.profile, config.sampleRate, config.baud, + config.markHz, config.spaceHz, config.polarity, + config.vhfMode, config.txPreambleFlags)) { + return; + } + m_impl->state->config = config; + ++m_impl->state->configuration; + m_impl->state->retire(); + m_impl->state->resetNotification = false; + m_impl->scheduleWorker(); + } + const QPointer guard(this); + emit sourceReset(); + if (guard) { + emit statusChanged(); + } +} + +void Ax25ReceiveModel::reset() +{ + m_impl->retire(); +} + +void Ax25ReceiveModel::setDiagnosticsLoggingEnabled(bool enabled) +{ + QMutexLocker lock(&m_impl->state->mutex); + m_impl->state->diagnosticsLogging = enabled; + m_impl->scheduleWorker(); +} + +QObject* Ax25ReceiveModel::workerForTest() const +{ + return m_impl->state->worker; +} + +Ax25ReceiveModel::QueueState Ax25ReceiveModel::queuesForTest() const +{ + QMutexLocker lock(&m_impl->state->mutex); + return {m_impl->state->frames, m_impl->state->input.size(), + m_impl->state->output.size(), m_impl->state->workerScheduled, + m_impl->state->outputScheduled}; +} + +bool Ax25ReceiveModel::isEnabled() const +{ + return m_impl->enabled; +} + +DecoderAudioModel::RouteStatus Ax25ReceiveModel::routeStatus() const +{ + return m_impl->audio->routeStatus(); +} + +} // namespace AetherSDR diff --git a/src/models/Ax25ReceiveModel.h b/src/models/Ax25ReceiveModel.h new file mode 100644 index 000000000..6e5bc4730 --- /dev/null +++ b/src/models/Ax25ReceiveModel.h @@ -0,0 +1,75 @@ +#pragma once + +#include "DecoderAudioModel.h" +#include "core/tnc/AetherAx25LibmodemShim.h" + +#include +#include +#include + +namespace AetherSDR { + +class RadioModel; +class SliceModel; + +// Retained by queued consumers as well as the model. An earlier listener may +// synchronously change the source, so each outward consumer checks this again. +struct Ax25ReceiveContext { + PcmEpochLease source; + quint64 generation = 0; + std::shared_ptr> activeGeneration; + bool current() const + { + return source.current() && activeGeneration + && activeGeneration->load() == generation; + } +}; + +// Selected receive audio and its existing modem worker. No sound device, +// external service or transmit authority is owned by this model. +class Ax25ReceiveModel final : public QObject { + Q_OBJECT +public: + explicit Ax25ReceiveModel(RadioModel& radio, QObject* parent = nullptr); + ~Ax25ReceiveModel() override; + + void setSlice(SliceModel* slice); + void setEnabled(bool enabled); + void configure(const Ax25DemodConfig& config); + void reset(); + void setDiagnosticsLoggingEnabled(bool enabled); + bool isEnabled() const; + DecoderAudioModel::RouteStatus routeStatus() const; + +signals: + void frameDecoded(const AetherSDR::Ax25DecodedFrame& frame, + const AetherSDR::Ax25ReceiveContext& context); + void diagnosticsUpdated(const AetherSDR::Ax25DecoderDiagnostics& diagnostics, + const AetherSDR::Ax25ReceiveContext& context); + void statusChanged(); + void routeStatusChanged(); + void sourceReset(); + // Capture observes the same admitted continuous mono24 source as the modem. + void pcmReady(const AetherSDR::DecoderPcmBlock& block, + const AetherSDR::Ax25ReceiveContext& context); + +private: + friend struct Ax25ReceiveModelTestAccess; + struct QueueState { + qsizetype inputFrames; + std::size_t inputBlocks; + std::size_t outputBlocks; + bool workerScheduled; + bool outputScheduled; + }; + // Read-only private seam for deterministic bounded-queue tests. Barriers + // live entirely in the test; production scheduling never waits on them. + QObject* workerForTest() const; + QueueState queuesForTest() const; + struct Impl; + std::unique_ptr m_impl; +}; + +} // namespace AetherSDR + +Q_DECLARE_METATYPE(AetherSDR::Ax25ReceiveContext) diff --git a/src/models/DaxReceiveModel.cpp b/src/models/DaxReceiveModel.cpp new file mode 100644 index 000000000..ac8933201 --- /dev/null +++ b/src/models/DaxReceiveModel.cpp @@ -0,0 +1,146 @@ +#include "DaxReceiveModel.h" +#ifdef HAVE_WEBSOCKETS +#include "RadioModel.h" +#include "SliceModel.h" +#include "core/TciServer.h" +#include "core/TciRxConverter.h" +#include +#include +#include +#include + +namespace AetherSDR { +struct DaxReceiveModel::State { + struct Channel { + TciRxBinding binding; + PcmEpochLease epoch; + quint64 nextSample = 0; + std::shared_ptr converter; + }; + struct Pin { + PcmEpochLease epoch; + std::shared_ptr> owner; + }; + QPointer model; + QPointer routing; + std::array channels; + QHash pins; + PcmFrameGate gate; + bool enabled = false; + bool processing = false; +}; + +DaxReceiveModel::DaxReceiveModel(RadioModel& model, TciServer& routing, QObject* parent) + : QObject(parent), m_state(std::make_unique()) +{ + Q_ASSERT(model.thread() == thread() && routing.thread() == thread()); + m_state->model = &model; + m_state->routing = &routing; + connect(&model, &RadioModel::backendSliceAudioFrameReady, this, &DaxReceiveModel::receive); + connect(&routing, &TciServer::rxBindingsChanged, this, &DaxReceiveModel::refreshBindings); + connect(&model, &RadioModel::capabilitiesChanged, this, &DaxReceiveModel::refreshBindings); + connect(&model, &RadioModel::connectionStateChanged, this, [this](bool connected) { + if (!connected) { setEnabled(false); } + }); + connect(&routing, &QObject::destroyed, this, [this] { setEnabled(false); }); +} + +DaxReceiveModel::~DaxReceiveModel() = default; + +void DaxReceiveModel::setEnabled(bool enabled) +{ + m_state->enabled = enabled; + refreshBindings(); +} + +void DaxReceiveModel::refreshBindings() +{ + QPointer self(this); + std::array next; + if (m_state->enabled && m_state->model && m_state->routing) { + const auto capability = m_state->model->backendCapabilities().receiveAudioExport; + if (capability) { + for (SliceModel* slice : m_state->model->slices()) { + const TciRxBinding binding = m_state->routing->sliceRxBinding(slice->sliceId()); + if (binding.current() && binding.trx >= 0 && binding.trx < 8 + && binding.trx < capability->maximumReceivers) { + next[binding.trx] = binding; + } + } + } + } + for (int i = 0; i < 8; ++i) { + State::Channel& channel = m_state->channels[i]; + if (channel.binding.alive == next[i].alive) { continue; } + channel = {}; + channel.binding = next[i]; + const int sliceId = next[i].current() ? next[i].sliceId : -1; + emit channelReset(i+1); + if (!self) { return; } + emit channelSliceChanged(i+1,sliceId); + if (!self) { return; } + } +} + +void DaxReceiveModel::receive(int sliceId, const PcmFrame& frame) +{ + const PcmFrame input = frame; + if (m_state->processing || !m_state->enabled || !m_state->model || !m_state->routing + || !input.current() || input.stream().purpose != PcmPurpose::Slice + || input.stream().sliceId != sliceId) { return; } + const auto capability = m_state->model->backendCapabilities().receiveAudioExport; + if (!capability || !capability->sampleRatesHz.contains(input.stream().format.sampleRateHz)) { return; } + const TciRxBinding binding = m_state->routing->sliceRxBinding(sliceId); + if (!binding.current() || binding.trx < 0 || binding.trx >= 8 + || binding.trx >= capability->maximumReceivers) { return; } + const int index = binding.trx; + State::Channel& channel = m_state->channels[index]; + if (channel.binding.alive != binding.alive) { return; } + + // A removed/replaced receiver retains a bounded tombstone while its old + // producer is live. Neither a new owner nor a competing source can seize it. + for (auto it = m_state->pins.begin(); it != m_state->pins.end();) { + if (!it->epoch.current()) { it = m_state->pins.erase(it); } + else { ++it; } + } + const auto pin = m_state->pins.constFind(sliceId); + if (pin != m_state->pins.cend()) { + if (pin->epoch.stream() != input.stream() || pin->owner != binding.alive) { return; } + } else if (m_state->pins.size() >= qsizetype(PcmFrameGate::kMaxStreams)) { return; } + if (!m_state->gate.accept(input)) { return; } + m_state->pins.insert(sliceId,{input.epochLease(),binding.alive}); + + m_state->processing = true; + QPointer self(this); + const auto processing = qScopeGuard([self] { if (self) { self->m_state->processing = false; } }); + if (channel.epoch.stream() != input.stream() || channel.nextSample != input.firstSample() + || input.discontinuity() || !channel.converter) { + channel.epoch = input.epochLease(); + channel.converter = std::make_shared(input.stream().format,24000); + emit channelReset(index+1); + if (!self || !m_state->enabled || !binding.current() + || channel.binding.alive != binding.alive || !input.current()) { return; } + } + channel.nextSample = input.firstSample() + input.frameCount(); + const std::shared_ptr converter = channel.converter; + if (!converter || !converter->valid()) { return; } + const auto current = [self,index,binding,input,converter] { + return self && self->m_state->enabled && input.current() && binding.current() + && self->m_state->channels[index].binding.alive == binding.alive + && self->m_state->channels[index].converter == converter; + }; + const bool accepted = converter->process(std::span(input.samples().constData(), input.samples().size()), + [self,index,current](QVector stereo) { + if (!current()) { return false; } + const QByteArray pcm(reinterpret_cast(stereo.constData()), + stereo.size()*qsizetype(sizeof(float))); + emit self->audioReady(index+1,pcm); + return current(); + }); + if (!accepted && self && channel.converter == converter) { + channel.converter.reset(); + emit channelReset(index+1); + } +} +} +#endif diff --git a/src/models/DaxReceiveModel.h b/src/models/DaxReceiveModel.h new file mode 100644 index 000000000..842e3fe53 --- /dev/null +++ b/src/models/DaxReceiveModel.h @@ -0,0 +1,34 @@ +#pragma once +#ifdef HAVE_WEBSOCKETS +#include +#include +#include + +namespace AetherSDR { +class RadioModel; +class TciServer; +class PcmFrame; + +// Owner-thread receive adapter for the existing virtual audio bridges. Uses +// TCI's live receiver bindings even while its listener is stopped. Owns no +// device, transport, thread or PCM queue; output is float32 stereo at 24 kHz. +class DaxReceiveModel final : public QObject { + Q_OBJECT +public: + DaxReceiveModel(RadioModel& model, TciServer& routing, QObject* parent = nullptr); + ~DaxReceiveModel() override; + void setEnabled(bool enabled); + +signals: + void audioReady(int channel, const QByteArray& stereo24); + void channelReset(int channel); + void channelSliceChanged(int channel, int sliceId); + +private: + void refreshBindings(); + void receive(int sliceId, const PcmFrame& frame); + struct State; + std::unique_ptr m_state; +}; +} +#endif diff --git a/src/models/DecoderAudioModel.cpp b/src/models/DecoderAudioModel.cpp index 6599dd9bc..012a56107 100644 --- a/src/models/DecoderAudioModel.cpp +++ b/src/models/DecoderAudioModel.cpp @@ -41,6 +41,16 @@ struct DecoderAudioModel::Impl { std::shared_ptr inbox; DecoderPcmAdapter adapter; + const char* consumerName() const + { + switch (consumer) { + case Consumer::Cw: return "CW"; + case Consumer::Rtty: return "RTTY"; + case Consumer::Ax25: return "AX.25"; + } + return "Decoder"; + } + void setRouteStatus(RouteStatus status) { if (routeStatus == status) { @@ -58,19 +68,23 @@ struct DecoderAudioModel::Impl { if (status == RouteStatus::SharedRxAudio) { // Not a warning: with no DAX assignment this is the ordinary Flex // path and the decoder still runs, just on the shared mix. - qCInfo(lcDsp) << (consumer == Consumer::Cw ? "CW" : "RTTY") + qCInfo(lcDsp) << consumerName() << "receive decoder: no DAX RX channel assigned to the selected slice;" << "decoding the shared receive audio (speaker gain and mute apply)"; } else if (status == RouteStatus::DaxTransportUnavailable) { - qCWarning(lcDsp) << (consumer == Consumer::Cw ? "CW" : "RTTY") + qCWarning(lcDsp) << consumerName() << "receive decoder: selected DAX transport unavailable; no input route"; } } PanadapterStream::DaxConsumer daxConsumer() const { - return consumer == Consumer::Cw ? PanadapterStream::DaxConsumer::CwDecoder - : PanadapterStream::DaxConsumer::RttyDecoder; + switch (consumer) { + case Consumer::Cw: return PanadapterStream::DaxConsumer::CwDecoder; + case Consumer::Rtty: return PanadapterStream::DaxConsumer::RttyDecoder; + case Consumer::Ax25: return PanadapterStream::DaxConsumer::Ax25Decoder; + } + Q_UNREACHABLE(); } void closeInbox() diff --git a/src/models/DecoderAudioModel.h b/src/models/DecoderAudioModel.h index 8eb85c388..2ed5f6269 100644 --- a/src/models/DecoderAudioModel.h +++ b/src/models/DecoderAudioModel.h @@ -18,7 +18,7 @@ class SliceModel; class DecoderAudioModel final : public QObject { Q_OBJECT public: - enum class Consumer { Cw, Rtty }; + enum class Consumer { Cw, Rtty, Ax25 }; // Bound means an input subscription exists, not that PCM has arrived. // SharedRxAudio is bound too: a DAX-capable radio with no channel assigned // to the selected slice decodes the radio's shared receive stream instead, diff --git a/src/models/MeterModel.cpp b/src/models/MeterModel.cpp index b2e551520..af2386a9d 100644 --- a/src/models/MeterModel.cpp +++ b/src/models/MeterModel.cpp @@ -55,6 +55,7 @@ QJsonObject meterToJson(const MeterDef& def, bool hasValue, float value, qint64 obj["low"] = def.low; obj["high"] = def.high; obj["description"] = def.description; + obj["unavailable_reason"] = def.unavailableReason; obj["has_value"] = hasValue; obj["value"] = hasValue ? QJsonValue(value) : QJsonValue(); // Milliseconds since this meter's value last updated (-1 = never). Lets a @@ -169,6 +170,11 @@ void MeterModel::defineMeter(const MeterDef& def) removeMeter(def.index); m_manifestSliceContext = sliceContext; } + if (redefinition && def.source == QStringLiteral("SLC") + && def.name == QStringLiteral("LEVEL") && previous->unit != def.unit) { + m_values.remove(def.index); + m_valueUpdatedMs.remove(def.index); + } const bool nativeUnitChanged = redefinition && def.index == m_nativeAlcIndex && previous->unit != def.unit; m_defs[def.index] = def; @@ -906,6 +912,16 @@ std::optional MeterModel::swrIfLive() const : std::nullopt; } +std::optional MeterModel::relativeLevelForSlice(int sliceIndex) const +{ + if (sliceIndex < 0) { return std::nullopt; } + const int index = findMeter(QStringLiteral("SLC"), QStringLiteral("LEVEL"), sliceIndex); + const MeterDef* def = meterDef(index); + if (!def || !def->unavailableReason.isEmpty() || def->unit != QStringLiteral("dBFS") + || !vitalIsFresh(true, valueAgeMs(index))) { return std::nullopt; } + return m_values.value(index); +} + std::optional MeterModel::sLevelForSlice(int sliceIndex) const { const auto it = m_sLevelIdxBySlice.constFind(sliceIndex); @@ -913,6 +929,7 @@ std::optional MeterModel::sLevelForSlice(int sliceIndex) const return std::nullopt; // this receiver declares no LEVEL meter } const int index = it.value(); + if (m_defs.value(index).unit != QStringLiteral("dBm")) { return std::nullopt; } // Declared is not fed, and fed is not current: m_sLevelIdxBySlice is populated // by the definition, so the index alone would publish a default no packet set. // vitalIsFresh is the same window `get radio` uses for PATEMP (#5516); at the @@ -1017,7 +1034,7 @@ void MeterModel::applyValues(const QVector& ids, const QVector& bool isSliceLevel = false; for (auto sit = m_sLevelIdxBySlice.constBegin(); sit != m_sLevelIdxBySlice.constEnd(); ++sit) { if (sit.value() == idx) { - emit sLevelChanged(sit.key(), v); + if (it->unit == QStringLiteral("dBm")) { emit sLevelChanged(sit.key(), v); } isSliceLevel = true; break; } diff --git a/src/models/MeterModel.h b/src/models/MeterModel.h index 004e2b798..b1c8933d5 100644 --- a/src/models/MeterModel.h +++ b/src/models/MeterModel.h @@ -115,6 +115,8 @@ class MeterModel : public QObject { // (Flex: the manifest's `num`; HL2: the receiver number; Icom: 0), so // RigctlProtocol can pass slice->sliceId(). std::optional sLevelForSlice(int sliceIndex) const; + // ADC-relative RF only. Never usable as calibrated dBm/S units. + std::optional relativeLevelForSlice(int sliceIndex) const; // The radio-wide S-meter reading — what `get meters` publishes as a scalar // — WHEN THERE IS ONE. Exactly one slice declaring a LEVEL meter is the diff --git a/src/models/RadioModel.cpp b/src/models/RadioModel.cpp index e52a74ddd..fb65ccc49 100644 --- a/src/models/RadioModel.cpp +++ b/src/models/RadioModel.cpp @@ -10107,8 +10107,13 @@ void RadioModel::dispatchSliceWfm(const SliceWfmRequest& request) void RadioModel::dispatchSliceSquelch(const SliceSquelchRequest& request) { if (SliceModel* source = receiveCommandSource()) { - reportReceiveDispatch(m_backend->requestSliceSquelch(source->sliceId(), request), - QStringLiteral("receive squelch")); + const std::optional sql = m_backend->capabilities().squelchLevelScale; + const bool automaticAllowed = request.kind != SliceSquelchRequest::Kind::Automatic + || (sql && sql->automaticInEngine && sql->appliesTo(source->mode()) + && !source->externalReceiveReplacementActive()); + reportReceiveDispatch(automaticAllowed + ? m_backend->requestSliceSquelch(source->sliceId(), request) + : ReceiveDispatch::Unsupported, QStringLiteral("receive squelch")); } } @@ -10160,8 +10165,8 @@ void RadioModel::wireSliceObservationsAndTxIntentToBackend(SliceModel* s) // a Flex arrives as a status echo and here has no other way of happening. connect(s, &SliceModel::activeSliceCommandIssued, this, [this, s]() { - if (m_backend) m_backend->setActiveSlice(s->sliceId()); - }); + if (receiveCommandSource() == s) { m_backend->setActiveSlice(s->sliceId()); } + }, Qt::DirectConnection); } void RadioModel::setBackendForTest(std::unique_ptr backend, diff --git a/src/models/SliceModel.cpp b/src/models/SliceModel.cpp index aa79786a3..9daadd389 100644 --- a/src/models/SliceModel.cpp +++ b/src/models/SliceModel.cpp @@ -869,6 +869,14 @@ void SliceModel::setSquelch(bool on, int level) }); } +void SliceModel::setAutomaticSquelch(bool enabled, int marginDb) +{ + if (refuseOffThread(__func__) || marginDb < 5 || marginDb > 20 + || externalReceiveReplacementActive()) { return; } + emit receiveSquelchRequested({enabled, m_squelchLevel, false, false, + SliceSquelchRequest::Kind::Automatic, marginDb}); +} + void SliceModel::setManualSquelch(bool on, int level) { const QPointer alive(this); @@ -973,6 +981,13 @@ void SliceModel::setTxSlice(bool on) void SliceModel::setActive(bool on) { + if (confirmsControls()) { + // Selection must follow the same live-object admission as tuning. + // An optimistic activeChanged edge can make the UI select a reused + // numeric slot before the owner refuses this retired object's intent. + if (on && QThread::currentThread() == thread()) { emit activeSliceCommandIssued(); } + return; + } if (on) { // Optimistic (#3854 review): activeSlice() prefers the radio's active // flag, so waiting for the echo leaves a one-round-trip window where @@ -1879,7 +1894,13 @@ void SliceModel::applyChanges(const SliceDelta& d) emit wfmReceptionDiagnosticsChanged(value); } } - if (d.squelchOn.has_value() || d.squelchLevel.has_value()) { + if (d.automaticSquelch) { m_automaticSquelch = *d.automaticSquelch; } + if (d.automaticSquelchMarginDb && *d.automaticSquelchMarginDb >= 5 + && *d.automaticSquelchMarginDb <= 20) { + m_automaticSquelchMarginDb = *d.automaticSquelchMarginDb; + } + if (d.squelchOn.has_value() || d.squelchLevel.has_value() + || d.automaticSquelch || d.automaticSquelchMarginDb) { m_squelchOnKnown |= d.squelchOn.has_value(); m_squelchLevelKnown |= d.squelchLevel.has_value(); if (d.squelchOn.has_value()) @@ -1894,8 +1915,13 @@ void SliceModel::applyChanges(const SliceDelta& d) // External-receive (Kiwi) slices keep their level in // m_externalReceiveSquelchLevel and never read the Flex manual // memory, so exclude them here exactly as setManualSquelch does. - if (m_squelchEchoIsManual && !m_externalReceiveAudioReplacement) + // Engine-owned Auto publishes its retained manual threshold, + // not a computed Auto level. Restore that memory even if the + // attached applet already marked its echo surface Off/Auto. + if ((m_squelchEchoIsManual || d.automaticSquelch.has_value()) + && !m_externalReceiveAudioReplacement) { setManualSquelchLevel(*d.squelchLevel); + } } emit squelchChanged(m_squelchOn, m_squelchLevel); } diff --git a/src/models/SliceModel.h b/src/models/SliceModel.h index fceb9cdc0..e7ca7a356 100644 --- a/src/models/SliceModel.h +++ b/src/models/SliceModel.h @@ -169,6 +169,9 @@ class SliceModel : public QObject { : m_audioMute; } bool flexAudioMute() const { return m_audioMute; } // Only inbound reports establish current-session truth; local setters do not. + bool automaticSquelch() const { return m_automaticSquelch; } + int automaticSquelchMarginDb() const { return m_automaticSquelchMarginDb; } + void setAutomaticSquelch(bool enabled, int marginDb); bool squelchStateKnown() const { return m_squelchOnKnown && m_squelchLevelKnown; } void invalidateSquelchState() { m_squelchOnKnown = false; m_squelchLevelKnown = false; } bool squelchOn() const { return m_squelchOn; } @@ -710,6 +713,8 @@ class SliceModel : public QObject { QString m_agcMode{"med"}; int m_agcThreshold{65}; int m_agcOffLevel{10}; + bool m_automaticSquelch{false}; + int m_automaticSquelchMarginDb{10}; bool m_squelchOnKnown{false}; bool m_squelchLevelKnown{false}; bool m_squelchOn{false}; diff --git a/tests/Ax25ReceiveFixture.h b/tests/Ax25ReceiveFixture.h new file mode 100644 index 000000000..11afe49c6 --- /dev/null +++ b/tests/Ax25ReceiveFixture.h @@ -0,0 +1,116 @@ +#pragma once + +// Independent receive fixture: fixed AX.25 bytes, CRC-16/X25, HDLC bit stuffing +// and NRZI Bell 202. It does not call the application's TX/modulator APIs. +#include +#include + +#include +#include +#include + +namespace AetherSDR::test::ax25rx { + +inline QByteArray frame() +{ + return QByteArray::fromHex( + "82a0a4a64040609c60868298987303f0" + "21343734322e30304e2f31323231372e3030573e4f46464c494e4520415052532052582046495854555245"); +} + +inline unsigned fcs(const QByteArray& bytes) +{ + unsigned crc = 0xffff; + for (unsigned char byte : bytes) { + crc ^= byte; + for (int bit = 0; bit < 8; ++bit) { + crc = (crc >> 1) ^ ((crc & 1) ? 0x8408 : 0); + } + } + return crc ^ 0xffff; +} + +inline QVector hdlcBits(int preambleFlags = 100, int postambleFlags = 32, + const QByteArray& payload = frame()) +{ + QVector bits; + const auto byte = [&](unsigned value) { + for (int bit = 0; bit < 8; ++bit) { + bits.append((value >> bit) & 1); + } + }; + for (int flag = 0; flag < preambleFlags; ++flag) { + byte(0x7e); + } + QByteArray bytes = payload; + const unsigned crc = fcs(bytes); + bytes.append(static_cast(crc & 255)); + bytes.append(static_cast(crc >> 8)); + int ones = 0; + for (unsigned char value : bytes) { + for (int index = 0; index < 8; ++index) { + const int bit = (value >> index) & 1; + bits.append(bit); + if (bit) { + if (++ones == 5) { + bits.append(0); + ones = 0; + } + } else { + ones = 0; + } + } + } + for (int flag = 0; flag < postambleFlags; ++flag) { + byte(0x7e); + } + return bits; +} + +inline QVector afsk(int sampleRate, int channels = 1, int preambleFlags = 100, + const QByteArray& payload = frame()) +{ + const QVector bits = hdlcBits(preambleFlags, 32, payload); + QVector samples; + samples.reserve(bits.size() * (sampleRate / 1200) * channels); + bool mark = true; + double phase = 0; + for (int bit : bits) { + if (!bit) { + mark = !mark; + } + const double step = 2 * std::numbers::pi * (mark ? 1200 : 2200) / sampleRate; + for (int index = 0; index < sampleRate / 1200; ++index) { + const float sample = static_cast(0.5 * std::sin(phase)); + phase = std::fmod(phase + step, 2 * std::numbers::pi); + samples.append(sample); + if (channels == 2) { + samples.append(sample * 0.75f); + } + } + } + return samples; +} + +inline QByteArray cu8FmIq(int sampleRate = 2400000, double deviationHz = 2500, + double carrierOffsetHz = 0, double warmupSeconds = 0.35, + int preambleFlags = 160) +{ + const QVector audio = afsk(sampleRate, 1, preambleFlags); + const qsizetype warmup = static_cast(warmupSeconds * sampleRate); + QByteArray iq(2 * (warmup + audio.size()), Qt::Uninitialized); + double phase = 0; + for (qsizetype index = 0; index < warmup + audio.size(); ++index) { + const double modulation = index < warmup ? 0 : 2 * audio[index - warmup]; + phase = std::remainder(phase + 2 * std::numbers::pi + * (carrierOffsetHz + deviationHz * modulation) / sampleRate, + 2 * std::numbers::pi); + iq[2 * index] = static_cast(std::clamp( + std::lround(127.5 + 38.4 * std::cos(phase)), 0L, 255L)); + iq[2 * index + 1] = static_cast(std::clamp( + std::lround(127.5 + 38.4 * std::sin(phase)), 0L, 255L)); + } + return iq; +} + +} // namespace AetherSDR::test::ax25rx diff --git a/tests/ax25_libmodem_shim_test.cpp b/tests/ax25_libmodem_shim_test.cpp index 1485f5a33..93b04d630 100644 --- a/tests/ax25_libmodem_shim_test.cpp +++ b/tests/ax25_libmodem_shim_test.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include @@ -839,14 +839,12 @@ void testReplayWavLoaderFeedsShim() const auto bits = lm::ax25::encode_bitstream(frameBytes, 0, 80, 8); const auto audio = afskPcmFromBits(bits, cfg); - QTemporaryFile file; - file.setFileTemplate(QStringLiteral("aether-ax25-capture-XXXXXX.wav")); - const bool opened = file.open(); - report("temporary replay WAV opened", opened); + QTemporaryDir directory; + const bool opened = directory.isValid(); + report("temporary replay WAV directory created", opened); if (!opened) return; - const QString path = file.fileName(); - file.close(); + const QString path = directory.filePath(QStringLiteral("capture.wav")); const QByteArray pcm(reinterpret_cast(audio.data()), static_cast(audio.size() * sizeof(float))); @@ -887,15 +885,13 @@ void testCaptureWriterSupportsTxStages() ax25AudioCapturePath(Ax25AudioCaptureStage::TxGenerated, captureId, 0).isEmpty()); - QTemporaryFile file; - file.setFileTemplate(QStringLiteral("aether-ax25-tx-capture-XXXXXX.wav")); - const bool opened = file.open(); - report("temporary TX capture WAV opened", opened); + QTemporaryDir directory; + const bool opened = directory.isValid(); + report("temporary TX capture WAV directory created", opened); if (!opened) { return; } - const QString path = file.fileName(); - file.close(); + const QString path = directory.filePath(QStringLiteral("capture.wav")); const float stereoFrames[] = { 0.25f, 0.25f, diff --git a/tests/ax25_receive_model_test.cpp b/tests/ax25_receive_model_test.cpp new file mode 100644 index 000000000..28cb8065c --- /dev/null +++ b/tests/ax25_receive_model_test.cpp @@ -0,0 +1,562 @@ +// Socket-free normalized PCM -> production selected route -> real modem worker. +// The independent fixture does not use the application's transmit encoder. +#include "TestSettingsProfile.h" +#include "Ax25ReceiveFixture.h" +#include "core/backends/IRadioBackend.h" +#include "models/Ax25ReceiveModel.h" +#include "models/RadioModel.h" +#include "models/SliceModel.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace AetherSDR { +struct Ax25ReceiveModelTestAccess { + static QObject* worker(Ax25ReceiveModel& model) { return model.workerForTest(); } + static auto queues(const Ax25ReceiveModel& model) { return model.queuesForTest(); } +}; +} +using namespace AetherSDR; +namespace { +int failures = 0; +int checks = 0; +void check(bool value, const char* message) +{ + ++checks; + if (!value) { + ++failures; + std::fprintf(stderr, "FAIL: %s\n", message); + } +} +template bool waitFor(Predicate predicate, int milliseconds = 4000) +{ + QElapsedTimer timer; + timer.start(); + while (!predicate() && timer.elapsed() < milliseconds) { + QCoreApplication::processEvents(); + QThread::msleep(1); + } + return predicate(); +} +void settle(int milliseconds = 30) +{ + QElapsedTimer timer; + timer.start(); + waitFor([&] { return timer.elapsed() >= milliseconds; }, milliseconds + 100); +} +class Source final : public IRadioBackend { +public: + RadioCapabilities capabilities() const override + { + RadioCapabilities caps; + caps.hasDaxStreams = dax; + caps.maxSlices = 8; + return caps; + } + bool ownsRxAudio() const override { return true; } + void connectRadio(const RadioConnectRequest&) override { live = true; emit connected(); } + void disconnectRadio() override { live = false; emit disconnected(); } + bool isConnected() const override { return live; } + void setSliceFrequency(int, double) override {} + void setSliceMode(int, const QString&) override {} + void setSliceFilter(int, int, int) override {} + void setSliceAgc(int, const QString&, int) override {} + void setPanCenter(const QString&, double, PanCenterIntent) override {} + void setKeying(bool, const TxCoordinator::Operation&, + const TxCoordinator::Completion&) override {} // no TX transport + void invokeExtension(const QString&, const QString&, quint64, const QVariant&) override {} + bool live = false; + bool dax = false; + std::unique_ptr stream; +}; +struct Fixture { + RadioModel radio; + Source* source = nullptr; + SliceModel* a = nullptr; + SliceModel* b = nullptr; + explicit Fixture(bool withDax = false) + { + auto backend = std::make_unique(); + source = backend.get(); + source->dax = withDax; + if (withDax) { + source->stream = std::make_unique(); + } + radio.setBackendForTest(std::move(backend), QStringLiteral("rtl"), source->stream.get()); + source->connectRadio({}); + a = add(3); + b = add(7); + } + SliceModel* add(int id) + { + SliceDelta delta; + delta.panId = QStringLiteral("aprs-pan"); + delta.frequency = 144.390; + delta.mode = QStringLiteral("FM"); + delta.inUse = true; + emit source->sliceChanged(id, delta); + return radio.slice(id); + } +}; +void bind(Ax25ReceiveModel& model, Fixture& fixture) +{ + model.configure(ax25DemodConfigForProfile(Ax25ModemProfile::Vhf1200)); + model.setSlice(fixture.a); + model.setEnabled(true); +} +void feed(Fixture& fixture, PcmProducer& producer, const QVector& samples, + int channels, const std::function& deliver = {}) +{ + constexpr int chunks[] = {1, 17, 255, 11, 1024, 513, 37}; + qsizetype offset = 0; + int chunk = 0; + while (offset < samples.size()) { + const qsizetype count = std::min(chunks[chunk++ % 7] * channels, + samples.size() - offset); + const auto frame = producer.produce(samples.mid(offset, count)); + check(frame.has_value(), "fixture creates a valid typed frame"); + if (!frame) { + return; + } + if (deliver) { + deliver(*frame); + } else { + emit fixture.source->sliceAudioFrameReady(frame->stream().sliceId, *frame); + } + offset += count; + QCoreApplication::processEvents(); + QThread::msleep(1); + } +} +void packetMatrix(QCoreApplication& app) +{ + check(test::ax25rx::fcs(test::ax25rx::frame()) == 0x1e9b, + "independent fixed AX25 payload has expected X25 FCS 9b1e on wire"); + for (const int rate : {24000, 48000}) { + for (const int channels : {1, 2}) { + Fixture fixture; + Ax25ReceiveModel model(fixture.radio); + bind(model, fixture); + fixture.radio.setPcAudioEnabled(false); + fixture.a->setAudioGain(0); + fixture.a->setAudioMute(true); + int frames = 0; + int pcm = 0; + int resets = 0; + Ax25ReceiveContext retained; + QObject::connect(&model, &Ax25ReceiveModel::sourceReset, &app, [&] { ++resets; }); + QObject::connect(&model, &Ax25ReceiveModel::pcmReady, &app, + [&](const DecoderPcmBlock& block, const Ax25ReceiveContext& context) { + ++pcm; + retained = context; + check(context.current() && block.current() + && block.source.stream().sliceId == 3 + && block.inputSampleRateHz == rate, + "mono24 output retains native selected source identity and rate"); + }); + QObject::connect(&model, &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context) { + ++frames; + check(context.current() && frame.fcsOk + && frame.ax25FrameNoFcs == test::ax25rx::frame(), + "known Bell202 packet survives production conversion with exact FCS"); + }); + PcmProducer producer; + producer.start(PcmPurpose::Slice, 3, + {rate, channels == 1 ? PcmLayout::Mono : PcmLayout::Stereo}); + feed(fixture, producer, test::ax25rx::afsk(rate, channels), channels); + check(waitFor([&] { return frames == 1; }) && pcm > 0, + "24/48 mono/stereo decode with awkward chunk boundaries and monitoring off"); + const int before = resets; + const Ax25ReceiveContext live = retained; + model.configure(ax25DemodConfigForProfile(Ax25ModemProfile::Vhf1200)); + model.setDiagnosticsLoggingEnabled(true); + model.setDiagnosticsLoggingEnabled(false); + fixture.a->setAudioGain(80); + fixture.a->setAudioMute(false); + fixture.radio.setPcAudioEnabled(true); + check(resets == before && live.current(), + "unchanged config, logging and monitor controls preserve receive context"); + model.reset(); + check(!live.current(), "explicit reset immediately retires already delivered contexts"); + settle(); + check(frames == 1, "one fixture yields one accepted packet"); + } + } +} +void routingAndDax(QCoreApplication& app) +{ + Fixture fixture; + Ax25ReceiveModel model(fixture.radio); + bind(model, fixture); + int pcm = 0; + QObject::connect(&model, &Ax25ReceiveModel::pcmReady, &app, + [&](const DecoderPcmBlock&, const Ax25ReceiveContext&) { ++pcm; }); + PcmProducer other; + PcmProducer speaker; + other.start(PcmPurpose::Slice, 7, {24000, PcmLayout::Mono}); + speaker.start(); + emit fixture.source->sliceAudioFrameReady(7, *other.produce({0.5f})); + emit fixture.source->audioFrameReady(*speaker.produce({0.5f, 0.5f})); + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + check(pcm == 0, "native selection rejects unrelated slice and speaker mix"); + + Fixture flex(true); + Ax25ReceiveModel ax25(flex.radio); + DecoderAudioModel cw(flex.radio, DecoderAudioModel::Consumer::Cw); + DecoderAudioModel rtty(flex.radio, DecoderAudioModel::Consumer::Rtty); + bind(ax25, flex); + check(ax25.routeStatus() == DecoderAudioModel::RouteStatus::SharedRxAudio + && flex.a->daxChannel() == 0, "Flex retains explicit shared fallback without auto assignment"); + int shared = 0; + QObject::connect(&ax25, &Ax25ReceiveModel::pcmReady, &app, + [&](const DecoderPcmBlock&, const Ax25ReceiveContext&) { ++shared; }); + PcmProducer sharedProducer; + sharedProducer.start(); + emit flex.source->audioFrameReady(*sharedProducer.produce({0.5f, 0.5f})); + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + check(shared == 1, "unassigned Flex fallback remains usable"); + int decoded = 0; + QObject::connect(&ax25, &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context) { + if (context.current() && frame.ax25FrameNoFcs == test::ax25rx::frame() && frame.fcsOk) { + ++decoded; + } + }); + feed(flex, sharedProducer, test::ax25rx::afsk(24000, 2), 2, + [&](const PcmFrame& frame) { emit flex.source->audioFrameReady(frame); }); + check(waitFor([&] { return decoded == 1; }), "shared Flex fallback decodes exact packet and FCS"); + flex.a->setDaxChannel(8); + cw.setSlice(flex.a); + rtty.setSlice(flex.a); + cw.setEnabled(true); + rtty.setEnabled(true); + using Holder = PanadapterStream::DaxConsumer; + PanadapterStream& stream = *flex.source->stream; + stream.acquireDaxChannel(8, Holder::Clock); + check(stream.daxChannelHeldBy(8, Holder::Ax25Decoder) + && stream.daxChannelHeldBy(8, Holder::CwDecoder) + && stream.daxChannelHeldBy(8, Holder::RttyDecoder) + && stream.daxChannelHeldBy(8, Holder::Clock) + && stream.daxChannelSnapshot().front().holders.contains(QStringLiteral("ax25-decoder")), + "AX25 has an independent DAX hold alongside CW RTTY and Clock"); + PcmProducer dax; + dax.start(PcmPurpose::Auxiliary, -1, {24000, PcmLayout::Mono}); + feed(flex, dax, test::ax25rx::afsk(24000), 1, + [&](const PcmFrame& frame) { emit stream.daxPcmReady(8, frame); }); + check(waitFor([&] { return decoded == 2; }), "assigned DAX path decodes exact packet"); + ax25.setEnabled(false); + check(!stream.daxChannelHeldBy(8, Holder::Ax25Decoder) + && stream.daxChannelHeldBy(8, Holder::CwDecoder) + && stream.daxChannelHeldBy(8, Holder::RttyDecoder) + && stream.daxChannelHeldBy(8, Holder::Clock), + "closing AX25 releases only its hold"); + Fixture absent; + absent.source->dax = true; + absent.a->setDaxChannel(1); + Ax25ReceiveModel unavailable(absent.radio); + bind(unavailable, absent); + check(unavailable.routeStatus() == DecoderAudioModel::RouteStatus::DaxTransportUnavailable, + "missing assigned DAX transport reports unavailable instead of changing sources"); +} + +// The model has only one queued owner call: worker publication. Intercepting +// it retires the lease after worker decode, immediately before owner delivery. +class RetireBeforePublication final : public QObject { +public: + std::function retire; + bool fired = false; + bool eventFilter(QObject*, QEvent* event) override + { + if (!fired && event->type() == QEvent::MetaCall) { + fired = true; + retire(); + } + return false; + } +}; +void stalePublication(QCoreApplication& app, int scenario) +{ + Fixture fixture; + Ax25ReceiveModel model(fixture.radio); + bind(model, fixture); + int frames = 0; + int diagnostics = 0; + QObject::connect(&model, &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame&, const Ax25ReceiveContext&) { ++frames; }); + QObject::connect(&model, &Ax25ReceiveModel::diagnosticsUpdated, &app, + [&](const Ax25DecoderDiagnostics&, const Ax25ReceiveContext&) { ++diagnostics; }); + PcmProducer producer; + producer.start(PcmPurpose::Slice, 3, {24000, PcmLayout::Mono}); + RetireBeforePublication filter; + filter.retire = [&] { + switch (scenario) { + case 0: model.setEnabled(false); break; + case 1: model.setSlice(fixture.b); break; + case 2: model.configure(ax25DemodConfigForProfile(Ax25ModemProfile::Hf300)); break; + case 3: producer.invalidate(); break; + case 4: + fixture.source->disconnectRadio(); + // setBackendForTest intentionally bypasses production lifecycle wiring. + emit fixture.radio.connectionStateChanged(false); + break; + default: model.reset(); break; + } + }; + model.installEventFilter(&filter); + // Less than one second: the first worker result is the known packet, not + // the modem's one-second diagnostic report. One block avoids input overflow. + const QVector packet = test::ax25rx::afsk(24000, 1, 40); + check(packet.size() < 24000, "publication fixture ends before diagnostics interval"); + emit fixture.source->sliceAudioFrameReady(3, *producer.produce(packet)); + check(waitFor([&] { return filter.fired; }), "worker decoded before owner retirement hook"); + settle(); + check(frames == 0 && diagnostics == 0, + "retired decoded output cannot escape after disable selection config epoch or disconnect"); + model.removeEventFilter(&filter); +} +void reentrantConsumers(QCoreApplication& app) +{ + Fixture fixture; + auto model = std::make_unique(fixture.radio); + bind(*model, fixture); + int first = 0; + int admittedSecond = 0; + Ax25ReceiveContext retained; + QObject::connect(model.get(), &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame&, const Ax25ReceiveContext& context) { + ++first; + retained = context; + model->reset(); + }); + QObject::connect(model.get(), &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame&, const Ax25ReceiveContext& context) { + if (context.current()) { + ++admittedSecond; + } + }); + PcmProducer producer; + producer.start(PcmPurpose::Slice, 3, {24000, PcmLayout::Mono}); + feed(fixture, producer, test::ax25rx::afsk(24000), 1); + check(waitFor([&] { return first == 1; }) && admittedSecond == 0 && !retained.current(), + "later synchronous listeners reject context retired by the first listener"); + model.reset(); + check(!retained.current(), "retained context is safe and stale after model destruction"); + + auto doomed = std::make_unique(fixture.radio); + bind(*doomed, fixture); + QPointer guard(doomed.get()); + QObject::connect(doomed.get(), &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame&, const Ax25ReceiveContext& context) { + retained = context; + doomed.reset(); + }); + PcmProducer fresh; + fresh.start(PcmPurpose::Slice, 3, {24000, PcmLayout::Mono}); + feed(fixture, fresh, test::ax25rx::afsk(24000), 1); + check(waitFor([&] { return !guard; }) && !retained.current(), + "frame callback may destroy model and join its worker without stale continuation"); +} +void multipleResultsOneBlock(QCoreApplication& app, bool resetFirst) +{ + Fixture fixture; + Ax25ReceiveModel model(fixture.radio); + bind(model, fixture); + QByteArray second = test::ax25rx::frame(); + second.back() = '2'; + QVector samples = test::ax25rx::afsk(24000, 1, 40); + samples += test::ax25rx::afsk(24000, 1, 40, second); + int frames = 0; + QObject::connect(&model, &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context) { + ++frames; + check(context.current() && frame.fcsOk + && frame.ax25FrameNoFcs == (frames == 1 ? test::ax25rx::frame() : second), + "single PCM block yields independently framed exact AX25 payloads"); + if (resetFirst) { + model.reset(); + } + }); + PcmProducer producer; + producer.start(PcmPurpose::Slice, 3, {24000, PcmLayout::Mono}); + check(samples.size() <= PcmFrame::kMaxFrames, "two packet fixture fits one bounded PCM block"); + emit fixture.source->sliceAudioFrameReady(3, *producer.produce(samples)); + check(waitFor([&] { return frames >= (resetFirst ? 1 : 2); }), + "two-frame control decodes both, or first callback resets model"); + settle(); + check(frames == (resetFirst ? 1 : 2), + "reset from first result prevents second result in the same worker output batch"); +} +class WorkerBarrier { +public: + struct State { + QSemaphore entered; + QSemaphore release; + std::atomic timedOut{false}; + }; + explicit WorkerBarrier(Ax25ReceiveModel& model) : state(std::make_shared()) + { + QMetaObject::invokeMethod(Ax25ReceiveModelTestAccess::worker(model), [box = state] { + box->entered.release(); + box->timedOut = !box->release.tryAcquire(1, 4000); + }, Qt::QueuedConnection); + check(state->entered.tryAcquire(1, 4000), "bounded worker barrier entered"); + } + ~WorkerBarrier() { state->release.release(); } + std::shared_ptr state; +}; +bool workerIdle(Ax25ReceiveModel& model) +{ + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + return waitFor([&] { + const auto queue = Ax25ReceiveModelTestAccess::queues(model); + return !queue.workerScheduled && queue.inputBlocks == 0; + }); +} +void boundedInput(QCoreApplication& app, bool frameLimit) +{ + Fixture fixture; + Ax25ReceiveModel model(fixture.radio); + bind(model, fixture); + PcmProducer producer; + producer.start(PcmPurpose::Slice, 3, {24000, PcmLayout::Mono}); + // Establish the route's initial discontinuity before counting overflow. + emit fixture.source->sliceAudioFrameReady(3, *producer.produce({0.0f})); + check(workerIdle(model), "initial route input drains"); + int resets = 0; + int decoded = 0; + QObject::connect(&model, &Ax25ReceiveModel::sourceReset, &app, [&] { ++resets; }); + QObject::connect(&model, &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context) { + if (context.current() && frame.fcsOk && frame.ax25FrameNoFcs == test::ax25rx::frame()) { + ++decoded; + } + }); + std::shared_ptr barrier; + { + WorkerBarrier blocked(model); + barrier = blocked.state; + const QVector input(frameLimit ? 65536 : 1, 0.0f); + const int blocks = frameLimit ? 2 : 257; + for (int index = 0; index < blocks; ++index) { + emit fixture.source->sliceAudioFrameReady(3, *producer.produce(input)); + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + const auto queue = Ax25ReceiveModelTestAccess::queues(model); + check(queue.inputFrames <= 65536 && queue.inputBlocks <= 256 + && queue.workerScheduled, + "stalled worker retains bounded PCM and one scheduled drain"); + } + const auto queue = Ax25ReceiveModelTestAccess::queues(model); + check(resets == 1 && queue.inputBlocks == 1 + && queue.inputFrames == input.size(), + "input overflow retires backlog once and retains only the latest block"); + } + check(workerIdle(model) && !barrier->timedOut, "worker barrier releases and retained input drains"); + feed(fixture, producer, test::ax25rx::afsk(24000), 1); + check(waitFor([&] { return decoded == 1; }), "exact packet recovers after bounded input overflow"); +} +void boundedOutput(QCoreApplication& app) +{ + Fixture fixture; + Ax25ReceiveModel model(fixture.radio); + // The output queue contract is independent of demodulator lane count. + // Use one supported lane for this 260-second fixture; other scenarios retain A+. + model.configure(ax25DemodConfigForProfile(Ax25ModemProfile::Vhf1200, + Ax25TonePolarity::Normal, VhfMode::A)); + model.setSlice(fixture.a); + model.setEnabled(true); + PcmProducer producer; + producer.start(PcmPurpose::Slice, 3, {24000, PcmLayout::Mono}); + emit fixture.source->sliceAudioFrameReady(3, *producer.produce({0.0f})); + check(workerIdle(model), "initial output route input drains"); + int resets = 0; + int decoded = 0; + int diagnostics = 0; + QObject::connect(&model, &Ax25ReceiveModel::sourceReset, &app, [&] { ++resets; }); + QObject::connect(&model, &Ax25ReceiveModel::diagnosticsUpdated, &app, + [&](const Ax25DecoderDiagnostics&, const Ax25ReceiveContext& context) { + check(context.current(), "only current diagnostics escape output overflow"); + ++diagnostics; + }); + QObject::connect(&model, &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context) { + if (context.current() && frame.fcsOk && frame.ax25FrameNoFcs == test::ax25rx::frame()) { + ++decoded; + } + }); + RetireBeforePublication hold; + hold.retire = [&] { + // Keep the original owner event on the stack, then allow it to run. + // This stalls publication without deleting or replacing a queued call. + const QVector silence(24000, 0.0f); + for (int index = 0; index < 259; ++index) { + emit fixture.source->sliceAudioFrameReady(3, *producer.produce(silence)); + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + check(workerIdle(model), "bounded output fixture drains one diagnostic input"); + const auto queue = Ax25ReceiveModelTestAccess::queues(model); + check(queue.outputBlocks <= 256 && queue.outputScheduled, + "stalled owner retains bounded results and its single pending publication"); + } + check(diagnostics == 0 && resets == 0, + "owner stall delays outward diagnostics and reset notification"); + }; + model.installEventFilter(&hold); + emit fixture.source->sliceAudioFrameReady(3, *producer.produce(QVector(24000, 0.0f))); + check(waitFor([&] { return hold.fired; }), "owner publication stall executed"); + model.removeEventFilter(&hold); + check(resets == 1 && diagnostics > 0 && diagnostics < 10, + "output overflow resets once and publishes only post-retirement diagnostics"); + feed(fixture, producer, test::ax25rx::afsk(24000), 1); + check(waitFor([&] { return decoded == 1; }), "exact packet recovers after bounded output overflow"); +} +void inputRetirement(QCoreApplication& app) +{ + Fixture fixture; + Ax25ReceiveModel model(fixture.radio); + bind(model, fixture); + int frames = 0; + QObject::connect(&model, &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame&, const Ax25ReceiveContext&) { ++frames; }); + PcmProducer producer; + producer.start(PcmPurpose::Slice, 3, {24000, PcmLayout::Mono}); + emit fixture.source->sliceAudioFrameReady(3, *producer.produce(test::ax25rx::afsk(24000))); + model.setSlice(fixture.b); + model.setSlice(fixture.a); + settle(); + check(frames == 0, "queued selected input is retired even after reselecting the same slice ID"); + feed(fixture, producer, test::ax25rx::afsk(24000), 1); + check(waitFor([&] { return frames == 1; }), "fresh audio recovers after selection retirement"); +} +} // namespace +int main(int argc, char** argv) +{ + TestSettingsProfile profile(QStringLiteral("ax25-receive-model")); + if (!profile.isValid()) { + return 2; + } + QCoreApplication app(argc, argv); + packetMatrix(app); + routingAndDax(app); + for (int scenario = 0; scenario < 6; ++scenario) { + stalePublication(app, scenario); + } + reentrantConsumers(app); + multipleResultsOneBlock(app, false); + multipleResultsOneBlock(app, true); + boundedInput(app, false); + boundedInput(app, true); + boundedOutput(app); + inputRetirement(app); + std::fprintf(stdout, "%d checks, %d failures\n", checks, failures); + return failures ? 1 : 0; +} diff --git a/tests/control_receive_test.cpp b/tests/control_receive_test.cpp index 0f1fd01c4..a98923884 100644 --- a/tests/control_receive_test.cpp +++ b/tests/control_receive_test.cpp @@ -657,16 +657,18 @@ void productionCapabilityContracts() && rtlFilters->modes[2].mode == QStringLiteral("WFM") && rtlFilters->modes[2].minimumLowHz == -100000 && rtlFilters->modes[2].maximumHighHz == 100000 + && rtlFilters->modes[2].minimumWidthHz == 30000 + && rtlFilters->modes[2].maximumWidthHz == 200000 && rtlCaps.broadcastFmReceive), "qualified WFM declares its own filter range and the broadcast-FM controls"); check(qualifiedWfm || !rtlCaps.broadcastFmReceive, "without qualified WFM, RTL offers no broadcast-FM controls"); check(rtlCaps.squelchLevelScale && rtlCaps.squelchLevelScale->modesExclusive - && rtlCaps.squelchLevelScale->autoSquelch + && rtlCaps.squelchLevelScale->automaticInEngine && !rtlCaps.squelchLevelScale->spectrumComparable && rtlCaps.squelchLevelScale->modes == QStringList{"FM", "FMN"} && rtlCaps.squelchLevelScale->offsetDb == -120 && rtlCaps.squelchLevelScale->dbPerStep == 1.2 - && rtlCaps.squelchLevelScale->unit == QStringLiteral("dBFS/bin"), + && rtlCaps.squelchLevelScale->unit == QStringLiteral("dBFS/2048-bin"), "RTL desktop squelch declares the implemented detector's modes and units"); #endif } diff --git a/tests/dax_receive_applet_test.cpp b/tests/dax_receive_applet_test.cpp new file mode 100644 index 000000000..503b9d090 --- /dev/null +++ b/tests/dax_receive_applet_test.cpp @@ -0,0 +1,75 @@ +// Socket-free real DAX applet: injected capabilities, no audio device or radio. +#include "TestSettingsProfile.h" +#include "gui/DaxApplet.h" +#include "gui/MeterSlider.h" +#include "models/RadioModel.h" +#include "models/SliceModel.h" +#include +#include +#include +#include + +using namespace AetherSDR; +class ReceiveBackend final : public IRadioBackend { +public: + RadioCapabilities caps; + RadioCapabilities capabilities() const override { return caps; } + bool isConnected() const override { return true; } + void connectRadio(const RadioConnectRequest&) override {} + void disconnectRadio() override {} + void setSliceFrequency(int,double) override {} + void setSliceMode(int,const QString&) override {} + void setSliceFilter(int,int,int) override {} + void setSliceAgc(int,const QString&,int) override {} + void setPanCenter(const QString&,double,PanCenterIntent) override {} + void setKeying(bool,const TxCoordinator::Operation&,const TxCoordinator::Completion&) override {} + void invokeExtension(const QString&,const QString&,quint64,const QVariant&) override {} +}; +int main(int argc,char** argv) +{ + TestSettingsProfile profile("dax-receive-applet"); + QApplication app(argc,argv); + int failures=0; + const auto check=[&](bool ok,const char* name) { + if(!ok) {++failures;std::fprintf(stderr,"FAIL: %s\n",name);} + }; + RadioModel model; + auto backend=std::make_unique(); + ReceiveBackend* source=backend.get(); + source->caps.canTransmit=false; + source->caps.maxSlices=8; + source->caps.receiveAudioExport=ReceiveAudioExport{{24000,48000},8}; + model.setBackendForTest(std::move(backend),QStringLiteral("test")); + DaxApplet widget; + widget.setRadioModel(&model); + widget.setMaxDaxChannels(8); + widget.setNativeReceiveRouting(true); + widget.show(); + MeterSlider* tx=nullptr; + int rx=0; + for(MeterSlider* meter:widget.findChildren()) { + if(meter->accessibleName()=="DAX TX gain") {tx=meter;} + else if(meter->accessibleName().startsWith("DAX RX ")) { + ++rx;check(meter->isEnabled() && meter->isVisible(),"all eight DAX receive gain controls are usable"); + } + } + check(rx==8,"DAX applet exposes eight receive channels"); + check(tx && !tx->isEnabled() && tx->isVisible() && !tx->toolTip().isEmpty() + && tx->accessibleDescription().contains("receive-only"), + "receive-only TX is dimmed with an accessible reason, not hidden"); + for(int i=0;i<8;++i) { + SliceDelta delta;delta.frequency=100.1+i*0.1;delta.mode=QStringLiteral("FM"); + emit source->sliceChanged(10+i,delta); + widget.setReceiveChannelSlice(i+1,model.slice(10+i)); + QLabel* label=widget.findChild(QStringLiteral("daxRxStatus%1").arg(i+1)); + check(label && label->text().contains("Slice"),"native route identifies its assigned slice in every DAX row"); + } + source->caps.receiveAudioExport.reset();source->caps.hasDaxStreams=true;source->caps.canTransmit=true; + emit model.capabilitiesChanged(true,source->caps); + widget.setNativeReceiveRouting(false); + check(tx && tx->isEnabled() && tx->toolTip().isEmpty() + && !tx->accessibleDescription().contains("receive-only"), + "returning to a transmit-capable DAX backend restores its TX control"); + std::printf("DAX applet: %d failures\n",failures); + return failures?1:0; +} diff --git a/tests/meter_model_test.cpp b/tests/meter_model_test.cpp index d5a9f3488..c46d1b977 100644 --- a/tests/meter_model_test.cpp +++ b/tests/meter_model_test.cpp @@ -2119,5 +2119,41 @@ int main(int argc, char** argv) testAmpPowerFlagTracksOnlyPowerMeters(); testWithdrawingAnAmpMeterAnnouncesItself(); + { + MeterModel model; + int absoluteReadings = 0; + QObject::connect(&model, &MeterModel::sLevelChanged, &model, + [&](int, float) { ++absoluteReadings; }); + MeterDef relative = slcMeter(700, 7); + relative.unit = "dBFS"; + model.defineMeter(relative); + model.updateValueByName("SLC", "LEVEL", -35.0f, 7); + report("relative RF never emits calibrated dBm", absoluteReadings == 0); + report("relative RF never enters absolute lookup", !model.sLevelForSlice(7)); + report("relative RF is available with its own reference", model.relativeLevelForSlice(7) == -35.0f); + model.removeMeter(700); + model.defineMeter(relative); + report("recreated receiver cannot inherit the old reading", !model.relativeLevelForSlice(7)); + for (int id = 0; id < 8; ++id) { + relative.index = 700 + id; relative.sourceIndex = id; + model.defineMeter(relative); + model.updateValueByName("SLC", "LEVEL", -20.0f - id, id); + } + model.removeMeter(703); + for (int id = 0; id < 8; ++id) { + report("relative RF stays associated with stable receiver ID", + id == 3 ? !model.relativeLevelForSlice(id) + : model.relativeLevelForSlice(id) == -20.0f - id); + } + report("unselected receiver cannot fall back to first meter", !model.relativeLevelForSlice(-1)); + relative.unit = "dBm"; + model.defineMeter(relative); + report("unit change cannot reinterpret an old relative sample", !model.sLevelForSlice(7)); + model.updateValueByName("SLC", "LEVEL", -73, 7); + report("calibrated meter still publishes dBm", model.sLevelForSlice(7) == -73 && absoluteReadings == 1); + model.clear(); + report("disconnect clears relative RF", !model.relativeLevelForSlice(7)); + } + return g_failed == 0 ? 0 : 1; } diff --git a/tests/pipewire_rx_bridge_test.cpp b/tests/pipewire_rx_bridge_test.cpp new file mode 100644 index 000000000..98b4ec432 --- /dev/null +++ b/tests/pipewire_rx_bridge_test.cpp @@ -0,0 +1,103 @@ +// Socket-free Linux bridge test. Own anonymous pipes replace its output FDs; +// no PulseAudio modules, audio devices, radio or helper process are opened. +#include "core/PipeWireAudioBridge.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AetherSDR { +class PipeWireRxBridgeTest { +public: + static int run() + { + int failures = 0; + const auto check = [&](bool ok,const char* name) { + if (!ok) { ++failures; std::fprintf(stderr,"FAIL: %s\n",name); } + }; + PipeWireAudioBridge bridge; + // Exercise the same descriptor-opening path as loadPipeSource, without + // launching pactl. A Flex writer must see EPIPE after its reader leaves. + QTemporaryDir directory; + if (!directory.isValid()) { return 1; } + const QString fifo = directory.filePath(QStringLiteral("rx")); + if (::mkfifo(fifo.toUtf8().constData(), 0600) != 0) { return 1; } + check(bridge.openRxDrain(0, fifo) && bridge.m_rx[0].drainFd == -1, + "legacy Flex path creates no drain reader"); + const int reader = ::open(fifo.toUtf8().constData(), O_RDONLY|O_NONBLOCK|O_CLOEXEC); + const int writer = ::open(fifo.toUtf8().constData(), O_WRONLY|O_NONBLOCK|O_CLOEXEC); + if (reader < 0 || writer < 0) { return 1; } + const auto closeWriter = qScopeGuard([&] { ::close(writer); }); + ::close(reader); + const auto previousSignal = std::signal(SIGPIPE, SIG_IGN); + const auto restoreSignal = qScopeGuard([&] { std::signal(SIGPIPE, previousSignal); }); + const char byte = 0; + check(::write(writer, &byte, 1) == -1 && errno == EPIPE, + "loss of Flex pipe reader still produces EPIPE"); + bridge.m_receiveOnly = true; + check(bridge.openRxDrain(0, fifo) && bridge.m_rx[0].drainFd >= 0, + "receive-only path creates its route-reset reader"); + check(::write(writer, &byte, 1) == 1, + "receive-only drain reader owns buffered route data"); + ::close(bridge.m_rx[0].drainFd); + bridge.m_rx[0].drainFd = -1; + check(!bridge.openRxDrain(0, directory.filePath(QStringLiteral("missing"))), + "receive-only drain open failure is reported"); + bridge.m_receiveOnly = false; + check(bridge.openRxDrain(0, directory.filePath(QStringLiteral("missing"))), + "legacy Flex never depends on opening a drain path"); + bridge.m_receiveOnly = true; + bridge.setTransmitting(true); + check(bridge.isReceiveOnly() && !bridge.m_transmitting, + "receive-only bridge refuses a transmit state transition"); + std::array readers{}; + readers.fill(-1); + const auto closeReaders = qScopeGuard([&] { + for (int fd : readers) { if (fd >= 0) { ::close(fd); } } + }); + bridge.m_open.store(true); + for (int i = 0; i < 8; ++i) { + int fds[2]; + if (::pipe2(fds,O_NONBLOCK|O_CLOEXEC) != 0) { return 1; } + readers[i] = fds[0]; + bridge.m_rx[i].fd = fds[1]; + bridge.m_rx[i].drainFd = ::dup(fds[0]); + check(::fcntl(fds[1],F_SETPIPE_SZ,65536) >= 65536,"bounded native receive pipe capacity available"); + bridge.setChannelGain(i+1,0.5f); + const std::array stereo{float(i+1)/8.0f,0,float(i+1)/8.0f,0}; + bridge.feedDaxAudio(i+1,QByteArray(reinterpret_cast(stereo.data()),sizeof(stereo))); + std::array output{}; + const ssize_t count=::read(readers[i],output.data(),sizeof(output)); + check(count==sizeof(output) && std::abs(output[0]-float(i+1)/64.0f)<1e-6f + && std::abs(output[1]-float(i+1)/32.0f)<1e-6f + && output[2]==output[1] && output[3]==output[1], + "all eight real bridge channels downmix stereo24 and interpolate mono48 with independent gain"); + bridge.feedDaxAudio(i+1,QByteArray(reinterpret_cast(stereo.data()),sizeof(stereo))); + bridge.resetRxChannel(i+1); + check(::read(readers[i],output.data(),sizeof(output)) < 0, + "route reset drains retained output from the old channel owner"); + const std::array silence{}; + bridge.feedDaxAudio(i+1,QByteArray(reinterpret_cast(silence.data()),sizeof(silence))); + output.fill(1); + check(::read(readers[i],output.data(),sizeof(output))==sizeof(output) + && output==silence,"route reset discards interpolation history before the new owner's silence"); + } + bridge.close(); + std::printf("PipeWire bridge: %d failures\n",failures); + return failures ? 1 : 0; + } +}; +} +int main(int argc,char** argv) +{ + QCoreApplication app(argc,argv); + return AetherSDR::PipeWireRxBridgeTest::run(); +} diff --git a/tests/rtl_aprs_receive_test.cpp b/tests/rtl_aprs_receive_test.cpp new file mode 100644 index 000000000..aa7202005 --- /dev/null +++ b/tests/rtl_aprs_receive_test.cpp @@ -0,0 +1,313 @@ +// Socket-free end-to-end RX regression: independent AX.25/Bell202 fixture -> +// CU8 callbacks -> real RTL worker/backend -> native slice PCM -> modem model. +// Only USB device operations are injected. No radio or sound device is opened. +#include "TestSettingsProfile.h" +#include "Ax25ReceiveFixture.h" +#include "core/backends/rtl/RtlSdrBackend.h" +#include "core/backends/rtl/RtlSdrWorker.h" +#include "models/Ax25ReceiveModel.h" +#include "models/RadioModel.h" +#include "models/SliceModel.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace AetherSDR; +using Transaction = rtl::RtlCaptureTransaction; +namespace AetherSDR::rtl { +struct RtlCaptureBackendTestAccess { + static void start(RtlSdrBackend& backend, std::unique_ptr device) + { + backend.m_receiverCapacity = 2; + backend.m_capture = RtlCaptureTransaction({8, 2}); + backend.m_requested.hardware = {144290000, 2400000, 0, 0, 0, 240}; + backend.m_requested.receivers = { + {{3, 144390000, -8000, 8000, 0, 3000, 3000}, Transaction::Mode::Fm, 0, 50, true}, + {{7, 144590000, -8000, 8000, 0, 3000, 3000}, Transaction::Mode::Fm, 0, 50, true}}; + backend.startCapture(std::make_unique(std::move(device), nullptr, 2)); + } + static std::optional diagnostics(const RtlSdrBackend& backend) + { + if (!backend.m_worker) { return {}; } + return backend.m_worker->diagnostics(); + } + static quint64 revision(const RtlSdrBackend& backend) { return backend.m_published.revision; } +}; +} +namespace { +using Clock = std::chrono::steady_clock; +constexpr int kSampleRate = 2400000; +constexpr int kCallbackFrames = 8192; +int failures = 0; +void check(bool condition, const char* message) +{ + if (!condition) { ++failures; std::fprintf(stderr, "FAIL: %s\n", message); } +} +template bool waitFor(Predicate predicate, int milliseconds = 15000) +{ + QElapsedTimer elapsed; + elapsed.start(); + while (!predicate() && elapsed.elapsed() < milliseconds) { + QCoreApplication::processEvents(); + QThread::msleep(1); + } + return predicate(); +} +struct DeviceState { + std::mutex mutex; + std::condition_variable changed; + bool playing = false; + bool canceled = false; + std::atomic finished{false}; + std::atomic callbacks{0}; + Transaction::Hardware hardware; + QByteArray iq; +}; +class RecordedDevice final : public rtl::RtlSdrWorker::Device { +public: + explicit RecordedDevice(std::shared_ptr state) : m_state(std::move(state)) {} + bool set(Transaction::Control control, std::int64_t value) override + { + switch (control) { + case Transaction::Control::DirectSampling: m_state->hardware.directSampling = int(value); break; + case Transaction::Control::SampleRate: m_state->hardware.sampleRateHz = std::uint32_t(value); break; + case Transaction::Control::Ppm: m_state->hardware.ppm = int(value); break; + case Transaction::Control::OffsetTuning: m_state->hardware.offsetTuning = int(value); break; + case Transaction::Control::Center: m_state->hardware.centerHz = std::uint32_t(value); break; + case Transaction::Control::Gain: m_state->hardware.gainTenths = int(value); break; + } + return true; + } + std::optional read() override { return m_state->hardware; } + bool resetBuffer() override { return true; } + int readAsync(Callback callback, void* context) override + { + std::unique_lock lock(m_state->mutex); + m_state->changed.wait(lock, [&] { return m_state->playing || m_state->canceled; }); + const auto start = Clock::now(); + for (qsizetype offset = 0; offset < m_state->iq.size() && !m_state->canceled;) { + const qsizetype count = std::min(2 * kCallbackFrames, m_state->iq.size() - offset); + auto* bytes = reinterpret_cast(m_state->iq.data() + offset); + lock.unlock(); + callback(bytes, static_cast(count), context); + ++m_state->callbacks; + lock.lock(); + offset += count; + const auto deadline = start + std::chrono::nanoseconds( + (offset / 2) * 1000000000LL / kSampleRate); + m_state->changed.wait_until(lock, deadline, [&] { return m_state->canceled; }); + } + m_state->finished = true; + m_state->changed.wait(lock, [&] { return m_state->canceled; }); + return 0; + } + void cancelAsync() override + { + std::lock_guard lock(m_state->mutex); + m_state->canceled = true; + m_state->changed.notify_all(); + } +private: + std::shared_ptr m_state; +}; +QByteArray captureFixture(std::uint32_t centerHz) +{ + QByteArray iq = test::ax25rx::cu8FmIq(kSampleRate, 2500, 144390000.0 - centerHz); + double phase = 0; + // A second real RF receiver carries a different continuous audio tone. + // Its PCM must exist without entering the selected receiver's decoder. + for (qsizetype index = 0; index < iq.size() / 2; ++index) { + const double tone = std::sin(2 * std::numbers::pi * 440 * double(index) / kSampleRate); + phase = std::remainder(phase + 2 * std::numbers::pi * (144590000.0 - centerHz + 2500 * tone) / kSampleRate, + 2 * std::numbers::pi); + const double i = (static_cast(iq[2 * index]) - 127.5) / 127.5; + const double q = (static_cast(iq[2 * index + 1]) - 127.5) / 127.5; + iq[2 * index] = char(std::clamp(std::lround(127.5 + 127.5 * (0.65 * i + 0.2 * std::cos(phase))), 0L, 255L)); + iq[2 * index + 1] = char(std::clamp(std::lround(127.5 + 127.5 * (0.65 * q + 0.2 * std::sin(phase))), 0L, 255L)); + } + return iq; +} +} + +int runScenario(QCoreApplication& app, bool changeMonitor) +{ + const int initialFailures = failures; + auto device = std::make_shared(); + check(test::ax25rx::fcs(test::ax25rx::frame()) == 0x1e9b, "independent fixture retains its known FCS"); + RadioModel radio; + auto backend = std::make_unique(); + rtl::RtlSdrBackend* source = backend.get(); + radio.setBackendForTest(std::move(backend), QStringLiteral("rtl")); + QString error; + QObject::connect(source, &IRadioBackend::connectionError, &app, [&](const QString& value) { error = value; }); + quint64 selectedFrames = 0; + quint64 otherFrames = 0; + quint64 convertedSamples = 0; + int wrongInput = 0; + int decoded = 0; + int resets = 0; + int nativeDiscontinuities = 0; + quint64 nativeNextSample = 0; + int sourceIdentityChanges = 0; + QJsonArray nativeBoundaries; + PcmEpochLease selectedSource; + QByteArray nativePcm; + QByteArray decoderPcm; + const QString dumpRoot = qEnvironmentVariable("AETHER_APRS_TEST_PCM_DIR"); + QObject::connect(&radio, &RadioModel::backendSliceAudioFrameReady, &app, + [&](int id, const PcmFrame& frame) { + if (!frame.current() || frame.stream().purpose != PcmPurpose::Slice + || frame.stream().sliceId != id || frame.stream().format.sampleRateHz != 48000) { + ++wrongInput; + } + if (id == 3) { + if (selectedFrames && selectedSource.stream() != frame.stream()) { ++sourceIdentityChanges; } + if (frame.discontinuity() || frame.firstSample() != nativeNextSample) { + ++nativeDiscontinuities; + const auto& stream = frame.stream(); + nativeBoundaries.append(QJsonObject{{"first_sample", qint64(frame.firstSample())}, + {"expected_sample", qint64(nativeNextSample)}, {"explicit_discontinuity", frame.discontinuity()}, + {"source", qint64(stream.source)}, {"session", qint64(stream.session)}, + {"format_generation", qint64(stream.formatGeneration)}, + {"receiver_instance", qint64(stream.receiverInstance)}, + {"capture_revision", qint64(rtl::RtlCaptureBackendTestAccess::revision(*source))}}); + std::fprintf(stderr, "NATIVE discontinuity=%d first=%llu expected=%llu frames=%lld\n", + frame.discontinuity(), frame.firstSample(), nativeNextSample, qint64(frame.frameCount())); + } + nativeNextSample = frame.firstSample() + frame.frameCount(); + selectedFrames += frame.frameCount(); selectedSource = frame.epochLease(); + if (!dumpRoot.isEmpty()) { + nativePcm.append(reinterpret_cast(frame.samples().constData()), + frame.samples().size() * qsizetype(sizeof(float))); + } + } + if (id == 7) { otherFrames += frame.frameCount(); } + }); + rtl::RtlCaptureBackendTestAccess::start(*source, std::make_unique(device)); + if (!waitFor([&] { return source->isConnected() && radio.slice(3) && radio.slice(7); })) { + check(false, "actual backend accepts the injected capture and publishes both sparse receivers"); + source->disconnectRadio(); + return 1; + } + // Startup deliberately places FM away from converter DC. Generate the + // independent RF carriers against actual device readback, not requested RF. + const std::uint32_t captureCenterHz = device->hardware.centerHz; + device->iq = captureFixture(captureCenterHz); + const QByteArray fixtureHash = QCryptographicHash::hash(device->iq, QCryptographicHash::Sha256).toHex(); + Ax25ReceiveModel modem(radio); + modem.configure(ax25DemodConfigForProfile(Ax25ModemProfile::Vhf1200)); + modem.setSlice(radio.slice(3)); + modem.setEnabled(true); + radio.setPcAudioEnabled(false); + check(radio.slice(3)->audioGain() == 0 && radio.slice(3)->audioMute() + && radio.slice(7)->audioGain() == 0 && radio.slice(7)->audioMute(), + "both receiver monitors are confirmed muted at zero gain before playback"); + QObject::connect(&modem, &Ax25ReceiveModel::sourceReset, &app, [&] { + ++resets; + const auto& stream = selectedSource.stream(); + std::fprintf(stderr, "RESET count=%d native=%llu converted=%llu source=%llu session=%llu generation=%llu instance=%llu current=%d\n", + resets, selectedFrames, convertedSamples, stream.source, stream.session, + stream.formatGeneration, stream.receiverInstance, selectedSource.current()); + }); + QObject::connect(&modem, &Ax25ReceiveModel::pcmReady, &app, + [&](const DecoderPcmBlock& block, const Ax25ReceiveContext& context) { + if (!context.current() || !block.current() || block.inputSampleRateHz != 48000 + || block.source.stream().sliceId != 3 || context.source.stream() != block.source.stream()) { + ++wrongInput; + } + convertedSamples += block.samples.size(); + if (!dumpRoot.isEmpty()) { + decoderPcm.append(reinterpret_cast(block.samples.constData()), + block.samples.size() * qsizetype(sizeof(float))); + } + }); + QObject::connect(&modem, &Ax25ReceiveModel::frameDecoded, &app, + [&](const Ax25DecodedFrame& frame, const Ax25ReceiveContext& context) { + ++decoded; + check(context.current() && context.source.stream() == selectedSource.stream() + && frame.fcsOk && frame.ax25FrameNoFcs == test::ax25rx::frame(), + "real RTL FM native48 -> selected mono24 model recovers exact AX25 packet and FCS"); + }); + { + std::lock_guard lock(device->mutex); + device->playing = true; + device->changed.notify_all(); + } + bool monitorChanged = false; + const bool completed = waitFor([&] { + if (changeMonitor && !monitorChanged && selectedFrames > 24000) { + monitorChanged = true; + std::fprintf(stderr, "MONITOR change native=%llu converted=%llu resets=%d\n", selectedFrames, convertedSamples, resets); + radio.slice(3)->setAudioGain(50); + radio.slice(3)->setAudioGain(0); + radio.slice(7)->setAudioPan(0); + radio.slice(7)->setAudioPan(100); + radio.setPcAudioEnabled(false); + } + return device->finished.load() && decoded > 0; + }); + const auto diagnostics = rtl::RtlCaptureBackendTestAccess::diagnostics(*source); + check(completed && error.isEmpty(), "paced CU8 capture completes without backend failure"); + check(decoded == 1, "only one exact packet emerges from the selected receiver"); + check(selectedFrames > 24000 && otherFrames > 24000 && convertedSamples > 12000 && !wrongInput, + "both real native48 slices run while only selected slice supplies continuous mono24"); + check(resets == nativeDiscontinuities && (changeMonitor || resets == 1), + "decoder resets exactly for native discontinuities, with one initial reset during steady monitoring-off RX"); + check(sourceIdentityChanges == 0, "monitor controls preserve the selected native source identity"); + check(diagnostics && diagnostics->droppedPackets == 0 && diagnostics->mixerRejectedBlocks == 0 + && diagnostics->mixerConfigurationFailures == 0, + "actual pipeline retains bounded packets and a valid mixer configuration"); + const int streamResets = resets; + source->disconnectRadio(); + check(!selectedSource.current(), "disconnect revokes the actual native source lease"); + if (!dumpRoot.isEmpty()) { + for (const auto& output : {std::pair{QStringLiteral("native48.f32"), nativePcm}, + std::pair{QStringLiteral("decoder24.f32"), decoderPcm}}) { + QFile file(dumpRoot + QLatin1Char('/') + + (changeMonitor ? QStringLiteral("controls-") : QStringLiteral("steady-")) + output.first); + check(file.open(QIODevice::WriteOnly) && file.write(output.second) == output.second.size(), + "requested diagnostic PCM is saved completely"); + } + } + const QJsonObject receipt{{"scenario", changeMonitor ? QStringLiteral("monitor-controls") : QStringLiteral("steady-monitor-off")}, + {"fixture_cu8_sha256", QString::fromLatin1(fixtureHash)}, + {"fixture_cu8_bytes", device->iq.size()}, {"capture_rate_hz", kSampleRate}, + {"capture_center_hz", qint64(captureCenterHz)}, + {"expected_fcs", QStringLiteral("1e9b")}, {"native_rate_hz", 48000}, + {"decoder_rate_hz", 24000}, {"selected_native_frames", qint64(selectedFrames)}, + {"other_native_frames", qint64(otherFrames)}, {"converted_samples", qint64(convertedSamples)}, + {"decoded_exact_packets", decoded}, {"stream_resets_before_disconnect", streamResets}, + {"native_discontinuities", nativeDiscontinuities}, + {"native_boundaries", nativeBoundaries}, {"source_identity_changes", sourceIdentityChanges}, + {"mixer_late_frames", diagnostics ? qint64(diagnostics->mixerLateFrames) : -1}, + {"dropped_packets", diagnostics ? qint64(diagnostics->droppedPackets) : -1}, + {"backend_error", error}, {"failures", failures - initialFailures}}; + std::puts(QJsonDocument(receipt).toJson().constData()); + return failures > initialFailures ? 1 : 0; +} + +int main(int argc, char** argv) +{ + TestSettingsProfile profile(QStringLiteral("rtl-aprs-receive")); + if (!profile.isValid()) { return 1; } + QCoreApplication app(argc, argv); + runScenario(app, false); + runScenario(app, true); + return failures ? 1 : 0; +} diff --git a/tests/rtl_backend_test.cpp b/tests/rtl_backend_test.cpp index 71fea9b84..91d3c8578 100644 --- a/tests/rtl_backend_test.cpp +++ b/tests/rtl_backend_test.cpp @@ -127,12 +127,40 @@ int main(int argc, char** argv) check(!caps.canCreateSlices, "RTL-SDR retains its fixed single receiver in P01"); check(caps.family == "rtl", "capabilities.family is rtl"); check(!caps.canTransmit, "RTL-SDR cannot transmit"); + check(caps.receiveAudioExport && caps.receiveAudioExport->maximumReceivers == caps.maxSlices + && caps.receiveAudioExport->sampleRatesHz.contains(24000) + && caps.receiveAudioExport->sampleRatesHz.contains(48000), + "RTL declares its typed native receive audio export independently of TX/IQ"); + check(!caps.hasDaxStreams,"native receive export does not advertise Flex DAX/IQ streams"); + check(caps.txPowerMaxWatts == 0.0, "RTL-SDR max TX power is 0"); check(!caps.hostModulates, "RTL-SDR does not modulate on host"); check(caps.maxSlices == 1, "RTL-SDR maxSlices is 1"); check(caps.maxPanadapters == 1, "RTL-SDR maxPanadapters is 1"); check(!caps.persistsMemories, "RTL-SDR does not persist memories on device"); +#if (defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_64)) \ + || (defined(Q_OS_MAC) && defined(Q_PROCESSOR_ARM_64)) \ + || (defined(Q_OS_WIN) && defined(Q_PROCESSOR_X86_64)) + qputenv("AETHER_RTL_EVALUATION_RECEIVERS", "8"); + qunsetenv("AETHER_AUTOMATION"); + { rtl::RtlSdrBackend normal; check(normal.capabilities().maxSlices == 1, "eight requires explicit evaluation environment"); } + qputenv("AETHER_AUTOMATION", "1"); + { + rtl::RtlSdrBackend eight; + const auto evaluationCaps = eight.capabilities(); + check(evaluationCaps.maxSlices == 8 && evaluationCaps.canCreateSlices, + "eight-receiver evaluation is advertised"); + check(evaluationCaps.receiveAudioExport && evaluationCaps.receiveAudioExport->maximumReceivers == 8, + "eight-receiver native DAX export is advertised"); + check(!evaluationCaps.canTransmit, "eight-receiver evaluation remains receive-only"); + } + qputenv("AETHER_RTL_EVALUATION_RECEIVERS", "9"); + { rtl::RtlSdrBackend invalid; check(invalid.capabilities().maxSlices == 1, "unsupported evaluation capacity is refused"); } + qunsetenv("AETHER_RTL_EVALUATION_RECEIVERS"); + qunsetenv("AETHER_AUTOMATION"); +#endif + // Check ClientSettingsDomains check(caps.clientSettingsDomains.testFlag(RadioCapabilities::ClientSettingsDomain::Tuning), "ClientSettingsDomain::Tuning declared"); diff --git a/tests/rtl_capture_worker_test.cpp b/tests/rtl_capture_worker_test.cpp index bd499aab1..fe8274cbd 100644 --- a/tests/rtl_capture_worker_test.cpp +++ b/tests/rtl_capture_worker_test.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include using namespace AetherSDR; using T = rtl::RtlCaptureTransaction; @@ -41,7 +43,11 @@ struct RtlCaptureBackendTestAccess { static T::State state(const RtlSdrBackend& backend) { return *backend.m_capture.confirmed(); } static void spectrum(RtlSdrBackend& backend, const QByteArray& frame, T::Token token) { emit backend.m_worker->spectrumFrameReady(token.session, token.revision, 0, frame); } + static void service(RtlSdrBackend& backend) { backend.serviceCapture(); } + static bool stopWorker(RtlSdrBackend& backend) { return backend.m_worker->stopReading(); } static RtlSdrWorker* worker(RtlSdrBackend& backend) { return backend.m_worker.get(); } + static RtlReceivePipeline::Diagnostics diagnostics(const RtlSdrBackend& backend) + { return backend.m_worker->diagnostics(); } }; } static int failures = 0; @@ -60,6 +66,61 @@ template bool waitFor(Predicate predicate, qint64 boundMs = 300 return predicate(); } +static void callbackMeasurement() +{ + auto device = std::make_shared(); + rtl::RtlSdrBackend backend; + rtl::RtlCaptureBackendTestAccess::start(backend, std::make_unique(device), 2); + device->releaseReadback(); + const bool connected = waitFor([&] { return backend.isConnected() && device->starts == 1; }); + check(connected, "callback measurement fixture establishes actual USB-worker acquisition"); + if (!connected) { backend.disconnectRadio(); return; } + // Deliver one block at a time through Device::readAsync, including malformed + // lengths that must be counted without touching their purported samples. + constexpr std::array bytes{16384, 4096, 2, 0, 1, 16385}; + for (const std::uint32_t length : bytes) { + const int expected = device->callbacks + 1; + device->callbackBytes = length; + device->block(); + check(waitFor([&] { return device->callbacks >= expected; }), + "measurement fixture completes each actual callback before changing its length"); + } + check(rtl::RtlCaptureBackendTestAccess::stopWorker(backend), + "callback counters are frozen only after acquisition joins"); + const auto measured = rtl::RtlCaptureBackendTestAccess::diagnostics(backend); + check(measured.callbackCount == 3 && measured.callbackIqSamples == 8192 + 2048 + 1 + && measured.malformedCallbacks == 3, + "valid callback sample totals exclude zero, odd and oversized blocks"); + check(measured.usbReadStarts == static_cast(device->starts.load()) + && measured.usbCancelRequests == static_cast(device->cancels.load()) + && measured.usbCancelRequests > 0, + "worker USB counters count actual read and cancellation API invocations"); + std::uint64_t histogramCount = 0; + std::uint64_t histogramLowerTotal = 0; + std::uint64_t histogramUpperTotal = 0; + for (std::size_t i = 0; i < measured.callbackDurationBuckets.size(); ++i) { + const std::uint64_t count = measured.callbackDurationBuckets[i]; + histogramCount += count; + if (count == 0) { continue; } + const std::uint64_t lower = i == 0 ? 0 : rtl::RtlSdrWorker::kCallbackDurationUpperBoundsNs[i - 1] + 1; + // Exact observed maximum safely bounds even the final overflow bucket. + const std::uint64_t upper = std::min(measured.callbackMaxNs, + rtl::RtlSdrWorker::kCallbackDurationUpperBoundsNs[i]); + check(measured.callbackMaxNs >= lower, + "occupied duration bucket agrees with the measured maximum"); + histogramLowerTotal += lower * count; + histogramUpperTotal += upper * count; + } + check(histogramCount == measured.callbackCount + && measured.callbackTotalNs > 0 && measured.callbackMaxNs > 0 + && measured.callbackTotalNs >= histogramLowerTotal + && measured.callbackTotalNs <= histogramUpperTotal + && measured.callbackTotalNs >= measured.callbackMaxNs + && measured.callbackDeadlineMisses <= measured.callbackCount, + "joined callback histogram, total, maximum and deadline accounting are consistent"); + backend.disconnectRadio(); +} + // A held pointer keeps issuing newer centers before each hardware readback. // Each completed capture must nevertheless publish a fresh, correctly framed // FFT before the next retune, without relaxing non-pan supersession rules. @@ -184,53 +245,94 @@ static void receiverTuneKeepsDisplayAverage() rtl::RtlSdrBackend receiver; rtl::RtlCaptureBackendTestAccess::start(receiver, std::make_unique(device)); device->releaseReadback(); - check(waitFor([&] { return receiver.isConnected(); }), "averaging worker connects"); + const bool connected = waitFor([&] { return receiver.isConnected(); }); + check(connected, "averaging worker connects"); + if (!connected) { receiver.disconnectRadio(); return; } receiver.setPanAverage({}, 100); int frames = 0; float lastDc = -999; + bool captureCorrected = false; + int correctedFrames = 0; + std::optional firstCorrectedDc; + T::Token firstCorrectedToken; QObject::connect(&receiver, &IRadioBackend::spectrumFrameReady, [&](int, const QByteArray& frame, const SpectrumCoverage&) { ++frames; std::memcpy(&lastDc, frame.constData() + frame.size() / 2, sizeof(lastDc)); + const T::State accepted = rtl::RtlCaptureBackendTestAccess::state(receiver); + if (captureCorrected && accepted.dcSuppression) { + ++correctedFrames; + if (!firstCorrectedDc) { + firstCorrectedDc = lastDc; + firstCorrectedToken = accepted.token; + } + } }); - // Every step below asserts on the FIRST window after an adoption, so no - // block may still be queued when a change adopts: a queued block would - // join the post-adoption window and shift which window the next frame - // is. block() only queues; the device thread consumes on its own time, - // which a sanitizer build slows enough to leave a backlog. So feed in - // lockstep, one consumed block at a time. - const auto feedOne = [&] { - const int before = device->callbacks; + + // Grant only one callback and wait for its completion before granting the + // next. Polling predicates must not enqueue work: waitFor evaluates its + // predicate again on return, and DeviceState retains permits across USB + // restarts. Bursts also outrun native WFM's nonblocking WDSP output ring. + const unsigned long callbackMicroseconds = static_cast(std::ceil( + 1.0e6 * (device->callbackBytes.load() / 2.0) / 2'400'000)); + const auto clockBlock = [&] { + { + std::lock_guard lock(device->mutex); + check(device->blocks == 0, "averaging callback clock has no queued permits"); + if (device->blocks != 0) { return false; } + } + const int completed = device->callbacks + 1; device->block(); - return waitFor([&] { return device->callbacks > before; }); + const bool finished = waitFor([&] { return device->callbacks >= completed; }); + check(finished, "averaging callback clock waits for actual completion"); + if (!finished) { return false; } + QThread::usleep(callbackMicroseconds); + // Publish an acknowledged adoption on its owner thread before another + // callback can complete its first FFT under an unpublished revision. + rtl::RtlCaptureBackendTestAccess::service(receiver); + QCoreApplication::processEvents(); + const bool exact = device->callbacks == completed; + check(exact, "averaging callback clock delivers exactly one block"); + return exact; }; - // Adopt a change at a real callback boundary: one consumed block per pass - // until the backend has published the new revision. - const auto adopt = [&] { - return waitFor([&] { feedOne(); return !rtl::RtlCaptureBackendTestAccess::busy(receiver); }); + const auto clockBlocks = [&](int count) { + for (int i = 0; i < count; ++i) { + if (!clockBlock()) { return false; } + } + return true; }; - // The first accepted frame after the latest adoption, one block at a time. - const auto nextFrame = [&] { - const int before = frames; - return waitFor([&] { - if (frames > before) { return true; } - feedOne(); QCoreApplication::processEvents(); - return frames > before; - }); + const auto adoptWithClock = [&] { + QElapsedTimer timer; + timer.start(); + while (rtl::RtlCaptureBackendTestAccess::busy(receiver) && timer.elapsed() < 3000) { + if (!clockBlock()) { return false; } + } + return timer.elapsed() < 3000 && !rtl::RtlCaptureBackendTestAccess::busy(receiver); }; - check(nextFrame(), "averaging worker seeds a complete observation"); + + if (!clockBlocks(8)) { receiver.disconnectRadio(); return; } + check(waitFor([&] { return frames > 0; }), "averaging worker seeds a complete observation"); const int writes = device->writes; - device->iqLevel = 190; receiver.setSliceFrequency(0, 100'100'000); - check(adopt(), "receiver-only tune adopts through the actual USB callback"); - check(nextFrame(), "tuned receiver emits new accepted FFT"); + const bool tuned = adoptWithClock(); + check(tuned, "receiver-only tune adopts through the actual USB callback"); + if (!tuned) { receiver.disconnectRadio(); return; } + // Keep the weak seed unchanged while asynchronous preparation runs. Its + // duration must not age the old average with the stronger test input. + device->iqLevel = 190; + const int beforeFrames = frames; + if (!clockBlocks(8)) { receiver.disconnectRadio(); return; } + check(waitFor([&] { return frames > beforeFrames; }), "tuned receiver emits new accepted FFT"); const double raw = 20 * std::log10(std::sqrt(2.) * (190 - 127.5) / 127.5 * .35875 * (65535. / 65536)); check(device->writes == writes && lastDc < raw - 8, "receiver-only adoption preserves display smoothing with unchanged hardware"); receiver.setPanRfGain({}, 25); - check(adopt(), "gain change adopts through verified hardware path"); - check(nextFrame(), "new gain emits complete frame"); + check(waitFor([&] { return !rtl::RtlCaptureBackendTestAccess::busy(receiver); }), + "gain change adopts through verified hardware path"); + const int beforeGainFrames = frames; + if (!clockBlocks(8)) { receiver.disconnectRadio(); return; } + check(waitFor([&] { return frames > beforeGainFrames; }), "new gain emits complete frame"); check(std::abs(lastDc - raw) < .02, "gain transition discards incompatible amplitude history"); const T::State beforePpm = rtl::RtlCaptureBackendTestAccess::state(receiver); @@ -257,16 +359,35 @@ static void receiverTuneKeepsDisplayAverage() rtl::RtlCaptureBackendTestAccess::spectrum(receiver, obsolete, afterPpm.token); check(frames == beforePpmFrames + 1, "PPM adoption accepts the same spectrum payload with the current token"); - check(nextFrame(), "PPM change emits complete observation"); + const int afterPpmControlFrames = frames; + if (!clockBlocks(8)) { receiver.disconnectRadio(); return; } + check(waitFor([&] { return frames > afterPpmControlFrames; }), "PPM change emits complete observation"); const double weak = 20 * std::log10(std::sqrt(2.) * (130 - 127.5) / 127.5 * .35875 * (65535. / 65536)); check(rtl::RtlCaptureBackendTestAccess::state(receiver).hardware.ppm == 1 && std::abs(lastDc - weak) < .02, "PPM change cannot reinterpret the previous RF history"); device->iqLevel = 190; + captureCorrected = true; // Arm before intent; never substitute a later FFT. receiver.invokeExtension("rtl", "dc_suppression.set", 2, true); - check(adopt(), "DC correction adopts on a real callback boundary"); - check(nextFrame(), "DC correction emits complete observation"); + const bool correctedAdopted = adoptWithClock(); + check(correctedAdopted, "DC correction adopts on a real callback boundary"); + if (!correctedAdopted) { receiver.disconnectRadio(); return; } + const T::State afterDc = rtl::RtlCaptureBackendTestAccess::state(receiver); + const int afterDcCallbacks = device->callbacks; + check(afterDc.dcSuppression && afterDc.token.session == afterPpm.token.session + && afterDc.token.revision > afterPpm.token.revision && correctedFrames == 0, + "DC adoption publishes before its first complete corrected window"); + // Adoption processes one 8192-sample block. Six more still cannot fill + // the 65536-sample FFT; precisely the seventh completes its first window. + if (!clockBlocks(6)) { receiver.disconnectRadio(); return; } + check(correctedFrames == 0, "DC correction cannot publish a partial first window"); + if (!clockBlock()) { receiver.disconnectRadio(); return; } + check(waitFor([&] { return firstCorrectedDc.has_value(); }), "DC correction emits complete observation"); + check(correctedFrames == 1 && firstCorrectedToken == afterDc.token + && rtl::RtlCaptureBackendTestAccess::state(receiver).token == afterDc.token + && device->callbacks == afterDcCallbacks + 7, + "DC observation is exactly the first complete window of the accepted revision"); const double pole = std::exp(-2 * std::numbers::pi * rtl::RtlDcBlocker::kCornerHz / 2'400'000); double correctedWindowGain = 0; for (int i = 0; i < 65536; ++i) { @@ -277,17 +398,107 @@ static void receiverTuneKeepsDisplayAverage() const double corrected = 20 * std::log10(std::sqrt(2.) * (190 - 127.5) / 127.5 * (1 + pole) / 2 * correctedWindowGain); check(rtl::RtlCaptureBackendTestAccess::state(receiver).dcSuppression - && std::abs(lastDc - corrected) < .03, + && firstCorrectedDc && std::abs(*firstCorrectedDc - corrected) < .03, "DC mode change discards old estimates and shows the actual first corrected window"); + const auto measured = rtl::RtlCaptureBackendTestAccess::diagnostics(receiver); + check(std::ranges::all_of(measured.receiverWithdrawals, [](std::uint64_t count) { return count == 0; }), + "paced averaging fixture never withdraws a native receiver for repair"); + { + std::lock_guard lock(device->mutex); + check(device->blocks == 0, "averaging fixture finishes without queued callback permits"); + } receiver.disconnectRadio(); } +static void rapidMonitorEditsKeepSpectrum(bool metersEnabled) +{ + auto device = std::make_shared(); + rtl::RtlSdrBackend backend; + rtl::RtlCaptureBackendTestAccess::start(backend, std::make_unique(device), 2); + device->releaseReadback(); + if (!waitFor([&] { return backend.isConnected(); })) { + check(false, "monitor averaging fixture connects"); + backend.disconnectRadio(); return; + } + backend.setPanAverage({}, 100); + backend.invokeExtension("rtl", "receive_meters.set", 1, metersEnabled); + const auto clockBlock = [&] { + const int completed = device->callbacks + 1; + device->block(); + check(waitFor([&] { return device->callbacks == completed; }), "monitor callback completes"); + QThread::usleep(3500); + rtl::RtlCaptureBackendTestAccess::service(backend); + QCoreApplication::processEvents(); + }; + check(backend.createSlice({}, 100'200'000), "monitor fixture admits independent second slice"); + for (int i = 0; i < 200 && rtl::RtlCaptureBackendTestAccess::busy(backend); ++i) { clockBlock(); } + const T::State before = rtl::RtlCaptureBackendTestAccess::state(backend); + check(before.receivers.size() == 2, "monitor fixture publishes both receivers"); + int frames = 0; + float lastDc = -999; + // Observe production worker frames before publication fencing: superseded + // tokens must still be rejected by the backend, but RF cadence must survive. + QObject::connect(rtl::RtlCaptureBackendTestAccess::worker(backend), + &rtl::RtlSdrWorker::spectrumFrameReady, &backend, + [&](quint64, quint64, int, const QByteArray& frame) { + ++frames; + std::memcpy(&lastDc, frame.constData() + frame.size() / 2, sizeof(lastDc)); + }); + for (int i = 0; i < 24; ++i) { clockBlock(); } + const int seededFrames = frames; + const int writes = device->writes; + const int starts = device->starts; + device->iqLevel = 190; + for (int i = 0; i < 96; ++i) { + if (i % 4 == 0) { + const int id = (i / 4) % 2; + // The second request supersedes the first before its callback; + // this exercises real compensation, not a sequential setter test. + backend.setSliceAudioGain(id, 20); + backend.setSliceAudioGain(id, 60 + id); + backend.setSliceAudioPan(id, 30 + id); + backend.setSliceAudioMute(id, true); + } + clockBlock(); + } + check(frames - seededFrames >= 8, + "rapid independent monitor edits preserve continuous FFT cadence through compensation"); + const double raw = 20 * std::log10(std::sqrt(2.) * (190 - 127.5) / 127.5 + * .35875 * (65535. / 65536)); + for (int i = 0; i < 200 && rtl::RtlCaptureBackendTestAccess::busy(backend); ++i) { clockBlock(); } + const int framesBeforeFinal = frames; + for (int i = 0; i < 12; ++i) { clockBlock(); } + check(frames > framesBeforeFinal && lastDc < raw - 3, + "rapid monitor edits retain accumulated averaging instead of reseeding raw power"); + const T::State after = rtl::RtlCaptureBackendTestAccess::state(backend); + check(!rtl::RtlCaptureBackendTestAccess::busy(backend) + && after.receivers.size() == 2 && after.receivers[0].audioGain == 60 + && after.receivers[1].audioGain == 61 && after.receivers[0].audioPan == 30 + && after.receivers[1].audioPan == 31 && after.receivers[0].audioMute + && after.receivers[1].audioMute && after.capture == before.capture + && after.hardware == before.hardware && device->writes == writes && device->starts == starts, + "latest per-slice monitor intent is confirmed without hardware writes or USB restart"); + const double retainedDb = raw - lastDc; + int staleFrames = 0; + QObject::connect(&backend, &IRadioBackend::spectrumFrameReady, + [&](int, const QByteArray&) { ++staleFrames; }); + rtl::RtlCaptureBackendTestAccess::spectrum(backend, + QByteArray(65536 * int(sizeof(float)), '\0'), before.token); + check(staleFrames == 0, "retaining RF history never admits obsolete revision output"); + std::printf("monitor_average meters=%d frames=%d retained_db=%.3f\n", + metersEnabled, frames - seededFrames, retainedDb); + backend.disconnectRadio(); +} + int main(int argc, char** argv) { QCoreApplication app(argc, argv); + callbackMeasurement(); sustainedPanProgress(false); sustainedPanProgress(true); receiverTuneKeepsDisplayAverage(); + rapidMonitorEditsKeepSpectrum(true); + rapidMonitorEditsKeepSpectrum(false); deferredPanCannotOutliveNewerIntent(); auto state = std::make_shared(); rtl::RtlSdrBackend backend; diff --git a/tests/rtl_device_settings_test.cpp b/tests/rtl_device_settings_test.cpp index 41d6c1fe3..bc9b7aa1a 100644 --- a/tests/rtl_device_settings_test.cpp +++ b/tests/rtl_device_settings_test.cpp @@ -40,6 +40,13 @@ int main(int argc, char** argv) check(Settings(first).saveAccepted({-12, true}, reason), "update accepted controls"); check(first.featureExact(Settings::featureName()).value("futureField").toObject().value("keep").toInt() == 123, "unknown members survive read-modify-write"); check(Settings(first).load().values == Settings::Values{-12, true}, "DC and PPM roundtrip independently"); + check(Settings(first).load().values.receiveMetersEnabled, "legacy settings default meters to enabled"); + check(Settings(first).saveAccepted({-12, true, false}, reason), "meter disable saves through device settings owner"); + check(!Settings(first).load().values.receiveMetersEnabled && Settings(second).load().values.receiveMetersEnabled, + "meter setting roundtrips per serial without leaking into another radio"); + Settings::Values valuesForInvalidMeter; + check(!Settings::decode({{"ppm", 0}, {"dcSuppression", false}, {"receiveMetersEnabled", "false"}}, valuesForInvalidMeter), + "meter setting refuses non-boolean values"); const QJsonObject future{{"privateFutureState", 42}}; check(first.setFeature(Settings::featureName(), 99, future), "seed future schema"); check(Settings(first).load().status == Settings::ReadStatus::Refused, "future document is not reinterpreted as current controls"); diff --git a/tests/rtl_model_acceptance_test.cpp b/tests/rtl_model_acceptance_test.cpp index 49e66f8b1..43324c102 100644 --- a/tests/rtl_model_acceptance_test.cpp +++ b/tests/rtl_model_acceptance_test.cpp @@ -121,11 +121,236 @@ class CaptureClock { qint64 m_nextBlockMs = 0; }; +static std::uint64_t monotonicMs() +{ + return std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); +} + +static void analogBankRestoreAndSharedCapture() +{ + using Access = rtl::RtlCaptureBackendTestAccess; + const RadioSettingsScope scope("rtl", "analog-bank-restore"); + QVector saved; + for (int id = 0; id < 8; ++id) { + RtlSliceSettings::Slice receiver; + receiver.id = id; + receiver.frequencyHz = 99'400'000 + id * 200'000; + receiver.mode = id < 2 ? QStringLiteral("WFM") : QStringLiteral("FMN"); + receiver.filterLowHz = id < 2 ? -90000 : -8000; + receiver.filterHighHz = -receiver.filterLowHz; + receiver.wfmDeemphasisUs = id == 0 ? 50 : 75; + receiver.wfmForceMono = id == 0; + receiver.audioGain = 40 + id * 5; + receiver.audioPan = id * 12; + receiver.audioMute = id == 3; + receiver.squelchEnabled = id >= 2; + receiver.automaticSquelch = id >= 2; + receiver.automaticSquelchMarginDb = 5 + id; + saved.append(receiver); + } + check(RtlSliceSettings(scope).patch(100'000'000, 2'400'000, saved), "eight analog saved recipes seeded"); + RadioModel model; + check(model.rebuildBackendForTest("rtl"), "restore bank model initialized"); + RadioModelSliceLifecycleTestAccess::restore(model, "analog-bank-restore"); + auto& backend = *static_cast(model.backend()); + auto device = std::make_shared(); + CaptureClock clock(device); + Access::start(backend, std::make_unique(device), 8); + device->releaseReadback(); + const auto settled = [&] { clock.advance(); return Access::idle(backend); }; + check(waitFor([&] { return settled() && model.slice(7); }), "eight analog saved receivers adopted"); + if (!model.slice(7)) { backend.disconnectRadio(); return; } + for (int id = 0; id < 8; ++id) { + const auto* slice = model.slice(id); + check(slice && slice->mode() == saved[id].mode && slice->frequency() * 1e6 == saved[id].frequencyHz + && slice->audioGain() == saved[id].audioGain && slice->audioPan() == saved[id].audioPan + && slice->audioMute() == saved[id].audioMute && slice->filterLow() == saved[id].filterLowHz + && slice->automaticSquelch() == saved[id].automaticSquelch + && slice->automaticSquelchMarginDb() == saved[id].automaticSquelchMarginDb, + "restore retains each stable receiver recipe and monitor settings"); + } + check(model.slice(0)->wfmForceMono() && model.slice(0)->wfmDeemphasisUs() == 50 + && !model.slice(1)->wfmForceMono() && model.slice(1)->wfmDeemphasisUs() == 75, + "restore retains distinct analog stereo and deemphasis policies"); + model.slice(2)->setActive(true); + const auto before = Access::state(backend); + backend.invokeExtension("rtl", "ppm.set", 800, -16); + backend.invokeExtension("rtl", "ppm.set", 801, -17); + check(waitFor([&] { return settled() && Access::state(backend).hardware.ppm == -17; }), + "coalesced PPM edit reaches the entire eight-receiver capture"); + check(Access::state(backend).receivers == before.receivers && model.slice(2)->isActive(), + "shared PPM changes preserve every recipe and selection"); + const auto valid = Access::state(backend); + backend.invokeExtension("rtl", "sample_rate.set", 802, 0); + check(Access::idle(backend) && Access::state(backend).token == valid.token, + "invalid capture rate refuses without altering eight receivers"); + device->failWriteAt = device->writes + 1; + backend.invokeExtension("rtl", "ppm.set", 803, -18); + check(waitFor([&] { return settled(); }) && backend.isConnected() + && Access::state(backend).hardware.ppm == -17 + && Access::state(backend).receivers == before.receivers && model.slice(2)->isActive(), + "failed PPM application compensates all eight recipes without moving focus"); + backend.disconnectRadio(); +} + +static void analogBankSelection(int capacity = 4) +{ + using Access = rtl::RtlCaptureBackendTestAccess; + RadioModel model; + check(model.rebuildBackendForTest("rtl"), "analog bank model initialized"); + auto& backend = *static_cast(model.backend()); + auto device = std::make_shared(); + CaptureClock clock(device); + Access::start(backend, std::make_unique(device), capacity); + device->releaseReadback(); + const auto settled = [&] { clock.advance(); return Access::idle(backend); }; + check(waitFor([&] { return settled() && model.slice(0); }), "analog bank initial receiver adopted"); + if (!model.slice(0)) { backend.disconnectRadio(); return; } + const auto selected = [&](int id) { + int count = 0; + for (int slot = 0; slot < 8; ++slot) { + if (const auto* slice = model.slice(slot); slice && slice->isActive()) { + ++count; + if (slot != id) { return false; } + } + } + return count == 1; + }; + check(selected(0), "bootstrap selects exactly one stable receiver"); + for (int id = 1; id < capacity; ++id) { + check(backend.createSlice({}, 99'500'000 + id * 200'000), "analog sibling creation admitted"); + check(waitFor([&] { return settled() && model.slice(id); }), "analog sibling creation adopted"); + if (!model.slice(id)) { backend.disconnectRadio(); return; } + model.slice(id)->setActive(true); + check(selected(id), "selection clears every previous active model"); + } + check(!backend.createSlice({}, 100'650'000), "receiver above configured capacity refused at evaluation capacity"); + check(waitFor([&] { + clock.advance(); + for (int id = 0; id < capacity; ++id) { + if (!model.meterModel().relativeLevelForSlice(id)) { return false; } + } + return true; + }), "real injected IQ reaches relative RF meters for every stable receiver ID"); + for (int id = 0; id < capacity; ++id) { + check(!model.meterModel().sLevelForSlice(id), "RTL never claims a calibrated S reading"); + } + + for (int id = 0; id < capacity; ++id) { + model.slice(id)->setAutomaticSquelch(true, 5 + id); + model.slice(id)->setAudioMute(id % 2); + } + check(waitFor([&] { return settled() && model.slice(capacity - 1)->automaticSquelch(); }), + "all receiver Auto requests adopt"); + for (int id = 0; id < capacity; ++id) { + check(model.slice(id)->automaticSquelchMarginDb() == 5 + id + && model.slice(id)->audioMute() == bool(id % 2), "SQL and mute stay per receiver"); + } + const auto beforeFocus = Access::state(backend); + const int beforeFocusWrites = device->writes; + for (int iteration = 0; iteration < 32; ++iteration) { + const int id = iteration % capacity; + model.slice(id)->setActive(true); + check(selected(id), "rapid focus retains one exact model identity"); + } + check(Access::state(backend).token == beforeFocus.token && device->writes == beforeFocusWrites + && Access::state(backend).receivers == beforeFocus.receivers, + "focus changes no capture revision, hardware, monitor or receiver recipe"); + model.slice(3)->setActive(true); // start the reentrant scenario on its observed source + bool redirected = false; + const auto redirect = QObject::connect(model.slice(3), &SliceModel::activeChanged, &model, + [&](bool active) { + if (!active && !redirected) { redirected = true; model.slice(2)->setActive(true); } + }); + model.slice(1)->setActive(true); + check(redirected && selected(2), "reentrant newer selection supersedes the old publication tail"); + QObject::disconnect(redirect); + model.slice(3)->setActive(true); + model.slice(1)->setMode("WFM"); + check(waitFor([&] { return settled() && model.slice(1)->mode() == "WFM"; }), + "WFM admitted beside three native analog receivers"); + model.slice(2)->setMode("WFM"); + check(waitFor([&] { return settled() && model.slice(2)->mode() == "WFM"; }), + "second WFM receiver independently adopted"); + model.slice(1)->setWfmForceMono(true); + model.slice(1)->setWfmDeemphasis(50); + model.slice(1)->setFilterWidth(-90000, 90000); + model.slice(2)->setWfmForceMono(false); + model.slice(2)->setWfmDeemphasis(75); + model.slice(2)->setFilterWidth(-80000, 80000); + check(waitFor([&] { return settled() && model.slice(1)->filterLow() == -90000 + && model.slice(2)->filterLow() == -80000; }), "two independent WFM recipes adopt"); + check(model.slice(1)->wfmForceMono() + && model.slice(1)->wfmDeemphasisUs() == 50 + && !model.slice(2)->wfmForceMono() + && model.slice(2)->wfmDeemphasisUs() == 75 && selected(3), + "WFM recipes and focus survive sibling publications"); + const auto beforeRefusal = Access::state(backend); + int warnings = 0; + QObject::connect(&backend, &IRadioBackend::configurationWarning, &model, [&](const QString&) { ++warnings; }); + model.slice(0)->setMode("AM"); + check(Access::idle(backend) && Access::state(backend).token == beforeRefusal.token + && model.slice(0)->mode() == "FM" && model.slice(1)->wfmForceMono() + && warnings >= 1, "unsupported legacy combinations refuse without altering recipes"); + model.slice(2)->setActive(true); + auto* pan = model.panadapter(model.slice(2)->panId()); + check(pan != nullptr, "multi receiver pan materialized"); + if (pan) { + model.requestPanCenter(pan->panId(), 105.0, -1.0, IRadioBackend::PanCenterIntent::Drag); + check(waitFor([&] { return settled() && Access::state(backend).receivingIds.empty(); }), + "all configured analog receivers can park together"); + for (int id = 0; id < capacity; ++id) { + check(!model.meterModel().relativeLevelForSlice(id), "parking withdraws live RF meter samples"); + } + + check(selected(2) && model.slice(1)->wfmDeemphasisUs() == 50 + && !model.slice(1)->wfmReceptionDiagnostics().valid, + "parked selection and recipe survive while reception clears"); + model.requestPanCenter(pan->panId(), 100.0, -1.0, IRadioBackend::PanCenterIntent::Drag); + check(waitFor([&] { return settled() && Access::state(backend).receivingIds.size() == static_cast(capacity); }), + "all configured analog receivers resume at their configured RF"); + } + QPointer retired = model.slice(2); + QObject::connect(&backend, &IRadioBackend::sliceRemoved, &model, [&](int id) { + if (id == 2 && retired) { QCoreApplication::removePostedEvents(retired, QEvent::DeferredDelete); } + }); + check(backend.removeSlice(2), "selected middle slot removal admitted"); + check(waitFor([&] { return settled() && !model.slice(2); }), "selected middle slot removal adopted"); + check(selected(0), "removed selection resolves deterministically to a surviving identity"); + check(model.meterModel().findMeter("SLC", "LEVEL", 2) == -1, + "removed receiver withdraws its meter definition"); + + check(backend.createSlice({}, 100'200'000), "middle stable slot reuse admitted"); + check(waitFor([&] { return settled() && model.slice(2); }), "middle stable slot replacement adopted"); + check(retired && model.slice(2) != retired && selected(0), "replacement never inherits retired selection"); + if (retired) { + int staleFocusEdges = 0; + QObject::connect(retired, &SliceModel::activeChanged, &model, + [&](bool) { ++staleFocusEdges; }); + retired->setActive(true); + check(selected(0) && staleFocusEdges == 0, + "retired selection cannot emit a UI focus edge or target a reused numeric slot"); + retired->deleteLater(); + } + const auto disconnectDuringSelect = QObject::connect(model.slice(0), &SliceModel::activeChanged, + &model, [&](bool active) { if (!active) { backend.disconnectRadio(); } }); + model.slice(1)->setActive(true); + check(!backend.isConnected() && !model.slice(1)->isActive(), + "reentrant disconnect cancels the unaccepted selection tail"); + check(!model.meterModel().relativeLevelForSlice(0), "disconnect clears relative meter samples"); + QObject::disconnect(disconnectDuringSelect); + backend.disconnectRadio(); +} + int main(int argc, char** argv) { TestSettingsProfile profile(QStringLiteral("rtl-model-acceptance")); if (!profile.isValid()) { return 1; } QCoreApplication app(argc, argv); AppSettings::instance().load(); + analogBankSelection(); + analogBankSelection(8); + analogBankRestoreAndSharedCapture(); const RadioSettingsScope scope("rtl", "model-accepted"); RtlSliceSettings::Slice saved; saved.id = 3; saved.frequencyHz = 99'900'000; saved.mode = "FM"; @@ -200,6 +425,23 @@ int main(int argc, char** argv) check(RtlSliceSettings(scope).load().document.slices[3].squelchEnabled && RtlSliceSettings(scope).load().document.slices[3].squelchLevel == 61, "only accepted squelch threshold reaches persistence"); + slice->setAutomaticSquelch(true, 12); + check(!slice->automaticSquelch(), "pending Auto intent is not published optimistically"); + check(waitFor([&] { captureClock.advance(); return rtl::RtlCaptureBackendTestAccess::idle(backend) + && slice->automaticSquelch(); }), "Auto command adopts on sparse stable receiver"); + check(slice->automaticSquelchMarginDb() == 12 && slice->squelchLevel() == 61, + "Auto margin is distinct from preserved manual threshold"); + RadioModelSliceLifecycleTestAccess::flush(model); + const auto sqlSaved = RtlSliceSettings(scope).load().document.slices[3]; + check(sqlSaved.automaticSquelch && sqlSaved.automaticSquelchMarginDb == 12, + "accepted per-receiver Auto persists"); + slice->setAutomaticSquelch(true, 100); + check(rtl::RtlCaptureBackendTestAccess::idle(backend) && slice->automaticSquelchMarginDb() == 12, + "invalid Auto margin is refused"); + slice->setSquelch(true, 61); + check(waitFor([&] { captureClock.advance(); return rtl::RtlCaptureBackendTestAccess::idle(backend) + && !slice->automaticSquelch(); }), "same manual threshold still exits Auto"); + RadioModelSliceLifecycleTestAccess::flush(model); const auto accepted = scope.featureExact("RtlSlices"); const int refusedBefore = observed; slice->setFilterWidth(9000, -9000); slice->setMode("not-a-mode"); diff --git a/tests/rtl_receive_pipeline_test.cpp b/tests/rtl_receive_pipeline_test.cpp index ab5973b07..7ccd731dd 100644 --- a/tests/rtl_receive_pipeline_test.cpp +++ b/tests/rtl_receive_pipeline_test.cpp @@ -24,6 +24,54 @@ namespace AetherSDR::rtl { struct RtlReceivePipelineTestAccess { // Observation seam only: the receiver supplies a fixed failure; this does // not claim to reproduce the underlying WDSP underrun. + static bool rfObservation(RtlReceivePipeline& pipeline) + { + pipeline.m_token = {19, 23}; + pipeline.m_capture.achievedSampleRateHz = 2048000; + pipeline.m_capture.centerHz = 100000000; + pipeline.m_receivingMask = 0xff; + std::array bins; bins.fill(-120); + for (int id = 0; id < 8; ++id) { + auto& band = pipeline.m_meterPassbands[id]; + band.stableId = id; band.carrierHz = 100000000 + (id - 4) * 100000; + band.filterLowHz = -1000; band.filterHighHz = 1000; + bins[1024 + (id - 4) * 100] = -20 - 7 * id; + } + pipeline.observeSpectrum(bins, 0); + RtlReceivePipeline::RfObservation observation; + if (!pipeline.takeRfObservation(observation) || observation.mask != 0xff + || observation.token != pipeline.m_token || !observation.producedMs) { return false; } + for (int id = 0; id < 8; ++id) { + if (observation.dbfs[id] != -20 - 7 * id) { return false; } + } + // Sparse membership never renumbers receivers. Removed/parked IDs have + // no observation; invalid input invalidates only its own passband. + pipeline.m_receivingMask = 0x89; + bins[1024 + 300] = std::numeric_limits::quiet_NaN(); + pipeline.m_token.revision++; + pipeline.observeSpectrum(bins, 4096); + if (!pipeline.takeRfObservation(observation) || observation.mask != 0x09 + || observation.token != pipeline.m_token || observation.dbfs[3] != -41) { return false; } + pipeline.m_receivingMask = 0xff; + bins[1324] = -33; + pipeline.observeSpectrum(bins, 8192); + if (!pipeline.takeRfObservation(observation) || observation.mask != 0xff + || observation.dbfs[7] != -33 || pipeline.takeRfObservation(observation)) { return false; } + pipeline.observeSpectrum(bins, 9000); // leave an old sample queued + pipeline.setReceiveMetersEnabled(false); + const auto write = pipeline.m_rfWrite.load(); + pipeline.observeSpectrum(bins, 10000); + if (pipeline.m_rfWrite.load() != write || pipeline.takeRfObservation(observation) + || !pipeline.m_spectrumFresh || pipeline.m_spectrumFirstSample != 10000) { return false; } + pipeline.setReceiveMetersEnabled(true); + if (pipeline.takeRfObservation(observation)) { return false; } + pipeline.observeSpectrum(bins, 11000); + pipeline.setReceiveMetersEnabled(false); + pipeline.setReceiveMetersEnabled(true); + if (pipeline.takeRfObservation(observation)) { return false; } // rapid off/on rejects queued sample + pipeline.observeSpectrum(bins, 12000); + return pipeline.takeRfObservation(observation) && observation.mask == 0xff; + } static bool traceObservation(RtlReceivePipeline& pipeline) { struct FailingReceiver final : RtlReceiverRegistry::Receiver { @@ -83,7 +131,8 @@ struct RtlReceivePipelineTestAccess { RtlReceivePipeline::Packet packet; if (!pipeline.takePacket(packet) || packet.slot != -1 || packet.firstSample != 100 || packet.frames != 128 || packet.samples[0] != 0 || packet.samples[2] != 0.25f || packet.samples[254] != 0 - || pipeline.takePacket(packet) || pipeline.diagnostics().mixerLateFrames != 2) { return false; } + || pipeline.takePacket(packet) || pipeline.diagnostics().mixerLateFrames != 2 + || pipeline.diagnostics().receiverWithdrawals[3] != 1) { return false; } // Fill the independent trace ring, then partially drain and wrap it. // Every accepted event must survive in FIFO order; only new ones drop. @@ -210,12 +259,177 @@ struct RtlReceivePipelineTestAccess { && diagnostic.mixerRejectedBlocks == 0 && diagnostic.droppedPackets == 0 && diagnostic.mixerConfigurationFailures == 0 && !pipeline.needsRepair(); } + struct MixerEpochPrefixResult { + bool configured = false; + bool retainedTap = false; + bool retiredPrefix = false; + bool crossingTap = false; + bool crossingSpeaker = false; + bool currentTap = false; + bool currentSpeaker = false; + bool lateRejected = false; + bool emptyRejected = false; + bool nonFiniteRejected = false; + bool bounded = false; + }; + // Reproduce the retained WFM block [383745, 384001) at the speaker + // epoch boundary observed when adding two narrow receivers. Inject only + // decoded PCM; publication, mixer validation and both queues remain real. + // The generated-RF integration separately exercises actual bank adoption. + static MixerEpochPrefixResult mixerEpochPrefix(RtlReceivePipeline& pipeline) + { + MixerEpochPrefixResult result; + pipeline.m_legacy = false; pipeline.m_token = {47, 4}; pipeline.m_captureEpoch = 18; + pipeline.m_capture = {47, 1, 100000000, 2400000, 1080000, 1080000}; + pipeline.m_mixerOrigin = 384001; + RtlReceiverRegistry::ReceiverSpec spec; + spec.handle = {47, 93, 3}; spec.epoch = 11; spec.passband.stableId = 3; + spec.dsp.wbfmReceive.emplace(); // already-decoded stereo bypasses narrow-FM squelch + const std::array inputs{{{3, 93, 11}}}; + result.configured = pipeline.m_mixer.configure(47, 18, inputs, 384001); + if (!result.configured) { return result; } + const auto takeAudio = [&](int slot, std::uint64_t first, + std::span left, std::span right, + bool discontinuity) { + RtlReceivePipeline::Packet packet; + if (!pipeline.takePacket(packet) || packet.slot != slot || packet.firstSample != first + || packet.frames != left.size() || packet.token != RtlCaptureTransaction::Token{47, 4} + || packet.captureEpoch != 18 || packet.discontinuity != discontinuity + || (slot >= 0 && (packet.instance != 93 || packet.receiverEpoch != 11))) { return false; } + for (std::size_t i = 0; i < left.size(); ++i) { + if (packet.samples[2 * i] != left[i] || packet.samples[2 * i + 1] != right[i]) { return false; } + } + return true; + }; + RtlReceivePipeline::Packet extra; + std::array left{}, right{}; + for (std::size_t i = 0; i < left.size(); ++i) { + left[i] = 0.125f + static_cast(i) / 2048; + right[i] = -0.375f - static_cast(i) / 2048; + } + pipeline.audioBlock(spec, 383745, left, right, false); + pipeline.m_mixer.drain(384129, pipeline); + result.retainedTap = takeAudio(3, 383745, left, right, false); + result.retiredPrefix = pipeline.m_mixer.rejectedBlocks() == 0 + && pipeline.m_mixer.nextSample() == 384001 && !pipeline.takePacket(extra); + + // The first half predates the new epoch; only the second half belongs + // in its first speaker quantum. The independent tap keeps both halves. + pipeline.audioBlock(spec, 383873, left, right, false); + pipeline.m_mixer.drain(384129, pipeline); + result.crossingTap = takeAudio(3, 383873, left, right, false); + result.crossingSpeaker = takeAudio(-1, 384001, std::span(left).subspan(128), + std::span(right).subspan(128), true) && !pipeline.takePacket(extra); + + std::array currentLeft{}, currentRight{}; + currentLeft.fill(0.5f); currentRight.fill(-0.25f); + pipeline.audioBlock(spec, 384129, currentLeft, currentRight, false); + pipeline.m_mixer.drain(384257, pipeline); + result.currentTap = takeAudio(3, 384129, currentLeft, currentRight, false); + result.currentSpeaker = takeAudio(-1, 384129, currentLeft, currentRight, false) + && pipeline.m_mixer.nextSample() == 384257 && !pipeline.takePacket(extra); + + // This block belongs to the current epoch but its quantum has already + // played. Comparing against the moving mixer cursor would conceal it. + const std::uint64_t beforeLate = pipeline.m_mixer.rejectedBlocks(); + pipeline.audioBlock(spec, 384129, currentLeft, currentRight, false); + pipeline.m_mixer.drain(384257, pipeline); + result.lateRejected = pipeline.m_mixer.rejectedBlocks() == beforeLate + 1 + && takeAudio(3, 384129, currentLeft, currentRight, false) && !pipeline.takePacket(extra); + + const std::uint64_t beforeEmpty = pipeline.m_mixer.rejectedBlocks(); + pipeline.audioBlock(spec, 383745, {}, {}, false); + result.emptyRejected = pipeline.m_mixer.rejectedBlocks() == beforeEmpty + 1; + while (pipeline.takePacket(extra)) {} // malformed taps are outside this mixer regression + + // Invalid samples must still fail validation even when all their + // coordinates predate the epoch. Check each stereo side separately. + result.nonFiniteRejected = true; + for (int side = 0; side < 2; ++side) { + const std::uint64_t beforeInvalid = pipeline.m_mixer.rejectedBlocks(); + if (side == 0) { left[17] = std::numeric_limits::quiet_NaN(); } + else { right[29] = std::numeric_limits::infinity(); } + pipeline.audioBlock(spec, 383745, left, right, false); + result.nonFiniteRejected &= pipeline.m_mixer.rejectedBlocks() == beforeInvalid + 1; + left[17] = 0.125f + 17.0f / 2048; + while (pipeline.takePacket(extra)) {} + } + pipeline.m_mixer.drain(384257, pipeline); + result.bounded = pipeline.m_mixer.lateFrames() == 0 && pipeline.droppedPackets() == 0 + && !pipeline.takePacket(extra) && !pipeline.needsRepair(); + return result; + } static void exhaustEpoch(RtlReceivePipeline& pipeline) { pipeline.m_nextEpoch = std::numeric_limits::max(); } static std::uint64_t requested(RtlReceivePipeline& pipeline) { return pipeline.m_registry.service().requested; } static std::uint64_t receiverEpoch(RtlReceivePipeline& pipeline, int slot) { return pipeline.m_specs[slot].epoch; } + static bool packetQueueAccounting(RtlReceivePipeline& pipeline, unsigned expectedCapacity) + { + RtlReceivePipeline::Packet packet; + packet.token = {7, 11}; packet.slot = 3; packet.captureEpoch = 19; + packet.receiverEpoch = 23; packet.frames = 256; packet.samples[0] = .375f; + if (pipeline.diagnostics().packetQueueCapacity != expectedCapacity) { return false; } + // Repeated fills cross the physical wrap. Drop-new must retain every + // accepted packet and preserve a genuine position gap on resumption. + for (unsigned cycle = 0; cycle < 3; ++cycle) { + const std::uint64_t first = cycle * (expectedCapacity + 1); + for (unsigned i = 0; i < expectedCapacity; ++i) { + packet.firstSample = first + i; + if (!pipeline.enqueue(packet)) { return false; } + } + const auto full = pipeline.diagnostics(); + packet.firstSample = first + expectedCapacity; + if (full.queuedPackets != expectedCapacity || full.packetQueueHighWater != expectedCapacity + || full.droppedPackets != cycle || pipeline.enqueue(packet)) { return false; } + for (unsigned i = 0; i < expectedCapacity; ++i) { + if (!pipeline.takePacket(packet) || packet.firstSample != first + i + || packet.token != RtlReceivePipeline::Transaction::Token{7, 11} + || packet.slot != 3 || packet.captureEpoch != 19 || packet.receiverEpoch != 23 + || packet.frames != 256 || packet.samples[0] != .375f || packet.discontinuity + || packet.enqueuedMonotonicNs == 0) { return false; } + } + if (pipeline.takePacket(packet)) { return false; } + } + const auto empty = pipeline.diagnostics(); + return empty.queuedPackets == 0 && empty.packetQueueHighWater == expectedCapacity + && empty.droppedPackets == 3; + } + static bool publicationHeadroom(RtlReceivePipeline& pipeline, unsigned receivers) + { + // 192 ms without an owner drain, below the original singleton's + // nominal 225 ms headroom. Four UI publications measured 151 ms. + // Replay the real analog packet cadence: each receiver's 256-frame + // tap plus the shared speaker's two 128-frame quanta, at 48 kHz. + RtlReceivePipeline::Packet packet; + packet.token = {1, 3}; + bool accepted = true; + for (std::uint64_t first = 0; first < 9216; first += 256) { + for (unsigned slot = 0; slot < receivers; ++slot) { + packet.slot = static_cast(slot); packet.firstSample = first; packet.frames = 256; + accepted = pipeline.enqueue(packet) && accepted; + } + for (unsigned half = 0; half < 2; ++half) { + packet.slot = -1; packet.firstSample = first + half * 128; packet.frames = 128; + accepted = pipeline.enqueue(packet) && accepted; + } + } + if (!accepted || pipeline.droppedPackets() != 0) { return false; } + for (std::uint64_t first = 0; first < 9216; first += 256) { + for (unsigned slot = 0; slot < receivers; ++slot) { + if (!pipeline.takePacket(packet) || packet.token != RtlReceivePipeline::Transaction::Token{1, 3} + || packet.slot != static_cast(slot) || packet.firstSample != first + || packet.frames != 256) { return false; } + } + for (unsigned half = 0; half < 2; ++half) { + if (!pipeline.takePacket(packet) || packet.token != RtlReceivePipeline::Transaction::Token{1, 3} + || packet.slot != -1 || packet.firstSample != first + half * 128 + || packet.frames != 128) { return false; } + } + } + return !pipeline.takePacket(packet); + } static bool rejectMalformedMixer(RtlReceivePipeline& pipeline) { pipeline.m_legacy = false; pipeline.m_token = {1, 1}; pipeline.m_captureEpoch = 1; @@ -332,17 +546,18 @@ static void measureSquelchPipeline() ddc.setSpectrumRateFps(1); // display throttling must not chatter the gate std::uint64_t first = 0; QVector> iq(8192); - std::array tapPeak{}, speakerPeak{}; - std::array counts{}; + std::array tapPeak{}, speakerPeak{}; + std::array counts{}; const auto start = std::chrono::steady_clock::now(); int phase = 0; constexpr std::uint64_t kPhaseSamples = 1920000; // 0.8 seconds per state - while (first < 4 * kPhaseSamples) { + while (first < 6 * kPhaseSamples) { const int wanted = first / kPhaseSamples; if (wanted != phase) { phase = wanted; state.token.revision++; state.receivers[0].squelchEnabled = phase != 3; + state.receivers[0].automaticSquelch = phase >= 4; state.receivers[0].squelchLevel = phase == 1 ? 25 : 100; check(pipeline->prepare(state) && ready(*pipeline) && pipeline->adopt(), "squelch edit adopts without rebuilding the demodulator"); @@ -351,7 +566,8 @@ static void measureSquelchPipeline() } for (int i = 0; i < iq.size(); ++i) { const double angle = 2.5 * std::sin(2 * std::numbers::pi * 1000 * (first + i) / 2400000.0); - iq[i] = {float(0.3 * std::cos(angle)), float(0.3 * std::sin(angle))}; + const double amplitude = phase == 4 ? 0.0 : 0.3; + iq[i] = {float(amplitude * std::cos(angle)), float(amplitude * std::sin(angle))}; } ddc.processIqData(iq, false); // real FFT, no legacy audio or USB const auto spectrum = ddc.takeSquelchSpectrum(); @@ -366,7 +582,7 @@ static void measureSquelchPipeline() for (std::size_t i = 0; i < packet.frames; ++i) { const std::uint64_t frame = packet.firstSample + i; const int window = frame / 38400; - if (window >= 4 || frame % 38400 < 24000 || frame % 38400 > 33600) { continue; } + if (window >= 6 || frame % 38400 < 24000 || frame % 38400 > 33600) { continue; } const double value = std::abs(packet.samples[2 * i]); if (packet.slot == 3) { tapPeak[window] = std::max(tapPeak[window], value); ++counts[window]; } else if (packet.slot == -1) { speakerPeak[window] = std::max(speakerPeak[window], value); } @@ -374,13 +590,13 @@ static void measureSquelchPipeline() } std::this_thread::sleep_until(start + std::chrono::microseconds(first * 1000000 / 2400000)); } - for (int window = 0; window < 4; ++window) { + for (int window = 0; window < 6; ++window) { check(counts[window] > 8000, "squelch phase contains settled tap samples"); - const bool open = window == 1 || window == 3; + const bool open = window == 1 || window == 3 || window == 5; std::printf("SQL window=%d tap_peak=%.6f speaker_peak=%.6f\n", window, tapPeak[window], speakerPeak[window]); check(open ? tapPeak[window] > 0.4 && speakerPeak[window] > 0.4 : tapPeak[window] == 0 && speakerPeak[window] == 0, - "real FFT threshold gates both sparse receiver tap and speaker; Off passes audio"); + "real FFT manual/Auto gates sparse receiver tap and speaker; Off passes audio"); } pipeline->stop(); } @@ -395,7 +611,28 @@ static void measureParkAndResume() state.capture = {111, 1, 100'000'000, 2'400'000, 1'080'000, 1'080'000}; state.receivers = {{{0, 100'000'000, -8000, 8000, 0, 3000, 3000}, T::Mode::Fm}}; state.receivingIds = {0}; - check(pipeline->prepare(state, true) && ready(*pipeline) && pipeline->adopt(), + const auto prepareAndAdopt = [&] { + const auto end = std::chrono::steady_clock::now() + 15s; + bool submitted = false; + while (std::chrono::steady_clock::now() < end) { + // Match the owner's control-side service: adoption only marks the + // old bank retired; slot reuse waits for off-thread destruction. + (void)pipeline->service(); + if (!submitted) { + const auto result = pipeline->prepareDetailed(state, true); + if (result == Pipeline::Submission::Failed) { return false; } + submitted = result == Pipeline::Submission::Accepted; + } + if (submitted) { + const auto status = pipeline->service(); + if (status == Pipeline::Preparation::Ready) { return pipeline->adopt(); } + if (status == Pipeline::Preparation::Failed) { return false; } + } + std::this_thread::sleep_for(1ms); + } + return false; + }; + check(prepareAndAdopt(), "captured receiver prepares before parking"); std::array, 8192> iq; iq.fill({0.25f, 0.0f}); @@ -409,6 +646,7 @@ static void measureParkAndResume() "audio keeps the adopted capture revision"); observed = true; } + (void)pipeline->service(); // owner timer reaps banks outside the sample callback std::this_thread::sleep_for(3ms); } check(observed == expectAudio, @@ -421,7 +659,7 @@ static void measureParkAndResume() state.capture.centerHz = 103'000'000; state.hardware.centerHz = 103'000'000; state.receivingIds.clear(); - check(pipeline->prepare(state, true) && ready(*pipeline) && pipeline->adopt(), + check(prepareAndAdopt(), "all-parked capture retains a valid empty receiver bank"); consume(2, false); @@ -430,7 +668,7 @@ static void measureParkAndResume() state.capture.centerHz = 100'000'000; state.hardware.centerHz = 100'000'000; state.receivingIds = {0}; - check(pipeline->prepare(state, true) && ready(*pipeline) && pipeline->adopt(), + check(prepareAndAdopt(), "returning capture rebuilds the preserved receiver"); consume(3, true); pipeline->stop(); @@ -439,6 +677,59 @@ static void measureParkAndResume() int main(int argc, char** argv) { QCoreApplication app(argc, argv); + { + auto rf = std::make_unique(); + check(AetherSDR::rtl::RtlReceivePipelineTestAccess::rfObservation(*rf), + "relative RF measurements preserve all eight IDs, sparse membership and revisions"); + } + { + auto epoch = std::make_unique(); + const std::size_t allocationsBefore = callbackAllocations; + inCallback = true; + const auto result = AetherSDR::rtl::RtlReceivePipelineTestAccess::mixerEpochPrefix(*epoch); + inCallback = false; + check(result.configured, "retained-receiver fixture configures a new speaker epoch"); + check(result.retainedTap, "retained pre-epoch PCM keeps its exact independent stereo tap and receiver identity"); + check(result.retiredPrefix, "retained PCM ending at the speaker epoch origin causes no rejection or speaker output"); + check(result.crossingTap, "epoch-crossing PCM keeps the complete independent stereo tap"); + check(result.crossingSpeaker, "epoch-crossing PCM contributes only its exact stereo suffix to the discontinuous speaker quantum"); + check(result.currentTap && result.currentSpeaker, "current-epoch stereo PCM continues both tap and speaker without an extra discontinuity"); + check(result.lateRejected, "genuinely late current-epoch PCM remains a counted mixer rejection"); + check(result.emptyRejected, "empty pre-epoch PCM remains a counted mixer rejection"); + check(result.nonFiniteRejected, "nonfinite pre-epoch PCM on either stereo side remains a counted mixer rejection"); + check(result.bounded, "epoch-prefix handling creates no missing frames, queue loss or receiver repair"); + check(callbackAllocations == allocationsBefore, "epoch-prefix publication and mixer validation allocate no callback memory"); + } + for (const auto [receivers, usable] : std::array, 4>{{ + {1, 127}, {2, 170}, {4, 254}, {8, 424}}}) { + auto queued = std::make_unique(receivers); + const auto allocationsBefore = callbackAllocations; + inCallback = true; + const bool observed = AetherSDR::rtl::RtlReceivePipelineTestAccess::packetQueueAccounting(*queued, usable); + inCallback = false; + check(observed, "bounded packet queue retains exact capacity, FIFO identity and drop-new gaps across wraps"); + check(callbackAllocations == allocationsBefore, "packet queue observations allocate no callback memory"); + } + for (std::size_t invalidCapacity : {std::size_t{0}, std::size_t{9}, + std::numeric_limits::max()}) { + auto invalid = std::make_unique(invalidCapacity); + T::State requested; + requested.token = {42, 1}; requested.hardware.centerHz = 100000000; + requested.capture = {42, 1, 100000000, 2400000, 1080000, 1080000}; + requested.receivers.push_back({{0, 100400000, -15000, 15000, 0, 3000, 3000}, T::Mode::Fm}); + requested.receivingIds.push_back(0); + check(invalid->diagnostics().packetQueueCapacity <= 424 && !invalid->prepare(requested, true), + "invalid receiver capacity remains refused and cannot allocate an unbounded packet ring"); + } + for (unsigned receivers : {1U, 2U, 4U, 8U}) { + auto queued = std::make_unique(receivers); + const auto allocationsBefore = callbackAllocations; + inCallback = true; + const bool retained = AetherSDR::rtl::RtlReceivePipelineTestAccess::publicationHeadroom(*queued, receivers); + inCallback = false; + check(retained, "each admitted analog bank retains the singleton publication headroom with exact FIFO tap/speaker data"); + check(callbackAllocations == allocationsBefore, "publication headroom allocates no acquisition or delivery memory"); + } { auto traced = std::make_unique(); inCallback = true; diff --git a/tests/rtl_receiver_settings_widget_test.cpp b/tests/rtl_receiver_settings_widget_test.cpp index 467f0cdc5..f3a486546 100644 --- a/tests/rtl_receiver_settings_widget_test.cpp +++ b/tests/rtl_receiver_settings_widget_test.cpp @@ -59,11 +59,11 @@ class ExtensionBackend final : public IRadioBackend { QVariant requested; QString verb; QVariantMap accepted{{"serial", "widget-device"}, {"applied", true}, - {"ppm", 7}, {"requestedPpm", 7}, {"dcSuppression", false}, {"pending", false}, {"saved", true}}; + {"ppm", 7}, {"requestedPpm", 7}, {"dcSuppression", false}, {"receiveMetersEnabled", true}, {"pending", false}, {"saved", true}}; ExtensionBackend() { caps.extensionNamespaces = {QStringLiteral("rtl")}; - caps.extensions["rtl"] = QVariantMap{{"settingsVersion", 1}}; + caps.extensions["rtl"] = QVariantMap{{"settingsVersion", 1}, {"receiveMetersControl", true}}; } RadioCapabilities capabilities() const override { return caps; } bool isConnected() const override { return connectedState; } @@ -91,7 +91,8 @@ class ExtensionBackend final : public IRadioBackend { // Copy before emitting: a result may synchronously reenter settings.get. const quint64 id = pendingId; const QVariant value = requested; - accepted[verb == QLatin1String("ppm.set") ? "ppm" : "dcSuppression"] = value; + accepted[verb == QLatin1String("ppm.set") ? "ppm" + : verb == QLatin1String("receive_meters.set") ? "receiveMetersEnabled" : "dcSuppression"] = value; accepted["pending"] = false; emit extensionStatus("rtl", "settings", accepted); emit extensionResult(id, value); @@ -124,6 +125,9 @@ int main(int argc, char** argv) auto* ppm = widget.findChild(QStringLiteral("rtlPpmRequest")); auto* apply = widget.findChild(QStringLiteral("rtlPpmApply")); auto* dc = widget.findChild(QStringLiteral("rtlDcSuppression")); + auto* meters = widget.findChild(QStringLiteral("rtlReceiveMeters")); + check(meters != nullptr, "RTL receiver settings expose the receive-meter switch"); + if (!meters) { return 1; } auto* applied = widget.findChild(QStringLiteral("rtlCorrectionsApplied")); auto* status = widget.findChild(QStringLiteral("rtlCorrectionsStatus")); if (!ppm || !apply || !dc || !applied || !status) { return 1; } @@ -326,6 +330,16 @@ int main(int argc, char** argv) ppm->setValue(10); apply->click(); pumpFor(260); check(source->controlRequests == 2 && applied->text().contains("10 ppm") && !status->text().contains("Applying"), "synchronous immediate Apply cancels debounce before invoking the extension"); + check(meters->isChecked() && meters->isEnabled(), "receive meters default to confirmed enabled state"); + meters->click(); + check(source->verb == QLatin1String("receive_meters.set") && source->requested.metaType().id() == QMetaType::Bool + && !source->requested.toBool() && meters->isChecked(), "meter toggle waits for accepted readback"); + source->confirm(); + check(!meters->isChecked() && !status->text().contains("Applying"), "confirmed meter disable clears pending state"); + meters->click(); source->refuse("test refusal"); + check(!meters->isChecked(), "refused enable cannot masquerade as enabled"); + meters->click(); source->confirm(); + check(meters->isChecked(), "confirmed re-enable restores the switch"); const int beforeDestroy = source->controlRequests; { auto closing = std::make_unique(model); diff --git a/tests/rtl_runtime_settings_test.cpp b/tests/rtl_runtime_settings_test.cpp index e9aea2d84..353248474 100644 --- a/tests/rtl_runtime_settings_test.cpp +++ b/tests/rtl_runtime_settings_test.cpp @@ -454,6 +454,38 @@ int main(int argc, char** argv) && movedDocument.slices[5].audioGain == outside.audioGain && movedDocument.slices[5].audioPan == outside.audioPan, "drag persists the new capture center without changing saved slice RF or monitor settings"); + const auto beforeMeters = rtl::RtlCaptureBackendTestAccess::state(live); + int meterResults = 0; + int meterErrors = 0; + QHash reasons; + QObject::connect(&live, &IRadioBackend::extensionResult, [&](quint64 id, const QVariant&) { + if (id >= 9100 && id <= 9102) { ++meterResults; } + }); + QObject::connect(&live, &IRadioBackend::extensionError, [&](quint64 id, const QString&) { + if (id >= 9100 && id <= 9102) { ++meterErrors; } + }); + QObject::connect(&live, &IRadioBackend::meterDefined, [&](const MeterDef& def) { + reasons[def.sourceIndex] = def.unavailableReason; + }); + live.invokeExtension("rtl", "receive_meters.set", 9100, false); + check(meterResults == 1 && meterErrors == 0 && reasons.size() == 3 + && !reasons[1].isEmpty() && !reasons[3].isEmpty() && !reasons[5].isEmpty() + && !RtlDeviceSettings(liveScope).load().values.receiveMetersEnabled, + "disabled meters publish reasons for sparse IDs and persist through the sole device owner"); + const auto afterMeters = rtl::RtlCaptureBackendTestAccess::state(live); + check(beforeMeters && afterMeters && beforeMeters->token == afterMeters->token + && beforeMeters->hardware == afterMeters->hardware + && beforeMeters->receivers == afterMeters->receivers + && beforeMeters->receivingIds == afterMeters->receivingIds + && beforeMeters->dcSuppression == afterMeters->dcSuppression + && !rtl::RtlCaptureBackendTestAccess::busy(live), + "meter toggle does not restart capture or change receivers, squelch or audio settings"); + live.invokeExtension("rtl", "receive_meters.set", 9101, QStringLiteral("true")); + check(meterErrors == 1 && meterResults == 1, "meter control rejects a string masquerading as boolean"); + live.invokeExtension("rtl", "receive_meters.set", 9102, true); + check(meterResults == 2 && reasons[1].isEmpty() && reasons[3].isEmpty() && reasons[5].isEmpty() + && RtlDeviceSettings(liveScope).load().values.receiveMetersEnabled, + "re-enabled sparse meters clear unavailable reasons and save accepted state"); live.disconnectRadio(); } reconnectAtCaptureLimits(); diff --git a/tests/rtl_spectrum_averaging_test.cpp b/tests/rtl_spectrum_averaging_test.cpp index ecd4a52c2..3cef59198 100644 --- a/tests/rtl_spectrum_averaging_test.cpp +++ b/tests/rtl_spectrum_averaging_test.cpp @@ -2,6 +2,7 @@ #include "core/backends/rtl/RtlSdrBackend.h" #include #include +#include #include #include #include @@ -310,6 +311,41 @@ void fixedRfGridDoesNotDiffuse() "invalidated converter evidence seeds measured RF rather than erasing current bins"); } } + +void alignedGridValidityAndReuse() +{ + for (bool logarithmic : {false, true}) { + SpectrumTemporalAverage average(6); + // With a 500 ms time constant, this interval retains exactly half. + const double halfLife = .5 * std::log(2.); + const auto matches = [logarithmic](const std::array& actual, + const std::array& expectedDomain) { + for (std::size_t i = 0; i < actual.size(); ++i) { + const double expected = logarithmic ? expectedDomain[i] + : 10 * std::log10(expectedDomain[i]); + if (!(std::abs(actual[i] - expected) < .00001)) { return false; } + } + return true; + }; + std::array values{2, 4, 6, 8, 10, 12}; + check(average.processFrame(values, 50, logarithmic, halfLife, 1000, 1, 1, 5) == 0 + && matches(values, {2, 4, 6, 8, 10, 12}), + "aligned first frame displays actual observations with no reused history"); + + average.invalidate(1002, 1002); + values = {20, 40, 60, 80, 100, 120}; + check(average.processFrame(values, 50, logarithmic, halfLife, 1000, 1, 2, 4) == 1, + "aligned reuse counts only old-valid bins inside the current usable interval"); + check(matches(values, {20, 22, 60, 44, 55, 120}), + "narrowing usable RF retains old-valid output for this frame while invalidated bins seed current observations"); + + values = {200, 400, 600, 800, 1000, 1200}; + check(average.processFrame(values, 50, logarithmic, halfLife, 1000, 1, 1, 5) == 2, + "aligned widening reuses only the two bins retained inside the previous usable interval"); + check(matches(values, {200, 400, 330, 422, 1000, 1200}), + "newly exposed aligned bins seed observations instead of resurrecting excluded history"); + } +} } int main(int argc, char** argv) { @@ -353,5 +389,6 @@ int main(int argc, char** argv) amountChangeContinuity(); captureTransitionContinuity(); fixedRfGridDoesNotDiffuse(); + alignedGridValidityAndReuse(); return failures ? 1 : 0; } diff --git a/tests/rtl_squelch_test.cpp b/tests/rtl_squelch_test.cpp index b24ef3dc6..7122067e9 100644 --- a/tests/rtl_squelch_test.cpp +++ b/tests/rtl_squelch_test.cpp @@ -3,6 +3,7 @@ #include #include #include +#include using Gate = AetherSDR::rtl::RtlSquelchGate; int main() @@ -75,5 +76,47 @@ int main() bins.fill(std::numeric_limits::quiet_NaN()); check(!AetherSDR::SpectrumSquelchLogic::suggest(bins, floor, -120, 1.2, 10), "invalid spectra produce no Auto command"); + // Auto belongs to each acquisition gate and consumes its detector bins, + // independent of display FFT size, smoothing, visibility or selected slice. + std::array receivers; + for (auto& receiver : receivers) { receiver.configure(true, 20, true, true, 10); } + bins.fill(-60); + for (int n = 0; n < 100; ++n) { + const auto frame = static_cast(n * 1600); + for (auto& receiver : receivers) { + receiver.observeSpectrum(bins, 1000, 1012, frame); + for (int i = 0; i < 1600; ++i) { receiver.gain(frame + i); } + check(receiver.gain(frame + 1599) == 0, "Auto closes detector noise in all eight slots"); + } + } + bins[1006] = -40; + receivers[7].observeSpectrum(bins, 1000, 1012, 160000); + for (int i = 0; i < 300; ++i) { receivers[7].gain(160000 + i); } + check(receivers[7].gain(160300) == 1, "Auto opens signal above local floor plus margin"); + check(receivers[0].gain(160300) == 0, "one receiver signal does not open another"); + bins.fill(-40); // sustained rise must be learned from the same detector + for (int n = 101; n < 201; ++n) { + receivers[7].observeSpectrum(bins, 1000, 1012, n * 1600); + for (int i = 0; i < 1600; ++i) { receivers[7].gain(n * 1600 + i); } + } + check(receivers[7].gain(321599) == 0, "Auto adapts to a rising noise floor"); + receivers[7].configure(true, 80, false, false, 10); + receivers[7].observeSpectrum(bins, 1000, 1012, 322000); + for (int i = 0; i < 300; ++i) { receivers[7].gain(322000 + i); } + check(receivers[7].gain(322300) == 0, "manual threshold replaces Auto without stale automatic state"); + Gate noisy; + noisy.configure(true, 20, true, true, 10); + std::mt19937 random(5468); + std::exponential_distribution noisePower(1.0); + int quietFrames = 0; + for (int n = 0; n < 300; ++n) { + // Complex Gaussian receiver noise has exponential FFT-bin power. + // A trimmed log mean underestimates that floor and holds noise open. + for (auto& bin : bins) { bin = -60 + 10 * std::log10(noisePower(random)); } + noisy.observeSpectrum(bins, 1000, 1012, n * 1600); + for (int i = 0; i < 1600; ++i) { noisy.gain(n * 1600 + i); } + quietFrames += noisy.gain(n * 1600 + 1599) == 0; + } + check(quietFrames >= 285, "default Auto margin rejects at least 95 percent of noise-only frames"); return failures ? 1 : 0; } diff --git a/tests/rtl_wfm_pipeline_test.cpp b/tests/rtl_wfm_pipeline_test.cpp index 6075e3bc8..96b7c7d6b 100644 --- a/tests/rtl_wfm_pipeline_test.cpp +++ b/tests/rtl_wfm_pipeline_test.cpp @@ -107,6 +107,7 @@ struct Audio { std::vector left; std::vector right; std::uint64_t next = 0; + std::uint64_t first = 0; std::uint64_t instance = 0; std::uint64_t receiverEpoch = 0; std::uint64_t captureEpoch = 0; @@ -116,14 +117,15 @@ struct Audio { bool discontinuity = false; double peak = 0; std::size_t clipped = 0; - void accept(const Pipeline::Packet& packet) + void accept(const Pipeline::Packet& packet, std::size_t sliceFrames = 256) { check(packet.sampleRateHz == 48000 && packet.channelCount == 2, "native audio declares 48 kHz paired stereo"); - check(packet.frames == (packet.slot < 0 ? 128u : 256u), + check(packet.frames == (packet.slot < 0 ? 128u : sliceFrames), "speaker and slice packet frame counts describe their actual output blocks"); if (seen) { check(packet.firstSample == next, "native output positions are contiguous at 48 kHz"); } else { + first = packet.firstSample; instance = packet.instance; receiverEpoch = packet.receiverEpoch; captureEpoch = packet.captureEpoch; @@ -157,8 +159,52 @@ std::complex referenceIq(std::uint64_t position, double rate) - 0.045 * (cosineIntegral(36000) - cosineIntegral(40000)) + 0.1 * sineIntegral(19000)); return {float(0.3 * std::cos(phase)), float(0.3 * std::sin(phase))}; } +void printRunFailure(Pipeline& pipeline, const Transaction::State& current, + const char* phase, std::uint64_t origin, + std::uint64_t position, std::uint64_t end) +{ + // The fault is latched until adoption. Report only after this phase ends; + // keep diagnostic work out of the generated-IQ feed and its pacing. + const auto diagnostics = pipeline.diagnostics(); + std::fprintf(stderr, "WFM_RUN_REPAIR phase=%s session=%llu revision=%llu origin=%llu position=%llu end=%llu mixer_configuration_failures=%llu trace_drops=%llu\n", + phase, static_cast(current.token.session), + static_cast(current.token.revision), static_cast(origin), + static_cast(position), static_cast(end), + static_cast(diagnostics.mixerConfigurationFailures), + static_cast(diagnostics.droppedTraceEvents)); + for (std::size_t slot = 0; slot < diagnostics.receiverWithdrawals.size(); ++slot) { + if (diagnostics.receiverWithdrawals[slot] == 0) { continue; } + std::fprintf(stderr, "WFM_RUN_WITHDRAWALS phase=%s slot=%zu count=%llu\n", phase, slot, + static_cast(diagnostics.receiverWithdrawals[slot])); + } + Pipeline::TraceEvent event; + unsigned traceCount = 0; + for (; traceCount < Pipeline::kTraceEvents && pipeline.takeTraceEvent(event); ++traceCount) { + std::fprintf(stderr, "WFM_RUN_TRACE phase=%s kind=%d session=%llu revision=%llu generation=%llu capture_epoch=%llu slot=%d stable_id=%d instance=%llu receiver_epoch=%llu capture_first=%llu capture_frames=%llu capture_clock=%llu quantum_first=%llu\n", + phase, static_cast(event.kind), static_cast(event.token.session), + static_cast(event.token.revision), static_cast(event.hardwareGeneration), + static_cast(event.captureEpoch), event.slot, event.stableId, + static_cast(event.instance), static_cast(event.receiverEpoch), + static_cast(event.captureFirst), static_cast(event.captureFrames), + static_cast(event.captureClock), static_cast(event.quantumFirst)); + if (event.failure) { + const auto& failure = *event.failure; + std::fprintf(stderr, "WFM_RUN_FAILURE phase=%s processing_reason=%d wdsp_result=%d has_expected=%d expected_capture_first=%llu capture_first=%llu capture_frames=%llu has_iq_first=%d iq_first=%llu iq_frames=%llu extraction_reason=%d converted_i=%d converted_q=%d\n", + phase, static_cast(failure.reason), static_cast(failure.processResult), + failure.hasExpectedCaptureFirst, static_cast(failure.expectedCaptureFirst), + static_cast(failure.captureFirst), static_cast(failure.captureFrames), + failure.hasIqFirst, static_cast(failure.iqFirst), + static_cast(failure.iqFrames), + failure.extraction ? static_cast(failure.extraction->reason) : -1, + failure.extraction ? failure.extraction->convertedI : 0, + failure.extraction ? failure.extraction->convertedQ : 0); + } + } + // An empty or dropped trace is missing evidence, not an inferred DSP cause. + std::fprintf(stderr, "WFM_RUN_TRACE_END phase=%s events=%u\n", phase, traceCount); +} std::array run(Pipeline& pipeline, const Transaction::State& current, - std::uint64_t& position, double seconds) + std::uint64_t& position, double seconds, const char* phase) { std::array result; for (Audio& audio : result) { @@ -200,7 +246,9 @@ std::array run(Pipeline& pipeline, const Transaction::State& current, std::this_thread::sleep_until(started + std::chrono::microseconds( std::uint64_t((position - origin) * 1'000'000.0 / rate))); } - check(!pipeline.needsRepair(), "real WDSP processing does not withdraw the receiver"); + const bool repair = pipeline.needsRepair(); + if (repair) { printRunFailure(pipeline, current, phase, origin, position, end); } + check(!repair, "real WDSP processing does not withdraw the receiver"); return result; } double amplitude(const std::vector& samples, double hz) @@ -223,7 +271,7 @@ void nativeRouting() "qualified WFM prepares and adopts the production native graph"); if (pipeline->legacy()) { pipeline->stop(); return; } std::uint64_t position = 25; - const auto output = run(*pipeline, current, position, 4.0); + const auto output = run(*pipeline, current, position, 4.0, "initial-stereo"); for (const Audio& audio : output) { const double left = amplitude(audio.left, 1000); const double leftLeak = amplitude(audio.left, 2000); @@ -244,7 +292,7 @@ void nativeRouting() current.token.revision++; current.receivers[0].audioMute = true; check(pipeline->prepare(current) && ready(*pipeline) && pipeline->adopt(), "monitor mute adopts without replacing the receiver"); - const auto muted = run(*pipeline, current, position, 0.5); + const auto muted = run(*pipeline, current, position, 0.5, "monitor-muted"); check(muted[0].peak > 0.001 && muted[1].peak == 0, "monitor mute silences speakers while the independent tap remains live"); check(muted[0].receiverEpoch == output[0].receiverEpoch, "monitor edits retain the decoder epoch"); @@ -252,7 +300,7 @@ void nativeRouting() current.receivers[0].audioMute = false; current.receivers[0].wfmDeemphasisUs = 50; check(pipeline->prepare(current) && ready(*pipeline) && pipeline->adopt(), "50 us deemphasis prepares a fresh immutable decoder recipe"); - const auto changed = run(*pipeline, current, position, 1.0); + const auto changed = run(*pipeline, current, position, 1.0, "deemphasis-50us"); check(changed[0].seen && changed[1].seen && changed[0].discontinuity && changed[1].discontinuity, "deemphasis changes mark both slice and speaker discontinuities"); check(changed[0].receiverEpoch != output[0].receiverEpoch && changed[1].captureEpoch != output[1].captureEpoch, @@ -261,7 +309,7 @@ void nativeRouting() current.token.revision++; current.receivers[0].wfmForceMono = true; check(pipeline->prepare(current) && ready(*pipeline) && pipeline->adopt(), "Force Mono prepares an accepted decoder recipe"); - const auto mono = run(*pipeline, current, position, 4.0); + const auto mono = run(*pipeline, current, position, 4.0, "force-mono"); for (const Audio& audio : mono) { check(audio.seen && audio.peak > 0.001 && audio.left == audio.right, "Force Mono is identical nonzero L/R at independent tap and speaker"); @@ -273,14 +321,14 @@ void nativeRouting() current.token.revision++; current.receivers[0].wfmForceMono = false; check(pipeline->prepare(current) && ready(*pipeline) && pipeline->adopt(), "Auto Stereo readopts"); - const auto automatic = run(*pipeline, current, position, 4.0); + const auto automatic = run(*pipeline, current, position, 4.0, "auto-stereo"); check(amplitude(automatic[0].left, 1000) > 100 * amplitude(automatic[0].right, 1000) && amplitude(automatic[1].right, 2000) > 100 * amplitude(automatic[1].left, 2000), "Auto Stereo restores separated native tap and speaker audio"); current.token.revision++; current.receivingIds.clear(); check(pipeline->prepare(current) && ready(*pipeline) && pipeline->adopt(), "parking admits an empty active bank"); - const auto parked = run(*pipeline, current, position, 0.05); + const auto parked = run(*pipeline, current, position, 0.05, "parked"); check(!parked[0].seen && !parked[1].seen, "parked WFM emits neither stale audio nor pilot observations"); current.token.revision++; current.receivingIds = {3}; @@ -297,7 +345,7 @@ void nativeRouting() } check(resume == Pipeline::Submission::Accepted && ready(*pipeline) && pipeline->adopt(), "resuming waits for safe retirement before constructing a fresh WFM receiver"); - const auto resumed = run(*pipeline, current, position, 1.5); + const auto resumed = run(*pipeline, current, position, 1.5, "resumed"); check(resumed[0].seen && resumed[0].instance != changed[0].instance && resumed[0].discontinuity && resumed[1].discontinuity, "resumed WFM cannot inherit the retired receiver's PCM or pilot lifetime"); @@ -316,7 +364,7 @@ void nativeRouting() current.token.revision++; check(pipeline->prepare(current) && ready(*pipeline) && pipeline->adopt(), "an unchanged WFM recipe can repair its withdrawn decoder"); - const auto repaired = run(*pipeline, current, position, 1.0); + const auto repaired = run(*pipeline, current, position, 1.0, "capture-gap-repaired"); check(repaired[0].seen && repaired[1].seen && repaired[0].receiverEpoch != resumed[0].receiverEpoch && repaired[1].captureEpoch != resumed[1].captureEpoch @@ -431,7 +479,7 @@ void workerUnderrunTrace() const bool repaired = pipeline->prepare(current) && ready(*pipeline) && pipeline->adopt(); check(repaired, "unchanged WFM recipe repairs after the real worker is released"); if (repaired) { - const auto audio = run(*pipeline, current, position, 2.0); + const auto audio = run(*pipeline, current, position, 2.0, "held-worker-repaired"); check(audio[0].seen && audio[1].seen && audio[0].receiverEpoch != firstSlice.receiverEpoch && audio[1].captureEpoch != firstSlice.captureEpoch && audio[0].discontinuity && audio[1].discontinuity, @@ -607,6 +655,277 @@ void boundedBenchmark() pipeline->stop(); } #endif + +struct AnalogCarrier { + int id; + double frequencyHz; + double leftHz; + double rightHz; // zero denotes narrow FM, otherwise a stereo multiplex +}; +constexpr std::array kAnalogCarriers{{ + {0, 99'100'000, 500, 0}, {2, 99'500'000, 1000, 2000}, + {5, 100'150'000, 3000, 4000}, {7, 100'800'000, 700, 0}}}; +std::complex carrierIq(const AnalogCarrier& carrier, std::uint64_t position, + const Transaction::State& current) +{ + const double time = position / current.capture.achievedSampleRateHz; + double phase = kTau * (carrier.frequencyHz - current.capture.centerHz) * time; + if (carrier.rightHz == 0) { + phase += 2000 / carrier.leftHz * std::sin(kTau * carrier.leftHz * time); + } else { + const auto sineIntegral = [time](double hz) { return -std::cos(kTau * hz * time) / hz; }; + const auto cosineIntegral = [time](double hz) { return std::sin(kTau * hz * time) / hz; }; + phase += 75000 * (0.1575 * sineIntegral(carrier.leftHz) + 0.09 * sineIntegral(carrier.rightHz) + + 0.07875 * (cosineIntegral(38000 - carrier.leftHz) - cosineIntegral(38000 + carrier.leftHz)) + - 0.045 * (cosineIntegral(38000 - carrier.rightHz) - cosineIntegral(38000 + carrier.rightHz)) + + 0.1 * sineIntegral(19000)); + } + return {float(0.15 * std::cos(phase)), float(0.15 * std::sin(phase))}; +} +using MultiAudio = std::array; // addressable identities, followed by speaker mix +MultiAudio runAnalogBank(Pipeline& pipeline, const Transaction::State& current, + std::uint64_t& position, double seconds) +{ + MultiAudio result; + for (Audio& audio : result) { + audio.left.reserve(std::size_t(seconds * 48000) + 2048); + audio.right.reserve(std::size_t(seconds * 48000) + 2048); + } + // All carriers and modulation tones are multiples of 100 Hz. Build one + // analytic period outside acquisition, then feed the same complete RF scene + // to every receiver. Parking never removes a carrier from this RF input. + std::vector> period(std::size_t(current.capture.achievedSampleRateHz / 100)); + for (std::size_t i = 0; i < period.size(); ++i) { + for (const AnalogCarrier& carrier : kAnalogCarriers) { period[i] += carrierIq(carrier, i, current); } + } + const std::uint64_t origin = position; + const std::uint64_t end = position + std::uint64_t(seconds * current.capture.achievedSampleRateHz); + const auto started = std::chrono::steady_clock::now(); + std::array, 8192> iq; + while (position < end) { + const std::size_t count = std::min(iq.size(), end - position); + for (std::size_t i = 0; i < count; ++i) { iq[i] = period[(position + i) % period.size()]; } + check(pipeline.process(position, std::span(iq).first(count)), + "analog bank accepts the shared continuous RF scene"); + position += count; + Pipeline::Packet packet; + while (pipeline.takePacket(packet)) { + check(packet.token == current.token, "each analog packet belongs to the adopted whole-bank revision"); + if (packet.slot == -1) { + check(!packet.wfmReception && !packet.wfmStereoDetected, + "mixed speaker output does not borrow a receiver's WFM observation"); + result.back().accept(packet); + continue; + } + const auto receiver = std::ranges::find_if(current.receivers, [&packet](const auto& value) { + return value.passband.stableId == packet.slot; + }); + const bool receiving = std::ranges::find(current.receivingIds, packet.slot) != current.receivingIds.end(); + check(receiver != current.receivers.end() && receiving && packet.slot >= 0 && packet.slot < 8, + "only an accepted receiving identity can publish independent audio"); + if (receiver == current.receivers.end() || packet.slot < 0 || packet.slot >= 8) { continue; } + const bool wide = receiver->mode == Transaction::Mode::Wfm; + check(wide || (!packet.wfmReception && !packet.wfmStereoDetected), + "narrow FM never inherits a sibling's WFM status"); + if (packet.wfmReception) { + check(wide && packet.wfmReception->valid && packet.wfmStereoDetected + == (packet.wfmReception->pilotLocked && !receiver->wfmForceMono), + "every WFM observation honors its own immutable stereo choice"); + } + result[packet.slot].accept(packet, wide ? 256 : 1024); + } + if (pipeline.needsRepair()) { check(false, "analog bank never withdraws a healthy receiver"); break; } + std::this_thread::sleep_until(started + std::chrono::duration( + double(position - origin) / current.capture.achievedSampleRateHz)); + } + const Pipeline::Diagnostics diagnostic = pipeline.diagnostics(); + std::printf("ANALOG_BANK revision=%llu receivers=%zu position=%llu drops=%llu late=%llu rejected=%llu repair=%d\n", + static_cast(current.token.revision), current.receivingIds.size(), + static_cast(position), static_cast(diagnostic.droppedPackets), + static_cast(diagnostic.mixerLateFrames), + static_cast(diagnostic.mixerRejectedBlocks), pipeline.needsRepair()); + check(diagnostic.droppedPackets == 0 && diagnostic.mixerLateFrames == 0 + && diagnostic.mixerRejectedBlocks == 0 && diagnostic.mixerConfigurationFailures == 0, + "analog mixtures keep the existing bounded mixer deadline without missing or rejected contributions"); + return result; +} +bool installBank(Pipeline& pipeline, const Transaction::State& current, bool reset = false) +{ + const auto deadline = std::chrono::steady_clock::now() + 20s; + Pipeline::Submission result; + do { + result = pipeline.prepareDetailed(current, reset); + if (result != Pipeline::Submission::RetryRetiringSlot && result != Pipeline::Submission::RetryPlannerBusy) { break; } + pipeline.service(); + std::this_thread::sleep_for(1ms); + } while (std::chrono::steady_clock::now() < deadline); + return result == Pipeline::Submission::Accepted && ready(pipeline) && pipeline.adopt(); +} +Transaction::Receiver analogReceiver(const AnalogCarrier& carrier) +{ + const bool wide = carrier.rightHz != 0; + Transaction::Receiver receiver{{carrier.id, carrier.frequencyHz, + wide ? -100'000.0 : -8000.0, wide ? 100'000.0 : 8000.0, 0, 3000, 3000}, + wide ? Transaction::Mode::Wfm : carrier.id == 0 ? Transaction::Mode::Fm : Transaction::Mode::Fmn}; + if (carrier.id == 5) { + receiver.wfmDeemphasisUs = 50; + receiver.passband.filterLowHz = -110'000; + receiver.passband.filterHighHz = 110'000; + } + return receiver; +} +void checkAnalogSignals(const MultiAudio& output, const Transaction::State& current) +{ + for (const Transaction::Receiver& receiver : current.receivers) { + const int id = receiver.passband.stableId; + if (std::ranges::find(current.receivingIds, id) == current.receivingIds.end()) { + check(!output[id].seen, "a parked receiver contributes no stale PCM or diagnostics"); + continue; + } + const auto carrier = std::ranges::find_if(kAnalogCarriers, [id](const auto& value) { return value.id == id; }); + const Audio& audio = output[id]; + const double left = amplitude(audio.left, carrier->leftHz); + const double right = amplitude(audio.right, carrier->rightHz == 0 ? carrier->leftHz : carrier->rightHz); + std::printf("ANALOG_SIGNAL id=%d frames=%zu left=%.6f right=%.6f peak=%.6f stereo=%d pilot=%d\n", + id, audio.left.size(), left, right, audio.peak, audio.stereo, audio.pilot); + check(audio.left.size() > 48000 && left > 0.001 && right > 0.001, + "every admitted analog receiver independently demodulates its actual nonzero carrier"); + check(audio.peak < 1 && audio.clipped == 0, "independent analog taps retain normalized PCM headroom"); + if (carrier->rightHz != 0) { + check(audio.pilot, "each WFM receiver acquires its own real pilot"); + if (receiver.wfmForceMono) { + check(!audio.stereo && audio.left == audio.right, "forced mono applies only to the requested WFM receiver"); + } else { + check(audio.stereo && left > 30 * amplitude(audio.right, carrier->leftHz) + && right > 30 * amplitude(audio.left, carrier->rightHz), + "each automatic WFM receiver preserves its own separated stereo program"); + } + } + for (const AnalogCarrier& sibling : kAnalogCarriers) { + if (sibling.id == id) { continue; } + check(left > 30 * amplitude(audio.left, sibling.leftHz), + "each analog demodulator rejects the other captured stations"); + } + } + check(output.back().seen && output.back().peak < 1 && output.back().clipped == 0, + "simultaneous analog reception reaches one unclipped speaker stream"); +} +void analogMultiReceiver() +{ + auto pipeline = std::make_unique(4, true); // fixture capacity; production remains one + Transaction::State current = state(); + current.receivers = {analogReceiver(kAnalogCarriers[0]), analogReceiver(kAnalogCarriers[3])}; + current.receivingIds = {0, 7}; + check(installBank(*pipeline, current, true), "two distinct FM and FM-N paths adopt together"); + std::uint64_t position = 0; + const MultiAudio narrow = runAnalogBank(*pipeline, current, position, 2.0); + checkAnalogSignals(narrow, current); + pipeline->stop(); + pipeline.reset(); + check(QThreadPool::globalInstance()->waitForDone(15000), "narrow pair drains before the independent WFM workload"); + + pipeline = std::make_unique(4, true); + current = state(); + current.receivers = {analogReceiver(kAnalogCarriers[1]), analogReceiver(kAnalogCarriers[2])}; + current.receivers[1].wfmForceMono = true; + current.receivingIds = {2, 5}; + check(installBank(*pipeline, current, true), "two analog WFM recipes adopt with distinct filters, deemphasis and stereo choices"); + position = 25; + const MultiAudio initial = runAnalogBank(*pipeline, current, position, 3.0); + checkAnalogSignals(initial, current); + current.token.revision++; + current.receivers[1].wfmDeemphasisUs = 75; + check(installBank(*pipeline, current), "one WFM receiver changes deemphasis independently"); + const MultiAudio deemphasis = runAnalogBank(*pipeline, current, position, 2.0); + checkAnalogSignals(deemphasis, current); + const double measuredResponse = amplitude(deemphasis[5].left, 4000) / amplitude(initial[5].left, 4000); + const double expectedResponse = std::sqrt((1 + std::pow(kTau * 4000 * 50e-6, 2)) + / (1 + std::pow(kTau * 4000 * 75e-6, 2))); + check(std::abs(measuredResponse - expectedResponse) < 0.04, + "changed receiver's actual 4 kHz audio follows its own 50-to-75 us deemphasis response"); + check(std::abs(amplitude(deemphasis[2].left, 1000) / amplitude(initial[2].left, 1000) - 1) < 0.01, + "sibling deemphasis and actual audio response remain unchanged"); + check(initial[2].receiverEpoch == deemphasis[2].receiverEpoch && !deemphasis[2].discontinuity + && initial[2].next == deemphasis[2].first, + "independent deemphasis replacement leaves the sibling decoder continuous"); + current.token.revision++; + current.receivers[1].wfmForceMono = false; + current.receivers[1].passband.filterLowHz = -100'000; + current.receivers[1].passband.filterHighHz = 100'000; + check(installBank(*pipeline, current), "one WFM recipe changes without reconstructing its sibling"); + const MultiAudio edited = runAnalogBank(*pipeline, current, position, 3.0); + checkAnalogSignals(edited, current); + check(initial[2].instance == edited[2].instance && initial[2].receiverEpoch == edited[2].receiverEpoch + && !edited[2].discontinuity && deemphasis[2].next == edited[2].first, + "editing one WFM decoder preserves the sibling's exact continuous receiver lifetime"); + check(initial[5].receiverEpoch != edited[5].receiverEpoch && edited[5].discontinuity, + "changed WFM controls retire only their originating decoder recipe"); + + current.token.revision++; + current.receivers.insert(current.receivers.begin(), analogReceiver(kAnalogCarriers[0])); + current.receivers.push_back(analogReceiver(kAnalogCarriers[3])); + current.receivingIds = {0, 2, 5, 7}; + check(installBank(*pipeline, current), "four real FM, FM-N and stereo WFM receivers adopt one shared RF capture"); + const MultiAudio four = runAnalogBank(*pipeline, current, position, 3.0); + checkAnalogSignals(four, current); + for (const int id : {2, 5}) { + check(four[id].instance == edited[id].instance && four[id].receiverEpoch == edited[id].receiverEpoch + && !four[id].discontinuity && four[id].first == edited[id].next, + "adding analog siblings retains the existing WFM decoder histories"); + } + current.token.revision++; + current.receivers[1].audioGain = 23; + current.receivers[1].audioMute = true; + current.receivers[2].audioGain = 40; + current.receivers[2].audioPan = 100; + check(installBank(*pipeline, current), "independent gain, mute and pan adopt without RF or decoder reconfiguration"); + const MultiAudio monitored = runAnalogBank(*pipeline, current, position, 1.5); + checkAnalogSignals(monitored, current); + for (const int id : {0, 2, 5, 7}) { + check(monitored[id].instance == four[id].instance && monitored[id].receiverEpoch == four[id].receiverEpoch + && monitored[id].captureEpoch == four[id].captureEpoch && !monitored[id].discontinuity, + "monitor controls never replace any receiver or speaker epoch"); + } + check(amplitude(monitored.back().left, 1000) < 0.001 && amplitude(monitored.back().right, 2000) < 0.001 + && amplitude(monitored[2].left, 1000) > 0.001, + "one WFM monitor mute leaves its independent recording tap nonzero"); + check(amplitude(monitored.back().left, 3000) < 0.001 + && amplitude(monitored.back().right, 4000) > 0.001, + "one WFM monitor pan affects only its own speaker contribution"); + + current.token.revision++; + ++current.capture.generation; + current.capture.centerHz = current.hardware.centerHz = 100'600'000; + current.receivingIds = {5, 7}; // the complete -500 kHz WFM passband no longer fits + const auto recipes = current.receivers; + check(installBank(*pipeline, current, true), "capture retune parks complete out-of-capture passbands"); + position = 0; + const MultiAudio partial = runAnalogBank(*pipeline, current, position, 2.0); + checkAnalogSignals(partial, current); + current.token.revision++; + ++current.capture.generation; + current.capture.centerHz = current.hardware.centerHz = 103'000'000; + current.receivingIds.clear(); + check(installBank(*pipeline, current, true), "all four configured receivers admit a capture with an empty receiving bank"); + position = 0; + const MultiAudio parked = runAnalogBank(*pipeline, current, position, 0.05); + check(std::ranges::none_of(parked, [](const Audio& audio) { return audio.seen; }), + "an empty analog bank emits no stale receiver or speaker packets"); + current.token.revision++; + ++current.capture.generation; + current.capture.centerHz = current.hardware.centerHz = 100'000'000; + current.receivingIds = {0, 2, 5, 7}; + check(current.receivers == recipes, "parking retains every exact configured RF and audio recipe"); + check(installBank(*pipeline, current, true), "returning capture readopts all four preserved analog recipes"); + position = 0; + const MultiAudio resumed = runAnalogBank(*pipeline, current, position, 3.0); + checkAnalogSignals(resumed, current); + for (const int id : {0, 2, 5, 7}) { + check(resumed[id].instance != monitored[id].instance && resumed[id].discontinuity, + "resumed identities cannot reuse retired PCM or decoder observations"); + } + pipeline->stop(); +} void admission() { auto legacy = std::make_unique(); @@ -628,6 +947,20 @@ void admission() invalid.receivingIds.push_back(4); check(!native->prepare(invalid), "WFM qualification does not increase production receiver admission"); native->stop(); + + auto multiple = std::make_unique(4, true); + for (const Transaction::Mode mode : {Transaction::Mode::Am, Transaction::Mode::Sam, + Transaction::Mode::Usb, Transaction::Mode::Lsb, Transaction::Mode::Cw, + Transaction::Mode::Cwr}) { + invalid = current; + invalid.receivers.push_back({{6, 103'000'000, -3000, 3000, 0, 3000, 3000}, mode}); + check(!multiple->prepare(invalid), + "parked legacy recipe cannot bypass configured-set singleton admission"); + std::reverse(invalid.receivers.begin(), invalid.receivers.end()); + check(!multiple->prepare(invalid), + "configured legacy singleton refusal is independent of receiver order"); + } + multiple->stop(); } } int main(int argc, char** argv) @@ -636,6 +969,7 @@ int main(int argc, char** argv) outputClockAlignment(); admission(); nativeRouting(); + analogMultiReceiver(); workerUnderrunTrace(); if (qEnvironmentVariableIntValue("AETHER_WFM_BENCHMARK") == 1) { #ifdef AETHER_BACKEND_RTL diff --git a/tests/rx_applet_squelch_reconciliation_test.cpp b/tests/rx_applet_squelch_reconciliation_test.cpp index e7c0ea108..d800a6dc8 100644 --- a/tests/rx_applet_squelch_reconciliation_test.cpp +++ b/tests/rx_applet_squelch_reconciliation_test.cpp @@ -15,6 +15,11 @@ using namespace AetherSDR; namespace AetherSDR { +struct RxAppletSquelchTestAccess { + static void engineSql(RxApplet& applet) { + applet.m_exclusiveSquelch = SquelchLevelScale{-120, 1.2, {"FM", "FMN"}, false, "dBFS/2048-bin", true, true, false}; + } +}; struct RadioModelWakeTestAccess { static void identity(RadioModel& radio, const QString& family, const QString& id) { @@ -56,6 +61,72 @@ class RxAppletSquelchReconciliationTest : public QObject Q_OBJECT private slots: + void engineAutoRestoreKeepsManualThresholdOnAttachedSlice() + { + SliceModel first(0); + RxApplet rx; + RxAppletSquelchTestAccess::engineSql(rx); + rx.setSlice(&first); + QSignalSpy commands(&first, &SliceModel::commandReady); + SliceDelta restored; + restored.mode = QStringLiteral("FMN"); + restored.squelchOn = true; + restored.squelchLevel = 54; + restored.automaticSquelch = true; + restored.automaticSquelchMarginDb = 13; + first.applyChanges(restored); + QCOMPARE(rx.sqlMode(), RxApplet::SqlMode::Auto); + QCOMPARE(rx.autoSqlMarginDb(), 13); + QCOMPARE(first.manualSquelchLevel(), 54); + QCOMPARE(rx.sqlManualLevel(), 54); + QVERIFY(commands.isEmpty()); + rx.cycleSqlModeExternal(); // Auto -> Off + rx.cycleSqlModeExternal(); // Off -> Manual restores the saved threshold + QCOMPARE(rx.sqlManualLevel(), 54); + QCOMPARE(first.squelchLevel(), 54); + } + + void engineAutoFollowsSelectedSliceWithoutCrossWrites() + { + SliceModel first(0), eighth(7); + status(first, true, 54, "FMN"); + status(eighth, true, 61, "FM"); + SliceDelta automatic; + automatic.automaticSquelch = true; + automatic.automaticSquelchMarginDb = 13; + eighth.applyChanges(automatic); + RxApplet rx; + RxAppletSquelchTestAccess::engineSql(rx); + QSignalSpy firstAuto(&first, &SliceModel::receiveSquelchRequested); + QSignalSpy eighthAuto(&eighth, &SliceModel::receiveSquelchRequested); + rx.setSlice(&eighth); + QCOMPARE(rx.sqlMode(), RxApplet::SqlMode::Auto); + QCOMPARE(rx.autoSqlMarginDb(), 13); + rx.setSlice(&first); + QCOMPARE(rx.sqlMode(), RxApplet::SqlMode::Manual); + QCOMPARE(rx.sqlManualLevel(), 54); + QVERIFY(firstAuto.isEmpty() && eighthAuto.isEmpty()); + rx.cycleSqlModeExternal(); + QCOMPARE(firstAuto.count(), 1); + QCOMPARE(qvariant_cast(firstAuto.at(0).at(0)).marginDb, 10); + QVERIFY(eighthAuto.isEmpty()); + rx.setSlice(&eighth); + rx.setSqlSliderValueExternal(17); + QCOMPARE(eighthAuto.count(), 1); + QCOMPARE(qvariant_cast(eighthAuto.at(0).at(0)).marginDb, 17); + QCOMPARE(firstAuto.count(), 1); + QCOMPARE(first.manualSquelchLevel(), 54); + QCOMPARE(eighth.manualSquelchLevel(), 61); + SliceDelta manual; + manual.automaticSquelch = false; + manual.squelchOn = true; + manual.squelchLevel = 72; + eighth.applyChanges(manual); + QCOMPARE(rx.sqlMode(), RxApplet::SqlMode::Manual); + QCOMPARE(rx.sqlManualLevel(), 72); + QCOMPARE(first.manualSquelchLevel(), 54); + } + void deferredWritesCoalesceAndFlush() { int writes = 0; diff --git a/tests/s_meter_geometry_test.cpp b/tests/s_meter_geometry_test.cpp index 99339b81c..e663979a4 100644 --- a/tests/s_meter_geometry_test.cpp +++ b/tests/s_meter_geometry_test.cpp @@ -1188,6 +1188,31 @@ int main(int argc, char** argv) expect(changedPixelCount(minimumRender) > 250, "minimum-size face renders"); expect(changedPixelCount(enlargedRender) > 1000, "enlarged face renders"); + meter.setTransmitting(false); + meter.setRelativeLevel(-40.0f, 7); + expect(std::isnan(meter.levelDbm()), "relative RF has no calibrated widget accessor value"); + expect(meter.accessibleDescription().contains("dBFS") + && meter.accessibleDescription().contains("uncalibrated"), "relative RF description names its actual reference"); + expect(meter.accessibleValueText().contains("dBFS") + && !meter.accessibleValueText().contains("dBm") + && !meter.sUnitsText().contains("S9"), "relative RF is never labeled dBm or S-units"); + const QImage relativeFace = render(meter, QSize(280, 140)); + meter.setRelativeLevel(std::nullopt, 2); + expect(meter.accessibleValueText().contains("unavailable"), "parked selection withdraws its old reading"); + expect(imageDigest(relativeFace) != imageDigest(render(meter, QSize(280, 140))), + "relative no-data face differs from a live RF reading"); + meter.setRelativeLevel(std::nullopt, 2, QStringLiteral("Receive meters disabled")); + expect(meter.accessibleValueText() == QStringLiteral("Receive meters disabled") + && meter.accessibleDescription() == QStringLiteral("Receive meters disabled"), + "disabled RF meter exposes its actual reason to assistive technology"); + const QImage offFace = render(meter, QSize(280, 140)); + meter.setRelativeLevel(-40.0f, 2); + expect(meter.accessibleValueText().contains("dBFS") + && imageDigest(offFace) != imageDigest(render(meter, QSize(280, 140))), + "re-enabled RF meter recovers the selected live face"); + meter.setLevel(-73); + expect(meter.accessibleValueText().contains("dBm"), "calibrated input restores the calibrated meter"); + if (g_failures == 0) { // SAY WHICH HALF RAN. Without this the binary exits 0 whether or not the // announcement section executed, and ctest reports a bare "Passed" for diff --git a/tests/spectrum_confirmed_geometry_test.cpp b/tests/spectrum_confirmed_geometry_test.cpp index 3fc7c725d..411fbc3ca 100644 --- a/tests/spectrum_confirmed_geometry_test.cpp +++ b/tests/spectrum_confirmed_geometry_test.cpp @@ -1,6 +1,7 @@ // Opt-in real-widget test: no sockets, radio, USB, or synthetic firmware peer. #include "TestSettingsProfile.h" #include "gui/SpectrumWidget.h" +#include "gui/VfoWidget.h" #include "gui/SpectrumOverlayMenu.h" #include "gui/MainWindowHelpers.h" #include "RtlInjectedDevice.h" diff --git a/tests/squelch_level_scale_test.cpp b/tests/squelch_level_scale_test.cpp index 8bed63840..30973099f 100644 --- a/tests/squelch_level_scale_test.cpp +++ b/tests/squelch_level_scale_test.cpp @@ -151,13 +151,17 @@ void recordsPerBackend() "ANAN: Flex's -160 + 1*level, every mode, with Auto SQL"); #ifdef AETHER_BACKEND_RTL { - // RtlSquelchGate is the receiver's own gate: its map, FM/FMN only, Auto - // SQL (the gate reads the pan's FFT), and SQL nowhere else. + // RTL owns its FM/FMN gate and engine Auto. Its detector bandwidth + // differs from the display, so no pan-level mapping is available. const SquelchLevelScale rtlScale{rtl::RtlSquelchGate::kReferenceDb, rtl::RtlSquelchGate::kStepDb, {QStringLiteral("FM"), QStringLiteral("FMN")}, - true, QStringLiteral("dBFS/bin"), true}; + false, QStringLiteral("dBFS/2048-bin"), true, true, false}; check(rtl::RtlSdrBackend().capabilities().squelchLevelScale == rtlScale, - "RTL-SDR: RtlSquelchGate's -120 + 1.2*level dBFS/bin, FM/FMN, exclusive"); + "RTL-SDR: RtlSquelchGate's -120 + 1.2*level dBFS/2048-bin, FM/FMN, engine Auto"); + check(!squelchScaleForMode(rtlScale, QStringLiteral("FMN")), + "RTL detector bandwidth does not create a false display SQL line"); + check(rtlScale.automaticInEngine && rtlScale.appliesTo(QStringLiteral("FMN")), + "RTL engine Auto remains available without display-driven Auto"); } // Only RTL owns its gate; every other family keeps the #2504 mode rule. check(!legacy.modesExclusive && !exclusiveSquelchScale(std::optional(legacy)), diff --git a/tests/tci_rx_audio_test.cpp b/tests/tci_rx_audio_test.cpp index bfcdda764..f0dc84014 100644 --- a/tests/tci_rx_audio_test.cpp +++ b/tests/tci_rx_audio_test.cpp @@ -6,8 +6,13 @@ #include "core/TciRxConverter.h" #include "core/backends/IRadioBackend.h" #include "models/RadioModel.h" +#include "models/DaxReceiveModel.h" #include "models/SliceModel.h" #include +#include +#include +#include +#include #include #include "core/TciClient.h" #include @@ -77,7 +82,12 @@ double energy(const QVector& samples, int channel) } class InjectedBackend final : public IRadioBackend { public: - RadioCapabilities capabilities() const override { return {}; } + RadioCapabilities capabilities() const override { + RadioCapabilities caps; + if (exportAvailable) { caps.receiveAudioExport = ReceiveAudioExport{{24000,48000},8}; } + return caps; + } + bool exportAvailable = true; bool ownsRxAudio() const override { return true; } void connectRadio(const RadioConnectRequest&) override {} // no transport void disconnectRadio() override { retirePcmStreams(); emit disconnected(); } @@ -832,8 +842,246 @@ class TciRxAudioTest { check(true, "copied network ingress remains inert after concurrent controller destruction"); } + static void eightNativeDaxConsumers() + { + const std::array ids{3,17,29,41,57,63,80,99}; + const std::array rates{8000,12000,24000,48000,8000,12000,24000,48000}; + for (int sourceRate : {24000,48000}) { + Fixture f; + DaxReceiveModel dax(f.model, f.server); + std::array received; + std::array resets{}; + std::array producers; + std::array clients{}; + QObject::connect(&dax, &DaxReceiveModel::audioReady, &dax, + [&](int channel, const QByteArray& pcm) { + check(channel >= 1 && channel <= 8 && !pcm.isEmpty() + && pcm.size() <= 1024*2*int(sizeof(float)) + && pcm.size() % (2*sizeof(float)) == 0, + "DAX emits bounded float stereo24 blocks on channels one through eight"); + if (channel >= 1 && channel <= 8) { received[channel-1].append(pcm); } + }); + QObject::connect(&dax, &DaxReceiveModel::channelReset, &dax, + [&](int channel) { ++resets[channel-1]; }); + for (int i = 0; i < 8; ++i) { + f.backend->add(ids[i]); + check(producers[i].start(PcmPurpose::Slice,ids[i],{sourceRate,PcmLayout::Stereo}), + "start eight distinct native receiver producers"); + clients[i] = f.client(24000,i); + SliceDelta mute; mute.audioMute = true; mute.audioGain = 0; + emit f.backend->sliceChanged(ids[i],mute); + } + dax.setEnabled(true); + std::array frames; + for (int i = 0; i < 8; ++i) { + frames[i] = frame(producers[i],sourceRate,float(i+1)/16.0f,-float(i+1)/32.0f); + f.feed(ids[i],frames[i]); + } + for (int i = 0; i < 8; ++i) { + headers(f.packets[clients[i]],24000,i,2,3); + check(std::abs(floats(f.packets[clients[i]]).size()/2-24000) <= 24000*256/sourceRate+1, + "eight independent TCI consumers retain negotiated rate and receiver identity"); + QByteArray comparison; + for (const QByteArray& packet : f.packets[clients[i]]) { comparison.append(packet.mid(64)); } + check(!received[i].isEmpty() && received[i] == comparison, + "each DAX channel is exactly the same independently converted PCM as its live TCI24 route"); + const int count = received[i].size()/int(2*sizeof(float)); + check(std::abs(count-24000) <= 256*24000/sourceRate+1, + "DAX duration follows the producer descriptor rather than relabeling its sample rate"); + float pair[2]{}; + if (received[i].size() >= int(sizeof(pair))) { + std::memcpy(pair,received[i].constData()+received[i].size()-sizeof(pair),sizeof(pair)); + } + check(std::abs(pair[0]-float(i+1)/16.0f)<0.001f + && std::abs(pair[1]+float(i+1)/32.0f)<0.001f, + "eight muted receivers preserve their distinct stereo channel signatures in DAX"); + const qsizetype size = received[i].size(); + f.feed(ids[i],frames[i]); + check(received[i].size()==size,"DAX replay guard rejects a repeated frame independently of TCI"); + } + f.packets.clear(); + for (int i = 0; i < 8; ++i) { + f.command(clients[i],QStringLiteral("audio_samplerate:%1;").arg(rates[i])); + f.feed(ids[i],frame(producers[i],sourceRate,float(i+1)/16.0f,-float(i+1)/32.0f)); + headers(f.packets[clients[i]],rates[i],i,2,3); + check(std::abs(floats(f.packets[clients[i]]).size()/2-rates[i]) <= rates[i]*256/sourceRate+1, + "eight live clients independently renegotiate supported TCI rates while DAX continues"); + } + const qsizetype beforeStop=received[7].size(); + dax.setEnabled(false); + f.feed(ids[7],frame(producers[7],512)); + check(received[7].size()==beforeStop,"disabled DAX does not feed while TCI remains live"); + dax.setEnabled(true); + f.feed(ids[7],frame(producers[7],4096)); + check(received[7].size()>beforeStop,"DAX resumes a live producer after its own stop without restarting TCI"); + const PcmFrame stale=frame(producers[0],4096); + const qsizetype beforeRemoval=received[0].size(); + f.backend->remove(ids[0]); + f.feed(ids[0],stale); + f.backend->add(ids[0]); + f.feed(ids[0],stale); + check(received[0].size()==beforeRemoval,"removal and same-id recreation cannot admit the old live producer"); + producers[0].invalidate(); + producers[0].start(PcmPurpose::Slice,ids[0],{sourceRate,PcmLayout::Stereo}); + f.feed(ids[0],frame(producers[0],4096)); + check(received[0].size()>beforeRemoval,"new receiver epoch resumes its original DAX channel"); + const qsizetype survivor=received[6].size(); + f.feed(ids[6],frame(producers[6],4096)); + check(received[6].size()>survivor,"recreating an earlier slice never renumbers a surviving DAX receiver"); + f.backend->remove(ids[0]); + QEventLoop settle; + QTimer::singleShot(550,&settle,&QEventLoop::quit); + settle.exec(); + f.backend->add(123); + PcmProducer replacement; + replacement.start(PcmPurpose::Slice,123,{sourceRate,PcmLayout::Stereo}); + const qsizetype beforeReuse=received[0].size(); + f.feed(123,frame(replacement,4096)); + check(received[0].size()>beforeReuse, + "a genuinely closed receiver's settled TCI index is reused by the same DAX input"); + const qsizetype survivorAfterReuse=received[7].size(); + f.feed(ids[7],frame(producers[7],4096)); + check(received[7].size()>survivorAfterReuse, + "reusing a sparse hole does not move the eighth surviving receiver"); + const qsizetype beforeListenerStop=received[7].size(); + f.server.stop(); + f.feed(ids[7],frame(producers[7],4096)); + check(!f.server.isRunning() && received[7].size()>beforeListenerStop, + "DAX continues independently when the TCI listener and clients are stopped"); + for (int reset : resets) { check(reset>0,"all eight DAX channels establish a reset boundary"); } + } + } + static void nativeDaxEpochBoundaries() + { + Fixture f; f.backend->add(17); + DaxReceiveModel dax(f.model,f.server); + QByteArray audio; + int resets=0; + QObject::connect(&dax,&DaxReceiveModel::audioReady,&dax, + [&](int,const QByteArray& pcm) { audio.append(pcm); }); + QObject::connect(&dax,&DaxReceiveModel::channelReset,&dax,[&](int) { ++resets; }); + dax.setEnabled(true); + PcmProducer producer,competing; + producer.start(PcmPurpose::Slice,17,{48000,PcmLayout::Stereo}); + f.feed(17,frame(producer,128,0.8f,0.8f)); + check(audio.isEmpty(),"DAX conversion retains a bounded partial source block"); + const PcmFrame stale=frame(producer,128,0.8f,0.8f); + producer.setFormat({24000,PcmLayout::Stereo}); + f.feed(17,stale); + check(audio.isEmpty(),"revoked queued epoch cannot complete old DAX converter staging"); + f.feed(17,frame(producer,4096,0,0)); + check(!audio.isEmpty() && std::all_of(audio.cbegin(),audio.cend(),[](char byte) {return byte==0;}), + "24k epoch starts silent with no retained 48k signal history"); + const qsizetype beforeCompeting=audio.size(); + competing.start(PcmPurpose::Slice,17,{24000,PcmLayout::Stereo}); + f.feed(17,frame(competing,4096)); + check(audio.size()==beforeCompeting,"a competing live producer cannot replace a pinned native DAX source"); + audio.clear(); + producer.setFormat({24000,PcmLayout::Mono}); + const auto mono=producer.produce({0.125f,-0.25f}); + f.feed(17,*mono); + float samples[4]{}; + if(audio.size()==int(sizeof(samples))) {std::memcpy(samples,audio.constData(),sizeof(samples));} + check(audio.size()==int(sizeof(samples)) && samples[0]==0.125f && samples[1]==0.125f + && samples[2]==-0.25f && samples[3]==-0.25f, + "native mono is deliberately duplicated into the bridge stereo ABI"); + audio.clear(); + const auto stillLive=producer.produce({0.5f,0.5f}); + f.replaceBackend(); f.backend->add(17); + f.feed(17,*stillLive); + check(audio.isEmpty(),"backend replacement retires old native DAX route even with a still-live producer"); + producer.invalidate(); producer.start(PcmPurpose::Slice,17); + f.feed(17,frame(producer,16)); + check(!audio.isEmpty() && resets>=4,"new backend route resumes after independent lifecycle resets"); + audio.clear(); + f.backend->exportAvailable=false; + emit f.model.capabilitiesChanged(f.model.isConnected(),f.backend->capabilities()); + f.feed(17,frame(producer,16)); + check(audio.isEmpty(),"withdrawing native export capability immediately refuses further DAX audio"); + f.backend->exportAvailable=true; + emit f.model.capabilitiesChanged(f.model.isConnected(),f.backend->capabilities()); + f.feed(17,frame(producer,16)); + check(!audio.isEmpty(),"redeclared native export resumes with a reset converter"); + audio.clear(); + bool revoke=true; + QObject::connect(&dax,&DaxReceiveModel::audioReady,&dax,[&](int,const QByteArray&) { + if(revoke) {revoke=false; producer.invalidate();} + }); + f.feed(17,frame(producer,4096)); + check(audio.size()==int(1024*2*sizeof(float)), + "revocation from a consumer callback prevents every remaining block of the same DAX input"); + } + + static void serverConstructedAfterReceiverRestore() + { + RadioModel model; + auto backend = std::make_unique(); + InjectedBackend* source = backend.get(); + model.setBackendForTest(std::move(backend), QStringLiteral("rtl")); + for (int id = 0; id < 8; ++id) { source->add(id); } + // MainWindow can construct the session's TCI server after the radio + // restored its slices. Their sliceAdded signals have already fired. + TciServer server(&model); + source->remove(3); + QEventLoop settle; + QTimer::singleShot(550, &settle, &QEventLoop::quit); + settle.exec(); + for (int id : {0, 1, 2, 4, 5, 6, 7}) { + const auto binding = server.sliceRxBinding(id); + check(binding.current() && binding.trx == id, + "late-constructed TCI server preserves surviving receiver assignments"); + } + for (const QJsonValue& value : server.routingSnapshot().value(QStringLiteral("endpoints")).toArray()) { + const QJsonObject endpoint = value.toObject(); + const int id = endpoint.value(QStringLiteral("sliceId")).toInt(); + check(endpoint.value(QStringLiteral("trx")).toInt() == server.sliceRxBinding(id).trx, + "routing diagnostics report the same stable receiver assignment as audio"); + } + source->add(99); + const auto replacement = server.sliceRxBinding(99); + check(replacement.current() && replacement.trx == 3, + "late-constructed TCI server reuses the actual removed receiver slot"); + } + + static void simultaneousEightReceiverHandshakes() + { + Fixture f; + for (int id = 0; id < 8; ++id) { f.backend->add(id); } + // Exercise the production mailbox with its existing worker paused. + // No server/socket is opened and no firmware peer is simulated. + QSemaphore paused; + QSemaphore resume; + f.server.m_io->m_accepting = true; + f.server.m_ioThread = std::make_unique(); + f.server.m_io->moveToThread(f.server.m_ioThread.get()); + f.server.m_ioThread->start(); + QMetaObject::invokeMethod(f.server.m_io.get(), [&] { + paused.release(); + resume.acquire(); + }, Qt::QueuedConnection); + paused.acquire(); + f.server.m_running.store(true); + for (int id = 0; id < 8; ++id) { + f.server.onClientOpened(std::make_shared(), + QHostAddress(QStringLiteral("192.0.2.1")), 50000 + id); + } + { + QMutexLocker lock(&f.server.m_io->m_mailboxMutex); + check(!f.server.m_io->m_overloaded, + "eight full receiver handshakes fit the unchanged bounded worker mailbox"); + check(f.server.m_io->m_mailboxBytes > 0, + "handshake payload bytes remain charged to the mailbox bound"); + } + check(f.server.m_clients.size() == 8, "eight simultaneous clients remain registered"); + resume.release(); + f.server.stop(); + } + static int run() { + serverConstructedAfterReceiverRestore(); + simultaneousEightReceiverHandshakes(); + eightNativeDaxConsumers(); nativeDaxEpochBoundaries(); ingressOutlivesController(); rateMatrixAndStereo(); unsupportedRatePreservesStream(); sparseRoutingAndSingleFeed(); formatEncoding(); nonFiniteInt16IsSilence(); replayAndEpochs(); resetIsolation(); subscriptionAndForwardGapStaging(); retiredRouteAndCapacity(); daxLifecycle(); daxOwnerTransition(); staleFinalCheckAndChurn(); levelCallbackRetirement(); negotiationClientChurn(); lifecycleChurnAndConcurrentRevocation(); failedSendIsolation(); failedSendSocketDeletion(); backlogDiagnostics(); refusalCloseDistinction(); daxRouteSurvivesSliceRecreate(); daxRouteSurvivesStreamUnregister(); pressureAndReplacement(); diff --git a/tests/tests.cmake b/tests/tests.cmake index 301be24e8..efb3caa26 100644 --- a/tests/tests.cmake +++ b/tests/tests.cmake @@ -162,6 +162,12 @@ target_link_libraries(decoder_audio_routing_test PRIVATE aethercore Qt6::Core) add_test(NAME decoder_audio_routing_test COMMAND decoder_audio_routing_test) set_tests_properties(decoder_audio_routing_test PROPERTIES TIMEOUT 30) +# Injected typed PCM and the real modem worker; no sockets, sound device or RF. +add_executable(ax25_receive_model_test tests/ax25_receive_model_test.cpp) +target_link_libraries(ax25_receive_model_test PRIVATE aethercore Qt6::Core) +add_test(NAME ax25_receive_model_test COMMAND ax25_receive_model_test) +set_tests_properties(ax25_receive_model_test PROPERTIES TIMEOUT 60) + # CW waveform at the selected pre-monitor boundary; no decoder, socket or TX. add_executable(cw_pcm_consumer_test tests/cw_pcm_consumer_test.cpp) target_link_libraries(cw_pcm_consumer_test PRIVATE aethercore Qt6::Core) @@ -1756,6 +1762,14 @@ if(AETHER_BACKEND_RTL) set_tests_properties(rtl_capture_worker_slow_device_test PROPERTIES TIMEOUT 60 ENVIRONMENT "AETHER_TEST_RTL_CALLBACK_DELAY_MS=30") + # Independent AX25/Bell202 CU8 fixture through actual RTL worker and model. + # USB device operations are injected; no sockets, USB enumeration or RF. + add_executable(rtl_aprs_receive_test tests/rtl_aprs_receive_test.cpp) + target_include_directories(rtl_aprs_receive_test PRIVATE src tests) + target_link_libraries(rtl_aprs_receive_test PRIVATE aethercore Qt6::Core) + add_test(NAME rtl_aprs_receive_test COMMAND rtl_aprs_receive_test) + set_tests_properties(rtl_aprs_receive_test PROPERTIES TIMEOUT 60) + # Generated IQ only: genuine resolution, continuity, gain and detector independence. add_executable(rtl_spectrum_resolution_test tests/rtl_spectrum_resolution_test.cpp) target_include_directories(rtl_spectrum_resolution_test PRIVATE src) @@ -4891,6 +4905,9 @@ if(PYTHON3_EXECUTABLE) # next bool from the count, and a bool added after one was never seen at # all. Running the checker against the real header cannot see either — # which is how the bug survived two review rounds on #5619. + add_test(NAME rtl_package_closure_test + COMMAND ${PYTHON3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/test_check_rtl_package.py) add_test(NAME capability_record_parser COMMAND ${PYTHON3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/test_check_capability_records.py) @@ -7733,6 +7750,35 @@ add_test(NAME rx_applet_squelch_reconciliation_test set_tests_properties(rx_applet_squelch_reconciliation_test PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") +# Socket-free real VFO/SmartMTR paint and label invalidation regression. +add_executable(vfo_meter_overlay_invalidation_test + tests/vfo_meter_overlay_invalidation_test.cpp + src/gui/RxApplet.cpp + src/gui/ControlAvailabilityRegistry.cpp + src/gui/VfoWidget.cpp + src/gui/ModeFilterPresets.cpp + src/gui/VfoDisplayDefaults.cpp + src/gui/FrequencyEntryParser.cpp + src/gui/DragValuePopup.cpp + src/gui/FilterPassbandWidget.cpp + src/gui/SliceColorManager.cpp + src/gui/SliceLabel.cpp + src/gui/PhaseKnob.cpp + src/gui/SmartMtrWidget.cpp + src/gui/SmartMtrConfig.cpp + src/gui/MeterViewController.cpp + src/gui/AdaptiveFilterControls.cpp + src/gui/GuardedSlider.h +) +target_include_directories(vfo_meter_overlay_invalidation_test PRIVATE src) +target_link_libraries(vfo_meter_overlay_invalidation_test PRIVATE + aethercore Qt6::Widgets Qt6::Test +) +add_test(NAME vfo_meter_overlay_invalidation_test + COMMAND vfo_meter_overlay_invalidation_test) +set_tests_properties(vfo_meter_overlay_invalidation_test PROPERTIES + ENVIRONMENT "QT_QPA_PLATFORM=offscreen") + # FM filter capability and edge routing through the real RX and VFO widgets. # Injects capability data; no socket, USB access or synthetic firmware peer. add_executable(fm_filter_controls_test @@ -8059,7 +8105,20 @@ target_link_libraries(CAT_Flex_test PRIVATE Qt6::Core Qt6::Network) # Every standalone test/tool target that takes $ # (rather than linking aethercore) needs the vendored SQLite engine. # Conditional targets are guarded with if(TARGET ...). +# Real applet with injected receive capabilities; no sockets/audio devices. +if(NOT WIN32) + add_executable(dax_receive_applet_test tests/dax_receive_applet_test.cpp + src/gui/DaxApplet.cpp src/gui/MeterSlider.h src/gui/DragValuePopup.cpp + src/gui/SliceLabel.cpp src/gui/SliceColorManager.cpp + src/gui/ControlAvailabilityRegistry.cpp) + target_include_directories(dax_receive_applet_test PRIVATE src tests) + target_link_libraries(dax_receive_applet_test PRIVATE aetherdesktop_support Qt6::Widgets) + add_test(NAME dax_receive_applet_test COMMAND dax_receive_applet_test) + set_tests_properties(dax_receive_applet_test PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") +endif() + set(AETHER_SETTINGS_CONSUMERS + dax_receive_applet_test reroute_dead_controls_test hl2_bandstack_digital_agc_test radio_model_client_state_test @@ -8072,6 +8131,8 @@ set(AETHER_SETTINGS_CONSUMERS tci_rx_audio_test bandscope_trace_render_test decoder_audio_routing_test + ax25_receive_model_test + rtl_aprs_receive_test cw_pcm_consumer_test noise_floor_auto_adjust_gate_test qso_recorder_rates_test @@ -8109,6 +8170,7 @@ set(AETHER_SETTINGS_CONSUMERS spectrum_overlay_fft_fps_bounds_test gui_nested_lifetime_test rx_applet_squelch_reconciliation_test + vfo_meter_overlay_invalidation_test fm_filter_controls_test wfm_controls_test spectrum_confirmed_geometry_test @@ -8851,3 +8913,11 @@ if(APPLE) target_link_libraries(sleep_inhibitor_backend_test PRIVATE "-framework IOKit" "-framework CoreFoundation") endif() add_test(NAME sleep_inhibitor_backend_test COMMAND sleep_inhibitor_backend_test) + +# Anonymous-pipe output injection: no sockets, audio devices or helper processes. +if(HAVE_PIPEWIRE) + add_executable(pipewire_rx_bridge_test tests/pipewire_rx_bridge_test.cpp) + target_include_directories(pipewire_rx_bridge_test PRIVATE src) + target_link_libraries(pipewire_rx_bridge_test PRIVATE aethercore Qt6::Core) + add_test(NAME pipewire_rx_bridge_test COMMAND pipewire_rx_bridge_test) +endif() diff --git a/tests/vfo_meter_overlay_invalidation_test.cpp b/tests/vfo_meter_overlay_invalidation_test.cpp new file mode 100644 index 000000000..b92c3d983 --- /dev/null +++ b/tests/vfo_meter_overlay_invalidation_test.cpp @@ -0,0 +1,189 @@ +// A moving in-flag bar must not invalidate the spectrum's separate value-label +// overlay when there are no labels to draw. Clearing the last label must still +// invalidate immediately, even inside the animation notification throttle. +#include "TestSettingsProfile.h" +#include "core/AppSettings.h" +#include "gui/MeterViewController.h" +#include "gui/SmartMtrWidget.h" +#include "gui/VfoWidget.h" + +#include +#include +#include + +using namespace AetherSDR; + +class PaintCountingVfo : public VfoWidget +{ +public: + int paints = 0; +protected: + void paintEvent(QPaintEvent* event) override + { + ++paints; + VfoWidget::paintEvent(event); + } +}; + +class VfoMeterOverlayInvalidationTest : public QObject +{ + Q_OBJECT +private slots: + void init() + { + MeterViewController::instance().setSmartMtr(true); + MeterViewController::instance().setShowExtremes(false); + MeterViewController::instance().setShowValues(DisplaySettings::MeterValues::None); + } + + void barWithoutValueLabelsDoesNotInvalidateOverlay_data() + { + QTest::addColumn("relative"); + QTest::newRow("calibrated") << false; + QTest::newRow("relative-dBFS") << true; + } + + void barWithoutValueLabelsDoesNotInvalidateOverlay() + { + QFETCH(bool, relative); + VfoWidget vfo; + vfo.show(); + SmartMtrWidget* meter = vfo.findChild(); + QVERIFY(meter); + QTest::qWait(60); + QSignalSpy invalidations(&vfo, &VfoWidget::smartMtrLabelsChanged); + const auto setLevel = [&vfo, relative](float level) { + if (relative) { + vfo.setRelativeSignalLevel(level); + } else { + vfo.setSignalLevel(level); + } + }; + setLevel(-100); + QTest::qWait(200); + const QImage low = meter->grab().toImage(); + setLevel(-20); + QTest::qWait(200); + const QImage high = meter->grab().toImage(); + QVERIFY(!low.isNull()); + QVERIFY(low != high); // the real bar still animates/renders + QVERIFY(meter->extremeLabels().isEmpty()); + QVERIFY2(invalidations.isEmpty(), "bar-only paints must not rebuild the spectrum label overlay"); + } + + void valueLabelsStillUpdateAndClearWithoutWaitingForThrottle() + { + MeterViewController::instance().setShowValues(DisplaySettings::MeterValues::Signal); + VfoWidget vfo; + vfo.show(); + SmartMtrWidget* meter = vfo.findChild(); + QVERIFY(meter); + QSignalSpy invalidations(&vfo, &VfoWidget::smartMtrLabelsChanged); + vfo.setRelativeSignalLevel(-60); + QTest::qWait(60); + meter->grab(); + QVERIFY(!invalidations.isEmpty()); + QCOMPARE(meter->extremeLabels().size(), 1); + QVERIFY(meter->extremeLabels().front().primary.contains(QStringLiteral("dBFS"))); + + // The immediately following disappearance must erase the overlay even + // when the preceding paint has just consumed the normal throttle slot. + invalidations.clear(); + vfo.setRelativeSignalLevel(std::nullopt); + meter->grab(); + QCOMPARE(invalidations.size(), 1); + QVERIFY(meter->extremeLabels().isEmpty()); + invalidations.clear(); + QTest::qWait(60); + meter->grab(); + QVERIFY(invalidations.isEmpty()); // no repeated clear redraws + + vfo.setRelativeSignalLevel(-40); + QTest::qWait(60); + meter->grab(); + QVERIFY(!invalidations.isEmpty()); + QCOMPARE(meter->extremeLabels().size(), 1); + invalidations.clear(); + vfo.setSignalLevel(-73); + QTest::qWait(60); + meter->grab(); + QVERIFY(!invalidations.isEmpty()); + QVERIFY(meter->extremeLabels().front().secondary.contains(QStringLiteral("dBm"))); + } + + void unavailableRelativeMetersStayQuietAndRecover() + { + PaintCountingVfo vfo; + vfo.show(); + SmartMtrWidget* meter = vfo.findChild(); + QVERIFY(meter); + vfo.setRelativeSignalLevel(-20); + QTest::qWait(100); + const QImage live = meter->grab().toImage(); + vfo.setRelativeSignalLevel(std::nullopt, QStringLiteral("Receive meters disabled")); + QTest::qWait(60); + const QImage off = meter->grab().toImage(); + QVERIFY(live != off); + QCOMPARE(meter->accessibleDescription(), QStringLiteral("Receive meters disabled")); + QVERIFY(meter->extremeLabels().isEmpty()); + QTest::qWait(60); + vfo.paints = 0; + for (int i = 0; i < 4; ++i) { + vfo.setRelativeSignalLevel(std::nullopt, QStringLiteral("Receive meters disabled")); + QTest::qWait(50); + } + QCOMPARE(vfo.paints, 0); + vfo.setRelativeSignalLevel(-40); + QTest::qWait(100); + QVERIFY(meter->grab().toImage() != off); + QVERIFY(meter->accessibleDescription().isEmpty()); + } + + void inactiveStandardMeterDoesNotAnimateParent() + { + PaintCountingVfo vfo; + vfo.show(); + SmartMtrWidget* meter = vfo.findChild(); + QVERIFY(meter); + // Isolate parent repaints from the visible child's legitimate animation. + meter->hide(); + vfo.setRelativeSignalLevel(-10); + QTest::qWait(300); + vfo.setRelativeSignalLevel(-110); + QTest::qWait(60); + vfo.paints = 0; + QTest::qWait(200); + QCOMPARE(vfo.paints, 0); + + MeterViewController::instance().setSmartMtr(false); + QTest::qWait(60); + vfo.setRelativeSignalLevel(-10); + QTest::qWait(60); + vfo.paints = 0; + vfo.setRelativeSignalLevel(-110); + QTest::qWait(200); + QVERIFY(vfo.paints > 1); // the selected standard meter still animates + + // Switching away must stop an already-running standard animation too. + MeterViewController::instance().setSmartMtr(true); + meter->hide(); + QTest::qWait(60); + vfo.paints = 0; + QTest::qWait(200); + QCOMPARE(vfo.paints, 0); + } +}; + +int main(int argc, char** argv) +{ + TestSettingsProfile profile(QStringLiteral("vfo-meter-overlay-invalidation")); + if (!profile.isValid()) { + return 1; + } + QApplication app(argc, argv); + AppSettings::instance().load(); + VfoMeterOverlayInvalidationTest test; + return QTest::qExec(&test, argc, argv); +} + +#include "vfo_meter_overlay_invalidation_test.moc" diff --git a/tests/wfm_controls_test.cpp b/tests/wfm_controls_test.cpp index 24ed2a607..fa8250044 100644 --- a/tests/wfm_controls_test.cpp +++ b/tests/wfm_controls_test.cpp @@ -14,6 +14,7 @@ #include "models/SliceModel.h" #include +#include #include #include #include @@ -476,6 +477,203 @@ private slots: QVERIFY(!rx.findChild(QStringLiteral("broadcastFmControls"))); } + void receiverIdentityAndRecipesFollowSelectionWithoutCommands() + { + RadioModel model; + WfmBackend* source = attachBackend(model); + SliceDelta first = initialWfm(); + first.letter = QStringLiteral("A"); + first.wfmForceMono = true; + emit source->sliceChanged(3, first); + SliceDelta second = initialWfm(); + second.letter = QStringLiteral("B"); + second.frequency = 100.9; + second.wfmDeemphasisUs = 50; + second.filterLow = -70000; + second.filterHigh = 80000; + emit source->sliceChanged(4, second); + WfmApplet applet; + applet.setRadioModel(&model); + applet.setSlice(model.slice(3)); + auto* identity = applet.findChild(QStringLiteral("wfmReceiverIdentity")); + auto* deemphasis = applet.findChild(QStringLiteral("wfmDeemphasis")); + auto* bandwidth = applet.findChild(QStringLiteral("wfmBandwidth")); + auto* cycle = applet.findChild(QStringLiteral("wfmAudioMode")); + auto* status = applet.findChild(QStringLiteral("wfmStereoStatus")); + QVERIFY(identity && deemphasis && bandwidth && cycle && status); + QCOMPARE(identity->text(), QStringLiteral("Slice A · 100.300000 MHz")); + QCOMPARE(identity->textFormat(), Qt::PlainText); + QVERIFY(identity->accessibleName().contains(identity->text())); + for (int pass = 0; pass < 20; ++pass) { + applet.setSlice(model.slice(4)); + QCOMPARE(identity->text(), QStringLiteral("Slice B · 100.900000 MHz")); + QCOMPARE(deemphasis->currentData().toInt(), 50); + QCOMPARE(bandwidth->currentText(), QStringLiteral("150 kHz (custom)")); + QCOMPARE(cycle->text(), QStringLiteral("Auto Stereo")); + applet.setSlice(model.slice(3)); + QCOMPARE(identity->text(), QStringLiteral("Slice A · 100.300000 MHz")); + QCOMPARE(deemphasis->currentData().toInt(), 75); + QCOMPARE(bandwidth->currentData().toInt(), 180000); + QCOMPARE(cycle->text(), QStringLiteral("Mono")); + } + QVERIFY(source->requests.isEmpty()); + QVERIFY(source->monoRequests.isEmpty()); + QVERIFY(source->monoRequests.isEmpty()); + QTest::keyClick(deemphasis, Qt::Key_Up); + QCOMPARE(source->requests.size(), 1); + QCOMPARE(source->requests.last(), qMakePair(3, 50)); + QCOMPARE(model.slice(3)->wfmDeemphasisUs(), 75); + QCOMPARE(model.slice(4)->wfmDeemphasisUs(), 50); + SliceDelta parked; + parked.inCapture = false; + emit source->sliceChanged(3, parked); + QCOMPARE(identity->text(), QStringLiteral("Slice A · 100.300000 MHz")); + QCOMPARE(cycle->text(), QStringLiteral("Mono")); + QCOMPARE(deemphasis->currentData().toInt(), 75); + QCOMPARE(status->text(), QStringLiteral("Unavailable")); + source->connected = false; + emit model.connectionStateChanged(false); + QCOMPARE(identity->text(), QStringLiteral("No selected WFM receiver")); + source->connected = true; + emit model.connectionStateChanged(true); + QCOMPARE(identity->text(), QStringLiteral("Slice A · 100.300000 MHz")); + model.slice(3)->invalidateFrequencyObservation(); + QCOMPARE(identity->text(), QStringLiteral("Slice A · Frequency unavailable")); + SliceDelta restored; + restored.frequency = 100.3; // same-value report must refresh identity too + restored.letter = QStringLiteral("Local receiver"); + emit source->sliceChanged(3, restored); + QCOMPARE(identity->text(), QStringLiteral("Slice Local receiver · 100.300000 MHz")); + emit source->sliceRemoved(3); + QCOMPARE(identity->text(), QStringLiteral("No selected WFM receiver")); + emit source->sliceChanged(3, first); + QCOMPARE(identity->text(), QStringLiteral("No selected WFM receiver")); + } + + void popupAndHeldButtonCannotTargetANewSelectionOrReusedId() + { + RadioModel model; + WfmBackend* source = attachBackend(model); + SliceDelta analog = initialWfm(); + emit source->sliceChanged(3, analog); + emit source->sliceChanged(4, analog); + WfmApplet applet; + applet.setRadioModel(&model); + applet.setSlice(model.slice(3)); + applet.show(); + QSignalSpy firstFilter(model.slice(3), &SliceModel::filterCommandIssued); + QSignalSpy secondFilter(model.slice(4), &SliceModel::filterCommandIssued); + for (const QString& name : {QStringLiteral("wfmDeemphasis"), QStringLiteral("wfmBandwidth")}) { + auto* combo = applet.findChild(name); + QVERIFY(combo && combo->isEnabled()); + applet.setSlice(model.slice(3)); + combo->showPopup(); + QVERIFY(combo->view()->isVisible()); + const int staleIndex = combo->currentIndex() == 0 ? 1 : 0; + applet.setSlice(model.slice(4)); + QVERIFY(!combo->view()->isVisible()); + // Qt's activation can follow popup closure; carry the opening + // identity through that interval, including identical menu rows. + const int acceptedIndex = combo->currentIndex(); + combo->setCurrentIndex(staleIndex); + QVERIFY(QMetaObject::invokeMethod(combo, "activated", Q_ARG(int, staleIndex))); + QCOMPARE(combo->currentIndex(), acceptedIndex); + } + QVERIFY(source->requests.isEmpty()); + QVERIFY(firstFilter.isEmpty()); + QVERIFY(secondFilter.isEmpty()); + auto* cycle = applet.findChild(QStringLiteral("wfmAudioMode")); + QVERIFY(cycle); + applet.setSlice(model.slice(3)); + QTest::mousePress(cycle, Qt::LeftButton); + applet.setSlice(model.slice(4)); + QTest::mouseRelease(cycle, Qt::LeftButton); + QVERIFY(source->monoRequests.isEmpty()); + cycle->click(); + QCOMPARE(source->monoRequests.size(), 1); + QCOMPARE(source->monoRequests.last().first, 4); + auto* deemphasis = applet.findChild(QStringLiteral("wfmDeemphasis")); + applet.setSlice(model.slice(3)); + deemphasis->showPopup(); + std::thread removalNotice([&model] { emit model.sliceRemoved(3); }); + removalNotice.join(); + emit source->sliceRemoved(3); + emit source->sliceChanged(3, analog); + applet.setSlice(model.slice(3)); + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + QVERIFY(applet.isAvailable()); + QVERIFY(QMetaObject::invokeMethod(deemphasis, "activated", Q_ARG(int, 0))); + QVERIFY(source->requests.isEmpty()); + QTest::keyClick(deemphasis, Qt::Key_Up); + QCOMPARE(source->requests.size(), 1); + QCOMPARE(source->requests.last(), qMakePair(3, 50)); + } + + void queuedOldSliceCallbacksCannotClearOrAppendNewScopeHistory() + { + RadioModel model; + WfmBackend* source = attachBackend(model); + emit source->sliceChanged(4, initialWfm()); + SliceModel* oldSlice = model.slice(3); + WfmApplet applet; + applet.setRadioModel(&model); + applet.setSlice(oldSlice); + applet.findChild(QStringLiteral("wfmShowLockScope"))->setChecked(true); + applet.show(); + auto* scope = applet.findChild(); + QVERIFY(scope && scope->isVisible()); + std::thread queued([oldSlice] { + emit oldSlice->frequencyChanged(100.7); + emit oldSlice->wfmForceMonoChanged(true); + emit oldSlice->wfmReceptionDiagnosticsChanged(measuredPilot()); + }); + queued.join(); + applet.setSlice(model.slice(4)); + SliceDelta measured; + measured.wfmStereoStatus = WfmStereoStatus::Stereo; + measured.wfmReceptionDiagnostics = measuredPilot(true); + emit source->sliceChanged(4, measured); + QCOMPARE(scope->sampleCount(), 1); + ++measured.wfmReceptionDiagnostics->observationSequence; + emit source->sliceChanged(4, measured); + QCOMPARE(scope->sampleCount(), 2); + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + QCOMPARE(scope->sampleCount(), 2); + auto* status = applet.findChild(QStringLiteral("wfmStereoStatus")); + QCOMPARE(status->text(), QStringLiteral("Stereo")); + + // Returning to the same pointer must not revive the previous binding. + applet.setSlice(oldSlice); + std::thread returning([oldSlice] { emit oldSlice->frequencyChanged(100.9); }); + returning.join(); + applet.setSlice(model.slice(4)); + applet.setSlice(oldSlice); + emit source->sliceChanged(3, measured); + QCOMPARE(scope->sampleCount(), 1); + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + QCOMPARE(scope->sampleCount(), 1); + } + + void queuedPreviousModelStatusCannotRepaintCurrentBinding() + { + RadioModel first; + attachBackend(first); + RadioModel second; + attachBackend(second); + WfmApplet applet; + applet.setRadioModel(&first); + applet.setSlice(first.slice(3)); + std::thread queued([&first] { emit first.capabilitiesChanged(false, RadioCapabilities{}); }); + queued.join(); + applet.setRadioModel(&second); + applet.setSlice(second.slice(3)); + QVERIFY(applet.isAvailable()); + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + QVERIFY(applet.isAvailable()); + auto* identity = applet.findChild(QStringLiteral("wfmReceiverIdentity")); + QCOMPARE(identity->text(), QStringLiteral("Slice D · 100.300000 MHz")); + } + void audioCycleWaitsForAcceptanceAndPreservesObservedPilot() { RadioModel model; diff --git a/tools/check_rtl_package.py b/tools/check_rtl_package.py new file mode 100644 index 000000000..01e20b249 --- /dev/null +++ b/tools/check_rtl_package.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +"""Assert that a staged RTL application actually links its shipped dependencies.""" +import argparse +from pathlib import Path +import re +import subprocess + +import os + + +def check_windows_runtime(app, libraries, system_dir, read_imports): + """Resolve the RTL dependency closure beside the EXE or in Windows itself. + + Never accept a build-tool directory on PATH, or a DLL hidden in a staging + subdirectory: neither is a portable application dependency. + """ + app_dir = app.parent.resolve() + staged = {path.name.lower(): path for path in app_dir.iterdir() if path.is_file()} + system = {path.name.lower() for path in system_dir.iterdir() if path.is_file()} + pending = list(libraries) + checked = set() + while pending: + binary = pending.pop() + if binary.parent.resolve() != app_dir: + raise ValueError(f"RTL runtime is not beside the executable: {binary}") + key = binary.name.lower() + if key in checked: + continue + checked.add(key) + # dumpbin emits one DLL name per line, for both normal and delay imports. + names = re.findall(r"^\s*([A-Za-z0-9_.+-]+\.dll)\s*$", read_imports(binary), re.MULTILINE | re.IGNORECASE) + if not names: + raise ValueError(f"No DLL imports found while inspecting {binary}") + for name in names: + key = name.lower() + if key in staged: + pending.append(staged[key]) + elif key in system or key.startswith(("api-ms-win-", "ext-ms-win-")): + continue + else: + raise ValueError(f"Unreachable RTL runtime dependency: {binary.name}: {name}") + return sorted(checked) + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("root", type=Path) + p.add_argument("--platform", choices=("macos", "linux", "windows"), required=True) + a = p.parse_args() + root = a.root.resolve() + patterns = {"macos": ("librtlsdr*.dylib", "libusb-1.0*.dylib", "libfftw3f*.dylib"), + "linux": ("librtlsdr.so*", "libusb-1.0.so*", "libfftw3f.so*"), + "windows": ("rtlsdr.dll", "libusb-1.0.dll", "libfftw3f-3.dll")}[a.platform] + libs = [] + for pattern in patterns: + found = sorted(x for x in root.rglob(pattern) if x.is_file()) + if not found: + raise SystemExit(f"Missing RTL package dependency: {pattern}") + libs.append(found[0]) + app_name = "AetherSDR.exe" if a.platform == "windows" else "AetherSDR" + apps = [x for x in root.rglob("*") if x.is_file() and x.name in ({"AetherSDR", "aethersdr"} if a.platform == "linux" else {app_name})] + if len(apps) != 1: + raise SystemExit(f"Expected one {app_name}, got {apps}") + def imports(path): + command = {"macos": ["otool", "-L"], "linux": ["readelf", "-d"], + "windows": ["dumpbin", "/nologo", "/dependents"]}[a.platform] + return subprocess.check_output(command + [str(path)], text=True).lower() + app_imports = imports(apps[0]) + for token in ("rtlsdr", "fftw3f"): + if token not in app_imports: + raise SystemExit(f"Application does not link {token}") + if "libusb" not in imports(libs[0]): + raise SystemExit("RTL driver does not link libusb") + if a.platform == "macos": + executable_dir = apps[0].parent + def rpaths(path): + data = subprocess.check_output(["otool", "-l", str(path)], text=True) + return re.findall(r"cmd LC_RPATH\s+cmdsize \d+\s+path (.*?) \(offset", data) + def expand(value, loader): + return Path(value.replace("@loader_path", str(loader.parent)).replace("@executable_path", str(executable_dir))) + main_rpaths = [expand(value, apps[0]) for value in rpaths(apps[0])] + magic = (b"\xfe\xed\xfa\xce", b"\xce\xfa\xed\xfe", b"\xfe\xed\xfa\xcf", b"\xcf\xfa\xed\xfe", b"\xca\xfe\xba\xbe", b"\xbe\xba\xfe\xca") + for path in root.rglob("*"): + if not path.is_file() or path.is_symlink(): + continue + with path.open("rb") as handle: + if handle.read(4) not in magic: + continue + search = [expand(value, path) for value in rpaths(path)] + main_rpaths + ids = subprocess.check_output(["otool", "-D", str(path)], text=True).splitlines()[1:] + own_id = ids[0].strip() if ids else None + for line in subprocess.check_output(["otool", "-L", str(path)], text=True).splitlines()[1:]: + name = line.strip().split(" (", 1)[0] + if name == own_id or name.startswith(("/usr/lib/", "/System/Library/")): + continue + candidates = [base / name[7:] for base in search] if name.startswith("@rpath/") else [expand(name, path)] + if not any(x.exists() and x.resolve().is_relative_to(root) for x in candidates): + raise SystemExit(f"Unreachable runtime dependency: {path}: {name}") + if a.platform == "linux": + # AppRun supplies usr/lib. Resolve the actual application/driver imports + # through that directory; a copy hidden elsewhere is not sufficient. + libdir = root / "usr/lib" + for binary in (apps[0], libs[0]): + needed = re.findall(r"Shared library: \[(.*?)\]", subprocess.check_output(["readelf", "-d", str(binary)], text=True)) + for name in needed: + if any(token in name for token in ("rtlsdr", "fftw3f", "libusb")) and not (libdir / name).is_file(): + raise SystemExit(f"Unreachable RTL dependency: {binary}: {name}") + if a.platform == "windows": + system_root = os.environ.get("SystemRoot") + if not system_root: + raise SystemExit("SystemRoot is required to identify Windows system DLLs") + try: + checked = check_windows_runtime(apps[0], libs, Path(system_root) / "System32", imports) + except ValueError as error: + raise SystemExit(str(error)) from error + print("RTL-WINDOWS-RUNTIME-CLOSURE:", ", ".join(checked)) + print("RTL-PACKAGE-OK:", root) + + +if __name__ == "__main__": + main() diff --git a/tools/test_check_rtl_package.py b/tools/test_check_rtl_package.py new file mode 100644 index 000000000..7e0a48b49 --- /dev/null +++ b/tools/test_check_rtl_package.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +"""Socket-free package closure checks using injected dumpbin reports.""" +from pathlib import Path +import tempfile +import unittest + +from check_rtl_package import check_windows_runtime + + +class WindowsRuntimeTest(unittest.TestCase): + def setUp(self): + self.temporary = tempfile.TemporaryDirectory() + self.addCleanup(self.temporary.cleanup) + self.root = Path(self.temporary.name) + self.app = self.root / "AetherSDR.exe" + self.app.touch() + self.system = self.root / "System32" + self.system.mkdir() + (self.system / "KERNEL32.dll").touch() + self.libraries = [self.root / name for name in + ("rtlsdr.dll", "libusb-1.0.dll", "libfftw3f-3.dll")] + for path in self.libraries: + path.touch() + self.reports = { + "rtlsdr.dll": " LIBUSB-1.0.dll\n pthreadVC3.dll\n KERNEL32.dll\n", + "libusb-1.0.dll": " KERNEL32.dll\n api-ms-win-crt-runtime-l1-1-0.dll\n", + "libfftw3f-3.dll": " KERNEL32.dll\n", + "pthreadvc3.dll": " helper.dll\n KERNEL32.dll\n", + "helper.dll": " pthreadVC3.dll\n KERNEL32.dll\n", + } + + def check(self): + return check_windows_runtime(self.app, self.libraries, self.system, + lambda path: self.reports[path.name.lower()]) + + def test_transitive_missing_dependency(self): + with self.assertRaisesRegex(ValueError, "pthreadVC3.dll"): + self.check() + # A nested copy does not satisfy the executable's loader search path. + nested = self.root / "unused" + nested.mkdir() + (nested / "pthreadVC3.dll").touch() + with self.assertRaisesRegex(ValueError, "pthreadVC3.dll"): + self.check() + (self.root / "pthreadVC3.dll").touch() + with self.assertRaisesRegex(ValueError, "helper.dll"): + self.check() + (self.root / "helper.dll").touch() + self.assertEqual(len(self.check()), 5) # Includes a cycle, visited once. + + def test_system_dependencies_need_no_package_copy(self): + self.reports["rtlsdr.dll"] = " libusb-1.0.dll\n KERNEL32.dll\n" + self.assertEqual(len(self.check()), 3) + + def test_unparseable_imports_fail_closed(self): + self.reports["libfftw3f-3.dll"] = "unexpected dumpbin output" + with self.assertRaisesRegex(ValueError, "No DLL imports"): + self.check() + + def test_runtime_must_be_beside_executable(self): + nested = self.root / "unused" + nested.mkdir() + self.libraries[0] = nested / "rtlsdr.dll" + self.libraries[0].touch() + with self.assertRaisesRegex(ValueError, "not beside"): + self.check() + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/test_require_rtl.py b/tools/test_require_rtl.py new file mode 100644 index 000000000..4337a9258 --- /dev/null +++ b/tools/test_require_rtl.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +"""Exercise release fail-closed versus deliberate optional developer builds.""" +import itertools +from pathlib import Path +import subprocess +import tempfile +module = Path(__file__).resolve().parents[1] / "cmake/AetherRequireRtl.cmake" +with tempfile.TemporaryDirectory() as tmp: + script = Path(tmp) / "probe.cmake" + script.write_text(f'include("{module.as_posix()}")\naether_require_rtl()\n') + for required, enabled, rtl, fftw in itertools.product((False, True), repeat=4): + values = dict(REQUIRE_RTL=required, ENABLE_RTL=enabled, RTLSDR_FOUND=rtl, RTL_FFTW3F_FOUND=fftw) + args = ["cmake"] + [f"-D{k}={'ON' if v else 'OFF'}" for k, v in values.items()] + result = subprocess.run(args + ["-P", str(script)], capture_output=True, text=True) + expected = not required or (enabled and rtl and fftw) + assert (result.returncode == 0) == expected, (values, result.stdout, result.stderr) +print("16 RTL requirement combinations passed") From a84233e55418125d95c4a2b28ddb32596abdec3f Mon Sep 17 00:00:00 2001 From: Ozy311 Date: Mon, 5 Oct 2026 18:40:01 -0700 Subject: [PATCH 2/9] Bundle pinned AppImage libusb by SONAME. Principle XI. Stage the pinned libusb required by librtlsdr before linuxdeploy, which excludes libusb by default. Reject missing or mismatched prefix libraries and copy through the runtime SONAME without a build-prefix symlink. Keep the existing RTL closure validator and run it in its own step before image creation. Validate staging with injected ELF inspection reports and a real ELF closure/relocation fixture on Nobara. Mutation checks reject a removed SONAME guard and a build-prefix symlink. Current main already provides the full-suite disk cleanup and reduced debug information; preserve that workflow during integration. --- .github/workflows/appimage.yml | 26 +++++++------ tools/stage_rtl_appimage.py | 57 ++++++++++++++++++++++++++++ tools/test_check_rtl_package.py | 67 ++++++++++++++++++++++++++++++++- 3 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 tools/stage_rtl_appimage.py diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 858f28127..aee42088a 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -397,7 +397,7 @@ jobs: wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${{ matrix.arch }}.AppImage chmod +x linuxdeploy-${{ matrix.arch }}.AppImage linuxdeploy-plugin-qt-${{ matrix.arch }}.AppImage - - name: Build AppImage + - name: Deploy AppDir dependencies env: QT_SELECT: qt6 # EXTRA_QT_PLUGINS is the deprecated alias of EXTRA_QT_MODULES and @@ -414,13 +414,6 @@ jobs: # which the "Install to AppDir" step above does. EXTRA_PLATFORM_PLUGINS: libqwayland.so run: | - # Sanitize the ref for the output filename: a workflow_dispatch on a - # branch like "ci/appimage-qtkeychain" puts a '/' in github.ref_name, - # which breaks mksquashfs ("Could not create destination file"). Tag - # pushes (v26.6.x) are already slash-free; this mirrors the - # sanitization windows-installer.yml already applies. - SAFE_REF="${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}" - export LDAI_OUTPUT="AetherSDR-${SAFE_REF}-${{ matrix.arch }}.AppImage" # linuxdeploy-plugin-qt locates the Qt to bundle via $QMAKE. This is # the third place that used to hardcode the x86_64 ".../gcc_64" path; # it now reuses the QT_ROOT exported by the aqt step, so there is one @@ -432,13 +425,17 @@ jobs: # copy). Qt6 DBus, which the keychain backend needs at runtime, is pulled # in transitively. export LD_LIBRARY_PATH="${{ github.workspace }}/third_party/rtl-deps/lib:${{ github.workspace }}/third_party/qtkeychain/lib:${{ github.workspace }}/third_party/onnxruntime/lib:${{ github.workspace }}/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}" + # Official RTL builds ship the pinned libusb despite linuxdeploy's + # excludelist. Stage its matching SONAME before the dependency walk. + python3 tools/stage_rtl_appimage.py third_party/rtl-deps AppDir ./linuxdeploy-${{ matrix.arch }}.AppImage \ --appdir AppDir \ --plugin qt \ --desktop-file AppDir/usr/share/applications/AetherSDR.desktop \ - --icon-file AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png \ - --output appimage - python3 tools/check_rtl_package.py AppDir --platform linux + --icon-file AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png + + - name: Validate RTL AppDir runtime closure + run: python3 tools/check_rtl_package.py AppDir --platform linux # Nothing else in the build would notice these plugins going missing: Qt # dlopen()s them at runtime rather than linking them, so no dependency walk @@ -496,6 +493,13 @@ jobs: echo "Wayland integration plugin dirs:" find "$plugins" -maxdepth 1 -type d -name 'wayland-*' | sort + - name: Build AppImage + run: | + # Branch refs contain slashes, which cannot appear in the output name. + SAFE_REF="${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}" + export LDAI_OUTPUT="AetherSDR-${SAFE_REF}-${{ matrix.arch }}.AppImage" + ./linuxdeploy-${{ matrix.arch }}.AppImage --appdir AppDir --output appimage + # The archive is only worth attaching if its build IDs are the ones the # AppImage actually ships, so read them back from the packaged binaries. # .tar.xz on purpose: sign-release.yml downloads and checksums *.zip, so diff --git a/tools/stage_rtl_appimage.py b/tools/stage_rtl_appimage.py new file mode 100644 index 000000000..9e1cb73a5 --- /dev/null +++ b/tools/stage_rtl_appimage.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Stage the pinned libusb SONAME that linuxdeploy's excludelist omits.""" +import argparse +from pathlib import Path +import re +import shutil +import subprocess +import tempfile + + +def elf_dynamic(path): + return subprocess.check_output(["readelf", "-d", str(path)], text=True) + + +def stage_libusb(prefix, appdir, read_dynamic=elf_dynamic): + libdir = (prefix / "lib").resolve() + driver = libdir / "librtlsdr.so.0" + soname = "libusb-1.0.so.0" + source = libdir / soname + for path in (driver, source): + if not path.is_file() or not path.resolve().is_relative_to(libdir): + raise ValueError(f"Missing pinned RTL dependency inside {libdir}: {path.name}") + needed = re.findall(r"\(NEEDED\).*?\[(.*?)\]", read_dynamic(driver)) + if [name for name in needed if name.startswith("libusb")] != [soname]: + raise ValueError(f"Pinned RTL driver must require {soname}: {needed}") + provided = re.findall(r"\(SONAME\).*?\[(.*?)\]", read_dynamic(source)) + if provided != [soname]: + raise ValueError(f"Pinned libusb SONAME does not match {soname}: {provided}") + + destination = appdir / "usr/lib" / soname + destination.parent.mkdir(parents=True, exist_ok=True) + # Dereference the build-prefix symlink and install under the runtime SONAME. + # Replace atomically, including a previous staging symlink if one exists. + with tempfile.NamedTemporaryFile(dir=destination.parent, delete=False) as handle: + temporary = Path(handle.name) + try: + shutil.copy2(source, temporary) + temporary.replace(destination) + finally: + temporary.unlink(missing_ok=True) + return destination + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("prefix", type=Path) + parser.add_argument("appdir", type=Path) + args = parser.parse_args() + try: + destination = stage_libusb(args.prefix, args.appdir) + except ValueError as error: + raise SystemExit(str(error)) from error + print("RTL-APPIMAGE-LIBUSB:", destination) + + +if __name__ == "__main__": + main() diff --git a/tools/test_check_rtl_package.py b/tools/test_check_rtl_package.py index 7e0a48b49..96b629e97 100644 --- a/tools/test_check_rtl_package.py +++ b/tools/test_check_rtl_package.py @@ -1,10 +1,75 @@ #!/usr/bin/env python3 -"""Socket-free package closure checks using injected dumpbin reports.""" +"""Socket-free package checks using injected binary inspection reports.""" from pathlib import Path import tempfile import unittest from check_rtl_package import check_windows_runtime +from stage_rtl_appimage import stage_libusb + + +class LinuxStagingTest(unittest.TestCase): + def setUp(self): + self.temporary = tempfile.TemporaryDirectory() + self.addCleanup(self.temporary.cleanup) + self.root = Path(self.temporary.name) + self.prefix = self.root / "pinned" + self.libdir = self.prefix / "lib" + self.libdir.mkdir(parents=True) + self.appdir = self.root / "AppDir" + (self.libdir / "librtlsdr.so.0").touch() + self.payload = self.libdir / "libusb-1.0.so.0.5.0" + self.payload.write_bytes(b"pinned libusb payload") + self.soname = self.libdir / "libusb-1.0.so.0" + self.soname.symlink_to(self.payload.name) + self.reports = { + "librtlsdr.so.0": " (NEEDED) Shared library: [libusb-1.0.so.0]\n", + "libusb-1.0.so.0": " (SONAME) Library soname: [libusb-1.0.so.0]\n", + } + + def stage(self): + return stage_libusb(self.prefix, self.appdir, + lambda path: self.reports[path.name]) + + def test_soname_copy_survives_removing_build_prefix(self): + destination = self.stage() + self.assertEqual(destination, self.appdir / "usr/lib/libusb-1.0.so.0") + self.assertFalse(destination.is_symlink()) + self.soname.unlink() + self.payload.unlink() + self.assertEqual(destination.read_bytes(), b"pinned libusb payload") + + def test_missing_prefix_never_uses_host_library(self): + host = self.root / "host" + host.mkdir() + (host / self.soname.name).write_bytes(b"host libusb payload") + with self.assertRaisesRegex(ValueError, "Missing pinned RTL dependency"): + stage_libusb(self.root / "absent", self.appdir, + lambda path: self.reports[path.name]) + self.assertFalse(self.appdir.exists()) + + def test_missing_soname_is_not_satisfied_by_versioned_payload(self): + self.soname.unlink() + with self.assertRaisesRegex(ValueError, "Missing pinned RTL dependency"): + self.stage() + + def test_source_must_stay_inside_pinned_prefix(self): + host = self.root / "host-libusb.so" + host.write_bytes(b"host libusb payload") + self.soname.unlink() + self.soname.symlink_to(host) + with self.assertRaisesRegex(ValueError, "Missing pinned RTL dependency"): + self.stage() + + def test_driver_and_library_sonames_must_match(self): + for binary in self.reports: + with self.subTest(binary=binary): + original = self.reports[binary] + self.reports[binary] = original.replace("libusb-1.0.so.0", "libusb-1.0.so.1") + with self.assertRaisesRegex(ValueError, "must require|does not match"): + self.stage() + self.reports[binary] = original + self.assertFalse(self.appdir.exists()) class WindowsRuntimeTest(unittest.TestCase): From fd4f4759d39c8da4d8aa01edc5ff381ced4622d2 Mon Sep 17 00:00:00 2001 From: Ozy311 Date: Mon, 5 Oct 2026 18:41:50 -0700 Subject: [PATCH 3/9] Scope AppImage staging tests to POSIX. Principle VIII. Keep all Windows runtime closure cases active without requiring symlink privileges on Windows. Preserve the dependency search prefixes during the second linuxdeploy pass, preferring the already validated AppDir closure. --- .github/workflows/appimage.yml | 3 +++ tools/test_check_rtl_package.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index aee42088a..d29416f89 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -498,6 +498,9 @@ jobs: # Branch refs contain slashes, which cannot appear in the output name. SAFE_REF="${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}" export LDAI_OUTPUT="AetherSDR-${SAFE_REF}-${{ matrix.arch }}.AppImage" + # linuxdeploy scans dependencies again when generating the image. + # Prefer the staged closure and retain the original build prefixes. + export LD_LIBRARY_PATH="$PWD/AppDir/usr/lib:$PWD/third_party/rtl-deps/lib:$PWD/third_party/qtkeychain/lib:$PWD/third_party/onnxruntime/lib:$PWD/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}" ./linuxdeploy-${{ matrix.arch }}.AppImage --appdir AppDir --output appimage # The archive is only worth attaching if its build IDs are the ones the diff --git a/tools/test_check_rtl_package.py b/tools/test_check_rtl_package.py index 96b629e97..abe1a489d 100644 --- a/tools/test_check_rtl_package.py +++ b/tools/test_check_rtl_package.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 """Socket-free package checks using injected binary inspection reports.""" from pathlib import Path +import sys import tempfile import unittest @@ -8,6 +9,7 @@ from stage_rtl_appimage import stage_libusb +@unittest.skipIf(sys.platform == "win32", "AppImage staging requires POSIX symlinks") class LinuxStagingTest(unittest.TestCase): def setUp(self): self.temporary = tempfile.TemporaryDirectory() From 62653f0bf2927b5b673d6549857329ff0b5ce057 Mon Sep 17 00:00:00 2001 From: Ozy311 Date: Mon, 5 Oct 2026 18:50:33 -0700 Subject: [PATCH 4/9] Publish engine SQL controls only after accepted state. Principle II. --- src/gui/RxApplet.cpp | 28 ++++++ .../rx_applet_squelch_reconciliation_test.cpp | 94 +++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/src/gui/RxApplet.cpp b/src/gui/RxApplet.cpp index b61b779a9..0cb9b2317 100644 --- a/src/gui/RxApplet.cpp +++ b/src/gui/RxApplet.cpp @@ -1044,6 +1044,10 @@ void RxApplet::buildUI() connect(m_sqlBtn, &QPushButton::clicked, this, &RxApplet::cycleSqlMode); connect(m_sqlSlider, &QSlider::valueChanged, this, [this](int v) { + if (usingEngineAutoSquelch() && m_slice) { + setSqlSliderValueExternal(v); + return; + } if (m_sqlMode == SqlMode::Manual) { // Cache the user's chosen manual level so re-entering // Manual from Auto/Off restores it on Flex. Kiwi replacement @@ -1592,6 +1596,19 @@ void RxApplet::setManualSqlLevelForCurrentSurface(int level) void RxApplet::setSqlSliderValueExternal(int v) { + if (usingEngineAutoSquelch() && m_slice) { + // A request may be refused or adopted later. Keep both sliders on + // the accepted state until the backend publishes squelchChanged. + if (m_sqlMode == SqlMode::Auto) { + m_slice->setAutomaticSquelch(true, std::clamp(v, 5, 20)); + emit autoSqlMarginDbChanged(autoSqlMarginDb()); + } else if (m_sqlMode == SqlMode::Manual) { + m_slice->setSquelch(true, clampManualSqlLevelForCurrentSurface(v)); + emit sqlModeChanged(static_cast(m_sqlMode)); + } + applySqlModeVisuals(); + return; + } if (m_sqlMode == SqlMode::Manual) { const int level = clampManualSqlLevelForCurrentSurface(v); setManualSqlLevelForCurrentSurface(level); @@ -1697,6 +1714,16 @@ void RxApplet::setSqlMode(SqlMode m, bool propagateToRadio) if (m == SqlMode::Auto && !autoSqlAvailable()) { m = SqlMode::Manual; } + if (propagateToRadio && usingEngineAutoSquelch() && m_slice) { + // Engine SQL is confirmed state, including transitions out of Auto. + // Only the accepted delta below may publish a new presentation. + if (m == SqlMode::Auto) { + m_slice->setAutomaticSquelch(true, autoSqlMarginDb()); + } else { + m_slice->setSquelch(m != SqlMode::Off, sqlManualLevel()); + } + return; + } if (propagateToRadio) { m_clientSqlAwaitingReport = false; } @@ -2778,6 +2805,7 @@ void RxApplet::connectSlice(SliceModel* s) const SqlMode mode = !on ? SqlMode::Off : (m_slice->automaticSquelch() ? SqlMode::Auto : SqlMode::Manual); setSqlMode(mode, /*propagateToRadio=*/false); + emit autoSqlMarginDbChanged(autoSqlMarginDb()); emit squelchStateChanged(on, level); return; } diff --git a/tests/rx_applet_squelch_reconciliation_test.cpp b/tests/rx_applet_squelch_reconciliation_test.cpp index d800a6dc8..dfceedabc 100644 --- a/tests/rx_applet_squelch_reconciliation_test.cpp +++ b/tests/rx_applet_squelch_reconciliation_test.cpp @@ -16,6 +16,7 @@ using namespace AetherSDR; namespace AetherSDR { struct RxAppletSquelchTestAccess { + static QSlider* slider(RxApplet& applet) { return applet.m_sqlSlider; } static void engineSql(RxApplet& applet) { applet.m_exclusiveSquelch = SquelchLevelScale{-120, 1.2, {"FM", "FMN"}, false, "dBFS/2048-bin", true, true, false}; } @@ -31,6 +32,33 @@ struct RadioModelWakeTestAccess { namespace { +class EngineSqlBackend final : public IRadioBackend { +public: + bool refuse = false; + QVector requests; + RadioCapabilities capabilities() const override { + RadioCapabilities caps; + caps.squelchLevelScale = SquelchLevelScale{-120, 1.2, {"FM", "FMN"}, false, + "dBFS/2048-bin", true, true, false}; + return caps; + } + bool isConnected() const override { return true; } + ReceiveControlPolicy receiveControlPolicy() const override { return ReceiveControlPolicy::Confirmed; } + ReceiveDispatch requestSliceSquelch(int, const SliceSquelchRequest& request) override { + requests.append(request); + return refuse ? ReceiveDispatch::Unsupported : ReceiveDispatch::Dispatched; + } + void connectRadio(const RadioConnectRequest&) override {} + void disconnectRadio() override {} + void setSliceFrequency(int, double) override {} + void setSliceMode(int, const QString&) override {} + void setSliceFilter(int, int, int) override {} + void setSliceAgc(int, const QString&, int) override {} + void setPanCenter(const QString&, double, PanCenterIntent) override {} + void setKeying(bool, const TxCoordinator::Operation&, const TxCoordinator::Completion&) override {} + void invokeExtension(const QString&, const QString&, quint64, const QVariant&) override {} +}; + // Inject normalized radio state, never a socket peer or a copied UI machine. void status(SliceModel& slice, bool on, int level, const QString& mode = {}) { @@ -61,6 +89,72 @@ class RxAppletSquelchReconciliationTest : public QObject Q_OBJECT private slots: + void engineSqlPublishesOnlyAcceptedModeAndMargin() + { + RadioModel radio; + auto backend = std::make_unique(); + EngineSqlBackend* source = backend.get(); + radio.setBackendForTest(std::move(backend), QStringLiteral("test")); + SliceDelta accepted; + accepted.mode = QStringLiteral("FMN"); + accepted.squelchOn = true; + accepted.squelchLevel = 61; + accepted.automaticSquelch = false; + accepted.automaticSquelchMarginDb = 12; + emit source->sliceChanged(0, accepted); + SliceModel* slice = radio.slice(0); + QVERIFY(slice); + RxApplet rx; + RxAppletSquelchTestAccess::engineSql(rx); + rx.setSlice(slice); + QSignalSpy modes(&rx, &RxApplet::sqlModeChanged); + QSignalSpy automatic(&rx, &RxApplet::sqlAutoChanged); + QSignalSpy margins(&rx, &RxApplet::autoSqlMarginDbChanged); + rx.cycleSqlModeExternal(); // pending Auto is not accepted Auto + QCOMPARE(source->requests.size(), 1); + QCOMPARE(rx.sqlMode(), RxApplet::SqlMode::Manual); + QCOMPARE(rx.sqlManualLevel(), 61); + QVERIFY(modes.isEmpty()); + QVERIFY(automatic.isEmpty()); + source->refuse = true; + rx.cycleSqlModeExternal(); + QCOMPARE(rx.sqlMode(), RxApplet::SqlMode::Manual); + QVERIFY(modes.isEmpty()); + QVERIFY(automatic.isEmpty()); + source->refuse = false; + accepted.automaticSquelch = true; + emit source->sliceChanged(0, accepted); + QCOMPARE(rx.sqlMode(), RxApplet::SqlMode::Auto); + QCOMPARE(rx.autoSqlMarginDb(), 12); + QCOMPARE(modes.size(), 1); + QCOMPARE(automatic.size(), 1); + margins.clear(); + QSlider* slider = RxAppletSquelchTestAccess::slider(rx); + QVERIFY(slider); + QCOMPARE(slider->value(), 12); + slider->setValue(16); + QCOMPARE(slider->value(), 12); + QCOMPARE(source->requests.last().marginDb, 16); + rx.setSqlSliderValueExternal(17); + QCOMPARE(rx.autoSqlMarginDb(), 12); + QCOMPARE(margins.last().at(0).toInt(), 12); + source->refuse = true; + rx.setSqlSliderValueExternal(17); + QCOMPARE(rx.autoSqlMarginDb(), 12); + QCOMPARE(margins.last().at(0).toInt(), 12); + accepted.automaticSquelchMarginDb = 17; + emit source->sliceChanged(0, accepted); + QCOMPARE(rx.autoSqlMarginDb(), 17); + QCOMPARE(slider->value(), 17); + QCOMPARE(margins.last().at(0).toInt(), 17); + rx.cycleSqlModeExternal(); // refused Off stays accepted Auto + QCOMPARE(rx.sqlMode(), RxApplet::SqlMode::Auto); + accepted.squelchOn = false; + accepted.automaticSquelch = false; + emit source->sliceChanged(0, accepted); + QCOMPARE(rx.sqlMode(), RxApplet::SqlMode::Off); + } + void engineAutoRestoreKeepsManualThresholdOnAttachedSlice() { SliceModel first(0); From 69d98f08ea13c1301f7f5d46631a8ad2f965da11 Mon Sep 17 00:00:00 2001 From: Ozy311 Date: Mon, 5 Oct 2026 19:04:09 -0700 Subject: [PATCH 5/9] Document required RTL and optional decoder switches. Principle VIII. --- BUILD-OPTIONS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BUILD-OPTIONS.md b/BUILD-OPTIONS.md index e4be88ed6..075a4517b 100644 --- a/BUILD-OPTIONS.md +++ b/BUILD-OPTIONS.md @@ -59,7 +59,8 @@ alone. MSVC flags and `Release` builds are unaffected. | Option | Default | Purpose and prerequisites | |---|---|---| | `ENABLE_DEEPFIST_EXPERIMENT` | OFF | Build the experimental DeepFist CW receive decoder. Requires ONNX Runtime at configure time (configuration fails without it) and a separate verified model bundle at runtime; see below. | -| `ENABLE_RTL` | ON | Build the experimental receive-only RTL-SDR USB backend when both `librtlsdr` and single-precision FFTW (`fftw3f`) are found. Missing either disables the backend. | +| `ENABLE_RTL` | ON | Build the experimental receive-only RTL-SDR USB backend when both `librtlsdr` and single-precision FFTW (`fftw3f`) are found. Missing either disables the backend unless `REQUIRE_RTL=ON`. | +| `ENABLE_HD_FM` | OFF | Retained optional Digital FM switch. This analog submission rejects ON and points to the separate RFC #6079 proposal; official release workflows explicitly keep it OFF. | | `AETHER_HL2_TX_TXA` | ON | Select WDSP's TXA chain for the Hermes-Lite 2 SSB transmit modulator. OFF builds the in-tree phasing modulator. This choice has no runtime toggle. | **DeepFist:** enabling `-DENABLE_DEEPFIST_EXPERIMENT=ON` alone does not supply its @@ -121,6 +122,7 @@ are useful for release builds that must include those capabilities. | Option | Default | Fail configuration when… | |---|---|---| +| `REQUIRE_RTL` | OFF | RTL is disabled, or `librtlsdr` or single-precision FFTW (`fftw3f`) is missing. Official release workflows set this ON together with `ENABLE_RTL=ON`. | | `REQUIRE_SERIALPORT` | OFF | Qt SerialPort is missing. | | `REQUIRE_KEYCHAIN` | OFF | QtKeychain is missing. | | `REQUIRE_ASR_ONNX` | OFF | ONNX Runtime is missing for Silero VAD, speaker labeling and the signal classifier. | From a3458b0919cfbb2ec0314444f876dd6c8296473f Mon Sep 17 00:00:00 2001 From: Ozy311 Date: Fri, 9 Oct 2026 15:47:48 -0700 Subject: [PATCH 6/9] Isolate analog RTL release controls and docs. Principle VIII. Remove the Digital FM placeholder option and release flags from the analog delivery. Retain mandatory RTL dependencies. Audit the extracted final AppImage after the final deployment pass, including its libusb payload. Document receive-only routing and the separate multi-receiver evaluation boundary. --- .github/workflows/appimage.yml | 13 ++++- .github/workflows/macos-dmg.yml | 1 - .github/workflows/windows-installer.yml | 2 +- BUILD-OPTIONS.md | 1 - CMakeLists.txt | 9 +-- docs/rtl-multirx-analog.md | 5 +- docs/rtl-release-packaging.md | 12 ++-- docs/user/docs/aethermodem-packet-radio.md | 10 +++- docs/user/docs/building-from-source.md | 1 + docs/user/docs/dax-virtual-audio.md | 24 +++++--- docs/user/docs/rtl-sdr.md | 43 +++++++++----- docs/user/docs/rx-controls.md | 6 +- docs/user/docs/tci-server.md | 1 + .../src/components/LogAnalyzer/rules.json | 56 +++++++++---------- packaging/macos/build-installer.sh | 2 +- scripts/build-windows-local.ps1 | 2 +- 16 files changed, 113 insertions(+), 75 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index d29416f89..7a7cd6e7d 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -270,7 +270,6 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ "-DCMAKE_C_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ "-DCMAKE_CXX_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ - -DENABLE_HD_FM=OFF \ -DENABLE_RTL=ON -DREQUIRE_RTL=ON \ -DCMAKE_INSTALL_PREFIX=/usr \ -DREQUIRE_SERIALPORT=ON \ @@ -503,6 +502,18 @@ jobs: export LD_LIBRARY_PATH="$PWD/AppDir/usr/lib:$PWD/third_party/rtl-deps/lib:$PWD/third_party/qtkeychain/lib:$PWD/third_party/onnxruntime/lib:$PWD/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}" ./linuxdeploy-${{ matrix.arch }}.AppImage --appdir AppDir --output appimage + - name: Validate RTL runtime in the completed AppImage + run: | + set -euo pipefail + SAFE_REF="${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}" + image="$PWD/AetherSDR-${SAFE_REF}-${{ matrix.arch }}.AppImage" + audit_dir=$(mktemp -d) + trap 'rm -rf "$audit_dir"' EXIT + (cd "$audit_dir" && "$image" --appimage-extract > /dev/null) + python3 tools/check_rtl_package.py "$audit_dir/squashfs-root" --platform linux + cmp AppDir/usr/lib/libusb-1.0.so.0 "$audit_dir/squashfs-root/usr/lib/libusb-1.0.so.0" + sha256sum "$audit_dir/squashfs-root/usr/lib/libusb-1.0.so.0" + # The archive is only worth attaching if its build IDs are the ones the # AppImage actually ships, so read them back from the packaged binaries. # .tar.xz on purpose: sign-release.yml downloads and checksums *.zip, so diff --git a/.github/workflows/macos-dmg.yml b/.github/workflows/macos-dmg.yml index 7c9c161ec..8dbdbfd2b 100644 --- a/.github/workflows/macos-dmg.yml +++ b/.github/workflows/macos-dmg.yml @@ -371,7 +371,6 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ "-DCMAKE_C_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ "-DCMAKE_CXX_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ - -DENABLE_HD_FM=OFF \ -DENABLE_RTL=ON -DREQUIRE_RTL=ON \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ diff --git a/.github/workflows/windows-installer.yml b/.github/workflows/windows-installer.yml index 03654a683..9ea4c596d 100644 --- a/.github/workflows/windows-installer.yml +++ b/.github/workflows/windows-installer.yml @@ -290,7 +290,7 @@ jobs: # native command), so a configure failure still fails this step — # the reason the bash legs need `set -o pipefail` and this one does # not. - cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log + cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log # AETHER_GPU_SPECTRUM=ON is a request, not a guarantee: CMake flips it # back OFF without failing when Qt6GuiPrivate can't be located, and diff --git a/BUILD-OPTIONS.md b/BUILD-OPTIONS.md index 075a4517b..e787e4efe 100644 --- a/BUILD-OPTIONS.md +++ b/BUILD-OPTIONS.md @@ -60,7 +60,6 @@ alone. MSVC flags and `Release` builds are unaffected. |---|---|---| | `ENABLE_DEEPFIST_EXPERIMENT` | OFF | Build the experimental DeepFist CW receive decoder. Requires ONNX Runtime at configure time (configuration fails without it) and a separate verified model bundle at runtime; see below. | | `ENABLE_RTL` | ON | Build the experimental receive-only RTL-SDR USB backend when both `librtlsdr` and single-precision FFTW (`fftw3f`) are found. Missing either disables the backend unless `REQUIRE_RTL=ON`. | -| `ENABLE_HD_FM` | OFF | Retained optional Digital FM switch. This analog submission rejects ON and points to the separate RFC #6079 proposal; official release workflows explicitly keep it OFF. | | `AETHER_HL2_TX_TXA` | ON | Select WDSP's TXA chain for the Hermes-Lite 2 SSB transmit modulator. OFF builds the in-tree phasing modulator. This choice has no runtime toggle. | **DeepFist:** enabling `-DENABLE_DEEPFIST_EXPERIMENT=ON` alone does not supply its diff --git a/CMakeLists.txt b/CMakeLists.txt index 18cb20eca..e26e2c24f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2819,17 +2819,10 @@ else() message(STATUS "RTL-SDR ... disabled — librtlsdr not found") endif() -# The optional decoder is a separate feature proposal (RFC #6079). -# Reserve the explicitly OFF option name for the separate experimental Digital FM PR. -option(ENABLE_HD_FM "Build experimental embedded digital FM" OFF) -if(ENABLE_HD_FM) - message(FATAL_ERROR "Digital FM is not included in the analog RTL submission. See RFC #6079; configure with -DENABLE_HD_FM=OFF.") -endif() - include(cmake/AetherRequireRtl.cmake) aether_require_rtl() -# Stage RTL independently of the optional NR4 and Digital FM features. +# Stage RTL independently of the optional NR4 feature. if(WIN32 AND AETHER_BACKEND_RTL) foreach(_rtl_dll "${CMAKE_SOURCE_DIR}/third_party/rtl-deps/bin/rtlsdr.dll" diff --git a/docs/rtl-multirx-analog.md b/docs/rtl-multirx-analog.md index eb6e79109..160101ad3 100644 --- a/docs/rtl-multirx-analog.md +++ b/docs/rtl-multirx-analog.md @@ -151,6 +151,7 @@ threshold is never replaced with a computed Auto threshold by the new engine. TCI already consumes the typed per-slice 48 kHz PCM route for RTL. This is a source-level route audit, not eight-client throughput or RadioReference service -qualification. RTL does not advertise Flex DAX streams; its virtual DAX and -DAX-IQ paths are not supplied by this change. RadioReference/Broadcastify +qualification. RTL does not advertise Flex DAX streams or DAX-IQ. Its native receive-only +DAX export on Linux/macOS follows the shared TCI receiver map; see +[native DAX receive](native-dax-receive.md). RadioReference/Broadcastify publishing still requires a separately validated external streaming workflow. diff --git a/docs/rtl-release-packaging.md b/docs/rtl-release-packaging.md index 624fa71e2..4c609ad63 100644 --- a/docs/rtl-release-packaging.md +++ b/docs/rtl-release-packaging.md @@ -4,9 +4,7 @@ Official AppImage (both architectures), macOS DMG (both architectures), and Windows installer/portable/MSIX pipelines configure `ENABLE_RTL=ON` and `REQUIRE_RTL=ON`. Missing librtlsdr or float FFTW is a configure error. An intentional developer-only build can still set `ENABLE_RTL=OFF` with the -requirement left OFF. `ENABLE_HD_FM` remains OFF by default and in official -release workflows. This analog-only branch rejects ON explicitly; the separate -Digital evaluation branch and RFC #6079 own opt-in decoder builds. +requirement left OFF. `scripts/setup/rtl-dependencies.json` pins RTL-SDR Blog (the existing qualified Blog V4 driver variant), libusb, and FFTW sources by URL and SHA256. Unix setup @@ -18,8 +16,7 @@ not an imported runtime of the library-only RTL target. Setup retains corresponding archives, copyright notices and the actual build recipes in `share/aethersdr-rtl-sources`. Those travel inside each payload. -Source archives include their original license/copyright files. Separate Digital evaluation packages carry their own decoder source and notices; -those dependencies are not part of this analog delivery. +Source archives include their original license/copyright files. No setup script installs a USB driver, changes udev rules, or opens a radio. `tools/check_rtl_package.py` checks that the app imports RTL and float FFTW, @@ -29,6 +26,9 @@ beside the executable and recursively resolves their normal and delay DLL imports from that same directory or Windows system libraries. A dependency hidden elsewhere in the staging tree or available only on a build-tool PATH does not satisfy the check. The existing complete deployment audit also runs. -This is packaging evidence, not physical USB or RF qualification. CI builds +The AppImage pipeline also extracts the completed image after the final +linuxdeploy pass, reruns the RTL dependency check on that payload and verifies +its libusb bytes against the final AppDir. This is packaging evidence, not +physical USB or RF qualification. CI builds and tests that only upload logs are not release producers and keep optional RTL behavior. Dependency-only build workflows do not build AetherSDR. diff --git a/docs/user/docs/aethermodem-packet-radio.md b/docs/user/docs/aethermodem-packet-radio.md index 2f9a3b577..065e7d88f 100644 --- a/docs/user/docs/aethermodem-packet-radio.md +++ b/docs/user/docs/aethermodem-packet-radio.md @@ -3,12 +3,12 @@ title: "AetherModem Packet Radio" slug: "/aethermodem-packet-radio" description: "AetherModem is AetherSDR's built-in AX.25 packet modem." status: "Supported" -applies_to: ["FlexRadio", "Hermes-Lite 2 (experimental)", "Networked Icom (early; IC-7300MK2 supported)"] +applies_to: ["FlexRadio", "Hermes-Lite 2 (experimental)", "Networked Icom (early; IC-7300MK2 supported)", "RTL-SDR (experimental, receive-only)"] --- :::info[Status] -**Status:** Supported · **Applies to:** FlexRadio, Hermes-Lite 2 (experimental), Networked Icom (early; IC-7300MK2 supported) +**Status:** Supported · **Applies to:** FlexRadio, Hermes-Lite 2 (experimental), Networked Icom (early; IC-7300MK2 supported), RTL-SDR (experimental, receive-only) ::: @@ -46,6 +46,12 @@ Packet length, retry timing and preamble are derived from the baud rate, not fix **Tips for receive level:** avoid clipping (peaks around −10 dBFS are fine) and don't chase every missed decode with AF gain once the tones are clearly visible. +### RTL-SDR receive-only packets + +On [RTL-SDR](./rtl-sdr.md), the modem decodes the selected slice's audio. For VHF APRS, choose **FM** or **FM-N**, select **1200 baud** and enable **Enable Modem**. The status reports **RX: selected slice**. Speaker mute and volume do not affect this source, but receiver squelch does. Slice selection changes the modem's receive source; there is no fixed-source selector. + +Parking, removing or reconfiguring that receiver clears stale decoder input. Decoded packets can populate the APRS table and reach receive consumers such as KISS and MQTT. RTL remains receive-only: beacons, acknowledgements, digipeating and packet transmit are unavailable. APRS-IS gateway operation is not provided. + ## Using AetherModem ### APRS diff --git a/docs/user/docs/building-from-source.md b/docs/user/docs/building-from-source.md index c273a98b3..61c3aa3d1 100644 --- a/docs/user/docs/building-from-source.md +++ b/docs/user/docs/building-from-source.md @@ -92,6 +92,7 @@ Start with [AGENTS.md](https://github.com/aethersdr/AetherSDR/blob/main/AGENTS.m | `CMAKE_BUILD_TYPE` | — | `RelWithDebInfo` for normal use, `Debug` for development | | `AETHER_GPU_SPECTRUM` | ON | QRhi GPU spectrum and waterfall. Needs Qt's private GUI headers; without them the CPU renderer is built. | | `ENABLE_RTL` | ON | Experimental receive-only RTL-SDR backend, when `librtlsdr` and single-precision FFTW are found | +| `REQUIRE_RTL` | OFF | Fail configure if the RTL backend cannot be built. Official release packages set this and `ENABLE_RTL` to ON. | | `ENABLE_ASR` | ON | Copy Assist on-device speech-to-text | | `ENABLE_NVIDIA_AFX` | ON | BNR NVIDIA GPU noise removal (x86-64 Linux and Windows) | | `AETHER_FETCH_QT` | OFF | Run the Qt setup script during configure if the pinned Qt is missing | diff --git a/docs/user/docs/dax-virtual-audio.md b/docs/user/docs/dax-virtual-audio.md index 71013125f..85c5fbe6e 100644 --- a/docs/user/docs/dax-virtual-audio.md +++ b/docs/user/docs/dax-virtual-audio.md @@ -3,19 +3,19 @@ title: "DAX Virtual Audio" slug: "/dax-virtual-audio" description: "DAX (Digital Audio eXchange) creates virtual audio devices on your computer that digital-mode programs (WSJT-X, fldigi, JS8Call, VARA and others) use as their sound card." status: "Supported" -applies_to: ["FlexRadio"] +applies_to: ["FlexRadio", "RTL-SDR (experimental, receive-only)"] platforms: ["Linux", "macOS"] --- :::info[Status] -**Status:** Supported · **Applies to:** FlexRadio · **Platforms:** Linux, macOS +**Status:** Supported · **Applies to:** FlexRadio, RTL-SDR (experimental, receive-only) · **Platforms:** Linux, macOS ::: DAX (Digital Audio eXchange) creates virtual audio devices on your computer that digital-mode programs (WSJT-X, fldigi, JS8Call, VARA and others) use as their sound card. Receive audio flows from a slice to the program, and the program's transmit audio flows back to the radio, all through AetherSDR with no cables. -DAX is a **FlexRadio** feature. On other radio families the DAX applet, menu entry and overlay button are hidden; use the [TCI Server](./tci-server.md) for audio there. +FlexRadio provides receive and transmit DAX. RTL-SDR provides experimental **receive-only** DAX on Linux and macOS; see [RTL-SDR receive audio](#rtl-sdr-receive-audio). On radios without an audio-export route, use the [TCI Server](./tci-server.md). ## Requirements @@ -34,7 +34,7 @@ DAX is a **FlexRadio** feature. On other radio families the DAX applet, menu ent ### Assigning a slice to a DAX channel -The radio sends a slice's audio to the DAX channel set on that slice. Choose it on the slice's **VFO widget → DAX tab** (**DAX Ch**). The channel list follows the radio's capacity, and a slice on a channel the radio cannot back shows **Off**. +On FlexRadio, the radio sends a slice's audio to the DAX channel set on that slice. Choose it on the slice's **VFO widget → DAX tab** (**DAX Ch**). The channel list follows the radio's capacity, and a slice on a channel the radio cannot back shows **Off**. The panadapter overlay's **DAX** flyout is for IQ instead: it holds **IQ Ch** and the **WFM** button (see [DAX IQ Streaming](./dax-iq-streaming.md)). @@ -49,12 +49,22 @@ Stale devices left by a crash are cleaned up the next time DAX starts. ## Using DAX +### RTL-SDR receive audio + +Turn on **DAX Audio** and select the matching **AetherSDR DAX n** input in your program. Native receive export requires a build with WebSockets support. Input 1 follows TCI receiver 0, input 2 follows receiver 1, and so on; the applet displays the assigned slice. These assignments do not use the FlexRadio **DAX Ch** selector, and neither a running TCI listener nor a connected TCI client is required. + +Speaker gain and mute do not alter exported audio. Receiver squelch still applies, and the DAX row has its own gain. Linux exports mono at 48 kHz; macOS exports stereo at 24 kHz through the installed DAX driver. Use stereo TCI when stereo WFM is required on Linux. RTL creates no TX endpoint and the TX control is unavailable. + +The normal RTL limit remains one receiver. Additional inputs are for the process-only [multi-receiver evaluation](./rtl-sdr.md#multi-receiver-evaluation). An older four-input macOS driver must be updated separately before evaluating all eight inputs; building AetherSDR does not update an installed driver. + ### Transmit audio -When a digital program transmits in a digital mode (DIGU, DIGL), its audio from **AetherSDR TX** goes to the radio. Voice modes use your microphone. You don't switch anything by hand. +On FlexRadio, when a digital program transmits in a digital mode (DIGU, DIGL), its audio from **AetherSDR TX** goes to the radio. Voice modes use your microphone. You don't switch anything by hand. ### Wiring WSJT-X (Linux and macOS) +This transmit-capable setup applies to FlexRadio. + 1. Put the slice in **DIGU** and set its DAX channel to **1**. 2. Turn DAX on, and turn on a **Rigctld** CAT port (see [CAT Control](./cat-control.md)). 3. In WSJT-X, **File → Settings → Audio**: Input **AetherSDR DAX 1**, Output **AetherSDR TX**. @@ -82,7 +92,7 @@ Give each program its own DAX channel and its own CAT port (with a different **V ### Devices -AetherSDR offers **up to eight RX channels plus one TX channel**. The number of RX channels follows the connected radio's slice capacity (for example 8 on a FLEX-6700, 4 on a FLEX-6600 or 8600, 2 on a FLEX-6300 or 6400). Rows and devices above that number are not shown. +For FlexRadio, AetherSDR offers **up to eight RX channels plus one TX channel**. The number of RX channels follows the connected radio's slice capacity (for example 8 on a FLEX-6700, 4 on a FLEX-6600 or 8600, 2 on a FLEX-6300 or 6400). Rows and devices above that number are not shown. | Device | Direction | Purpose | |---|---|---| @@ -109,7 +119,7 @@ Each RX row shows **DAX n:**, the slice currently assigned to that channel (or " The slice has no DAX channel, or the program is reading a different device. -1. On the slice's **VFO widget → DAX tab**, set a **DAX Ch**. +1. On FlexRadio, set a **DAX Ch** on the slice's **VFO widget → DAX tab**. On RTL-SDR, check the automatic slice assignment shown in the DAX applet. 2. In the program, choose the matching **AetherSDR DAX n** input. 3. On Linux, `pactl list source-outputs` shows which device each program is reading. diff --git a/docs/user/docs/rtl-sdr.md b/docs/user/docs/rtl-sdr.md index 260988364..7ac1521de 100644 --- a/docs/user/docs/rtl-sdr.md +++ b/docs/user/docs/rtl-sdr.md @@ -14,17 +14,9 @@ applies_to: ["RTL-SDR"] AetherSDR can use an RTL-SDR USB dongle as a receiver. Support is **experimental and receive-only**: one slice on one panadapter, demodulated on this computer. FlexRadio remains the supported target (see [Supported Radios](./supported-radios.md)). -## Where to go next - -- [Panadapter Controls](./panadapter-controls.md): FFT AVG, Black Level and NB Blank on the dongle's panadapter. -- [RX Controls](./rx-controls.md): modes, filters and squelch. -- [DSP Noise Mitigation](./dsp-noise-mitigation.md): client-side noise reduction. -- [Memory Channels](./memory-channels.md): stored on this computer. -- [Building from Source](./building-from-source.md): building with the RTL-SDR backend. - ## Requirements -The RTL-SDR backend is built only when the `librtlsdr` library is available, so whether your copy of AetherSDR has it depends on the package. If **Connect by IP → Radio type:** has no **RTL-SDR (USB)** entry, and no dongle ever appears in the radio list, your build does not include it. The Windows installer is built without it ([#5800](https://github.com/aethersdr/AetherSDR/issues/5800)). +Official AppImage, macOS DMG and Windows release packages require the RTL-SDR backend and bundle its RTL-SDR, USB and FFTW libraries. A developer build can omit it. If **Connect by IP → Radio type:** has no **RTL-SDR (USB)** entry, and no dongle ever appears in the radio list, your build does not include it. Packaging does not install a USB driver or Linux device-access rules. Building from source: the `ENABLE_RTL` option is on by default and takes effect when both `librtlsdr` and single-precision FFTW (`fftw3f`) are found. See [Building from Source](./building-from-source.md). @@ -41,12 +33,32 @@ Building from source: the `ENABLE_RTL` option is on by default and takes effect - **Sample rates** up to 3.0 MS/s; 2.4 MS/s is the default. - **Browsing the capture:** pan around the captured bandwidth and your slice keeps its frequency and settings. A slice that falls outside the usable capture is parked (silent) and resumes when it fits again. - **Zoom** uses a continuous 65,536-point FFT, which gives 36.6 Hz bins at 2.4 MS/s. -- **FM and FM-N** have symmetric filters, 48 kHz audio and their own squelch. Squelch exists only in FM and FM-N; its level is a signal-level gate in dBFS per bin, not calibrated dBm. +- **FM and FM-N** have symmetric filters, 48 kHz audio and their own squelch. Squelch exists only in FM and FM-N; its manual threshold is a signal-level gate in dBFS per 2048-point detector bin, not calibrated dBm. **AUTO** follows a nearby noise estimate inside each receiver, independently of display zoom and FFT averaging. Its margin is 5–20 dB (default 10 dB). The SQL threshold line is not drawn against the differently scaled display FFT. - **Confirmed settings:** a change is shown only after the dongle confirms it, so a refused setting never becomes a saved one. - Manual **Black Level**, waterfall **NB Blank** and **FFT AVG** work on the panadapter. See [Panadapter Controls](./panadapter-controls.md). - **Tools → Radio Health...** shows the dongle's status. -Only one receiver is available, even though the capture is wide. +Normal launches admit **one receiver**, even though the capture is wide. + +### Broadcast FM + +Select **WFM** to use the **Broadcast FM** applet. It shows the selected slice letter and confirmed frequency. **Mono / Auto Stereo**, **De-emphasis** (50 or 75 µs) and **Bandwidth** belong to that receiver. **Auto Stereo** falls back to mono without a detected stereo pilot. The observed pilot status is separate from your selected audio mode; parking clears observations while retaining the controls. + +### Receive meters + +The RTL receive meter reports relative RF peak-bin level in **dBFS**, not calibrated antenna dBm or audio level. **Settings → Radio Setup... → RTL Receiver → Enable receive meters** turns these updates on or off. Turning them off clears the readout; reception, squelch and audio continue. + +### Sending receive audio to another program + +Use the [TCI Server](./tci-server.md) on Linux, macOS or Windows. On Linux and macOS, [DAX Virtual Audio](./dax-virtual-audio.md) can also export receive audio when the build includes WebSockets support. DAX input 1 follows TCI receiver 0, and the applet shows its slice assignment. Linux DAX is mono; macOS DAX and stereo TCI retain left and right WFM audio. Speaker volume and mute do not change either export; receiver squelch still applies. + +[AetherModem](./aethermodem-packet-radio.md) can decode packets from the selected RTL slice. For VHF APRS, choose **FM** or **FM-N** and **1200 baud**. The modem follows slice selection; it has no fixed-source selector or APRS-IS gateway. + +### Multi-receiver evaluation + +The process-only evaluation setting admits 2, 4 or 8 receivers on Linux x86-64, Windows x64 and macOS arm64. It requires both `AETHER_AUTOMATION=1` and `AETHER_RTL_EVALUATION_RECEIVERS=2`, `4` or `8` at launch; it is not saved and cannot resize a running session. Use an isolated settings profile for evaluation. It does **not** change the one-receiver production limit or qualify a platform for sustained multi-receiver use. + +FM, FM-N and analog WFM can share the capture in this evaluation. Each receiver retains its frequency, filter, squelch and audio controls when parked. Selecting another slice does not retune the capture or change its audio settings. AM, SAM, USB, LSB, CW and CW-R remain singleton configurations, even if another configured receiver is parked. See the [evaluation procedure](https://github.com/aethersdr/AetherSDR/blob/main/docs/rtl-multirx-analog.md) for the validation boundaries. ## Reference @@ -58,6 +70,7 @@ Only one receiver is available, even though the capture is wide. |---------|-----| | **Frequency correction:** | Your dongle's crystal error in ppm (−1000 to +1000). Click **Apply frequency correction** to send it. | | **Suppress IQ DC** | Removes the spike at the centre of the capture. Off by default. | +| **Enable receive meters** | Enables relative RF meter updates. On by default. | | **Device serial** | The serial number the dongle reports. | The page shows the values the dongle actually applied and whether they were saved. Corrections are saved per dongle serial number. A dongle that reports no serial keeps its corrections for the current session only. @@ -71,8 +84,8 @@ Frequency, mode, passband, sample rate and RF gain are saved per dongle, keyed b ### Not available on an RTL-SDR - Transmit. -- DAX, SmartLink or Multi-Flex. -- More than one slice or panadapter. +- DAX transmit, DAX IQ, SmartLink or Multi-Flex. +- More than one receiver in a normal launch, or more than one panadapter. ## Known issues @@ -111,6 +124,10 @@ The dongle's crystal is off. ## See also +- [RX Controls](./rx-controls.md) +- [DAX Virtual Audio](./dax-virtual-audio.md) +- [TCI Server](./tci-server.md) +- [AetherModem Packet Radio](./aethermodem-packet-radio.md) - [Supported Radios](./supported-radios.md) - [Building from Source](./building-from-source.md) - [Panadapter Controls](./panadapter-controls.md) diff --git a/docs/user/docs/rx-controls.md b/docs/user/docs/rx-controls.md index 4cad13468..a6031b8ac 100644 --- a/docs/user/docs/rx-controls.md +++ b/docs/user/docs/rx-controls.md @@ -102,9 +102,9 @@ The **SQL** button cycles through three states: own scale. On radios where Auto SQL is not available, a slice set to AUTO drops to manual at its manual level. -> **Upgrading from an older version:** AetherSDR no longer saves the squelch -> level between sessions. On connect, each slice uses the level the radio -> reports. +On RTL-SDR FM/FM-N, **AUTO** uses each receiver's own detector noise estimate, independently of panadapter zoom, FFT averaging or whether the applet is visible. Manual SQL uses dBFS per 2048-point detector bin; its threshold line is suppressed because the display FFT has a different scale. Manual and Auto settings are saved per RTL receiver, and Auto does not overwrite the manual threshold. WFM does not expose this FM/FM-N squelch control. + +> **Upgrading from an older version:** FlexRadio squelch levels come from the radio at connection. For RTL-SDR, explicitly choose **AUTO** after upgrading to enable the receiver-owned Auto policy; an older display-Auto preference does not enable it. ### AGC diff --git a/docs/user/docs/tci-server.md b/docs/user/docs/tci-server.md index b27e5763f..451bc4734 100644 --- a/docs/user/docs/tci-server.md +++ b/docs/user/docs/tci-server.md @@ -127,6 +127,7 @@ TCI audio is sent and received as binary frames on the same WebSocket. - **Audio doesn't flow until the client asks for it** with `audio_start:;`. - **PC Audio does not need to be on.** On a FlexRadio, TCI receive audio comes from DAX channels that the TCI server claims for itself; your speaker volume and mute don't affect it. On other radios it comes straight from the slice. +- RTL-SDR receive audio follows the stable receiver map and remains independent of speaker gain and mute; receiver squelch still applies. Normal launches offer one RTL receiver. Extra receivers belong to the [process-only evaluation](./rtl-sdr.md#multi-receiver-evaluation), not a higher production limit. RTL has no transmit or IQ export. - If a client reconnects more than 10 seconds later, its receive audio is re-bound to the right DAX channels automatically. - TX audio from a client is resampled from the rate the client declares, so 8, 12 and 24 kHz programs are not mis-pitched. diff --git a/docs/user/src/components/LogAnalyzer/rules.json b/docs/user/src/components/LogAnalyzer/rules.json index f8672e47a..146eb8cac 100644 --- a/docs/user/src/components/LogAnalyzer/rules.json +++ b/docs/user/src/components/LogAnalyzer/rules.json @@ -9,7 +9,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7713, + "line": 7770, "text": "else if (key == \"model\")" } ] @@ -21,7 +21,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7722, + "line": 7779, "text": "else if (key == \"software_ver\")" } ] @@ -94,7 +94,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7451, + "line": 7502, "text": "\"RadioModel: terminal GUI client registration failure\"" } ] @@ -111,12 +111,12 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7381, + "line": 7432, "text": "RadioModel: this GUI client was force-disconnected by another client" }, { "file": "src/models/RadioModel.cpp", - "line": 7425, + "line": 7476, "text": "RadioModel: radio disconnected this session because another GUI client used the same ID" } ] @@ -133,7 +133,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7291, + "line": 7342, "text": "RadioModel: multiFLEX disabled, other clients present" } ] @@ -167,7 +167,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 8416, + "line": 8473, "text": "consecutive pings unanswered — forcing disconnect" } ] @@ -193,7 +193,7 @@ }, { "file": "src/models/RadioModel.cpp", - "line": 7722, + "line": 7779, "text": "else if (key == \"software_ver\")" } ] @@ -210,7 +210,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7610, + "line": 7667, "text": "RadioModel: no VITA-49 UDP data received after 10s" } ] @@ -227,7 +227,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7588, + "line": 7645, "text": "RadioModel: UDP stream setup failed" }, { @@ -237,7 +237,7 @@ }, { "file": "src/models/RadioModel.cpp", - "line": 7663, + "line": 7720, "text": "RadioModel: client udpport collision" } ] @@ -354,12 +354,12 @@ "source": [ { "file": "src/core/AudioEngine.cpp", - "line": 4279, + "line": 4280, "text": "AudioEngine: failed to open RX audio sink on any negotiated format" }, { "file": "src/core/AudioEngine.cpp", - "line": 4315, + "line": 4316, "text": "AudioEngine: QAudioSink stopped with error, not auto-restarting RX" } ] @@ -376,17 +376,17 @@ "source": [ { "file": "src/core/AudioEngine.cpp", - "line": 8767, + "line": 8876, "text": "AudioEngine: no audio input device available" }, { "file": "src/core/AudioEngine.cpp", - "line": 8998, + "line": 9107, "text": "AudioEngine: failed to open audio source at" }, { "file": "src/core/AudioEngine.cpp", - "line": 9050, + "line": 9159, "text": "AudioEngine: all TX source formats failed" } ] @@ -425,12 +425,12 @@ }, { "file": "src/core/PipeWireAudioBridge.cpp", - "line": 232, + "line": 236, "text": "PipeWireAudioBridge: pactl failed:" }, { "file": "src/core/PipeWireAudioBridge.cpp", - "line": 146, + "line": 147, "text": "PipeWireAudioBridge: failed to create RX pipe" }, { @@ -452,12 +452,12 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 1334, + "line": 1335, "text": "RadioModel: dax_rx stream create for channel" }, { "file": "src/core/backends/flex/PanadapterStream.cpp", - "line": 1766, + "line": 1790, "text": "stream create failed/dropped" } ] @@ -474,7 +474,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 12671, + "line": 12733, "text": "RadioModel: DAX TX create failed" } ] @@ -491,12 +491,12 @@ "source": [ { "file": "src/models/DaxIqModel.cpp", - "line": 351, + "line": 353, "text": "DaxIqWorker: pactl load-module failed for IQ ch" }, { "file": "src/models/DaxIqModel.cpp", - "line": 360, + "line": 362, "text": "DaxIqWorker: cannot open IQ pipe" } ] @@ -547,7 +547,7 @@ "source": [ { "file": "src/gui/SpectrumWidget.cpp", - "line": 12879, + "line": 13049, "text": "\"SpectrumWidget: QRhi failure:\"" }, { @@ -574,7 +574,7 @@ "source": [ { "file": "src/gui/SpectrumWidget.cpp", - "line": 1977, + "line": 1995, "text": "SpectrumWidget: AETHER_NO_GPU/QT_OPENGL=software" } ] @@ -608,7 +608,7 @@ "source": [ { "file": "src/main.cpp", - "line": 84, + "line": 85, "text": "Non-fatal X11 error suppressed (error_code=" } ] @@ -713,7 +713,7 @@ "source": [ { "file": "src/core/AudioEngine.cpp", - "line": 7603, + "line": 7712, "text": "AudioEngine: NR2 FFTW wisdom unavailable on enable;" } ] @@ -890,7 +890,7 @@ "source": [ { "file": "src/core/backends/hl2/Hl2Backend.cpp", - "line": 2731, + "line": 2736, "text": "HL2: could not open the UDP socket" } ] diff --git a/packaging/macos/build-installer.sh b/packaging/macos/build-installer.sh index 0f51183d7..3c65d6af6 100755 --- a/packaging/macos/build-installer.sh +++ b/packaging/macos/build-installer.sh @@ -15,7 +15,7 @@ echo "=== Building AetherSDR macOS installer v${VERSION} ===" export MACOS_DEPLOYMENT_TARGET="${MACOS_DEPLOYMENT_TARGET:-14.0}" bash scripts/setup/setup-macos-deps.sh export PKG_CONFIG_PATH="$PWD/third_party/macos-deps/lib/pkgconfig:${PKG_CONFIG_PATH:-}" -cmake -B "${BUILD_DIR}" -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOS_DEPLOYMENT_TARGET" -DCMAKE_PREFIX_PATH="$PWD/third_party/macos-deps;${CMAKE_PREFIX_PATH:-}" +cmake -B "${BUILD_DIR}" -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOS_DEPLOYMENT_TARGET" -DCMAKE_PREFIX_PATH="$PWD/third_party/macos-deps;${CMAKE_PREFIX_PATH:-}" cmake --build "${BUILD_DIR}" -j$(sysctl -n hw.ncpu) # 1b. Build HAL plugin (separate build because libASPL FetchContent conflicts with main Ninja build) diff --git a/scripts/build-windows-local.ps1 b/scripts/build-windows-local.ps1 index 7dfec50c4..8c8d9dc89 100644 --- a/scripts/build-windows-local.ps1 +++ b/scripts/build-windows-local.ps1 @@ -113,7 +113,7 @@ Write-Host " VULKAN_SDK = $env:VULKAN_SDK" -ForegroundColor Green Write-Host "== Configure ==" -ForegroundColor Cyan cmake -B build -G "Ninja" ` -DCMAKE_BUILD_TYPE=RelWithDebInfo ` - -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON ` + -DENABLE_RTL=ON -DREQUIRE_RTL=ON ` -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON ` -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON ` -DCMAKE_PREFIX_PATH="$QtDir;$env:VULKAN_SDK" ` From 44fcc73d5c6d446479eae1ef464e639bc7058d7a Mon Sep 17 00:00:00 2001 From: Ozy311 Date: Mon, 5 Oct 2026 18:49:05 -0700 Subject: [PATCH 7/9] Restack experimental Digital FM on typed receive dispatch. Principle II. --- CMakeLists.txt | 19 + THIRD_PARTY_LICENSES | 23 + cmake/AetherHdFm.cmake | 135 + cmake/AetherHdFmWindows.cmake | 76 + docs/architecture/aetherd-touchpoints.md | 4 +- docs/architecture/radio-capabilities-map.md | 6 + docs/rtl-hd-fm-experimental.md | 170 + docs/rtl-hd-fm-portable-packaging.md | 116 + docs/rtl-multirx-analog.md | 22 +- docs/rtl-release-packaging.md | 7 +- docs/rtl-wfm.md | 4 +- packaging/windows/README-HD-FM.txt.in | 48 + packaging/windows/stage-hd-portable.ps1 | 184 + src/core/AudioEngine.cpp | 2 +- src/core/DecoderPcmAdapter.cpp | 4 +- src/core/HdFmReception.h | 94 + src/core/PcmFrame.h | 10 +- src/core/RtlSliceSettings.cpp | 11 + src/core/RtlSliceSettings.h | 2 + src/core/backends/IRadioBackend.h | 8 + src/core/backends/RadioCapabilities.h | 4 + src/core/backends/ReceiveCommand.h | 6 +- src/core/backends/SliceDelta.h | 4 + src/core/backends/rtl/HdFmIqAdapter.cpp | 70 + src/core/backends/rtl/HdFmIqAdapter.h | 46 + src/core/backends/rtl/HdFmReceiver.cpp | 374 + src/core/backends/rtl/HdFmReceiver.h | 15 + src/core/backends/rtl/HdFmTypes.h | 56 + src/core/backends/rtl/Nrsc5FmDecoder.cpp | 245 + src/core/backends/rtl/Nrsc5FmDecoder.h | 42 + .../backends/rtl/RtlCaptureTransaction.cpp | 22 +- src/core/backends/rtl/RtlCaptureTransaction.h | 6 + src/core/backends/rtl/RtlReceivePipeline.cpp | 107 +- src/core/backends/rtl/RtlReceivePipeline.h | 28 +- src/core/backends/rtl/RtlReceiverRegistry.cpp | 16 + src/core/backends/rtl/RtlReceiverRegistry.h | 12 + src/core/backends/rtl/RtlSdrBackend.cpp | 309 +- src/core/backends/rtl/RtlSdrBackend.h | 21 + src/core/backends/rtl/RtlSdrWorker.h | 1 + src/gui/DxClusterDialog.cpp | 12 + src/gui/MainWindow_Wiring.cpp | 12 + src/gui/SpectrumWidget.cpp | 124 + src/gui/SpectrumWidget.h | 11 + src/gui/WfmApplet.cpp | 313 +- src/gui/WfmApplet.h | 11 + src/gui/WfmBroadcastOverlay.cpp | 94 + src/gui/WfmBroadcastOverlay.h | 53 + src/gui/WfmBroadcastTicker.h | 110 + src/gui/WfmLockScope.cpp | 116 +- src/gui/WfmLockScope.h | 20 +- src/gui/WfmPresentationSettings.cpp | 58 + src/gui/WfmPresentationSettings.h | 21 + src/models/RadioModel.cpp | 46 +- src/models/RadioModel.h | 2 + src/models/SliceModel.cpp | 26 + src/models/SliceModel.h | 13 + tests/broadcast_overlay_test.cpp | 396 + tests/dax_receive_applet_test.cpp | 2 +- tests/decoder_pcm_adapter_test.cpp | 10 + tests/hd_fm_iq_adapter_test.cpp | 92 + tests/hd_fm_receiver_test.cpp | 366 + tests/nrsc5_fm_decoder_test.cpp | 98 + tests/nrsc5_windows_compat_test.c | 137 + tests/pcm_frame_test.cpp | 15 +- tests/rtl_capture_transaction_test.cpp | 84 + tests/rtl_model_acceptance_test.cpp | 295 +- tests/rtl_receive_pipeline_test.cpp | 3 +- tests/rtl_slice_settings_test.cpp | 51 + tests/rtl_wfm_pipeline_test.cpp | 6 + tests/spectrum_confirmed_geometry_test.cpp | 93 + tests/tci_rx_audio_test.cpp | 12 +- tests/tci_rx_converter_test.cpp | 12 +- tests/tests.cmake | 47 + tests/wfm_controls_test.cpp | 520 +- third_party/faad_hdc/README.aethersdr.md | 45 + third_party/faad_hdc/SOURCE-MANIFEST.json | 692 + third_party/faad_hdc/upstream/AUTHORS | 17 + third_party/faad_hdc/upstream/CMakeLists.txt | 304 + third_party/faad_hdc/upstream/COPYING | 350 + third_party/faad_hdc/upstream/ChangeLog | 237 + third_party/faad_hdc/upstream/README | 66 + third_party/faad_hdc/upstream/frontend/main.c | 1379 ++ .../faad_hdc/upstream/include/faad.h.in | 36 + .../faad_hdc/upstream/include/neaacdec.h | 266 + .../faad_hdc/upstream/libfaad/analysis.h | 52 + third_party/faad_hdc/upstream/libfaad/bits.c | 292 + third_party/faad_hdc/upstream/libfaad/bits.h | 422 + third_party/faad_hdc/upstream/libfaad/cfft.c | 1050 + third_party/faad_hdc/upstream/libfaad/cfft.h | 56 + .../faad_hdc/upstream/libfaad/cfft_tab.h | 1823 ++ .../faad_hdc/upstream/libfaad/codebook/hcb.h | 137 + .../upstream/libfaad/codebook/hcb_1.h | 191 + .../upstream/libfaad/codebook/hcb_10.h | 314 + .../upstream/libfaad/codebook/hcb_11.h | 417 + .../upstream/libfaad/codebook/hcb_2.h | 192 + .../upstream/libfaad/codebook/hcb_3.h | 196 + .../upstream/libfaad/codebook/hcb_4.h | 201 + .../upstream/libfaad/codebook/hcb_5.h | 196 + .../upstream/libfaad/codebook/hcb_6.h | 184 + .../upstream/libfaad/codebook/hcb_7.h | 162 + .../upstream/libfaad/codebook/hcb_8.h | 175 + .../upstream/libfaad/codebook/hcb_9.h | 372 + .../upstream/libfaad/codebook/hcb_sf.h | 276 + .../faad_hdc/upstream/libfaad/common.c | 497 + .../faad_hdc/upstream/libfaad/common.h | 443 + .../faad_hdc/upstream/libfaad/decoder.c | 1307 ++ third_party/faad_hdc/upstream/libfaad/drc.c | 172 + third_party/faad_hdc/upstream/libfaad/drc.h | 49 + .../faad_hdc/upstream/libfaad/drm_dec.c | 1023 + .../faad_hdc/upstream/libfaad/drm_dec.h | 101 + third_party/faad_hdc/upstream/libfaad/error.c | 70 + third_party/faad_hdc/upstream/libfaad/error.h | 44 + .../faad_hdc/upstream/libfaad/faad2.pc.in | 11 + .../faad_hdc/upstream/libfaad/filtbank.c | 408 + .../faad_hdc/upstream/libfaad/filtbank.h | 61 + third_party/faad_hdc/upstream/libfaad/fixed.h | 293 + third_party/faad_hdc/upstream/libfaad/hcr.c | 433 + .../faad_hdc/upstream/libfaad/huffman.c | 594 + .../faad_hdc/upstream/libfaad/huffman.h | 47 + .../faad_hdc/upstream/libfaad/ic_predict.c | 281 + .../faad_hdc/upstream/libfaad/ic_predict.h | 252 + .../faad_hdc/upstream/libfaad/iq_table.h | 16458 ++++++++++++++++ third_party/faad_hdc/upstream/libfaad/is.c | 106 + third_party/faad_hdc/upstream/libfaad/is.h | 67 + .../faad_hdc/upstream/libfaad/kbd_win.h | 2297 +++ .../faad_hdc/upstream/libfaad/lt_predict.c | 215 + .../faad_hdc/upstream/libfaad/lt_predict.h | 66 + third_party/faad_hdc/upstream/libfaad/mdct.c | 301 + third_party/faad_hdc/upstream/libfaad/mdct.h | 48 + .../faad_hdc/upstream/libfaad/mdct_tab.h | 3655 ++++ third_party/faad_hdc/upstream/libfaad/mp4.c | 313 + third_party/faad_hdc/upstream/libfaad/mp4.h | 53 + third_party/faad_hdc/upstream/libfaad/ms.c | 77 + third_party/faad_hdc/upstream/libfaad/ms.h | 44 + .../faad_hdc/upstream/libfaad/output.c | 563 + .../faad_hdc/upstream/libfaad/output.h | 48 + third_party/faad_hdc/upstream/libfaad/pns.c | 270 + third_party/faad_hdc/upstream/libfaad/pns.h | 57 + .../faad_hdc/upstream/libfaad/ps_dec.c | 2040 ++ .../faad_hdc/upstream/libfaad/ps_dec.h | 155 + .../faad_hdc/upstream/libfaad/ps_syntax.c | 552 + .../faad_hdc/upstream/libfaad/ps_tables.h | 550 + third_party/faad_hdc/upstream/libfaad/pulse.c | 58 + third_party/faad_hdc/upstream/libfaad/pulse.h | 43 + third_party/faad_hdc/upstream/libfaad/rvlc.c | 551 + third_party/faad_hdc/upstream/libfaad/rvlc.h | 56 + .../faad_hdc/upstream/libfaad/sbr_dct.c | 2279 +++ .../faad_hdc/upstream/libfaad/sbr_dct.h | 52 + .../faad_hdc/upstream/libfaad/sbr_dec.c | 706 + .../faad_hdc/upstream/libfaad/sbr_dec.h | 262 + .../faad_hdc/upstream/libfaad/sbr_e_nf.c | 510 + .../faad_hdc/upstream/libfaad/sbr_e_nf.h | 50 + .../faad_hdc/upstream/libfaad/sbr_fbt.c | 767 + .../faad_hdc/upstream/libfaad/sbr_fbt.h | 55 + .../faad_hdc/upstream/libfaad/sbr_hfadj.c | 1742 ++ .../faad_hdc/upstream/libfaad/sbr_hfadj.h | 57 + .../faad_hdc/upstream/libfaad/sbr_hfgen.c | 684 + .../faad_hdc/upstream/libfaad/sbr_hfgen.h | 49 + .../faad_hdc/upstream/libfaad/sbr_huff.c | 365 + .../faad_hdc/upstream/libfaad/sbr_huff.h | 46 + .../faad_hdc/upstream/libfaad/sbr_noise.h | 564 + .../faad_hdc/upstream/libfaad/sbr_qmf.c | 635 + .../faad_hdc/upstream/libfaad/sbr_qmf.h | 55 + .../faad_hdc/upstream/libfaad/sbr_qmf_c.h | 368 + .../faad_hdc/upstream/libfaad/sbr_syntax.c | 939 + .../faad_hdc/upstream/libfaad/sbr_syntax.h | 68 + .../faad_hdc/upstream/libfaad/sbr_tf_grid.c | 262 + .../faad_hdc/upstream/libfaad/sbr_tf_grid.h | 47 + .../faad_hdc/upstream/libfaad/sine_win.h | 4304 ++++ .../faad_hdc/upstream/libfaad/specrec.c | 1365 ++ .../faad_hdc/upstream/libfaad/specrec.h | 49 + third_party/faad_hdc/upstream/libfaad/ssr.c | 175 + third_party/faad_hdc/upstream/libfaad/ssr.h | 59 + .../faad_hdc/upstream/libfaad/ssr_fb.c | 184 + .../faad_hdc/upstream/libfaad/ssr_fb.h | 53 + .../faad_hdc/upstream/libfaad/ssr_ipqf.c | 191 + .../faad_hdc/upstream/libfaad/ssr_ipqf.h | 46 + .../faad_hdc/upstream/libfaad/ssr_win.h | 635 + .../faad_hdc/upstream/libfaad/structs.h | 446 + .../faad_hdc/upstream/libfaad/syntax.c | 2968 +++ .../faad_hdc/upstream/libfaad/syntax.h | 138 + third_party/faad_hdc/upstream/libfaad/tns.c | 339 + third_party/faad_hdc/upstream/libfaad/tns.h | 51 + third_party/faad_hdc/upstream/properties.json | 4 + third_party/nrsc5/COPYING.GPL-3.0 | 674 + third_party/nrsc5/LICENSE-SOURCE.json | 6 + third_party/nrsc5/README.aethersdr.md | 104 + third_party/nrsc5/SOURCE-MANIFEST.json | 326 + third_party/nrsc5/compat/msvc/complex.h | 70 + third_party/nrsc5/compat/msvc/pthread.h | 109 + third_party/nrsc5/patches/windows-msvc.patch | 56 + third_party/nrsc5/upstream/CMakeLists.txt | 316 + third_party/nrsc5/upstream/LICENSE | 12 + third_party/nrsc5/upstream/README.md | 164 + third_party/nrsc5/upstream/include/nrsc5.h | 914 + third_party/nrsc5/upstream/src/CMakeLists.txt | 123 + third_party/nrsc5/upstream/src/acquire.c | 380 + third_party/nrsc5/upstream/src/acquire.h | 42 + third_party/nrsc5/upstream/src/bitwriter.h | 43 + third_party/nrsc5/upstream/src/config.h.in | 25 + third_party/nrsc5/upstream/src/conv.h | 37 + third_party/nrsc5/upstream/src/conv_dec.c | 478 + third_party/nrsc5/upstream/src/conv_gen.h | 123 + third_party/nrsc5/upstream/src/conv_neon.h | 173 + third_party/nrsc5/upstream/src/conv_sse.h | 323 + third_party/nrsc5/upstream/src/decode.c | 579 + third_party/nrsc5/upstream/src/decode.h | 79 + third_party/nrsc5/upstream/src/defines.h | 138 + .../nrsc5/upstream/src/firdecim_cf32.c | 98 + .../nrsc5/upstream/src/firdecim_cf32.h | 11 + third_party/nrsc5/upstream/src/frame.c | 795 + third_party/nrsc5/upstream/src/frame.h | 56 + third_party/nrsc5/upstream/src/here_images.c | 146 + third_party/nrsc5/upstream/src/here_images.h | 22 + third_party/nrsc5/upstream/src/input.c | 208 + third_party/nrsc5/upstream/src/input.h | 44 + third_party/nrsc5/upstream/src/libnrsc5.map | 31 + third_party/nrsc5/upstream/src/libnrsc5.sym | 25 + third_party/nrsc5/upstream/src/log.c | 104 + third_party/nrsc5/upstream/src/log.h | 34 + third_party/nrsc5/upstream/src/main.c | 1174 ++ third_party/nrsc5/upstream/src/nrsc5.c | 1206 ++ third_party/nrsc5/upstream/src/output.c | 983 + third_party/nrsc5/upstream/src/output.h | 140 + third_party/nrsc5/upstream/src/pids.c | 1102 ++ third_party/nrsc5/upstream/src/pids.h | 99 + third_party/nrsc5/upstream/src/private.h | 97 + third_party/nrsc5/upstream/src/rs_char.h | 52 + third_party/nrsc5/upstream/src/rs_decode.c | 210 + third_party/nrsc5/upstream/src/rs_init.c | 133 + third_party/nrsc5/upstream/src/rtltcp.c | 197 + third_party/nrsc5/upstream/src/rtltcp.h | 23 + third_party/nrsc5/upstream/src/strndup.c | 32 + third_party/nrsc5/upstream/src/sync.c | 841 + third_party/nrsc5/upstream/src/sync.h | 37 + third_party/nrsc5/upstream/src/unicode.c | 78 + third_party/nrsc5/upstream/src/unicode.h | 6 + .../upstream/support/faad2-hdc-support.patch | 958 + tools/hd_fm_recording_probe.cpp | 327 + tools/hd_fm_worker_recording_probe.cpp | 410 + 240 files changed, 86861 insertions(+), 120 deletions(-) create mode 100644 cmake/AetherHdFm.cmake create mode 100644 cmake/AetherHdFmWindows.cmake create mode 100644 docs/rtl-hd-fm-experimental.md create mode 100644 docs/rtl-hd-fm-portable-packaging.md create mode 100644 packaging/windows/README-HD-FM.txt.in create mode 100644 packaging/windows/stage-hd-portable.ps1 create mode 100644 src/core/HdFmReception.h create mode 100644 src/core/backends/rtl/HdFmIqAdapter.cpp create mode 100644 src/core/backends/rtl/HdFmIqAdapter.h create mode 100644 src/core/backends/rtl/HdFmReceiver.cpp create mode 100644 src/core/backends/rtl/HdFmReceiver.h create mode 100644 src/core/backends/rtl/HdFmTypes.h create mode 100644 src/core/backends/rtl/Nrsc5FmDecoder.cpp create mode 100644 src/core/backends/rtl/Nrsc5FmDecoder.h create mode 100644 src/gui/WfmBroadcastOverlay.cpp create mode 100644 src/gui/WfmBroadcastOverlay.h create mode 100644 src/gui/WfmBroadcastTicker.h create mode 100644 src/gui/WfmPresentationSettings.cpp create mode 100644 src/gui/WfmPresentationSettings.h create mode 100644 tests/broadcast_overlay_test.cpp create mode 100644 tests/hd_fm_iq_adapter_test.cpp create mode 100644 tests/hd_fm_receiver_test.cpp create mode 100644 tests/nrsc5_fm_decoder_test.cpp create mode 100644 tests/nrsc5_windows_compat_test.c create mode 100644 third_party/faad_hdc/README.aethersdr.md create mode 100644 third_party/faad_hdc/SOURCE-MANIFEST.json create mode 100644 third_party/faad_hdc/upstream/AUTHORS create mode 100644 third_party/faad_hdc/upstream/CMakeLists.txt create mode 100644 third_party/faad_hdc/upstream/COPYING create mode 100644 third_party/faad_hdc/upstream/ChangeLog create mode 100644 third_party/faad_hdc/upstream/README create mode 100644 third_party/faad_hdc/upstream/frontend/main.c create mode 100644 third_party/faad_hdc/upstream/include/faad.h.in create mode 100644 third_party/faad_hdc/upstream/include/neaacdec.h create mode 100644 third_party/faad_hdc/upstream/libfaad/analysis.h create mode 100644 third_party/faad_hdc/upstream/libfaad/bits.c create mode 100644 third_party/faad_hdc/upstream/libfaad/bits.h create mode 100644 third_party/faad_hdc/upstream/libfaad/cfft.c create mode 100644 third_party/faad_hdc/upstream/libfaad/cfft.h create mode 100644 third_party/faad_hdc/upstream/libfaad/cfft_tab.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_1.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_10.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_11.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_2.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_3.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_4.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_5.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_6.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_7.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_8.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_9.h create mode 100644 third_party/faad_hdc/upstream/libfaad/codebook/hcb_sf.h create mode 100644 third_party/faad_hdc/upstream/libfaad/common.c create mode 100644 third_party/faad_hdc/upstream/libfaad/common.h create mode 100644 third_party/faad_hdc/upstream/libfaad/decoder.c create mode 100644 third_party/faad_hdc/upstream/libfaad/drc.c create mode 100644 third_party/faad_hdc/upstream/libfaad/drc.h create mode 100644 third_party/faad_hdc/upstream/libfaad/drm_dec.c create mode 100644 third_party/faad_hdc/upstream/libfaad/drm_dec.h create mode 100644 third_party/faad_hdc/upstream/libfaad/error.c create mode 100644 third_party/faad_hdc/upstream/libfaad/error.h create mode 100644 third_party/faad_hdc/upstream/libfaad/faad2.pc.in create mode 100644 third_party/faad_hdc/upstream/libfaad/filtbank.c create mode 100644 third_party/faad_hdc/upstream/libfaad/filtbank.h create mode 100644 third_party/faad_hdc/upstream/libfaad/fixed.h create mode 100644 third_party/faad_hdc/upstream/libfaad/hcr.c create mode 100644 third_party/faad_hdc/upstream/libfaad/huffman.c create mode 100644 third_party/faad_hdc/upstream/libfaad/huffman.h create mode 100644 third_party/faad_hdc/upstream/libfaad/ic_predict.c create mode 100644 third_party/faad_hdc/upstream/libfaad/ic_predict.h create mode 100644 third_party/faad_hdc/upstream/libfaad/iq_table.h create mode 100644 third_party/faad_hdc/upstream/libfaad/is.c create mode 100644 third_party/faad_hdc/upstream/libfaad/is.h create mode 100644 third_party/faad_hdc/upstream/libfaad/kbd_win.h create mode 100644 third_party/faad_hdc/upstream/libfaad/lt_predict.c create mode 100644 third_party/faad_hdc/upstream/libfaad/lt_predict.h create mode 100644 third_party/faad_hdc/upstream/libfaad/mdct.c create mode 100644 third_party/faad_hdc/upstream/libfaad/mdct.h create mode 100644 third_party/faad_hdc/upstream/libfaad/mdct_tab.h create mode 100644 third_party/faad_hdc/upstream/libfaad/mp4.c create mode 100644 third_party/faad_hdc/upstream/libfaad/mp4.h create mode 100644 third_party/faad_hdc/upstream/libfaad/ms.c create mode 100644 third_party/faad_hdc/upstream/libfaad/ms.h create mode 100644 third_party/faad_hdc/upstream/libfaad/output.c create mode 100644 third_party/faad_hdc/upstream/libfaad/output.h create mode 100644 third_party/faad_hdc/upstream/libfaad/pns.c create mode 100644 third_party/faad_hdc/upstream/libfaad/pns.h create mode 100644 third_party/faad_hdc/upstream/libfaad/ps_dec.c create mode 100644 third_party/faad_hdc/upstream/libfaad/ps_dec.h create mode 100644 third_party/faad_hdc/upstream/libfaad/ps_syntax.c create mode 100644 third_party/faad_hdc/upstream/libfaad/ps_tables.h create mode 100644 third_party/faad_hdc/upstream/libfaad/pulse.c create mode 100644 third_party/faad_hdc/upstream/libfaad/pulse.h create mode 100644 third_party/faad_hdc/upstream/libfaad/rvlc.c create mode 100644 third_party/faad_hdc/upstream/libfaad/rvlc.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_dct.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_dct.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_dec.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_dec.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_e_nf.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_e_nf.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_fbt.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_fbt.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_hfadj.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_hfadj.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_hfgen.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_hfgen.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_huff.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_huff.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_noise.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_qmf.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_qmf.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_qmf_c.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_syntax.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_syntax.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_tf_grid.c create mode 100644 third_party/faad_hdc/upstream/libfaad/sbr_tf_grid.h create mode 100644 third_party/faad_hdc/upstream/libfaad/sine_win.h create mode 100644 third_party/faad_hdc/upstream/libfaad/specrec.c create mode 100644 third_party/faad_hdc/upstream/libfaad/specrec.h create mode 100644 third_party/faad_hdc/upstream/libfaad/ssr.c create mode 100644 third_party/faad_hdc/upstream/libfaad/ssr.h create mode 100644 third_party/faad_hdc/upstream/libfaad/ssr_fb.c create mode 100644 third_party/faad_hdc/upstream/libfaad/ssr_fb.h create mode 100644 third_party/faad_hdc/upstream/libfaad/ssr_ipqf.c create mode 100644 third_party/faad_hdc/upstream/libfaad/ssr_ipqf.h create mode 100644 third_party/faad_hdc/upstream/libfaad/ssr_win.h create mode 100644 third_party/faad_hdc/upstream/libfaad/structs.h create mode 100644 third_party/faad_hdc/upstream/libfaad/syntax.c create mode 100644 third_party/faad_hdc/upstream/libfaad/syntax.h create mode 100644 third_party/faad_hdc/upstream/libfaad/tns.c create mode 100644 third_party/faad_hdc/upstream/libfaad/tns.h create mode 100644 third_party/faad_hdc/upstream/properties.json create mode 100644 third_party/nrsc5/COPYING.GPL-3.0 create mode 100644 third_party/nrsc5/LICENSE-SOURCE.json create mode 100644 third_party/nrsc5/README.aethersdr.md create mode 100644 third_party/nrsc5/SOURCE-MANIFEST.json create mode 100644 third_party/nrsc5/compat/msvc/complex.h create mode 100644 third_party/nrsc5/compat/msvc/pthread.h create mode 100644 third_party/nrsc5/patches/windows-msvc.patch create mode 100644 third_party/nrsc5/upstream/CMakeLists.txt create mode 100644 third_party/nrsc5/upstream/LICENSE create mode 100644 third_party/nrsc5/upstream/README.md create mode 100644 third_party/nrsc5/upstream/include/nrsc5.h create mode 100644 third_party/nrsc5/upstream/src/CMakeLists.txt create mode 100644 third_party/nrsc5/upstream/src/acquire.c create mode 100644 third_party/nrsc5/upstream/src/acquire.h create mode 100644 third_party/nrsc5/upstream/src/bitwriter.h create mode 100644 third_party/nrsc5/upstream/src/config.h.in create mode 100644 third_party/nrsc5/upstream/src/conv.h create mode 100644 third_party/nrsc5/upstream/src/conv_dec.c create mode 100644 third_party/nrsc5/upstream/src/conv_gen.h create mode 100644 third_party/nrsc5/upstream/src/conv_neon.h create mode 100644 third_party/nrsc5/upstream/src/conv_sse.h create mode 100644 third_party/nrsc5/upstream/src/decode.c create mode 100644 third_party/nrsc5/upstream/src/decode.h create mode 100644 third_party/nrsc5/upstream/src/defines.h create mode 100644 third_party/nrsc5/upstream/src/firdecim_cf32.c create mode 100644 third_party/nrsc5/upstream/src/firdecim_cf32.h create mode 100644 third_party/nrsc5/upstream/src/frame.c create mode 100644 third_party/nrsc5/upstream/src/frame.h create mode 100644 third_party/nrsc5/upstream/src/here_images.c create mode 100644 third_party/nrsc5/upstream/src/here_images.h create mode 100644 third_party/nrsc5/upstream/src/input.c create mode 100644 third_party/nrsc5/upstream/src/input.h create mode 100644 third_party/nrsc5/upstream/src/libnrsc5.map create mode 100644 third_party/nrsc5/upstream/src/libnrsc5.sym create mode 100644 third_party/nrsc5/upstream/src/log.c create mode 100644 third_party/nrsc5/upstream/src/log.h create mode 100644 third_party/nrsc5/upstream/src/main.c create mode 100644 third_party/nrsc5/upstream/src/nrsc5.c create mode 100644 third_party/nrsc5/upstream/src/output.c create mode 100644 third_party/nrsc5/upstream/src/output.h create mode 100644 third_party/nrsc5/upstream/src/pids.c create mode 100644 third_party/nrsc5/upstream/src/pids.h create mode 100644 third_party/nrsc5/upstream/src/private.h create mode 100644 third_party/nrsc5/upstream/src/rs_char.h create mode 100644 third_party/nrsc5/upstream/src/rs_decode.c create mode 100644 third_party/nrsc5/upstream/src/rs_init.c create mode 100644 third_party/nrsc5/upstream/src/rtltcp.c create mode 100644 third_party/nrsc5/upstream/src/rtltcp.h create mode 100644 third_party/nrsc5/upstream/src/strndup.c create mode 100644 third_party/nrsc5/upstream/src/sync.c create mode 100644 third_party/nrsc5/upstream/src/sync.h create mode 100644 third_party/nrsc5/upstream/src/unicode.c create mode 100644 third_party/nrsc5/upstream/src/unicode.h create mode 100644 third_party/nrsc5/upstream/support/faad2-hdc-support.patch create mode 100644 tools/hd_fm_recording_probe.cpp create mode 100644 tools/hd_fm_worker_recording_probe.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e26e2c24f..8f598e693 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -947,6 +947,9 @@ set(CORE_SOURCES src/core/backends/rtl/RtlReceivePipeline.cpp src/core/backends/rtl/RtlAudioMixer.cpp src/core/backends/rtl/RtlRfExtractor.cpp + src/core/backends/rtl/HdFmIqAdapter.cpp + src/core/backends/rtl/Nrsc5FmDecoder.cpp + src/core/backends/rtl/HdFmReceiver.cpp src/core/backends/rtl/RtlReceiverRegistry.cpp # RFC #5468 prepared receiver ownership; no USB dependency ${AETHER_SETTINGS_SOURCES} src/core/RadioStateMemory.cpp # RFC #4603 client-side radio memory @@ -1393,6 +1396,8 @@ set(GUI_SOURCES src/gui/WaveformWidget.cpp src/gui/AetherClockApplet.cpp src/gui/WfmApplet.cpp + src/gui/WfmPresentationSettings.cpp + src/gui/WfmBroadcastOverlay.cpp src/gui/WfmLockScope.cpp src/gui/ClockAlignmentWidget.cpp src/gui/ClientEqApplet.cpp @@ -2840,6 +2845,20 @@ if(WIN32 AND AETHER_BACKEND_RTL) endforeach() endif() +include(cmake/AetherHdFm.cmake) +if(TARGET aether_nrsc5) + target_link_libraries(aethercore PRIVATE aether_nrsc5) + target_compile_definitions(aethercore PUBLIC AETHER_ENABLE_NRSC5=1) + # Explicit diagnostic: a user-supplied recorded IQ file drives the real + # embedded decoder. It is never run by CTest or built by the default target. + add_executable(hd_fm_recording_probe EXCLUDE_FROM_ALL tools/hd_fm_recording_probe.cpp) + target_link_libraries(hd_fm_recording_probe PRIVATE aethercore aether_nrsc5 Qt6::Core) + # Separate source-clock-paced production worker/mixer diagnostic. No USB, + # sockets or audio devices; intentionally absent from default builds/CTest. + add_executable(hd_fm_worker_recording_probe EXCLUDE_FROM_ALL tools/hd_fm_worker_recording_probe.cpp) + target_link_libraries(hd_fm_worker_recording_probe PRIVATE aethercore aether_nrsc5 Qt6::Core) +endif() + # Ulanzi Dial backend — one per platform. All three implementations # expose the same Qt signal contract (see UlanziDialBackend.h); the # mapper dialog and MainWindow dispatcher are unchanged across diff --git a/THIRD_PARTY_LICENSES b/THIRD_PARTY_LICENSES index 782759cc2..7d254a3c0 100644 --- a/THIRD_PARTY_LICENSES +++ b/THIRD_PARTY_LICENSES @@ -726,3 +726,26 @@ tools/docs/pdf/fonts/OFL.txt. Copyright: Copyright 2022 The Noto Project Authors License: SIL Open Font License, Version 1.1 Source: https://github.com/notofonts/symbols + +Experimental native HD FM — nrsc5 and FAAD2 HDC +--------------------------------------------- +Enabled only by ENABLE_HD_FM for Linux/GNU C, macOS/AppleClang and +Windows/MSVC evaluation builds. Both libraries are linked statically, +with no decoder command-line application or download +fallback. AetherSDR retains sole ownership of RTL USB capture. + +nrsc5 is pinned to theori-io/nrsc5 commit +0225922b6f68109df39d07391f4d855464598ab8, GPL-3.0-or-later. +Source: https://github.com/theori-io/nrsc5 +Original license and complete GPLv3 text: third_party/nrsc5/upstream/LICENSE +and third_party/nrsc5/COPYING.GPL-3.0. Per-file copyrights remain in the sources. + +FAAD2 is pinned to knik0/faad2 tag 2.11.2, commit +673a22a3c7c33e96e2ff7aae7c4d2bc190dfbf92, GPL-2.0-or-later, with exactly the +HDC-support patch shipped by the nrsc5 revision above. +Source: https://github.com/knik0/faad2 +Code from FAAD2 is copyright (c) Nero AG, www.nero.com +Preserved original notices: third_party/faad_hdc/upstream/COPYING and README. + +Both directories contain README.aethersdr.md and SOURCE-MANIFEST.json with +exact upstream and vendored file hashes, patch provenance and omitted paths. diff --git a/cmake/AetherHdFm.cmake b/cmake/AetherHdFm.cmake new file mode 100644 index 000000000..fc4dfc894 --- /dev/null +++ b/cmake/AetherHdFm.cmake @@ -0,0 +1,135 @@ +# Embedded HD FM dependencies. Upstream build files are retained as provenance +# only: they include download fallbacks, CLI/shared targets and install hooks. +include_guard(GLOBAL) + +option(ENABLE_HD_FM "Build experimental embedded digital FM" OFF) +if(NOT ENABLE_HD_FM) + return() +endif() + +if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_C_COMPILER_ID STREQUAL "GNU") + AND NOT (APPLE AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang") + AND NOT (WIN32 AND MSVC)) + message(FATAL_ERROR + "ENABLE_HD_FM supports Linux/GNU C, macOS/AppleClang, and Windows/MSVC qualification builds. " + "This compiler/platform has not been qualified. Use -DENABLE_HD_FM=OFF.") +endif() +if(NOT AETHER_BACKEND_RTL) + message(FATAL_ERROR "ENABLE_HD_FM requires the enabled RTL backend and its existing dependencies.") +endif() +foreach(dependency_variable RTLSDR_TARGET RTL_FFTW3F_TARGET) + if(NOT DEFINED ${dependency_variable} OR "${${dependency_variable}}" STREQUAL "") + message(FATAL_ERROR "ENABLE_HD_FM requires an existing ${dependency_variable}; no download fallback is provided.") + endif() + if(NOT TARGET "${${dependency_variable}}") + message(FATAL_ERROR "ENABLE_HD_FM dependency target ${${dependency_variable}} does not exist.") + endif() +endforeach() + +# Scope feature checks and upstream config names to this function. Do not alter +# the parent project's C dialect, optimization flags or generic BUILD_CLI cache. +function(aether_add_hd_fm_dependencies) + include(CheckSymbolExists) + include(CheckCSourceCompiles) + find_package(Threads REQUIRED) + if(NOT WIN32) + find_library(AETHER_HD_MATH_LIBRARY NAMES m REQUIRED) + endif() + get_filename_component(vendor_root "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../third_party" ABSOLUTE) + set(nrsc5_root "${vendor_root}/nrsc5/upstream") + set(faad_root "${vendor_root}/faad_hdc/upstream") + set(config_root "${CMAKE_CURRENT_BINARY_DIR}/aether-hd-fm") + + if(WIN32) + include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/AetherHdFmWindows.cmake") + aether_hd_windows_compiler() + set(AETHER_NRSC5_HAVE_STRNDUP OFF) + set(AETHER_NRSC5_HAVE_CMPLXF ON) + set(AETHER_NRSC5_HAVE_COMPLEX_I ON) + set(AETHER_NRSC5_HAVE_IMAGINARY_I OFF) + check_symbol_exists(lrintf "math.h" AETHER_FAAD_HAVE_LRINTF) + else() + set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) + set(CMAKE_REQUIRED_LIBRARIES "${AETHER_HD_MATH_LIBRARY}") + string(APPEND CMAKE_REQUIRED_FLAGS " -std=gnu11") + check_c_source_compiles("#include \nint main(void) { float complex z = 1.0f + 2.0f * I; return crealf(z) != 1.0f; }" + AETHER_NRSC5_HAVE_C_COMPLEX) + if(NOT AETHER_NRSC5_HAVE_C_COMPLEX) + message(FATAL_ERROR "ENABLE_HD_FM requires working GNU C11 complex arithmetic.") + endif() + check_symbol_exists(strndup "string.h" AETHER_NRSC5_HAVE_STRNDUP) + check_symbol_exists(CMPLXF "complex.h" AETHER_NRSC5_HAVE_CMPLXF) + check_symbol_exists(_Imaginary_I "complex.h" AETHER_NRSC5_HAVE_IMAGINARY_I) + check_symbol_exists(_Complex_I "complex.h" AETHER_NRSC5_HAVE_COMPLEX_I) + check_symbol_exists(lrintf "math.h" AETHER_FAAD_HAVE_LRINTF) + endif() + + set(USE_FAAD2 ON) + set(HAVE_STRNDUP "${AETHER_NRSC5_HAVE_STRNDUP}") + set(HAVE_CMPLXF "${AETHER_NRSC5_HAVE_CMPLXF}") + set(HAVE_IMAGINARY_I "${AETHER_NRSC5_HAVE_IMAGINARY_I}") + set(HAVE_COMPLEX_I "${AETHER_NRSC5_HAVE_COMPLEX_I}") + # Upstream level 5 disables its synchronous stderr logging. Reception and + # processing health are surfaced by the typed wrapper, not unbounded logs. + set(LIBRARY_DEBUG_LEVEL 5) + configure_file("${nrsc5_root}/src/config.h.in" "${config_root}/config.h" @ONLY) + + # Exactly the patched floating-point HDC decoder. No ordinary/DRM/fixed + # variants, frontend, shared library, downloader or install target is added. + set(faad_sources + bits.c cfft.c common.c decoder.c drc.c drm_dec.c error.c filtbank.c + hcr.c huffman.c ic_predict.c is.c lt_predict.c mdct.c mp4.c ms.c + output.c pns.c ps_dec.c ps_syntax.c pulse.c rvlc.c sbr_dct.c sbr_dec.c + sbr_e_nf.c sbr_fbt.c sbr_hfadj.c sbr_hfgen.c sbr_huff.c sbr_qmf.c + sbr_syntax.c sbr_tf_grid.c specrec.c ssr.c ssr_fb.c ssr_ipqf.c syntax.c tns.c) + list(TRANSFORM faad_sources PREPEND "${faad_root}/libfaad/") + add_library(aether_faad_hdc STATIC ${faad_sources}) + # These pinned headers must precede package-manager includes: a stock FAAD + # header can exist beside FFTW but does not declare the HDC entry point. + target_include_directories(aether_faad_hdc PUBLIC "${faad_root}/include") + target_include_directories(aether_faad_hdc PRIVATE "${faad_root}/libfaad") + target_compile_definitions(aether_faad_hdc PRIVATE + HDC_SUPPORT APPLY_DRC HAVE_INTTYPES_H=1 HAVE_MEMCPY=1 HAVE_STRING_H=1 + HAVE_SYS_STAT_H=1 HAVE_SYS_TYPES_H=1 PACKAGE_VERSION="2.11.2") + if(NOT WIN32) + target_compile_definitions(aether_faad_hdc PRIVATE HAVE_STRINGS_H=1) + endif() + if(AETHER_FAAD_HAVE_LRINTF) + target_compile_definitions(aether_faad_hdc PRIVATE HAVE_LRINTF=1) + endif() + # Match the pinned FAAD GNU compiler setting, without changing other targets. + if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_compile_options(aether_faad_hdc PRIVATE -ffloat-store) + endif() + target_link_libraries(aether_faad_hdc PRIVATE "${AETHER_HD_MATH_LIBRARY}") + + # BUILD_CLI=OFF by construction: neither upstream CMakeLists is evaluated. + # The retained rtltcp/device API is not exposed; Aether's wrapper uses + # only open_pipe/pipe_samples_cf32/close; it is the sole USB capture owner. + set(nrsc5_sources + acquire.c decode.c frame.c here_images.c input.c nrsc5.c output.c + pids.c rtltcp.c sync.c firdecim_cf32.c conv_dec.c rs_init.c rs_decode.c + unicode.c strndup.c) + list(TRANSFORM nrsc5_sources PREPEND "${nrsc5_root}/src/") + if(WIN32) + aether_hd_windows_nrsc5("${nrsc5_sources}") + else() + add_library(aether_nrsc5 STATIC ${nrsc5_sources}) + endif() + target_include_directories(aether_nrsc5 PUBLIC "${nrsc5_root}/include") + target_include_directories(aether_nrsc5 PRIVATE "${config_root}") + # Upstream separately defines HAVE_FAAD2 for output.h declarations; + # generated USE_FAAD2 enables their implementation in output.c. + target_compile_definitions(aether_nrsc5 PRIVATE + _GNU_SOURCE HAVE_FAAD2=1 GIT_COMMIT_HASH="0225922b6f68109df39d07391f4d855464598ab8") + target_compile_definitions(aether_nrsc5 INTERFACE AETHER_ENABLE_NRSC5=1) + target_link_libraries(aether_nrsc5 PRIVATE aether_faad_hdc + "${RTL_FFTW3F_TARGET}" "${RTLSDR_TARGET}" Threads::Threads "${AETHER_HD_MATH_LIBRARY}") + + set_target_properties(aether_faad_hdc aether_nrsc5 PROPERTIES + C_STANDARD 11 C_STANDARD_REQUIRED ON C_EXTENSIONS ON + POSITION_INDEPENDENT_CODE ON AUTOMOC OFF AUTOUIC OFF AUTORCC OFF) + message(STATUS "HD FM ... experimental pinned nrsc5 + HDC enabled; embedded pipe API, CLI OFF") +endfunction() + +aether_add_hd_fm_dependencies() diff --git a/cmake/AetherHdFmWindows.cmake b/cmake/AetherHdFmWindows.cmake new file mode 100644 index 000000000..7e3b96a1b --- /dev/null +++ b/cmake/AetherHdFmWindows.cmake @@ -0,0 +1,76 @@ +# nrsc5 alone needs GNU C complex arithmetic and VLAs. Keep the application and +# HDC decoder on MSVC, and compile these C objects with clang-cl's matching ABI. +# No compiler/runtime download or global compiler/flag change is performed. +include_guard(GLOBAL) + +macro(aether_hd_windows_compiler) + if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8 OR NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|amd64|x86_64)$") + message(FATAL_ERROR "ENABLE_HD_FM Windows qualification supports x64 only.") + endif() + if(CMAKE_MSVC_RUNTIME_LIBRARY AND NOT CMAKE_MSVC_RUNTIME_LIBRARY STREQUAL "MultiThreaded$<$:Debug>DLL") + message(FATAL_ERROR "ENABLE_HD_FM Windows requires the shared MSVC runtime (/MD or /MDd).") + endif() + find_program(AETHER_HD_CLANG_CL NAMES clang-cl HINTS "C:/Program Files/LLVM/bin" REQUIRED) + execute_process(COMMAND "${AETHER_HD_CLANG_CL}" --target=x86_64-pc-windows-msvc + /clang:--rtlib=compiler-rt /clang:-print-libgcc-file-name + RESULT_VARIABLE runtime_result OUTPUT_VARIABLE AETHER_HD_COMPILER_RT + ERROR_VARIABLE runtime_error OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT runtime_result EQUAL 0 OR NOT EXISTS "${AETHER_HD_COMPILER_RT}") + message(FATAL_ERROR "ENABLE_HD_FM requires the installed LLVM x64 compiler-rt builtins: ${runtime_error}") + endif() + file(TO_CMAKE_PATH "${AETHER_HD_COMPILER_RT}" AETHER_HD_COMPILER_RT) + set(AETHER_HD_COMPILER_RT "${AETHER_HD_COMPILER_RT}" CACHE INTERNAL "Installed LLVM complex arithmetic builtins" FORCE) +endmacro() + +function(aether_hd_windows_c_objects output_variable object_group sources includes definitions) + get_filename_component(vendor_root "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../third_party" ABSOLUTE) + set(faad_root "${vendor_root}/faad_hdc/upstream") + set(config_root "${CMAKE_BINARY_DIR}/aether-hd-fm") + set(object_dir "${config_root}/${object_group}/$") + set(include_flags) + foreach(include_dir IN LISTS includes) + list(APPEND include_flags "-I${include_dir}") + endforeach() + set(definition_flags) + foreach(definition IN LISTS definitions) + list(APPEND definition_flags "-D${definition}") + endforeach() + # This finite vendored subtree is small. Rebuild on every header change, + # including generated config and private compatibility headers. + file(GLOB_RECURSE nrsc5_headers CONFIGURE_DEPENDS + "${vendor_root}/nrsc5/*.h" "${faad_root}/include/*.h") + foreach(include_dir IN LISTS includes) + file(GLOB dependency_headers CONFIGURE_DEPENDS "${include_dir}/*.h") + list(APPEND nrsc5_headers ${dependency_headers}) + endforeach() + set(objects) + foreach(source IN LISTS sources) + get_filename_component(source_name "${source}" NAME_WE) + set(object "${object_dir}/${source_name}.obj") + add_custom_command(OUTPUT "${object}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${object_dir}" + COMMAND "${AETHER_HD_CLANG_CL}" /nologo --target=x86_64-pc-windows-msvc + /clang:-std=gnu11 "$,/MDd,/MD>" + "$,/Od,/O2>" /Z7 /W3 + -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS + ${include_flags} ${definition_flags} /c "${source}" "/Fo${object}" + DEPENDS "${source}" ${nrsc5_headers} "${config_root}/config.h" + VERBATIM COMMENT "Building native HD C object ${object_group}/${source_name}") + list(APPEND objects "${object}") + endforeach() + set_source_files_properties(${objects} PROPERTIES GENERATED TRUE EXTERNAL_OBJECT TRUE) + set(${output_variable} "${objects}" PARENT_SCOPE) +endfunction() + +function(aether_hd_windows_nrsc5 sources) + get_target_property(rtl_includes "${RTLSDR_TARGET}" INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(fftw_includes "${RTL_FFTW3F_TARGET}" INTERFACE_INCLUDE_DIRECTORIES) + set(includes "${vendor_root}/nrsc5/compat/msvc" "${config_root}" + "${nrsc5_root}/include" "${faad_root}/include" ${rtl_includes} ${fftw_includes}) + set(definitions HAVE_FAAD2=1 FFTW_DLL= GIT_COMMIT_HASH="0225922b6f68109df39d07391f4d855464598ab8") + aether_hd_windows_c_objects(objects nrsc5 "${sources}" "${includes}" "${definitions}") + add_library(aether_nrsc5 STATIC ${objects}) + set_target_properties(aether_nrsc5 PROPERTIES LINKER_LANGUAGE C) + target_link_libraries(aether_nrsc5 PRIVATE "${AETHER_HD_COMPILER_RT}" ws2_32) + message(STATUS "HD FM Windows C compiler: ${AETHER_HD_CLANG_CL}; runtime: ${AETHER_HD_COMPILER_RT}") +endfunction() diff --git a/docs/architecture/aetherd-touchpoints.md b/docs/architecture/aetherd-touchpoints.md index d68694e25..fc007cf12 100644 --- a/docs/architecture/aetherd-touchpoints.md +++ b/docs/architecture/aetherd-touchpoints.md @@ -247,13 +247,13 @@ Burndown manifest for the engine/UI decoupling ([RFC](../aetherd-headless-engine | `models/Nr2SettingsModel.h` | 5 | universal — Process-wide owner of client-side NR2 configuration as one versioned object (Principle V), so every editing surface shares state. Engine DSP configuration; radio-agnostic. | unconverted | | `models/PanadapterModel.h` | 5 | mixed(flex) — Per-pan display state is core-profile; DAX IQ ch, client_handle, VITA stream IDs, SmartSDR kv parsing are flex. | unconverted | | `models/ProfileLoadCommand.h` | 1 | vendor(flex) — Parses SmartSDR 'profile global/tx/mic load' wire command + recall-hold timing/suppression sentinels; Flex-only | unconverted | -| `models/RadioModel.h` | 53 | mixed(flex) — Central radio aggregate: core slice/pan/TX/meter/memory state fused with Flex protocol, DAX, SmartLink, Multi-Flex | unconverted | +| `models/RadioModel.h` | 54 | mixed(flex) — Central radio aggregate: core slice/pan/TX/meter/memory state fused with Flex protocol, DAX, SmartLink, Multi-Flex | unconverted | | `models/RadioSession.h` | 1 | universal — Per-radio session aggregate: owns RadioModel + id/label; session concept is core-profile, no vendor surface | unconverted | | `models/RadioStatusOwnership.h` | 1 | vendor(flex) — SmartSDR status parsing helpers: Flex hex handles, client_handle ownership, remote_audio_rx, interlock gate | unconverted | | `models/ReceiverSlotCount.h` | 1 | universal — How many receiver letters (A, B, C, ...) the UI offers — the RX applet's slice tabs and the CAT applet's VFO targets — tracked from the connection/capability/slice edges. Family-agnostic: the backend's declared ceiling, floored by the slots live receivers occupy (#5775, #5776). | unconverted | | `models/Rn2SettingsModel.h` | 1 | universal — Process-wide owner of client-side RN2 configuration as one versioned object (Principle V). Engine DSP configuration; radio-agnostic. | unconverted | | `models/SliceLinkPolicy.h` | 2 | universal — Pure decision logic for Slice Link (cross-panadapter VFO link) — echo classification on integer Hz, no QObject, no I/O, no model access. Canonical slice state only. | unconverted | -| `models/SliceModel.h` | 36 | mixed(flex) — Slice state (freq/mode/filter/DSP) is core-profile; DAX, index_letter, SmartSDR status KVs are flex ext | unconverted | +| `models/SliceModel.h` | 37 | mixed(flex) — Slice state (freq/mode/filter/DSP) is core-profile; DAX, index_letter, SmartSDR status KVs are flex ext | unconverted | | `models/SpotModel.h` | 1 | universal — Panadapter spot store (callsign/freq/mode/lifetime/priority) on canonical state; kv ingest is trivially generic | unconverted | | `models/TnfModel.h` | 1 | universal — Tracking notch filter state (freq/width/depth/permanent, global enable) — generic DSP notch surface; kv parse is transport detail | unconverted | | `models/TransmitModel.h` | 17 | mixed(flex) — TX state model: power/MOX/VOX/CW/filter are core-profile; ATU, DAX, APD, profiles, interlock are Flex. | unconverted | diff --git a/docs/architecture/radio-capabilities-map.md b/docs/architecture/radio-capabilities-map.md index 7b450308a..6330535b3 100644 --- a/docs/architecture/radio-capabilities-map.md +++ b/docs/architecture/radio-capabilities-map.md @@ -57,6 +57,12 @@ semantics see [local frequency control](../aetherd-local-slice-frequency-control ### Broadcast FM receiver controls +The experimental Digital FM build extends this record with `hdStereo`. Only +RTL with `AETHER_ENABLE_NRSC5` declares it; all other builds and families leave +it false. `requestSliceWfm` carries audio-mode and discovered-program intents +through the same identity/capability/refusal path. A program change additionally +requires current synchronized service metadata and backend freshness checks. + | Record | Flex | HL2 | Sim | Icom | ANAN | RTL-SDR | Consumers | |---|---|---|---|---|---|---|---| | `broadcastFmReceive` | absent | absent | absent | absent | absent | de-emphasis 50/75 µs, force Mono, measured reception diagnostics | `WfmApplet` availability; `RadioModel::dispatchSliceWfm` validates the capability and routes `requestSliceWfm`, with explicit refusal reporting | diff --git a/docs/rtl-hd-fm-experimental.md b/docs/rtl-hd-fm-experimental.md new file mode 100644 index 000000000..29847090d --- /dev/null +++ b/docs/rtl-hd-fm-experimental.md @@ -0,0 +1,170 @@ +# Experimental native digital FM receiver + +This is the separate draft proposal in RFC #6079, following the analog RTL +delivery in PR #6083 (replacement for #6077). Analog RFC #5468 does not depend on this feature. Maintainer architecture approval remains pending. Native build, +recorded-input and packaged-startup evidence is recorded separately for each +tested checkpoint. The approved analog WFM work does not approve these dependencies, +worker, or digital receiver. No merge or release readiness is implied. + +## Build and dependency boundary + +`ENABLE_HD_FM` defaults OFF. Opt-in build paths support Linux with GNU C, +macOS with AppleClang, and Windows x64 with MSVC plus an installed `clang-cl` +and matching LLVM compiler-rt builtins. Other compiler/platform combinations +fail configure when explicitly opted in. Each path requires the existing +enabled RTL/FFTW3f dependencies; none downloads a compiler or runtime. Default +builds on every platform retain analog WFM. + +On Windows, the application and FAAD HDC decoder remain on MSVC. Only nrsc5's +C11 complex/VLA translation units use `clang-cl`, the same Windows SDK/MSVC +ABI, and the matching shared CRT (`/MD`, or `/MDd` for Debug). Compiler-rt +supplies the complex arithmetic helpers. `cmake/AetherHdFmWindows.cmake` +keeps these compiler options local to the decoder; it does not switch the +application's compiler or global C dialect. The private compatibility headers +under `third_party/nrsc5/compat/msvc` do not change the upstream source pins. + +Application release workflows and the local macOS/Windows packaging scripts +explicitly set `-DENABLE_HD_FM=OFF`; an isolated evaluation build can still +opt in with `-DENABLE_HD_FM=ON`. + +The embedded sources are pinned to nrsc5 +`0225922b6f68109df39d07391f4d855464598ab8` and FAAD2 2.11.2 +`673a22a3c7c33e96e2ff7aae7c4d2bc190dfbf92` with the pinned nrsc5 HDC patch. +`third_party/nrsc5` and `third_party/faad_hdc` contain per-file provenance, +licenses, and notices. The explicit source lists in `cmake/AetherHdFm.cmake` +build static libraries. Neither upstream build is evaluated: there is no +network download, CLI, external decoder process, or install hook. + +Aether owns the RTL device. The wrapper exposes only the synchronous +`nrsc5_open_pipe` / `nrsc5_pipe_samples_cf32` path. Native open/close operations +share Aether's process-wide single-precision FFTW planner lock. + +## Capture, worker, and audio contracts + +The initial receiver admits one active wide digital receiver. It does not expand +receiver capacity. A complete digital footprint of ±225 kHz plus at least +3 kHz transition guards must fit the real usable capture, independently of +stored analog filter edges. Invalid original filter geometry remains invalid. +Selecting Digital uses the existing prepare/adopt/rollback transaction; a rejected +recipe does not publish or persist a selection. + +`HdFmIqAdapter` consumes original capture IQ before the analog filter or +FM discriminator. It translates phase-continuously and uses paired resamplers +to produce exactly **744187.5 complex samples/s**, without integer rounding. +Supported achieved input rates are integral 900001 through 3000000 samples/s. +A capture gap or nonfinite input withdraws that adapter epoch. + +The registry prepares and retires a dedicated decoder worker off acquisition. +A process-wide reservation allows at most two resident workers for the old/new +handoff. IQ submission uses 32 fixed slots of at most 8192 complex samples; +a queued source distance above 250 ms or a full queue fails the receiver. +The acquisition path does not wait for decoding. Private decoder callbacks copy +borrowed data immediately into bounded queues. No upstream device worker starts. + +The selected program must be synchronized and emit an unflagged, bounded, +even-length native stereo callback before audio is valid. Valid digital silence +is accepted. Unavailable or concealed frames do not establish valid audio. +Program discovery is separate and remains selectable through a concealed frame. + +The independent slice tap is true **44100 Hz stereo**, before monitor gain, +pan, or mute. It uses decoded-frame positions and original source identity. +A paired explicit conversion supplies **48000 Hz** to the existing speaker +mixer; speaker/auxiliary PCM domains and global defaults remain 24/48 kHz. +Decoder consumers explicitly convert the new slice rate to their fixed domain. + +The initial worker has 32768-frame PCM queues and an 8192-frame 48 kHz prefill. +Acquisition publishes intentional speaker zeros while acquiring; it never +fabricates decoded slice PCM. Post-ready underrun retires the audio epoch. +These queue and prefill choices require recorded and live decoder-burst +qualification; they are not a demonstrated guarantee for arbitrary RF input. +The existing mixer deadline is unchanged. + +Receiver, capture, session, revision, selected program, audio epoch, and original +production time travel with data. Buffered speaker contributions retain their +original token across control revisions. Readiness and withdrawal have a separate +monotonic publication sequence; they do not invent decoder measurements or +refresh their timestamps. Backend acceptance rejects malformed observations +before mutating accepted identity or PCM. Original observation/audio ages expire +at 500 ms, independently of delayed owner-thread receipt. Epoch changes reset +prefill before tap publication. Health exposes observed IQ drops, PCM drops, +and playout underruns cumulatively since connection. + +## Presentation and persistence + +The WFM applet cycles Mono → Auto Stereo → Digital only when the backend declares +the implemented digital capability. Accepted selection, sync, and selected audio +validity are separate states. The selector lists actual discovered audio +programs as P1 through P8 (Program 1 through Program 8). SIG channel/port numbers +are not guessed to be program numbers, so program names remain a truthful fallback for now. + +Digital diagnostics show actual MER, CBER, frequency offset, and sync history. +The API's lower/upper MER labels use the NRSC-5 spectral convention, inverted +relative to RF sidebands. Digital does not reuse the analog pilot trace or invent +constellation, SNR, or quality metrics. + +One bounded owner-thread snapshot supplies station-name and selected-program +ID3 text to the applet and passive local panadapter overlay. Complete text +updates replace the same slice/program record. Retune, selection, loss, park, +and disconnect clear mismatched information. There is no cluster spot creation +or external publication. + +SpotHub → Display contains one ordinary **WFM RDS** on/off button made by the +same factory as Memory. It defaults ON for a fresh profile, preserves saved +OFF, and controls only this local overlay. The applet and decoding continue +while it is off. `WfmPresentationSettings` owns its AppSettings document, +preserving sibling settings. Actual displayed source is Digital; +analog RDS/RBDS remains a follow-up. No automatic analog/digital blending exists. + +## Validation boundaries + +The socket-free tests cover fractional IQ conversion and partition invariance, +full-footprint admission, documented event reduction, bounded worker queues, +loss during queue drains, readiness, identity, persistence, and UI updates. +`nrsc5_fm_decoder_test` and `hd_fm_receiver_test` run only with `ENABLE_HD_FM=ON`; +they do not silently join the frozen per-PR gate. Injected API callbacks prove +client lifecycle behavior, not over-the-air decoder correctness. + +The explicit `hd_fm_recording_probe` target is excluded from the default build +and CTest. Supply an already-decompressed unsigned-IQ recording: + +```sh +cmake -S . -B build -DENABLE_HD_FM=ON +cmake --build build --parallel 4 --target hd_fm_recording_probe +./build/hd_fm_recording_probe sample.cu8 --program 0 --compressed-source sample.xz +``` + +It drives the production adapter and native decoder, reports source/executable +hashes, actual metadata, distinct-channel statistics, native callback bursts, +and gaps measured on the source sample clock. It opens no USB device or socket +and does not play or export audio. Recorded decoding is separate from the +bounded worker, speaker playout, GUI, live 104.7 MHz reception, or listening. +A separate explicit `hd_fm_worker_recording_probe` target replays the same CU8 +recording at absolute 1× source-clock deadlines through the production pipeline, +registry, native decoder worker and shared mixer: + +```sh +cmake --build build --parallel 4 --target hd_fm_worker_recording_probe +./build/hd_fm_worker_recording_probe sample.cu8 --program 0 --compressed-source sample.xz +``` + +It reports delivered 44.1 kHz slice and 48 kHz speaker PCM, original time/identity +checks, readiness transitions, queue-drop and mixer-fault counters, scheduling +lateness, process CPU and measured teardown. Acquiring speaker silence is +separate from source-produced PCM. Rejected real PCM, expiry or observed faults +cannot be hidden by a short successful prefix. It drains immediately on the +same thread and applies owner-like acceptance checks; it does not instantiate +the backend, GUI, audio device or USB conversion/DC suppression path. End of +file adds no RF padding or decoder flush, so totals describe the delivered +prefix, with a pending tail discarded at stop. Queue occupancy is not measured. +The process has a 120-second watchdog; teardown is measured for this input, +not guaranteed for arbitrary decoder behavior. + +Native qualification is specific to the tested architecture, compiler and +checkpoint. The Apple Silicon lane uses AppleClang and the Windows lane uses +MSVC/clang-cl on x64; neither qualifies Intel Mac, Windows ARM, or other ARM +systems. A compiled executable is not packaged-startup evidence. Preserve the +HD-ON tests, HD-OFF tests, direct/worker recording probes, dependency audits and +clean-extraction startup results separately with their source/binary hashes. +Current sanitizer and live RF/listening qualification remain separate work. +See [portable packaging](rtl-hd-fm-portable-packaging.md) for source delivery, +isolated settings, runtime closure and clean-start checks. diff --git a/docs/rtl-hd-fm-portable-packaging.md b/docs/rtl-hd-fm-portable-packaging.md new file mode 100644 index 000000000..df75d67b6 --- /dev/null +++ b/docs/rtl-hd-fm-portable-packaging.md @@ -0,0 +1,116 @@ +# Packaging an experimental digital FM checkpoint + +This recipe is for local, explicitly requested test artifacts. It does not +change `ENABLE_HD_FM`'s default or publish, sign a release, or install drivers. +Record the exact source revision and actual toolchain for each platform. Build +`AetherSDR` and `aetherd` with `ENABLE_HD_FM=ON` and `ENABLE_RTL=ON`, run the +relevant socket-free receiver tests, and preserve the separate decoder-OFF result. +Recorded-IQ decoding and disconnected startup are not live RF or audible proof. + +## Matching source and notices + +Prepare a source archive at the exact signed application revision, including +all tracked application sources, vendored source, original notices, local +patches, manifests, CMake files, setup and packaging scripts. `git archive` +does not include submodules: enumerate gitlinks and add their exact revisions +if present. Do not archive an entire working directory or its `.git` directory. + +Add complete matching source for externally supplied libraries whose licenses +require it, with their exact upstream version/commit, retrieved source hash, +binary hash, build/configure recipe and any local changes. In particular, +nrsc5 and patched FAAD2 are already in the application source tree, but FFTW +and RTL-SDR are not supplied by those decoder manifests. Include the source +for the actual FFTW/RTL/libusb and Qt runtimes in the payload as applicable; +a URL or a header/import library alone is not a matching source archive. +Use the dependency caches and original build records to identify them before +fetching anything. Do not label uncertain provenance as verified. + +Preserve nrsc5's original license, full GPLv3 text, and license-source record; +FAAD2's COPYING, README, AUTHORS and the Nero acknowledgment; both source +manifests; and external runtime licenses. Keep original per-file notices in +the source archive. The source archive's own README records reproducible build +steps and dependencies. Never include private RF captures, user settings, +credentials, PDBs or build caches in the shareable source or binary archive. + +## Windows staging + +Use PowerShell 7 from the existing native MSVC/Qt environment. Supply a fresh +output directory, the verified matching-source archive and its SHA-256, an +external-dependency notices directory, and a public-safe `BUILD-INFO.json`: + +```json +{ + "revision": "", + "platform": "windows-x64", + "hdFm": true, + "rtl": true, + "toolchain": { "c": "", "cxx": "", "qt": "" }, + "signing": "", + "binarySha256": { "AetherSDR.exe": "", "aetherd.exe": "" }, + "buildOptions": [""], + "dependencies": [""], + "testScope": [""] +} +``` + +Invoke `packaging/windows/stage-hd-portable.ps1` with `-BuildDir`, `-OutputDir`, +`-Revision`, `-SourceArchive`, `-SourceSha256`, `-DependencyNoticesDir`, +`-BuildInfo` and `-QtDir`. It copies only the application/daemon/helper files +and staged DLLs, plus the known loose DFNR model filenames when present, runs +`windeployqt` and the existing app-local MSVC runtime +stager and dependency audit, and adds the notices, source, friend README, +isolated-settings launcher and per-file hashes. It does not create the ZIP or +claim successful startup. Do not mutate a shared Qt install to satisfy a +deployment failure; identify the missing module and stage matching files in +task-owned paths if the deployment tool needs a separate resolution copy. +An active external-model DFNR runtime must have its model beside the executable; +the stager refuses that configuration when the known model payload is missing. + +The existing import audit indexes DLLs by filename throughout the payload. +That is intentionally more permissive than Windows' loader. The evaluation stager +additionally checks that each shipped import is beside the executable or its +importing plugin. Inspect loaded module paths in the clean-start test too. +Test `qwindows.dll` as well as the offscreen plugin. + +## Clean-extraction verification + +1. Create a ZIP from the staged payload and extract into a new task-owned + directory. Check every `FILES-SHA256.json` entry against the extracted bytes. +2. Launch from that extracted directory with `PATH` restricted to + `%SystemRoot%\System32;%SystemRoot%`; clear `QT_PLUGIN_PATH`, + `QT_QPA_PLATFORM_PLUGIN_PATH`, `QML2_IMPORT_PATH` and `QML_IMPORT_PATH`. + Use a fresh `AETHER_SETTINGS_DIR` outside the payload. Set + `AutoConnectToLastRadio=False` through the app's `--config set` CLI, and + verify its `--config path` resolves to the isolated directory. +3. Prefer the automation MCP's fresh-instance lifecycle. Where unavailable, + use the documented bridge with a unique socket and identity, an ephemeral + in-memory token, `AETHER_AUTOMATION_NO_TX=1`, and no + `AETHER_AUTOMATION_ALLOW_TX`. Never write or echo the token. The launcher + only selects an isolated settings store; it is not a general network or + credential sandbox. +4. Confirm authenticated `ping`/`whoami`, source identity, TX refusal and + disconnected radio state. Inspect runtime modules and plugin paths for + accidental dependency on Qt, MSVC or dependency development directories. + Verify native decoder availability through the real decoder tests; a + disconnected WFM widget alone cannot prove runtime decode readiness. +5. Exercise native Windows plugin startup in a task-owned instance, shut down + only that process, and preserve the result separately. Do not connect a + radio, change a driver, kill existing apps or alter the active user's session. +6. Retrieve the ZIP, separate source archive, checksums and readable report to + the Mac. Recompute the retrieved hashes. Publish only after a separate + explicit authorization. Keep test profiles and logs outside the friend ZIP. + +## macOS bundle + +Deploy the matching Qt and non-system runtime dependencies inside a separate +`.app`, rewrite only task-owned load paths, and audit all Mach-O imports and +architectures recursively. Standard system frameworks remain host-provided. +Test with developer search paths removed and an isolated `AETHER_SETTINGS_DIR`; +check the bundled Qt plugins load from the bundle. Preserve the running primary +app and launcher. Report observed code-signing/notarization status accurately: +an ad-hoc signature is not a Developer ID signature or notarization. + +Create or refresh the task-specific launcher only after the bundle passes. +Local Apple Silicon evidence does not qualify Intel Mac, and Windows x64 +evidence does not qualify Windows ARM. Preserve private fixture evidence +separately from the artifacts intended for sharing. diff --git a/docs/rtl-multirx-analog.md b/docs/rtl-multirx-analog.md index 160101ad3..7b909cbc1 100644 --- a/docs/rtl-multirx-analog.md +++ b/docs/rtl-multirx-analog.md @@ -2,13 +2,13 @@ This checkpoint implements the analog multi-receiver path from RFC #5468. It does not raise the qualified default capacity, enable multiple digital decoders, -or establish release qualification. The free-pan amendment remains subject to maintainer review. Optional -Digital FM is separate RFC #6079 and is absent from this analog branch. Native qualification results belong to the exact +or establish release qualification. The free-pan amendment and separate Digital RFC #6079 remain +subject to maintainer review. Native qualification results belong to the exact tested revision; component or injected-USB tests do not establish live RF proof. ## Evaluation admission -On Linux x86_64, Windows x64 and macOS arm64, start an explicitly isolated evaluation process with +On Linux x86_64 only, start an explicitly isolated evaluation process with `AETHER_AUTOMATION=1` and `AETHER_RTL_EVALUATION_RECEIVERS=2`, `4` or `8`. This chooses the same capacity for the transaction and private worker at construction. It cannot resize a running session, is never persisted, and @@ -18,9 +18,11 @@ authorization and TX exclusion. Use a separate `AETHER_SETTINGS_DIR` and preserve the normal launcher/profile before hardware testing. FM, FM-N and native analog WFM can coexist. Each receiver owns its exact RF -filter, Mono/Stereo choice, deemphasis and monitor gain/pan/mute. AM/SAM/SSB/CW remain singleton legacy configurations, including when a -configured sibling is parked. A refusal reports the unsupported combination -without adopting a partial recipe. Digital FM is outside this branch. +filter, Mono/Stereo choice, deemphasis and monitor gain/pan/mute. AM/SAM/SSB/CW +and digital remain singleton configurations, including when a configured sibling +is parked. A refusal reports the unsupported combination without adopting a +partial recipe. Changing back from a remembered digital recipe to analog remains +an explicit operator action. ## Selection and presentation @@ -35,7 +37,8 @@ One WFM applet shows the selected receiver's letter and accepted frequency. Queued status callbacks and controls retain their originating model binding; an old popup or held button cannot command a new selection or reused slot. Parking clears reception observations while retaining configured controls. -Digital station/program metadata and overlays are deferred with RFC #6079. +Passive broadcast overlays retain each originating receiver's station/service +identity independently of focus and display visibility. ## Measurements and interpretation @@ -51,7 +54,7 @@ bounds are conservative; the final bucket is unbounded. `rtlReceiverRecipes` reports each last-published accepted receiver recipe, including its `stableId`, current `selected` identity, `receiving`/`parked` membership, `carrierHz`, `mode`, `filterLowHz`/`filterHighHz`, `wfmForceMono`, -`wfmDeemphasisUs`, and `audioGain`/`audioPan`/`audioMute`. +`wfmHdStereo`, `wfmDeemphasisUs`, and `audioGain`/`audioPan`/`audioMute`. An observer can freeze every receiver's accepted settings without moving selection to inspect an applet. These are accepted control recipes, not an independent measurement of the demodulated signal or observed stereo lock. @@ -59,7 +62,8 @@ independent measurement of the demodulated signal or observed stereo lock. `rtlPcmStreams` contains eight stable receiver slots followed by the speaker mix. Frames, nonzero samples, nonfinite input samples, delivered discontinuities, left/right energy, peak, queue residence and delivery gaps are cumulative -owner-thread observations. Queue residence uses a separate enqueue clock. Counts and +owner-thread observations. Queue residence uses a separate enqueue clock and +does not replace the digital decoder's original production-age fence. Counts and energies can be differenced across a frozen workload. Lifetime maxima and delivery gaps can include intentional parking/reconfiguration; they must not be called steady-state dropouts without checking that interval. Finite, diff --git a/docs/rtl-release-packaging.md b/docs/rtl-release-packaging.md index 4c609ad63..04ee00078 100644 --- a/docs/rtl-release-packaging.md +++ b/docs/rtl-release-packaging.md @@ -4,7 +4,8 @@ Official AppImage (both architectures), macOS DMG (both architectures), and Windows installer/portable/MSIX pipelines configure `ENABLE_RTL=ON` and `REQUIRE_RTL=ON`. Missing librtlsdr or float FFTW is a configure error. An intentional developer-only build can still set `ENABLE_RTL=OFF` with the -requirement left OFF. +requirement left OFF. `ENABLE_HD_FM` remains OFF by default and in official +release workflows; local Digital evaluation packages explicitly opt in. `scripts/setup/rtl-dependencies.json` pins RTL-SDR Blog (the existing qualified Blog V4 driver variant), libusb, and FFTW sources by URL and SHA256. Unix setup @@ -16,7 +17,9 @@ not an imported runtime of the library-only RTL target. Setup retains corresponding archives, copyright notices and the actual build recipes in `share/aethersdr-rtl-sources`. Those travel inside each payload. -Source archives include their original license/copyright files. +Source archives include their original license/copyright files. Private HD +packages additionally carry the application source (including patched nrsc5 +and FAAD-HDC), decoder notices, and verified external dependency provenance. No setup script installs a USB driver, changes udev rules, or opens a radio. `tools/check_rtl_package.py` checks that the app imports RTL and float FFTW, diff --git a/docs/rtl-wfm.md b/docs/rtl-wfm.md index eb726434b..4bbca82a4 100644 --- a/docs/rtl-wfm.md +++ b/docs/rtl-wfm.md @@ -97,7 +97,7 @@ second plus actual status changes. Unknown or stale measurements are unavailable no quality percentage is invented. Configuration and processing-health readouts are distinct from reception measurements. -Native Digital FM is a separate proposal in RFC #6079, +Native NRSC-5 digital FM is the separate proposal in RFC #6079, not covered by the existing analog architecture approval or the qualification below. The analog checkpoint exposes no ineffective digital state or empty metadata controls. Future digital program/ID3 metadata and analog RDS have different decoder @@ -210,7 +210,7 @@ architecture-specific and operator acceptance work remains separate. ## Future digital reception -Digital FM implementation is deferred to standalone RFC #6079. +Digital FM/nrsc5 implementation is deferred until the current RTL sprint finishes. Its future branch needs wide capture IQ before this analog filter, its own station-centered extraction and 744187.5 Hz conversion, and explicit paired 44.1→48 kHz PCM conversion. RADE's demodulated 24 kHz speech input is unsuitable. diff --git a/packaging/windows/README-HD-FM.txt.in b/packaging/windows/README-HD-FM.txt.in new file mode 100644 index 000000000..2d0679aed --- /dev/null +++ b/packaging/windows/README-HD-FM.txt.in @@ -0,0 +1,48 @@ +AetherSDR - experimental digital FM test build (Windows x64) +Source revision: @REVISION@ + +Getting started +1. Extract the ENTIRE ZIP to a writable folder. Do not run inside the ZIP. +2. Run Start-AetherSDR-HD.cmd. It keeps test settings in this folder's profile + subfolder and starts disconnected. Running AetherSDR.exe directly instead + uses your normal Windows AetherSDR settings. +3. Release the RTL-SDR from other SDR software before connecting it here. + The USB device needs a working compatible WinUSB driver already installed. + This package does not install or change drivers. Follow your dongle vendor's + instructions if Windows does not show it as an available RTL receiver. +4. Connect the RTL-SDR, select WFM, and tune a local FM station carrying digital broadcasts. + In the WFM applet, click the audio mode until it says Digital. + The cycle is Mono -> Auto Stereo -> Digital. Choose a discovered digital + program from the Digital program selector when available. +5. Watch the status: Digital acquiring, Digital synced / awaiting audio, and Digital audio + valid describe different stages. Selecting Digital alone does not mean + the station has been decoded. Return to Auto Stereo for analog reception. + +Limits of this test build +This is an experimental local development build, not an official release. +See BUILD-INFO.json for the actual toolchain, enabled features and executable +signing status. A valid source commit signature does not sign the Windows app. +Windows may warn about an unsigned or unfamiliar application. + +Use one RTL receiver and one digital program at a time. Digital reception needs the whole digital +station footprint inside the captured band, and a usable digital signal. +There is no automatic analog fallback. Keep the test receive-only. +Startup and recorded-IQ tests do not establish reception or audible playback +on your radio, PC, antenna, or station. See the separately supplied test report. + +Useful feedback +Send the revision above, Windows version, CPU, RTL model, station/frequency, +selected digital program, and whether analog FM works. Note the exact status shown, +time to lock, audio dropouts, channel balance, metadata, and what action caused +any problem. A screenshot of the WFM diagnostics and short reproduction steps +are useful. Review logs before sharing; do not send credentials or raw RF +recordings. Do not send the profile folder as routine feedback. + +Source and notices +The matching source is included in source/@SOURCE_ARCHIVE@. Keep it with this +build if you pass the ZIP onward. It contains source, dependency provenance, +patches and build instructions for this checkpoint. licenses/ contains the +AetherSDR and bundled-component notices. Digital decoding is embedded; no external +decoder command-line program or decoder download is needed. + +Code from FAAD2 is copyright (c) Nero AG, www.nero.com diff --git a/packaging/windows/stage-hd-portable.ps1 b/packaging/windows/stage-hd-portable.ps1 new file mode 100644 index 000000000..6a8351238 --- /dev/null +++ b/packaging/windows/stage-hd-portable.ps1 @@ -0,0 +1,184 @@ +<# +.SYNOPSIS + Stage a local, experimental digital FM portable payload without publishing it. +.DESCRIPTION + Run with PowerShell 7 in the existing MSVC/Qt build environment after the + application, daemon, and focused tests pass at the stated revision. This + script does not build, download, install drivers, launch radios, or zip an + untested payload. Use a new output directory for every attempt. Validate a + clean extracted ZIP before handing it to another operator; see the guide. +#> +# ASCII-only so the file is also readable from older Windows consoles. +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)][string]$BuildDir, + [Parameter(Mandatory = $true)][string]$OutputDir, + [Parameter(Mandatory = $true)][ValidatePattern('^[0-9a-f]{40}$')][string]$Revision, + [Parameter(Mandatory = $true)][string]$SourceArchive, + [Parameter(Mandatory = $true)][ValidatePattern('^[0-9a-fA-F]{64}$')][string]$SourceSha256, + [Parameter(Mandatory = $true)][string]$DependencyNoticesDir, + [Parameter(Mandatory = $true)][string]$BuildInfo, + [string]$QtDir = $env:QT_ROOT_DIR +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest +if ($PSVersionTable.PSVersion.Major -lt 7) { throw 'Use PowerShell 7 (pwsh).' } +$repo = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path +$build = (Resolve-Path -LiteralPath $BuildDir).Path +$output = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputDir) +if (Test-Path -LiteralPath $output) { throw "Output directory already exists: $output" } + +function Require-File([string]$Path) { + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { throw "Missing file: $Path" } +} + +foreach ($name in @('AetherSDR.exe', 'aetherd.exe', 'CMakeCache.txt')) { + Require-File (Join-Path $build $name) +} +Require-File $SourceArchive +Require-File $BuildInfo +Require-File (Join-Path $QtDir 'bin\windeployqt.exe') +if (-not (Test-Path -LiteralPath $DependencyNoticesDir -PathType Container)) { + throw 'DependencyNoticesDir must contain the verified external runtime licenses and provenance.' +} +$facts = Get-Content -LiteralPath $BuildInfo -Raw | ConvertFrom-Json +if ($facts.revision -ne $Revision) { throw 'BuildInfo revision does not match Revision.' } +if ($facts.platform -ne 'windows-x64' -or $facts.hdFm -ne $true -or $facts.rtl -ne $true) { + throw 'BuildInfo must describe this Windows x64 digital FM + RTL build.' +} +foreach ($name in @('AetherSDR.exe', 'aetherd.exe')) { + $expected = $facts.binarySha256.PSObject.Properties[$name] + $actual = (Get-FileHash -LiteralPath (Join-Path $build $name) -Algorithm SHA256).Hash + if (-not $expected -or $expected.Value -ne $actual) { throw "BuildInfo hash mismatch: $name" } +} +$cache = Get-Content -LiteralPath (Join-Path $build 'CMakeCache.txt') -Raw +foreach ($option in @('ENABLE_HD_FM', 'ENABLE_RTL')) { + if ($cache -notmatch "(?m)^${option}:BOOL=ON\r?$") { throw "$option must be ON." } +} +if ((Get-FileHash -LiteralPath $SourceArchive -Algorithm SHA256).Hash -ne $SourceSha256) { + throw 'Matching-source archive checksum mismatch.' +} +# A DFNR build may use an embedded model or a loose model beside the program. +# Copy only the known model names; an unrestricted archive glob could include +# recordings or build inputs. Refuse an enabled external-model runtime whose +# payload is absent rather than shipping an apparently available broken mode. +$modelNames = @('DeepFilterNet3_onnx.tar.gz', 'DeepFilterNet3_onnx.dfmodel') +$looseModels = @($modelNames | Where-Object { + Test-Path -LiteralPath (Join-Path $build $_) -PathType Leaf +}) +if ((Test-Path -LiteralPath (Join-Path $build 'deepfilter.dll') -PathType Leaf) -and + $cache -match '(?m)^AETHER_EMBED_DFNR_MODEL:BOOL=OFF\r?$' -and + $looseModels.Count -eq 0) { + throw 'External DFNR model is missing from the build directory.' +} + +New-Item -ItemType Directory -Path $output | Out-Null +foreach ($name in @('AetherSDR.exe', 'aetherd.exe')) { + Copy-Item -LiteralPath (Join-Path $build $name) -Destination $output +} +foreach ($name in (@('aether-dv-waveform.exe', 'AetherDV.cfg') + $looseModels)) { + $path = Join-Path $build $name + if (Test-Path -LiteralPath $path -PathType Leaf) { Copy-Item -LiteralPath $path -Destination $output } +} +# CMake stages enabled non-Qt DLLs beside the executable. Never copy build/ +# recursively: that also copies profiles, fixtures, credentials and large PDBs. +Get-ChildItem -LiteralPath $build -File -Filter '*.dll' | + ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination $output } +foreach ($name in @('AetherSDR.exe', 'aetherd.exe')) { + & (Join-Path $QtDir 'bin\windeployqt.exe') (Join-Path $output $name) ` + --release --no-translations --no-system-d3d-compiler --no-compiler-runtime + if ($LASTEXITCODE -ne 0) { throw "windeployqt failed for $name." } +} +# Qt's dependency lookup can find another qtkeychain build in its shared bin +# directory. Retain the exact task-built dependency that the app was linked to. +$keychain = Join-Path $build 'qt6keychain.dll' +if (Test-Path -LiteralPath $keychain -PathType Leaf) { + $deployedKeychain = Join-Path $output 'qt6keychain.dll' + Copy-Item -LiteralPath $keychain -Destination $deployedKeychain -Force + if ((Get-FileHash -LiteralPath $keychain -Algorithm SHA256).Hash -ne + (Get-FileHash -LiteralPath $deployedKeychain -Algorithm SHA256).Hash) { + throw 'Task-built qtkeychain deployment hash mismatch.' + } +} +& (Join-Path $PSScriptRoot 'stage-msvc-runtime.ps1') -OutputDir $output +& (Join-Path $PSScriptRoot 'check-deploy-dependencies.ps1') -DeployDir $output +# The shared audit accepts a matching basename anywhere in the payload. A +# plugin in another subdirectory is not on this process's DLL search path. +$images = @(Get-ChildItem -LiteralPath $output -Recurse -File | + Where-Object { $_.Extension -in '.dll', '.exe' }) +$shipped = @{} +foreach ($file in $images) { $shipped[$file.Name] = $true } +foreach ($file in $images) { + $imports = & dumpbin.exe /nologo /dependents $file.FullName + if ($LASTEXITCODE -ne 0) { throw "Cannot inspect imports for $($file.Name)." } + foreach ($line in $imports) { + $name = "$line".Trim() + if ($name -notmatch '^[^\s]+\.dll$' -or -not $shipped.ContainsKey($name)) { continue } + if (-not (Test-Path -LiteralPath (Join-Path $output $name)) -and + -not (Test-Path -LiteralPath (Join-Path $file.DirectoryName $name))) { + throw "$($file.Name) imports $name from an unreachable package subdirectory." + } + } +} +foreach ($name in @('Qt6Widgets.dll', 'platforms\qwindows.dll', 'vcruntime140.dll')) { + Require-File (Join-Path $output $name) +} +if (-not ((Test-Path (Join-Path $output 'rtlsdr.dll')) -or + (Test-Path (Join-Path $output 'librtlsdr.dll')))) { + throw 'The RTL runtime DLL is absent.' +} + +$licenses = Join-Path $output 'licenses' +New-Item -ItemType Directory -Path $licenses | Out-Null +Copy-Item -LiteralPath (Join-Path $repo 'LICENSE') -Destination (Join-Path $licenses 'AetherSDR-GPL-3.0.txt') +Copy-Item -LiteralPath (Join-Path $repo 'THIRD_PARTY_LICENSES') -Destination (Join-Path $licenses 'THIRD_PARTY_LICENSES.txt') +foreach ($vendor in @('nrsc5', 'faad_hdc')) { + $origin = Join-Path $repo "third_party\$vendor" + $target = Join-Path $licenses $vendor + New-Item -ItemType Directory -Path $target | Out-Null + foreach ($name in @('README.aethersdr.md', 'SOURCE-MANIFEST.json', 'COPYING.GPL-3.0', 'LICENSE-SOURCE.json')) { + $path = Join-Path $origin $name + if (Test-Path -LiteralPath $path -PathType Leaf) { Copy-Item -LiteralPath $path -Destination $target } + } + foreach ($name in @('LICENSE', 'COPYING', 'README', 'AUTHORS')) { + $path = Join-Path $origin "upstream\$name" + if (Test-Path -LiteralPath $path -PathType Leaf) { Copy-Item -LiteralPath $path -Destination $target } + } +} +Copy-Item -LiteralPath $DependencyNoticesDir -Destination (Join-Path $licenses 'runtime-dependencies') -Recurse +New-Item -ItemType Directory -Path (Join-Path $output 'source') | Out-Null +Copy-Item -LiteralPath $SourceArchive -Destination (Join-Path $output 'source') +Copy-Item -LiteralPath $BuildInfo -Destination (Join-Path $output 'BUILD-INFO.json') +$readme = Get-Content -LiteralPath (Join-Path $PSScriptRoot 'README-HD-FM.txt.in') -Raw +$readme.Replace('@REVISION@', $Revision).Replace('@SOURCE_ARCHIVE@', (Split-Path $SourceArchive -Leaf)) | + Set-Content -LiteralPath (Join-Path $output 'README-HD-FM.txt') -Encoding utf8NoBOM +@' +@echo off +setlocal +cd /d "%~dp0" +set "AETHER_SETTINGS_DIR=%~dp0profile" +set "AETHER_AUTOMATION=" +set "AETHER_AUTOMATION_ALLOW_TX=" +set "AETHER_AUTOMATION_NO_TX=1" +"%~dp0AetherSDR.exe" --config set AutoConnectToLastRadio False +if errorlevel 1 ( + echo Could not prepare isolated settings. Extract the whole ZIP to a writable folder. + pause + exit /b 1 +) +start "AetherSDR Digital test" /wait "%~dp0AetherSDR.exe" +'@ | Set-Content -LiteralPath (Join-Path $output 'Start-AetherSDR-HD.cmd') -Encoding ascii + +# Store only package-relative paths, hashes and sizes. Test evidence, cache +# paths, host usernames and raw recordings belong outside the friend payload. +$inventory = @(Get-ChildItem -LiteralPath $output -Recurse -File | Sort-Object FullName | ForEach-Object { + [ordered]@{ + path = [IO.Path]::GetRelativePath($output, $_.FullName).Replace('\', '/') + bytes = $_.Length + sha256 = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant() + } +}) +$inventory | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $output 'FILES-SHA256.json') -Encoding utf8NoBOM +Write-Host "HD-PORTABLE-STAGED: $output" +Write-Host 'Staging is not startup qualification. Follow the clean-extraction checks before delivery.' diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 87493b1de..9721bbb73 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -4979,7 +4979,7 @@ void AudioEngine::setRxDeviceRate(int rate) void AudioEngine::feedPcmFrame(const PcmFrame& frame) { if (frame.stream().purpose != PcmPurpose::Speaker || !frame.current() - || !frame.stream().format.valid()) { + || !frame.stream().format.validFor(PcmPurpose::Speaker)) { return; } // A speaker route has one producer. A second live producer must not diff --git a/src/core/DecoderPcmAdapter.cpp b/src/core/DecoderPcmAdapter.cpp index 84f58f819..60350c6d2 100644 --- a/src/core/DecoderPcmAdapter.cpp +++ b/src/core/DecoderPcmAdapter.cpp @@ -98,8 +98,8 @@ std::optional DecoderPcmAdapter::accept(const PcmFrame& frame) reset(); m_segmentSource = frame.epochLease(); m_segmentFirstInputSample = frame.firstSample(); - if (stream.format.sampleRateHz == 48000) { - m_resampler = std::make_unique(48000, DecoderPcmBlock::kSampleRateHz, + if (stream.format.sampleRateHz != DecoderPcmBlock::kSampleRateHz) { + m_resampler = std::make_unique(stream.format.sampleRateHz, DecoderPcmBlock::kSampleRateHz, kInputBatchFrames); } } diff --git a/src/core/HdFmReception.h b/src/core/HdFmReception.h new file mode 100644 index 000000000..dbd2025a7 --- /dev/null +++ b/src/core/HdFmReception.h @@ -0,0 +1,94 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace AetherSDR { +// Operator selection is independent of observed sync and successful audio. +enum class WfmAudioMode { Mono, Stereo, HdStereo }; +inline constexpr bool validWfmAudioMode(WfmAudioMode mode) +{ + return mode == WfmAudioMode::Mono || mode == WfmAudioMode::Stereo + || mode == WfmAudioMode::HdStereo; +} +struct HdFmService { + int program = -1; // decoder program 0..7; displayed as HD1..HD8 + QString name; + bool audioAvailable = false; // an actually discovered audio service + bool operator==(const HdFmService&) const = default; +}; +// Owner-thread normalized, complete snapshot from the native NRSC-5 decoder. +// Text is HD SIS/SIG/ID3 data; this type never claims analog RDS/RBDS decoding. +struct HdFmReception { + bool valid = false; + quint64 sessionId = 0; + quint64 receiverEpoch = 0; + quint64 revision = 0; + qint64 frequencyHz = 0; + int selectedProgram = -1; + bool synced = false; + bool audioValid = false; // recent unflagged selected-program PCM while synced + QVector services; + QString stationName; + QString title; + QString artist; + // Exact upstream measurements. MER labels use NRSC-5's FM spectral + // convention, inverted relative to RF lower/upper sidebands. + std::optional merLowerDb; + std::optional merUpperDb; + std::optional cber; + std::optional frequencyOffsetHz; + quint64 observationSequence = 0; + quint32 syncLossCount = 0; + quint32 reacquisitionCount = 0; + quint32 syncDurationMs = 0; + bool operator==(const HdFmReception&) const = default; +}; +// Bound external text before scanning. Consumers render as plain text only. +inline QString boundedBroadcastText(const QString& input, qsizetype limit) +{ + if (limit <= 0 || limit > 1024) { return {}; } + QString output; + output.reserve(limit); + for (QChar character : input.left(limit * 4)) { + if (character.isSpace()) { character = QLatin1Char(' '); } + if (!character.isPrint() || character.category() == QChar::Other_Format) { continue; } + output.append(character); + if (output.size() == limit) { break; } + } + return output.simplified(); +} +inline HdFmReception normalizedHdFmReception(HdFmReception value) +{ + if (!value.valid || !value.sessionId || !value.receiverEpoch || !value.revision + || value.frequencyHz <= 0 || value.frequencyHz > 1'766'000'000 + || value.selectedProgram < 0 || value.selectedProgram > 7 + || value.services.size() > 8 || !value.observationSequence) { return {}; } + for (const auto& metric : {value.merLowerDb, value.merUpperDb, value.cber, value.frequencyOffsetHz}) { + if (metric && !std::isfinite(*metric)) { return {}; } + } + if (value.cber && (*value.cber < 0 || *value.cber > 1)) { return {}; } + unsigned programs = 0; + for (HdFmService& service : value.services) { + if (service.program < 0 || service.program > 7 || (programs & (1u << service.program))) { return {}; } + programs |= 1u << service.program; + service.name = boundedBroadcastText(service.name, 64); + } + value.stationName = boundedBroadcastText(value.stationName, 64); + value.title = boundedBroadcastText(value.title, 256); + value.artist = boundedBroadcastText(value.artist, 128); + if (!value.synced) { + value.audioValid = false; + value.services.clear(); + value.stationName.clear(); value.title.clear(); value.artist.clear(); + value.merLowerDb.reset(); value.merUpperDb.reset(); value.cber.reset(); + value.syncDurationMs = 0; + } + return value; +} +} // namespace AetherSDR +Q_DECLARE_METATYPE(AetherSDR::WfmAudioMode) +Q_DECLARE_METATYPE(AetherSDR::HdFmReception) diff --git a/src/core/PcmFrame.h b/src/core/PcmFrame.h index cf627d7ec..39d5322c1 100644 --- a/src/core/PcmFrame.h +++ b/src/core/PcmFrame.h @@ -26,9 +26,13 @@ struct PcmFormat { int channels() const { return layout == PcmLayout::Mono ? 1 : 2; } bool valid() const { - return (sampleRateHz == 24000 || sampleRateHz == 48000) + return (sampleRateHz == 24000 || sampleRateHz == 44100 || sampleRateHz == 48000) && (layout == PcmLayout::Mono || layout == PcmLayout::Stereo); } + // HD supplies a real 44.1 kHz slice tap. Speaker and auxiliary domains + // remain 24/48 kHz; the HD receiver explicitly converts its speaker path. + bool validFor(PcmPurpose purpose) const + { return valid() && (sampleRateHz != 44100 || purpose == PcmPurpose::Slice); } bool operator==(const PcmFormat&) const = default; }; @@ -138,7 +142,7 @@ class PcmProducer final { PcmFormat format = {}, quint64 session = 0, quint64 receiverInstance = 0) { - if (!format.valid() || m_source == 0 || (session != 0 && session <= m_session) + if (!format.validFor(purpose) || m_source == 0 || (session != 0 && session <= m_session) || (purpose != PcmPurpose::Speaker && purpose != PcmPurpose::Slice && purpose != PcmPurpose::Auxiliary) || (purpose == PcmPurpose::Slice ? sliceId < 0 : sliceId != -1) @@ -160,7 +164,7 @@ class PcmProducer final { bool setFormat(PcmFormat format) { if (!m_epoch || !m_epoch->active.load(std::memory_order_acquire) - || !format.valid() || m_generation == std::numeric_limits::max()) { + || !format.validFor(m_epoch->descriptor.purpose) || m_generation == std::numeric_limits::max()) { return false; } if (format == m_epoch->descriptor.format) { diff --git a/src/core/RtlSliceSettings.cpp b/src/core/RtlSliceSettings.cpp index 939008efd..96df12ad4 100644 --- a/src/core/RtlSliceSettings.cpp +++ b/src/core/RtlSliceSettings.cpp @@ -57,6 +57,8 @@ QJsonObject encodeSlice(const RtlSliceSettings::Slice& slice, QJsonObject previo previous.insert(QStringLiteral("audioPan"), slice.audioPan); previous.insert(QStringLiteral("wfmDeemphasisUs"), slice.wfmDeemphasisUs); previous.insert(QStringLiteral("wfmForceMono"), slice.wfmForceMono); + previous.insert(QStringLiteral("wfmHdStereo"), slice.wfmHdStereo); + previous.insert(QStringLiteral("hdProgram"), slice.hdProgram); return previous; } @@ -126,6 +128,15 @@ bool RtlSliceSettings::decode(const QJsonObject& object, Document& output, QStri if (!entry.value(QStringLiteral("wfmForceMono")).isBool()) { return false; } slice.wfmForceMono = entry.value(QStringLiteral("wfmForceMono")).toBool(); } + if (entry.contains(QStringLiteral("wfmHdStereo"))) { + if (!entry.value(QStringLiteral("wfmHdStereo")).isBool()) { return false; } + slice.wfmHdStereo = entry.value(QStringLiteral("wfmHdStereo")).toBool(); + } + if (entry.contains(QStringLiteral("hdProgram"))) { + double program = 0; + if (!number(entry, QStringLiteral("hdProgram"), 0, 7, program, true)) { return false; } + slice.hdProgram = static_cast(program); + } const QJsonObject squelch = entry.value(QStringLiteral("squelch")).toObject(); if (!squelch.value(QStringLiteral("enabled")).isBool() || !percent(squelch, QStringLiteral("level"), slice.squelchLevel)) { diff --git a/src/core/RtlSliceSettings.h b/src/core/RtlSliceSettings.h index 206e1ee42..b23632208 100644 --- a/src/core/RtlSliceSettings.h +++ b/src/core/RtlSliceSettings.h @@ -34,6 +34,8 @@ class RtlSliceSettings { // Optional in schema 1; old documents retain the legacy 75 us default. int wfmDeemphasisUs = 75; bool wfmForceMono = false; + bool wfmHdStereo = false; + int hdProgram = 0; bool automaticSquelch = false; int automaticSquelchMarginDb = 10; }; diff --git a/src/core/backends/IRadioBackend.h b/src/core/backends/IRadioBackend.h index 977bb1b6a..9f1614038 100644 --- a/src/core/backends/IRadioBackend.h +++ b/src/core/backends/IRadioBackend.h @@ -691,6 +691,14 @@ class IRadioBackend : public QObject { // SliceDelta only after the receiver adopts the requested configuration. virtual void setSliceWfmForceMono(int sliceId, bool forceMono) { Q_UNUSED(sliceId); Q_UNUSED(forceMono); } + virtual void setSliceWfmAudioMode(int sliceId, WfmAudioMode mode) + { + if (mode == WfmAudioMode::Mono || mode == WfmAudioMode::Stereo) { + setSliceWfmForceMono(sliceId, mode == WfmAudioMode::Mono); + } + } + virtual void setSliceHdProgram(int sliceId, int program) + { Q_UNUSED(sliceId); Q_UNUSED(program); } virtual void setSliceWfmDeemphasis(int sliceId, int microseconds) { Q_UNUSED(sliceId); Q_UNUSED(microseconds); diff --git a/src/core/backends/RadioCapabilities.h b/src/core/backends/RadioCapabilities.h index 8efb3aadd..a1f8c17f8 100644 --- a/src/core/backends/RadioCapabilities.h +++ b/src/core/backends/RadioCapabilities.h @@ -74,6 +74,10 @@ struct BroadcastFmReceive { QVector deemphasisUs; bool forceMonoControl = false; bool receptionDiagnostics = false; + // Grants the full Mono/Stereo/HD cycle through atomic setSliceWfmAudioMode + // and discovered-service + // setSliceHdProgram verbs plus native observed sync/audio/metadata. + bool hdStereo = false; }; struct ReceivePanRangeControl { SliceFrequencyControl::Authority authority{SliceFrequencyControl::Authority::Unknown}; diff --git a/src/core/backends/ReceiveCommand.h b/src/core/backends/ReceiveCommand.h index c7411af6d..00cec878d 100644 --- a/src/core/backends/ReceiveCommand.h +++ b/src/core/backends/ReceiveCommand.h @@ -100,14 +100,16 @@ struct SliceAudioRequest { }; struct SliceWfmRequest { - enum class Field { ForceMono, Deemphasis }; + enum class Field { ForceMono, Deemphasis, AudioMode, HdProgram }; Field field{Field::ForceMono}; int value{0}; bool valid() const { return (field == Field::ForceMono && (value == 0 || value == 1)) - || (field == Field::Deemphasis && (value == 50 || value == 75)); + || (field == Field::Deemphasis && (value == 50 || value == 75)) + || (field == Field::AudioMode && value >= 0 && value <= 2) + || (field == Field::HdProgram && value >= 0 && value < 8); } }; diff --git a/src/core/backends/SliceDelta.h b/src/core/backends/SliceDelta.h index 4b7d8c116..725e6de87 100644 --- a/src/core/backends/SliceDelta.h +++ b/src/core/backends/SliceDelta.h @@ -2,6 +2,7 @@ #include #include "core/WfmReceptionDiagnostics.h" +#include "core/HdFmReception.h" #include "core/backends/NoiseBlankerKind.h" @@ -111,6 +112,9 @@ struct SliceDelta { std::optional automaticSquelchMarginDb; std::optional wfmDeemphasisUs; std::optional wfmForceMono; + std::optional wfmAudioMode; + std::optional hdProgram; + std::optional hdFmReception; std::optional wfmReceptionDiagnostics; std::optional wfmStereoStatus; std::optional ritOn; diff --git a/src/core/backends/rtl/HdFmIqAdapter.cpp b/src/core/backends/rtl/HdFmIqAdapter.cpp new file mode 100644 index 000000000..fc7993ed4 --- /dev/null +++ b/src/core/backends/rtl/HdFmIqAdapter.cpp @@ -0,0 +1,70 @@ +#include "HdFmIqAdapter.h" +#include "core/Resampler.h" +#include +#include +#include +#include + +namespace AetherSDR::rtl { +HdFmIqAdapter::HdFmIqAdapter(const SharedCapturePolicy::CaptureDescriptor& capture, + const SharedCapturePolicy::SliceDescriptor& footprint) + : m_capture(capture), m_footprint(footprint) +{ + if (!std::isfinite(capture.achievedSampleRateHz) + || capture.achievedSampleRateHz != std::floor(capture.achievedSampleRateHz) + || capture.achievedSampleRateHz < 900001 || capture.achievedSampleRateHz > 3000000) { return; } + const SharedCapturePolicy::CenterDomain fixed{capture.centerHz, capture.centerHz, capture.centerHz, 1}; + const auto fit = SharedCapturePolicy::restoreFixedCapture(capture, std::span(&footprint, 1), + std::span(&fixed, 1), {SharedCapturePolicy::kMaxEntries, 1}); + if (fit.error != SharedCapturePolicy::Error::None || fit.accepted.size() != 1) { return; } + const double step = -2 * std::numbers::pi + * (footprint.carrierHz + footprint.translationHz - capture.centerHz) / capture.achievedSampleRateHz; + m_step = {std::cos(step), std::sin(step)}; + m_i = std::make_unique(capture.achievedSampleRateHz, kOutputRate, kChunk, 10.0); + m_q = std::make_unique(capture.achievedSampleRateHz, kOutputRate, kChunk, 10.0); + m_outI.reserve(16384 * sizeof(float)); + m_outQ.reserve(16384 * sizeof(float)); + m_valid = true; +} +HdFmIqAdapter::~HdFmIqAdapter() = default; +bool HdFmIqAdapter::process(std::uint64_t first, + std::span> samples, Sink& sink) noexcept +{ + if (!m_valid || samples.empty() || samples.data() == nullptr || samples.size() > kMaxInput + || first > std::numeric_limits::max() - samples.size() + || (m_started && first != m_next) + || !std::ranges::all_of(samples, [](const auto& value) { + return std::isfinite(value.real()) && std::isfinite(value.imag()); + })) { m_valid = false; return false; } + if (!m_started) { + const long double phase = std::remainder(-2 * std::numbers::pi_v + * (m_footprint.carrierHz + m_footprint.translationHz - m_capture.centerHz) + * first / m_capture.achievedSampleRateHz, 2 * std::numbers::pi_v); + m_phase = {static_cast(std::cos(phase)), static_cast(std::sin(phase))}; + m_started = true; + } + m_next = first + samples.size(); + for (const auto& value : samples) { + const std::complex shifted = std::complex(value) * m_phase; + m_inI[m_staged] = static_cast(shifted.real()); + m_inQ[m_staged++] = static_cast(shifted.imag()); + m_phase *= m_step; + if (++m_normalize == 1024) { m_phase /= std::abs(m_phase); m_normalize = 0; } + if (m_staged != kChunk) { continue; } + m_staged = 0; + const int count = m_i->process(m_inI.data(), kChunk, m_outI); + const int countQ = m_q->process(m_inQ.data(), kChunk, m_outQ); + if (count < 0 || count != countQ || count > 16384 + || m_outputFrames > std::numeric_limits::max() - static_cast(count)) { m_valid = false; return false; } + const auto* i = reinterpret_cast(m_outI.constData()); + const auto* q = reinterpret_cast(m_outQ.constData()); + for (int n = 0; n < count; ++n) { + if (!std::isfinite(i[n]) || !std::isfinite(q[n])) { m_valid = false; return false; } + m_interleaved[2 * n] = i[n]; m_interleaved[2 * n + 1] = q[n]; + } + if (count && !sink.hdIq(std::span(m_interleaved).first(2 * count))) { m_valid = false; return false; } + m_outputFrames += static_cast(count); + } + return true; +} +} // namespace AetherSDR::rtl diff --git a/src/core/backends/rtl/HdFmIqAdapter.h b/src/core/backends/rtl/HdFmIqAdapter.h new file mode 100644 index 000000000..c82794d9e --- /dev/null +++ b/src/core/backends/rtl/HdFmIqAdapter.h @@ -0,0 +1,46 @@ +#pragma once + +#include "core/SharedCapturePolicy.h" +#include +#include +#include +#include +#include +#include + +namespace AetherSDR { +class Resampler; +namespace rtl { +// Worker-owned, exact fractional-rate RF conversion. Positions are local to +// this decoder epoch; they are never presented as decoded audio timestamps. +class HdFmIqAdapter final { +public: + static constexpr double kOutputRate = 1488375.0 / 2.0; + static constexpr std::size_t kMaxInput = 8192; + class Sink { + public: + virtual ~Sink() = default; + virtual bool hdIq(std::span interleaved) noexcept = 0; + }; + HdFmIqAdapter(const SharedCapturePolicy::CaptureDescriptor& capture, + const SharedCapturePolicy::SliceDescriptor& footprint); + ~HdFmIqAdapter(); + bool valid() const noexcept { return m_valid; } + bool process(std::uint64_t first, std::span> samples, Sink& sink) noexcept; + std::uint64_t outputFrames() const noexcept { return m_outputFrames; } +private: + static constexpr std::size_t kChunk = 256; + const SharedCapturePolicy::CaptureDescriptor m_capture; + const SharedCapturePolicy::SliceDescriptor m_footprint; + std::unique_ptr m_i, m_q; + QByteArray m_outI, m_outQ; + std::array m_inI{}, m_inQ{}; + std::array m_interleaved{}; + std::size_t m_staged = 0; + std::complex m_phase{1, 0}, m_step{1, 0}; + std::uint64_t m_next = 0, m_outputFrames = 0; + std::uint32_t m_normalize = 0; + bool m_valid = false, m_started = false; +}; +} // namespace rtl +} // namespace AetherSDR diff --git a/src/core/backends/rtl/HdFmReceiver.cpp b/src/core/backends/rtl/HdFmReceiver.cpp new file mode 100644 index 000000000..93b17b43e --- /dev/null +++ b/src/core/backends/rtl/HdFmReceiver.cpp @@ -0,0 +1,374 @@ +#include "HdFmReceiver.h" +#include "HdFmIqAdapter.h" +#include "Nrsc5FmDecoder.h" +#include "RtlCaptureTransaction.h" +#include "core/Resampler.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AetherSDR::rtl { +namespace { +using Registry = RtlReceiverRegistry; +template class Queue { +public: + bool push(const T& value) noexcept + { + const unsigned write = m_write.load(std::memory_order_relaxed); + if (write - m_read.load(std::memory_order_acquire) == N) { return false; } + m_values[write % N] = value; + m_write.store(write + 1, std::memory_order_release); + return true; + } + bool peek(T& value) const noexcept + { + const unsigned read = m_read.load(std::memory_order_relaxed); + if (read == m_write.load(std::memory_order_acquire)) { return false; } + value = m_values[read % N]; + return true; + } + bool pop(T& value) noexcept + { + const unsigned read = m_read.load(std::memory_order_relaxed); + if (read == m_write.load(std::memory_order_acquire)) { return false; } + value = m_values[read % N]; + m_read.store(read + 1, std::memory_order_release); + return true; + } +private: + std::array m_values{}; + alignas(64) std::atomic m_write{0}; + alignas(64) std::atomic m_read{0}; +}; +std::atomic residentWorkers{0}; +class ResidentLease { +public: + ResidentLease() + { + unsigned count = residentWorkers.load(); + while (count < 2) { + if (residentWorkers.compare_exchange_weak(count, count + 1)) { m_held = true; break; } + } + } + ~ResidentLease() { if (m_held) { residentWorkers.fetch_sub(1); } } + explicit operator bool() const { return m_held; } +private: + bool m_held = false; +}; +SharedCapturePolicy::SliceDescriptor footprint(const Registry::ReceiverSpec& spec) +{ + RtlCaptureTransaction::Receiver receiver{spec.passband, RtlCaptureTransaction::Mode::Wfm}; + receiver.wfmHdStereo = true; + return RtlCaptureTransaction::effectivePassband(receiver); +} +struct IqJob { + std::uint64_t first = 0, session = 0, revision = 0; + std::size_t frames = 0; + std::array, 8192> samples{}; +}; +struct AudioBlock { + HdFmAudioIdentity identity; + std::uint64_t first = 0; + std::size_t frames = 0; + std::array left{}, right{}; +}; +class HdFmReceiver final : public Registry::Receiver, private HdFmIqAdapter::Sink, + private Nrsc5FmDecoder::Sink { +public: + HdFmReceiver(const Registry::ReceiverSpec& spec, std::unique_ptr pipe, + std::optional reservation) + : m_reservation(std::move(reservation)), m_spec(spec), m_iq(spec.capture, footprint(spec)), + m_leftRate(44100, 48000, 2048), m_rightRate(44100, 48000, 2048) + { + if (!m_lease || !spec.hdFm || !m_iq.valid()) { return; } + HdFmRawReception initial; + initial.receiverEpoch = spec.epoch; + initial.frequencyHz = static_cast(std::llround(spec.passband.carrierHz)); + initial.selectedProgram = spec.hdFm->program; + m_last = initial; + m_decoder = std::make_unique(initial, static_cast(*this), std::move(pipe)); + if (!m_decoder->valid()) { return; } + m_convertedLeft.reserve(16384 * sizeof(float)); m_convertedRight.reserve(16384 * sizeof(float)); + m_thread = std::thread([this] { run(); }); + m_valid = true; + } + ~HdFmReceiver() override + { + // Registry retirement executor only, after the acquisition reader's + // acknowledgement. Stop ignores the remaining queue, finishes only + // the current bounded IQ job, then closes the pipe on this executor. + m_stop.store(true, std::memory_order_release); + if (m_thread.joinable()) { m_thread.join(); } + } + bool valid() const noexcept { return m_valid; } + WdspChannel::ProcessResult processIq(std::span, std::span) noexcept override + { return WdspChannel::ProcessResult::InvalidBuffer; } + std::span left() const noexcept override { return {}; } + std::span right() const noexcept override { return {}; } + bool processCapture(const Registry::SampleBlock& block, Registry::AudioSink& sink) noexcept override + { + // The registry marks the first adopted delivery discontinuous. A new + // prepared decoder has no prior stream to preserve; later boundaries + // must still withdraw instead of splicing independent RF histories. + if (!m_valid || block.session != m_spec.handle.session || block.capture != m_spec.capture + || (block.discontinuity && m_haveInput) || !block.publicationSession || !block.publicationRevision + || block.samples.empty() || block.samples.data() == nullptr || block.samples.size() > 8192 + || block.firstSample > std::numeric_limits::max() - block.samples.size() + || (m_haveInput && block.firstSample != m_nextInput)) { return fail(sink); } + if (m_last.sessionId == 0) { + m_last.sessionId = block.publicationSession; m_last.revision = block.publicationRevision; + } + m_haveInput = true; m_nextInput = block.firstSample + block.samples.size(); + m_latestInput.store(m_nextInput, std::memory_order_release); + m_job.first = block.firstSample; m_job.session = block.publicationSession; + m_job.revision = block.publicationRevision; m_job.frames = block.samples.size(); + std::copy(block.samples.begin(), block.samples.end(), m_job.samples.begin()); + if (!m_failed.load(std::memory_order_acquire) && !m_input.push(m_job)) { + m_iqDrops.fetch_add(1); m_failed.store(true, std::memory_order_release); + } + HdFmRawReception observation; + for (unsigned i = 0; i < 32 && m_observations.pop(observation); ++i) { m_last = observation; } + reconcileEpoch(); + if (m_failed.load(std::memory_order_acquire)) { return fail(sink); } + m_last.iqDrops = m_iqDrops.load(); m_last.pcmDrops = m_pcmDrops.load(); + m_last.playoutUnderruns = m_underruns; + publish(sink); + AudioBlock audio; + for (unsigned i = 0; i < 32 && m_speaker.peek(audio); ++i) { + reconcileEpoch(); + // Retirement is released before any new-epoch PCM is published. + // Re-read after peek; never discard new PCM against a cached epoch. + if (!m_speaker.pop(audio)) { break; } + reconcileEpoch(); + if (audio.identity.audioEpoch != m_playoutEpoch || m_waitingReset) { continue; } + if (m_buffered + audio.frames > kPlayoutFrames) { + m_pcmDrops.fetch_add(1); return fail(sink); + } + for (std::size_t n = 0; n < audio.frames; ++n) { + const std::size_t at = (m_readPosition + m_buffered++) % kPlayoutFrames; + m_playoutLeft[at] = audio.left[n]; m_playoutRight[at] = audio.right[n]; + m_playoutIdentities[at] = audio.identity; + } + } + reconcileEpoch(); + if (!m_ready && !m_waitingReset && m_last.audioValid && m_buffered >= kPrefillFrames) { + m_ready = true; + } + publish(sink); + for (unsigned i = 0; i < 32 && m_tap.peek(audio); ++i) { + // Producer publishes successful-audio status before PCM. Draining + // can reveal a new epoch, which must acquire its own prebuffer. + for (unsigned j = 0; j < 32 && m_observations.pop(observation); ++j) { m_last = observation; } + reconcileEpoch(); + publish(sink); + if (!m_ready && audio.identity.audioEpoch == m_playoutEpoch) { break; } + if (!m_tap.pop(audio)) { break; } + reconcileEpoch(); + if (!m_ready || !m_last.audioValid || audio.identity.audioEpoch != m_playoutEpoch + || m_waitingReset || audio.identity.sessionId != block.publicationSession + || audio.identity.revision != block.publicationRevision) { continue; } + sink.hdDecodedAudio(m_spec, audio.identity, audio.first, + std::span(audio.left).first(audio.frames), std::span(audio.right).first(audio.frames), m_firstTap); + m_firstTap = false; + } + const auto rate = static_cast(block.capture.achievedSampleRateHz); + if (!m_speakerNext) { + const auto aligned = RtlRfExtractor::alignedCaptureFirst(block.firstSample, rate, 48000); + if (!aligned) { return fail(sink); } + const std::uint64_t divisor = std::gcd(rate, std::uint64_t{48000}); + const std::uint64_t sourceQuantum = rate / divisor, audioQuantum = 48000 / divisor; + if (*aligned / sourceQuantum > std::numeric_limits::max() / audioQuantum) { + return fail(sink); + } + m_speakerNext = (*aligned / sourceQuantum) * audioQuantum; + } + const std::uint64_t end = static_cast(static_cast(m_nextInput) * 48000 / rate); + // Emit complete mixer quanta only. On source retirement no residual + // partial quantum can retain a former program's audio in the mixer. + while (end >= *m_speakerNext && end - *m_speakerNext >= 128) { + const std::size_t frames = std::min(1024, ((end - *m_speakerNext) / 128) * 128); + reconcileEpoch(); + publish(sink); + m_audioLeft.fill(0); m_audioRight.fill(0); + HdFmAudioIdentity identity{block.publicationSession, block.publicationRevision, m_playoutEpoch, 0}; + if (m_ready && m_buffered < frames) { + ++m_underruns; m_ready = false; m_waitingReset = true; m_buffered = 0; + m_resetAudio.store(true, std::memory_order_release); + m_last.audioValid = false; m_last.playoutUnderruns = m_underruns; + publish(sink, true); + } + if (m_ready) { + for (std::size_t n = 0; n < frames; ++n) { + m_audioLeft[n] = m_playoutLeft[m_readPosition]; + m_audioRight[n] = m_playoutRight[m_readPosition]; + const auto& source = m_playoutIdentities[m_readPosition]; + // A control-only revision can leave an older prefix. Keep + // its original token so the owner can reject that quantum. + if (identity.producedMonotonicMs == 0 || source.revision < identity.revision + || (source.revision == identity.revision + && source.producedMonotonicMs < identity.producedMonotonicMs)) { + identity = source; + } + m_readPosition = (m_readPosition + 1) % kPlayoutFrames; --m_buffered; + } + } + sink.hdSpeakerAudio(m_spec, identity, *m_speakerNext, + std::span(m_audioLeft).first(frames), std::span(m_audioRight).first(frames), m_firstSpeaker); + m_firstSpeaker = false; *m_speakerNext += frames; + } + return true; + } +private: + static constexpr std::size_t kPlayoutFrames = 32768; + static constexpr std::size_t kPrefillFrames = 8192; + ResidentLease m_lease; + // Remains charged across shared receiver reuse by a control-only bank. + // Destroy after the decoder, rather than releasing with the original bank. + std::optional m_reservation; + const Registry::ReceiverSpec m_spec; + HdFmIqAdapter m_iq; + Resampler m_leftRate, m_rightRate; + QByteArray m_convertedLeft, m_convertedRight; + std::unique_ptr m_decoder; + Queue m_input; + Queue m_tap, m_speaker; + Queue m_observations; + std::thread m_thread; + std::atomic m_stop{false}, m_failed{false}, m_resetAudio{false}; + std::atomic m_audioEpoch{1}, m_latestInput{0}, m_iqDrops{0}, m_pcmDrops{0}; + bool m_valid = false; + // Acquisition-only state. + IqJob m_job; + HdFmRawReception m_last, m_published; + bool m_haveInput = false, m_firstTap = true, m_firstSpeaker = true; + std::uint64_t m_nextInput = 0, m_playoutEpoch = 1, m_underruns = 0, m_publicationSequence = 0; + std::optional m_speakerNext; + std::array m_playoutLeft{}, m_playoutRight{}; + std::array m_playoutIdentities{}; + std::array m_audioLeft{}, m_audioRight{}; + std::size_t m_readPosition = 0, m_buffered = 0; + bool m_ready = false, m_waitingReset = false; + // Worker-only feed context and converter sequence. + std::uint64_t m_feedSession = 0, m_feedRevision = 0, m_convertedFrames = 0; + bool fail(Registry::AudioSink& sink) noexcept + { + m_failed.store(true, std::memory_order_release); + m_last.valid = false; m_last.audioValid = false; m_last.synced = false; + m_last.iqDrops = m_iqDrops.load(); m_last.pcmDrops = m_pcmDrops.load(); + publish(sink, true); + return false; + } + void reconcileEpoch() noexcept + { + const std::uint64_t epoch = m_audioEpoch.load(std::memory_order_acquire); + if (epoch != m_playoutEpoch) { + m_playoutEpoch = epoch; m_buffered = 0; m_readPosition = 0; + m_ready = false; m_waitingReset = false; m_firstTap = true; + } + if (m_last.audioEpoch != epoch || m_waitingReset) { + m_last.audioEpoch = epoch; m_last.audioValid = false; + } + } + void publish(Registry::AudioSink& sink, bool force = false) noexcept + { + reconcileEpoch(); + HdFmRawReception value = m_last; + // Worker snapshots do not own acquisition/queue counters; stamp every + // publication after all possible observation drains and epoch changes. + value.iqDrops = m_iqDrops.load(std::memory_order_relaxed); + value.pcmDrops = m_pcmDrops.load(std::memory_order_relaxed); + value.playoutUnderruns = m_underruns; + value.audioValid = value.audioValid && m_ready && !m_waitingReset; + if (force || value.observationSequence != m_published.observationSequence + || value.audioEpoch != m_published.audioEpoch || value.revision != m_published.revision + || value.audioValid != m_published.audioValid || value.iqDrops != m_published.iqDrops + || value.pcmDrops != m_published.pcmDrops || value.playoutUnderruns != m_published.playoutUnderruns) { + value.publicationSequence = ++m_publicationSequence; + sink.hdObservation(m_spec, value); m_published = value; + } + } + bool hdIq(std::span samples) noexcept override + { return !m_stop.load() && m_decoder->feed(samples, m_feedSession, m_feedRevision); } + void audioRetired(std::uint64_t epoch) noexcept override + { + m_leftRate.reset(); m_rightRate.reset(); m_convertedFrames = 0; + m_audioEpoch.store(epoch, std::memory_order_release); + } + bool queueAudio(Queue& queue, const HdFmAudioIdentity& identity, + std::uint64_t first, std::span left, std::span right) noexcept + { + for (std::size_t offset = 0; offset < left.size(); offset += 1024) { + AudioBlock block; + block.identity = identity; block.first = first + offset; + block.frames = std::min(1024, left.size() - offset); + std::copy_n(left.data() + offset, block.frames, block.left.data()); + std::copy_n(right.data() + offset, block.frames, block.right.data()); + if (!queue.push(block)) { m_pcmDrops.fetch_add(1); return false; } + } + return true; + } + bool decoded(const HdFmAudioIdentity& identity, std::uint64_t first, + std::span left, std::span right) noexcept override + { + if (!m_observations.push(m_decoder->reception())) { return false; } + if (!queueAudio(m_tap, identity, first, left, right)) { return false; } + const int count = m_leftRate.process(left.data(), static_cast(left.size()), m_convertedLeft); + const int other = m_rightRate.process(right.data(), static_cast(right.size()), m_convertedRight); + if (count < 0 || count != other || count > 16384) { return false; } + const std::span convertedLeft(reinterpret_cast(m_convertedLeft.constData()), count); + const std::span convertedRight(reinterpret_cast(m_convertedRight.constData()), count); + if (!std::ranges::all_of(convertedLeft, [](float v) { return std::isfinite(v); }) + || !std::ranges::all_of(convertedRight, [](float v) { return std::isfinite(v); })) { return false; } + if (!queueAudio(m_speaker, identity, m_convertedFrames, convertedLeft, convertedRight)) { return false; } + m_convertedFrames += static_cast(count); + return true; + } + void run() + { + IqJob job; + while (!m_stop.load(std::memory_order_acquire)) { + if (m_failed.load(std::memory_order_acquire)) { m_decoder->withdraw(); break; } + if (m_resetAudio.exchange(false, std::memory_order_acq_rel)) { m_decoder->resetAudio(); } + // Expiry is independent of whether RF callbacks keep arriving. + m_decoder->expireAudio(); + if (!m_input.pop(job)) { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + continue; + } + const std::uint64_t latest = m_latestInput.load(std::memory_order_acquire); + if (latest < job.first || latest - job.first > m_spec.capture.achievedSampleRateHz / 4) { + m_iqDrops.fetch_add(1); m_failed.store(true, std::memory_order_release); continue; + } + m_feedSession = job.session; m_feedRevision = job.revision; + if (!m_iq.process(job.first, std::span(job.samples).first(job.frames), *this)) { + m_failed.store(true, std::memory_order_release); continue; + } + if (!m_observations.push(m_decoder->reception())) { + // Losing a status barrier is a stream failure, never permission + // to keep a green claim based on subsequently queued PCM. + m_failed.store(true, std::memory_order_release); + } + } + if (m_failed.load()) { m_decoder->withdraw(); m_observations.push(m_decoder->reception()); } + } +}; +} +std::unique_ptr prepareHdFmReceiver(const Registry::ReceiverSpec& spec, std::string& error, + std::unique_ptr pipe, std::optional reservation) +{ + if (!spec.hdFm || spec.hdFm->program < 0 || spec.hdFm->program >= 8) { + error = "Invalid digital program"; return nullptr; + } + auto receiver = std::make_unique(spec, std::move(pipe), std::move(reservation)); + if (!receiver->valid()) { + error = "Digital decoder unavailable, RF footprint invalid, or two resident workers already reserved"; + return nullptr; + } + return receiver; +} +} // namespace AetherSDR::rtl diff --git a/src/core/backends/rtl/HdFmReceiver.h b/src/core/backends/rtl/HdFmReceiver.h new file mode 100644 index 000000000..94740b532 --- /dev/null +++ b/src/core/backends/rtl/HdFmReceiver.h @@ -0,0 +1,15 @@ +#pragma once + +#include "RtlReceiverRegistry.h" +#include "Nrsc5FmDecoder.h" +#include +#include + +namespace AetherSDR::rtl { +// Called by the registry preparation executor; destruction and worker joins +// remain on that executor after acquisition acknowledges bank retirement. +std::unique_ptr prepareHdFmReceiver( + const RtlReceiverRegistry::ReceiverSpec& spec, std::string& error, + std::unique_ptr pipe = {}, + std::optional reservation = {}); +} // namespace AetherSDR::rtl diff --git a/src/core/backends/rtl/HdFmTypes.h b/src/core/backends/rtl/HdFmTypes.h new file mode 100644 index 000000000..f0b90e56a --- /dev/null +++ b/src/core/backends/rtl/HdFmTypes.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +namespace AetherSDR::rtl { +struct HdFmRecipe { + int program = 0; + bool operator==(const HdFmRecipe&) const = default; +}; +struct HdFmAudioIdentity { + std::uint64_t sessionId = 0; + std::uint64_t revision = 0; + std::uint64_t audioEpoch = 1; + std::uint64_t producedMonotonicMs = 0; +}; +struct HdFmRawService { + int program = -1; + std::array name{}; + bool audioAvailable = false; +}; +// Fixed-size worker/acquisition value. Qt text/model construction belongs to +// the backend owner. Publication sequence is distinct from real audio progress. +struct HdFmRawReception { + bool valid = false; + std::uint64_t sessionId = 0; + std::uint64_t receiverEpoch = 0; + std::uint64_t revision = 0; + std::int64_t frequencyHz = 0; + int selectedProgram = 0; + bool synced = false; + bool audioValid = false; + std::array services{}; + std::array stationName{}; + std::array title{}; + std::array artist{}; + std::optional merLowerDb; + std::optional merUpperDb; + std::optional cber; + std::optional frequencyOffsetHz; + std::uint64_t observationMonotonicMs = 0; + std::uint64_t audioMonotonicMs = 0; + // Acquisition publication order is independent of the decoder measurement clock. + std::uint64_t publicationSequence = 0; + std::uint64_t observationSequence = 0; + std::uint64_t audioSequence = 0; + std::uint64_t audioEpoch = 1; + std::uint32_t syncLossCount = 0; + std::uint32_t reacquisitionCount = 0; + std::uint32_t syncDurationMs = 0; + std::uint64_t iqDrops = 0; + std::uint64_t pcmDrops = 0; + std::uint64_t playoutUnderruns = 0; +}; +} // namespace AetherSDR::rtl diff --git a/src/core/backends/rtl/Nrsc5FmDecoder.cpp b/src/core/backends/rtl/Nrsc5FmDecoder.cpp new file mode 100644 index 000000000..9a0dbfb6b --- /dev/null +++ b/src/core/backends/rtl/Nrsc5FmDecoder.cpp @@ -0,0 +1,245 @@ +#include "Nrsc5FmDecoder.h" +#include "HdFmIqAdapter.h" +#include "core/dsp/FftwPlannerLock.h" +#include +#include +#include +#include +#include +#ifdef AETHER_ENABLE_NRSC5 +// The pinned C API header does not declare C++ linkage guards. +extern "C" { +#include +} +#endif + +namespace AetherSDR::rtl { +namespace { +class NativePipe final : public Nrsc5FmDecoder::Pipe { +public: + NativePipe() + { +#ifdef AETHER_ENABLE_NRSC5 + const auto lock = fftwfPlannerLock(); + if (nrsc5_open_pipe(&m_decoder) != 0) { m_decoder = nullptr; } +#endif + } + ~NativePipe() override + { +#ifdef AETHER_ENABLE_NRSC5 + if (m_decoder) { const auto lock = fftwfPlannerLock(); nrsc5_close(m_decoder); } +#endif + } + bool valid() const noexcept override + { +#ifdef AETHER_ENABLE_NRSC5 + return m_decoder != nullptr; +#else + return false; +#endif + } + bool feed(std::span input, Callback callback, void* context) noexcept override + { +#ifdef AETHER_ENABLE_NRSC5 + nrsc5_set_callback(m_decoder, callback, context); + return nrsc5_pipe_samples_cf32(m_decoder, input.data(), static_cast(input.size())) == 0; +#else + (void)input; (void)callback; (void)context; + return false; +#endif + } +private: +#ifdef AETHER_ENABLE_NRSC5 + nrsc5_t* m_decoder = nullptr; +#endif +}; +std::uint64_t monotonicMs() +{ + return static_cast(std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); +} +template void copyText(std::array& destination, const char* source) +{ + destination.fill(0); + if (!source) { return; } + for (std::size_t i = 0; i + 1 < N && source[i]; ++i) { + const unsigned char value = static_cast(source[i]); + destination[i] = value < 0x20 || value == 0x7f ? ' ' : source[i]; + } +} +void increment(std::uint32_t& value) +{ + if (value < std::numeric_limits::max()) { ++value; } +} +} +struct Nrsc5FmDecoder::State { + HdFmRawReception value; + Sink& sink; + bool open = false; + bool failed = false; + bool hadSync = false; + std::uint64_t decodedFrames = 0; + std::uint64_t iqFrames = 0; + std::uint64_t syncFirst = 0; + std::array left{}, right{}; + std::chrono::steady_clock::time_point lastAudio{}; + std::unique_ptr pipe; + State(HdFmRawReception initial, Sink& output) : value(initial), sink(output) {} + void retire() + { + if (value.audioValid || decodedFrames != 0) { + value.audioValid = false; + decodedFrames = 0; + ++value.audioEpoch; + sink.audioRetired(value.audioEpoch); + } + } + static void callback(const nrsc5_event_t* event, void* context) + { +#ifdef AETHER_ENABLE_NRSC5 + if (event) { static_cast(context)->event(*event); } +#else + (void)event; (void)context; +#endif + } +#ifdef AETHER_ENABLE_NRSC5 + void event(const nrsc5_event_t& event) + { + if (failed) { return; } + switch (event.event) { + case NRSC5_EVENT_SYNC: + if (!value.synced) { + if (hadSync) { increment(value.reacquisitionCount); } + hadSync = true; syncFirst = iqFrames; + } + value.synced = true; value.valid = true; + value.frequencyOffsetHz = std::isfinite(event.sync.freq_offset) + ? std::optional(event.sync.freq_offset) : std::nullopt; + break; + case NRSC5_EVENT_LOST_SYNC: + if (value.synced) { increment(value.syncLossCount); } + value.synced = false; value.valid = false; value.syncDurationMs = 0; + value.stationName.fill(0); value.title.fill(0); value.artist.fill(0); + value.services = {}; value.merLowerDb.reset(); value.merUpperDb.reset(); + value.cber.reset(); value.frequencyOffsetHz.reset(); + retire(); + break; + case NRSC5_EVENT_MER: + value.merLowerDb = std::isfinite(event.mer.lower) + ? std::optional(event.mer.lower) : std::nullopt; + value.merUpperDb = std::isfinite(event.mer.upper) + ? std::optional(event.mer.upper) : std::nullopt; + break; + case NRSC5_EVENT_BER: + value.cber = std::isfinite(event.ber.cber) && event.ber.cber >= 0 && event.ber.cber <= 1 + ? std::optional(event.ber.cber) : std::nullopt; + break; + case NRSC5_EVENT_STATION_NAME: + copyText(value.stationName, event.station_name.name); + break; + case NRSC5_EVENT_ID3: + if (event.id3.program == static_cast(value.selectedProgram)) { + copyText(value.title, event.id3.title); copyText(value.artist, event.id3.artist); + } + break; + case NRSC5_EVENT_AUDIO_SERVICE: + if (event.audio_service.program < value.services.size()) { + value.services[event.audio_service.program].program = static_cast(event.audio_service.program); + value.services[event.audio_service.program].audioAvailable = true; + } + break; + case NRSC5_EVENT_AUDIO_SERVICE_DESCRIPTOR: + if (event.asd.program < value.services.size()) { + value.services[event.asd.program].program = static_cast(event.asd.program); + value.services[event.asd.program].audioAvailable = true; + } + break; + // SIG channel/port values are not assumed to be program numbers. + // Names remain empty (UI HD1..HD8 fallback) until that mapping is + // verified; bounded station and selected-program ID3 text is exposed. + case NRSC5_EVENT_AUDIO: { + if (event.audio.program >= value.services.size()) { break; } + auto& service = value.services[event.audio.program]; + service.program = static_cast(event.audio.program); + const bool accepted = value.synced && event.audio.flags == NRSC5_AUDIO_FLAGS_NONE + && event.audio.data && event.audio.count > 0 && event.audio.count % 2 == 0 + && event.audio.count <= 2 * left.size(); + service.audioAvailable = true; // Discovered service; validity belongs to selected PCM. + if (event.audio.program != static_cast(value.selectedProgram)) { break; } + if (!accepted) { retire(); break; } + const std::size_t frames = event.audio.count / 2; + for (std::size_t i = 0; i < frames; ++i) { + left[i] = event.audio.data[2 * i] / 32768.0f; + right[i] = event.audio.data[2 * i + 1] / 32768.0f; + } + // Publish the state before its PCM through the sink. The consumer + // drains that status barrier before exposing a decoded block. + ++value.audioSequence; ++value.observationSequence; + value.audioValid = true; value.valid = true; + value.audioMonotonicMs = monotonicMs(); + value.observationMonotonicMs = value.audioMonotonicMs; + const HdFmAudioIdentity identity{value.sessionId, value.revision, value.audioEpoch, value.audioMonotonicMs}; + if (!sink.decoded(identity, decodedFrames, std::span(left).first(frames), std::span(right).first(frames))) { + failed = true; value.valid = false; value.audioValid = false; decodedFrames = 0; + sink.audioRetired(++value.audioEpoch); break; + } + decodedFrames += frames; + value.audioValid = true; value.valid = true; + lastAudio = std::chrono::steady_clock::now(); + break; + } + default: return; + } + ++value.observationSequence; value.observationMonotonicMs = monotonicMs(); + } +#endif +}; +Nrsc5FmDecoder::Nrsc5FmDecoder(HdFmRawReception initial, Sink& sink, std::unique_ptr pipe) + : m_state(std::make_unique(initial, sink)) +{ + if (initial.selectedProgram < 0 || initial.selectedProgram >= 8) { return; } + // NativePipe serializes all open/close planner work with every other + // single-precision FFTW user. Pipe mode has no upstream worker. + m_state->pipe = pipe ? std::move(pipe) : std::make_unique(); + m_state->open = m_state->pipe->valid(); +} +Nrsc5FmDecoder::~Nrsc5FmDecoder() = default; +bool Nrsc5FmDecoder::valid() const noexcept { return m_state->open && !m_state->failed; } +const HdFmRawReception& Nrsc5FmDecoder::reception() const noexcept { return m_state->value; } +bool Nrsc5FmDecoder::feed(std::span input, std::uint64_t session, + std::uint64_t revision) noexcept +{ + if (!valid() || !session || !revision || input.empty() || input.size() % 2 != 0 + || input.size() > 32768 || !std::ranges::all_of(input, [](float value) { return std::isfinite(value); })) { + withdraw(); return false; + } + auto& state = *m_state; + state.value.sessionId = session; state.value.revision = revision; + if (!state.pipe->feed(input, State::callback, &state)) { + withdraw(); return false; + } + state.iqFrames += input.size() / 2; + if (state.value.synced) { + state.value.syncDurationMs = static_cast(std::min( + 1000.0L * (state.iqFrames - state.syncFirst) / HdFmIqAdapter::kOutputRate, + static_cast(std::numeric_limits::max()))); + } + return valid(); +} +void Nrsc5FmDecoder::withdraw() noexcept +{ + m_state->failed = true; m_state->retire(); + m_state->value.valid = false; m_state->value.synced = false; + ++m_state->value.observationSequence; m_state->value.observationMonotonicMs = monotonicMs(); +} +void Nrsc5FmDecoder::resetAudio() noexcept +{ + m_state->retire(); ++m_state->value.observationSequence; m_state->value.observationMonotonicMs = monotonicMs(); +} +void Nrsc5FmDecoder::expireAudio() noexcept +{ + if (m_state->value.audioValid && std::chrono::steady_clock::now() - m_state->lastAudio > std::chrono::milliseconds(500)) { + m_state->retire(); ++m_state->value.observationSequence; m_state->value.observationMonotonicMs = monotonicMs(); + } +} +} // namespace AetherSDR::rtl diff --git a/src/core/backends/rtl/Nrsc5FmDecoder.h b/src/core/backends/rtl/Nrsc5FmDecoder.h new file mode 100644 index 000000000..ac359f358 --- /dev/null +++ b/src/core/backends/rtl/Nrsc5FmDecoder.h @@ -0,0 +1,42 @@ +#pragma once + +#include "HdFmTypes.h" +#include +#include + +struct nrsc5_event_t; + +namespace AetherSDR::rtl { +// Serialized decoder-thread owner. No device/tuner API is exposed. +class Nrsc5FmDecoder final { +public: + class Sink { + public: + virtual ~Sink() = default; + virtual bool decoded(const HdFmAudioIdentity& identity, std::uint64_t first, + std::span left, std::span right) noexcept = 0; + virtual void audioRetired(std::uint64_t epoch) noexcept = 0; + }; + // The injectable serialized pipe is the socket-free event-test seam. Its + // callback contract is the pinned upstream C API, including borrowed data. + class Pipe { + public: + using Callback = void (*)(const ::nrsc5_event_t*, void*); + virtual ~Pipe() = default; + virtual bool valid() const noexcept = 0; + virtual bool feed(std::span, Callback, void*) noexcept = 0; + }; + Nrsc5FmDecoder(HdFmRawReception initial, Sink& sink, std::unique_ptr pipe = {}); + ~Nrsc5FmDecoder(); + bool valid() const noexcept; + bool feed(std::span interleaved, std::uint64_t session, + std::uint64_t revision) noexcept; + const HdFmRawReception& reception() const noexcept; + void withdraw() noexcept; + void expireAudio() noexcept; + void resetAudio() noexcept; +private: + struct State; + std::unique_ptr m_state; +}; +} // namespace AetherSDR::rtl diff --git a/src/core/backends/rtl/RtlCaptureTransaction.cpp b/src/core/backends/rtl/RtlCaptureTransaction.cpp index 760658e0c..ca2ea2835 100644 --- a/src/core/backends/rtl/RtlCaptureTransaction.cpp +++ b/src/core/backends/rtl/RtlCaptureTransaction.cpp @@ -29,7 +29,7 @@ std::vector passbands(const std::vector& r std::vector result; result.reserve(receivers.size()); for (const T::Receiver& receiver : receivers) { - result.push_back(receiver.passband); + result.push_back(T::effectivePassband(receiver)); } return result; } @@ -50,7 +50,9 @@ Policy::Error validateConfigured(const std::vector& receivers, } if (used[id]) { return Policy::Error::DuplicateId; } used[id] = true; - const auto occupied = Policy::occupiedInterval(receiver.passband); + const auto configured = Policy::occupiedInterval(receiver.passband); + if (!configured.interval) { return configured.error; } + const auto occupied = Policy::occupiedInterval(T::effectivePassband(receiver)); if (!occupied.interval) { return occupied.error; } if ((receiver.mode == T::Mode::Fm || receiver.mode == T::Mode::Fmn) && (receiver.passband.filterLowHz < -21600 @@ -99,7 +101,7 @@ std::vector receivingPassbands(const T::State& state) const auto receiver = std::ranges::find_if(state.receivers, [id](const T::Receiver& value) { return value.passband.stableId == id; }); - if (receiver != state.receivers.end()) { result.push_back(receiver->passband); } + if (receiver != state.receivers.end()) { result.push_back(T::effectivePassband(*receiver)); } } return result; } @@ -244,6 +246,7 @@ RtlCaptureTransaction::Submission RtlCaptureTransaction::submit(const Desired& d || receiver.squelchLevel < 0 || receiver.squelchLevel > 100 || receiver.automaticSquelchMarginDb < 5 || receiver.automaticSquelchMarginDb > 20 || (receiver.automaticSquelch && !receiver.squelchEnabled) + || receiver.hdProgram < 0 || receiver.hdProgram > 7 || (receiver.wfmDeemphasisUs != 50 && receiver.wfmDeemphasisUs != 75) || (receiver.squelchEnabled && receiver.mode != Mode::Fm && receiver.mode != Mode::Fmn)) { return {{}, Policy::Error::InvalidNumber}; @@ -281,7 +284,7 @@ RtlCaptureTransaction::Submission RtlCaptureTransaction::submit(const Desired& d if (!followed) { return {{}, Policy::Error::NoLegalCenter}; } } const std::vector required = followed - ? std::vector{followed->passband} + ? std::vector{effectivePassband(*followed)} : std::vector{}; // Operator-requested free RF browsing intentionally parks receivers that // leave capture. The earlier all-active/refuse RFC policy needs maintainer @@ -469,4 +472,15 @@ RtlCaptureTransaction::Result RtlCaptureTransaction::execute(const Work& work, } return {work.token, ResultCode::Invalid, {}, work.operation}; } +SharedCapturePolicy::SliceDescriptor RtlCaptureTransaction::effectivePassband(const Receiver& receiver) +{ + auto result = receiver.passband; + if (receiver.mode == Mode::Wfm && receiver.wfmHdStereo) { + result.filterLowHz = -225000; result.filterHighHz = 225000; + result.guardLowHz = std::max(result.guardLowHz, 3000.0); + result.guardHighHz = std::max(result.guardHighHz, 3000.0); + } + return result; +} + } // namespace AetherSDR::rtl diff --git a/src/core/backends/rtl/RtlCaptureTransaction.h b/src/core/backends/rtl/RtlCaptureTransaction.h index 79d1ccf6f..ff4904aa8 100644 --- a/src/core/backends/rtl/RtlCaptureTransaction.h +++ b/src/core/backends/rtl/RtlCaptureTransaction.h @@ -33,6 +33,8 @@ class RtlCaptureTransaction final { int squelchLevel = 20; int wfmDeemphasisUs = 75; // broadcast standard; validated 50 or 75 only bool wfmForceMono = false; // selected decoder matrix policy, not observed pilot + bool wfmHdStereo = false; + int hdProgram = 0; // decoder program 0..7, persisted only after adoption bool automaticSquelch = false; int automaticSquelchMarginDb = 10; bool operator==(const Receiver&) const = default; @@ -119,6 +121,10 @@ class RtlCaptureTransaction final { // converter's DC. This is capture placement, not an IQ/DC subtraction. static constexpr double kDcSeparationHz = 48'000; static bool dcClear(const State& state); + // HD carries digital sidebands outside the saved analog passband. Its + // outer 198.402 kHz carriers, CFO search and transition need 225 kHz + // plus guard each side. Never overwrite the operator's analog edges. + static SharedCapturePolicy::SliceDescriptor effectivePassband(const Receiver& receiver); private: SharedCapturePolicy::ReceiverLimits m_limits; diff --git a/src/core/backends/rtl/RtlReceivePipeline.cpp b/src/core/backends/rtl/RtlReceivePipeline.cpp index 9bb3c6300..4cba442c2 100644 --- a/src/core/backends/rtl/RtlReceivePipeline.cpp +++ b/src/core/backends/rtl/RtlReceivePipeline.cpp @@ -85,6 +85,16 @@ RtlReceivePipeline::Submission RtlReceivePipeline::prepareDetailed( || receiver.mode == Transaction::Mode::Fmn || (m_enableWfm && receiver.mode == Transaction::Mode::Wfm); if (!native && state.receivers.size() != 1) { return Submission::Failed; } + if (receiver.mode == Transaction::Mode::Wfm && receiver.wfmHdStereo) { + if (!m_enableWfm) { return Submission::Failed; } +#ifndef AETHER_ENABLE_NRSC5 + return Submission::Failed; +#else + if (receiver.hdProgram < 0 || receiver.hdProgram >= 8 || state.receivers.size() != 1) { + return Submission::Failed; + } +#endif + } validated[id] = true; } const unsigned faults = m_faults.load(std::memory_order_acquire); @@ -113,7 +123,9 @@ RtlReceivePipeline::Submission RtlReceivePipeline::prepareDetailed( || !wide || !prior.dsp.wbfmReceive || prior.passband != receiver->passband || prior.dsp.wbfmReceive->deemphasis != deemphasis - || prior.dsp.wbfmReceive->forceMono != receiver->wfmForceMono; + || prior.dsp.wbfmReceive->forceMono != receiver->wfmForceMono + || prior.hdFm.has_value() != receiver->wfmHdStereo + || (prior.hdFm && prior.hdFm->program != receiver->hdProgram); } } if (legacy && (state.receivers.size() != 1 || state.receivingIds.size() != 1)) { return Submission::Failed; } @@ -169,10 +181,14 @@ RtlReceivePipeline::Submission RtlReceivePipeline::prepareDetailed( spec.dsp.fmReceive = WdspChannel::FmReceive{}; spec.dsp.fmDeviationHz = receiver.mode == Transaction::Mode::Fmn ? 2500.0 : 5000.0; } + if (receiver.mode == Transaction::Mode::Wfm && receiver.wfmHdStereo) { + spec.hdFm = HdFmRecipe{receiver.hdProgram}; + } spec.dsp.filterLowHz = receiver.passband.filterLowHz; spec.dsp.filterHighHz = receiver.passband.filterHighHz; if (resetCapture || (faults & (1u << id)) || m_specs[id].handle != spec.handle - || m_specs[id].passband != spec.passband || m_specs[id].dsp != spec.dsp) { + || m_specs[id].passband != spec.passband || m_specs[id].dsp != spec.dsp + || m_specs[id].hdFm != spec.hdFm) { if (epochs[id] == std::numeric_limits::max()) { return Submission::Failed; } ++epochs[id]; } @@ -213,6 +229,7 @@ bool RtlReceivePipeline::adopt() noexcept if (!m_reader.adoptPrepared(m_session, m_nextCapture, m_prepared)) { return false; } if (m_token.session != m_nextToken.session || m_captureEpoch != m_nextEpoch) { m_mixerOrigin.reset(); + m_hdSpeakerEpoch = 0; m_hdAudioValid = false; m_hdSpeakerIdentities.fill({}); } const bool resetSquelch = m_captureEpoch != m_nextEpoch; for (std::size_t slot = 0; slot < m_monitor.size(); ++slot) { @@ -232,7 +249,8 @@ bool RtlReceivePipeline::adopt() noexcept bool RtlReceivePipeline::process(std::uint64_t firstSample, std::span> samples) noexcept { - return m_reader.processBlock({m_session, m_capture, firstSample, false, samples}, *this, + return m_reader.processBlock({m_session, m_capture, firstSample, false, samples, + m_token.session, m_token.revision}, *this, m_reader.activeRevision()); } void RtlReceivePipeline::stop() { m_reader.stop(); } @@ -309,7 +327,7 @@ void RtlReceivePipeline::process(const RtlReceiverRegistry::SampleBlock& block, const auto& spec = *views[i].spec; const unsigned monitor = m_monitor[spec.handle.slot].load(std::memory_order_relaxed); m_traceStableIds[spec.handle.slot] = spec.passband.stableId; - inputs[i] = {spec.handle.slot, spec.handle.instance, spec.epoch, + inputs[i] = {spec.handle.slot, spec.handle.instance, spec.hdFm ? m_hdSpeakerEpoch : spec.epoch, (monitor & 255) / 100.0f, ((monitor >> 8) & 255) / 100.0f, (monitor & (1 << 16)) != 0}; } const auto clock = [this](std::uint64_t sample) { @@ -435,9 +453,90 @@ void RtlReceivePipeline::speakerBlock(std::uint64_t first, std::span left, + std::span right, bool discontinuity) noexcept +{ + if (!spec.hdFm || left.empty() || left.size() != right.size() || left.size() > 1024 + || (m_faults.load(std::memory_order_acquire) & (1u << spec.handle.slot))) { return; } + Packet packet; + packet.token = {identity.sessionId, identity.revision}; packet.captureEpoch = m_captureEpoch; + packet.instance = spec.handle.instance; packet.receiverEpoch = spec.epoch; + packet.audioEpoch = identity.audioEpoch; packet.producedMonotonicMs = identity.producedMonotonicMs; + packet.slot = spec.handle.slot; packet.firstSample = first; packet.frames = left.size(); + packet.sampleRateHz = 44100; packet.discontinuity = discontinuity; + for (std::size_t i = 0; i < left.size(); ++i) { + packet.samples[2 * i] = left[i]; packet.samples[2 * i + 1] = right[i]; + } + enqueue(packet); // Native decoded tap; no monitor gain/pan/mute and no resampling. +} +void RtlReceivePipeline::hdSpeakerAudio(const RtlReceiverRegistry::ReceiverSpec& spec, + const HdFmAudioIdentity& identity, std::uint64_t first, + std::span left, std::span right, bool discontinuity) noexcept +{ + (void)discontinuity; // Receiver/audio epoch retirement owns PCM lifetime changes. + if (!spec.hdFm || left.empty() || left.size() != right.size() || left.size() > 1024 + || (m_faults.load(std::memory_order_acquire) & (1u << spec.handle.slot))) { return; } + const unsigned monitor = m_monitor[spec.handle.slot].load(std::memory_order_relaxed); + const RtlAudioMixer::Input input{spec.handle.slot, spec.handle.instance, identity.audioEpoch, + (monitor & 255) / 100.0f, ((monitor >> 8) & 255) / 100.0f, (monitor & (1 << 16)) != 0}; + if (!m_mixer.configure(m_token.session, m_captureEpoch, std::span(&input, 1), first)) { + m_mixerConfigurationFailures.fetch_add(1); m_faults.fetch_or(1u << spec.handle.slot); return; + } + m_hdSpeakerEpoch = identity.audioEpoch; + for (std::size_t i = 0; i < left.size(); ++i) { + m_hdSpeakerIdentities[(first + i) % RtlAudioMixer::kCapacity] = identity; + } + m_mixer.push(spec.handle.slot, spec.handle.instance, identity.audioEpoch, first, left, right); +} RtlReceivePipeline::TraceEvent RtlReceivePipeline::traceContext() const noexcept { TraceEvent event; diff --git a/src/core/backends/rtl/RtlReceivePipeline.h b/src/core/backends/rtl/RtlReceivePipeline.h index bee15d03b..7fc802262 100644 --- a/src/core/backends/rtl/RtlReceivePipeline.h +++ b/src/core/backends/rtl/RtlReceivePipeline.h @@ -50,7 +50,11 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, std::uint64_t captureEpoch = 0; std::uint64_t instance = 0; std::uint64_t receiverEpoch = 0; - std::uint64_t enqueuedMonotonicNs = 0; // mailbox residence instrumentation + std::uint64_t audioEpoch = 0; // nonzero for HD, separately retired on sync/audio loss + std::uint64_t producedMonotonicMs = 0; // oldest decoded contribution; zero for intentional HD silence + // Mailbox residence instrumentation. Separate from the HD producer's + // causal timestamp and never used to assert decoded-audio freshness. + std::uint64_t enqueuedMonotonicNs = 0; std::uint64_t firstSample = 0; int slot = -1; std::size_t frames = 0; @@ -79,6 +83,16 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, const auto generation = m_rfGeneration.load(std::memory_order_acquire); return (generation & 1) && observation.generation == generation; } + struct HdFmObservation { + Transaction::Token token; + std::uint64_t captureEpoch = 0; + std::uint64_t instance = 0; + std::uint64_t receiverEpoch = 0; + std::uint64_t audioEpoch = 0; + int slot = -1; + HdFmRawReception reception; + }; + bool takeHdObservation(HdFmObservation& output) noexcept; // Bounded acquisition observations; no formatting, clock reads, allocation, // or logging on the sample callback. The owner drains across bank adoption // so repair cannot erase the fault that requested it. @@ -162,6 +176,18 @@ class RtlReceivePipeline final : private RtlReceiverRegistry::BlockProcessor, bool m_legacy = true; std::uint8_t m_receivingMask = 0; RtlAudioMixer m_mixer; + void hdObservation(const RtlReceiverRegistry::ReceiverSpec&, const HdFmRawReception&) noexcept override; + void hdDecodedAudio(const RtlReceiverRegistry::ReceiverSpec&, const HdFmAudioIdentity&, std::uint64_t, + std::span, std::span, bool) noexcept override; + void hdSpeakerAudio(const RtlReceiverRegistry::ReceiverSpec&, const HdFmAudioIdentity&, std::uint64_t, + std::span, std::span, bool) noexcept override; + static constexpr unsigned kHdObservations = 64; + std::array m_hdObservations; + alignas(64) std::atomic m_hdWrite{0}; + alignas(64) std::atomic m_hdRead{0}; + std::uint64_t m_hdSpeakerEpoch = 0; + std::array m_hdSpeakerIdentities{}; + bool m_hdAudioValid = false; // Set on the first capture block of a new mixer epoch, then retained. // Unchanged epochs must keep their existing sample coordinate and buffers. std::optional m_mixerOrigin; diff --git a/src/core/backends/rtl/RtlReceiverRegistry.cpp b/src/core/backends/rtl/RtlReceiverRegistry.cpp index 63fa22587..767821dc0 100644 --- a/src/core/backends/rtl/RtlReceiverRegistry.cpp +++ b/src/core/backends/rtl/RtlReceiverRegistry.cpp @@ -1,5 +1,7 @@ #include "core/backends/rtl/RtlReceiverRegistry.h" #include "core/backends/rtl/RtlRfExtractor.h" +#include "HdFmReceiver.h" +#include "RtlCaptureTransaction.h" #include @@ -214,6 +216,13 @@ class WdspReceiver final : public Registry::Receiver, private RtlRfExtractor::Si std::unique_ptr prepareWdsp(const Registry::ReceiverSpec& spec, WdspChannel::Reservation& reservation, std::string& error) { + // The existing reservation conservatively bounds all prepared receivers. + // HD additionally reserves one of two process-wide worker/session leases. + if (spec.hdFm) { + // Admission permits one HD receiver, so this is the complete one-slot + // reservation. It must follow the receiver across reused banks. + return prepareHdFmReceiver(spec, error, {}, std::move(reservation)); + } std::unique_ptr channel = WdspChannel::create(spec.dsp, reservation, &error); if (!channel) { return nullptr; @@ -661,6 +670,8 @@ RtlReceiverRegistry::Result RtlReceiverRegistry::submitImpl(const Capture& captu if (slot < 0 || static_cast(slot) >= state.limits.slotCount || used[slot] || spec.handle.session != state.session.load() || state.reservations[slot] != spec.handle || spec.passband.stableId != slot || !boundedDsp(spec.dsp) + || (spec.hdFm && (desired.size() != 1 || !spec.extractRf || !spec.dsp.wbfmReceive + || spec.hdFm->program < 0 || spec.hdFm->program >= 8)) || (spec.dsp.wbfmReceive && (spec.passband.guardLowHz < WdspChannel::WbfmReceive::kRfTransitionGuardHz || spec.passband.guardHighHz < WdspChannel::WbfmReceive::kRfTransitionGuardHz)) @@ -676,6 +687,11 @@ RtlReceiverRegistry::Result RtlReceiverRegistry::submitImpl(const Capture& captu } used[slot] = true; passbands[i] = spec.passband; + if (spec.hdFm) { + RtlCaptureTransaction::Receiver receiver{spec.passband, RtlCaptureTransaction::Mode::Wfm}; + receiver.wfmHdStereo = true; + passbands[i] = RtlCaptureTransaction::effectivePassband(receiver); + } } // The capture is fixed here: this singleton domain checks passband fit, // not tuner legality. M1 must validate the hardware domain/readback first. diff --git a/src/core/backends/rtl/RtlReceiverRegistry.h b/src/core/backends/rtl/RtlReceiverRegistry.h index ca227377d..c00812eaa 100644 --- a/src/core/backends/rtl/RtlReceiverRegistry.h +++ b/src/core/backends/rtl/RtlReceiverRegistry.h @@ -1,5 +1,7 @@ #pragma once +#include "HdFmTypes.h" + #include "core/SharedCapturePolicy.h" #include "core/backends/rtl/RtlRfExtractor.h" #include "core/dsp/WdspChannel.h" @@ -51,6 +53,7 @@ class RtlReceiverRegistry final Capture capture; bool extractRf = false; std::uint64_t epoch = 0; // reprepare on a gap without reusing DSP history + std::optional hdFm; // alternative native decoder; analog edges remain in passband bool operator==(const ReceiverSpec&) const = default; }; @@ -89,6 +92,11 @@ class RtlReceiverRegistry final virtual ~AudioSink() = default; virtual void audioBlock(const ReceiverSpec& spec, std::uint64_t firstSample, std::span left, std::span right, bool discontinuity) noexcept = 0; + virtual void hdObservation(const ReceiverSpec&, const HdFmRawReception&) noexcept {} + virtual void hdDecodedAudio(const ReceiverSpec&, const HdFmAudioIdentity&, std::uint64_t, + std::span, std::span, bool) noexcept {} + virtual void hdSpeakerAudio(const ReceiverSpec&, const HdFmAudioIdentity&, std::uint64_t, + std::span, std::span, bool) noexcept {} // Optional decoder observation for the same live receiver. The decoder // publishes its latest completed DSP block, not an RF arrival timestamp. virtual void audioBlockWithStatus(const ReceiverSpec& spec, std::uint64_t firstSample, @@ -117,6 +125,10 @@ class RtlReceiverRegistry final std::uint64_t firstSample = 0; bool discontinuity = false; std::span> samples; + // Capture transaction identity, captured before queuing asynchronous IQ. + // The registry's independent session above must not substitute for it. + std::uint64_t publicationSession = 0; + std::uint64_t publicationRevision = 0; }; class BlockProcessor { public: diff --git a/src/core/backends/rtl/RtlSdrBackend.cpp b/src/core/backends/rtl/RtlSdrBackend.cpp index bc5dc8e9e..cdfd57b5c 100644 --- a/src/core/backends/rtl/RtlSdrBackend.cpp +++ b/src/core/backends/rtl/RtlSdrBackend.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include namespace AetherSDR::rtl { @@ -24,6 +25,12 @@ namespace { Q_LOGGING_CATEGORY(lcRtlReceive, "aether.rtl.receive", QtWarningMsg) +std::uint64_t hdMonotonicMs() +{ + return static_cast(std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); +} + constexpr double kMinTuneHz = 24'000.0; constexpr double kMaxTuneHz = 1'766'000'000.0; @@ -32,7 +39,7 @@ bool supportsAnalogBank(const RtlCaptureTransaction::Receiver& receiver) return receiver.mode == RtlCaptureTransaction::Mode::Fm || receiver.mode == RtlCaptureTransaction::Mode::Fmn || (RtlReceivePipeline::kQualifiedWfmEnabled - && receiver.mode == RtlCaptureTransaction::Mode::Wfm); + && receiver.mode == RtlCaptureTransaction::Mode::Wfm && !receiver.wfmHdStereo); } bool hasReceivingNarrowFm(const RtlCaptureTransaction::State& state) @@ -238,9 +245,15 @@ RadioCapabilities RtlSdrBackend::capabilities() const c.receiveFilterControl->modes.append({QStringLiteral("WFM"), -100000, -15000, 15000, 100000, 30000, 200000}); c.broadcastFmReceive = BroadcastFmReceive{{50, 75}, true, true}; +#ifdef AETHER_ENABLE_NRSC5 + c.broadcastFmReceive->hdStereo = true; +#endif } c.receiveAudioControl = ReceiveAudioControl{SliceFrequencyControl::Authority::Engine}; c.receiveAudioExport = ReceiveAudioExport{{24000, 48000}, m_receiverCapacity}; +#ifdef AETHER_ENABLE_NRSC5 + c.receiveAudioExport->sampleRatesHz.append(44100); +#endif c.panSpanModel = PanSpanModel{false, false}; if (m_lastPublished && hasReceivingNarrowFm(*m_lastPublished)) { c.receiveCapturePlacement = ReceiveCapturePlacement{ @@ -440,6 +453,12 @@ void RtlSdrBackend::startCapture(std::unique_ptr worker) m_pcmDiagnostics.fill({}); m_wfmStatus.fill(WfmStereoStatus::Unavailable); m_wfmReception.fill({}); + m_hdReception.fill({}); m_hdIdentity.fill({}); + m_hdDiagnosticsObserved = false; m_hdDiagnosticTotals.fill(0); m_hdPriorCounters.fill({}); + m_hdObservationProducedMs.fill(0); m_hdAudioProducedMs.fill(0); + for (auto& age : m_hdObservationAge) { age.invalidate(); } + for (auto& age : m_hdAudioAge) { age.invalidate(); } + for (auto& age : m_hdPublicationAge) { age.invalidate(); } m_wfmLastObservation.fill(std::nullopt); for (auto& age : m_wfmPublicationAge) { age.invalidate(); } for (auto& age : m_wfmObservationAge) { age.invalidate(); } @@ -517,6 +536,12 @@ void RtlSdrBackend::disconnectRadio() retireNativeAudio(); m_wfmStatus.fill(WfmStereoStatus::Unavailable); m_wfmReception.fill({}); + m_hdReception.fill({}); m_hdIdentity.fill({}); + m_hdDiagnosticsObserved = false; m_hdDiagnosticTotals.fill(0); m_hdPriorCounters.fill({}); + m_hdObservationProducedMs.fill(0); m_hdAudioProducedMs.fill(0); + for (auto& age : m_hdObservationAge) { age.invalidate(); } + for (auto& age : m_hdAudioAge) { age.invalidate(); } + for (auto& age : m_hdPublicationAge) { age.invalidate(); } m_wfmLastObservation.fill(std::nullopt); for (auto& age : m_wfmPublicationAge) { age.invalidate(); } for (auto& age : m_wfmObservationAge) { age.invalidate(); } @@ -653,6 +678,7 @@ IRadioBackend::HealthSnapshot RtlSdrBackend::healthSnapshot() const recipe.insert(QStringLiteral("filterLowHz"), receiver.passband.filterLowHz); recipe.insert(QStringLiteral("filterHighHz"), receiver.passband.filterHighHz); recipe.insert(QStringLiteral("wfmForceMono"), receiver.wfmForceMono); + recipe.insert(QStringLiteral("wfmHdStereo"), receiver.wfmHdStereo); recipe.insert(QStringLiteral("wfmDeemphasisUs"), receiver.wfmDeemphasisUs); recipe.insert(QStringLiteral("audioGain"), receiver.audioGain); recipe.insert(QStringLiteral("audioPan"), receiver.audioPan); @@ -690,6 +716,18 @@ IRadioBackend::HealthSnapshot RtlSdrBackend::healthSnapshot() const snapshot.order.append(row.first); snapshot.labels.insert(row.first, row.second); } + if (m_hdDiagnosticsObserved) { + const QStringList keys{QStringLiteral("rtlHdIqDrops"), QStringLiteral("rtlHdPcmDrops"), + QStringLiteral("rtlHdPlayoutUnderruns")}; + snapshot.order.append(keys); + snapshot.sections.insert(keys.front(), tr("Digital processing (since connect)")); + snapshot.labels.insert(keys[0], tr("Digital IQ blocks dropped")); + snapshot.labels.insert(keys[1], tr("Digital PCM blocks dropped")); + snapshot.labels.insert(keys[2], tr("Digital speaker playout underruns")); + for (qsizetype i = 0; i < keys.size(); ++i) { + snapshot.values.insert(keys[i], QVariant::fromValue(m_hdDiagnosticTotals[i])); + } + } if (m_lastPublished) { const auto& capture = m_lastPublished->capture; const QStringList keys{QStringLiteral("rtlCaptureCenterHz"), QStringLiteral("rtlCaptureRateHz"), @@ -735,7 +773,7 @@ bool RtlSdrBackend::createSlice(const QString& panId, double frequencyHz) || (panId != QStringLiteral("0xe1000000") && !panId.isEmpty()) || m_requested.receivers.size() >= static_cast(m_receiverCapacity)) { return false; } if (!std::ranges::all_of(m_requested.receivers, supportsAnalogBank)) { - emit configurationWarning(tr("Additional RTL slices support FM, FM-N and analog WFM only. AM/SSB/CW require a single configured receiver.")); + emit configurationWarning(tr("Additional RTL slices support FM, FM-N and analog WFM only. HD and AM/SSB/CW require a single configured receiver.")); return false; } auto desired = m_requested; @@ -907,14 +945,75 @@ void RtlSdrBackend::setSliceFilter(int sliceId, int lowHz, int highHz) void RtlSdrBackend::setSliceWfmForceMono(int sliceId, bool forceMono) { - if (!RtlReceivePipeline::kQualifiedWfmEnabled || !hasAcceptedSlice(sliceId)) { return; } + setSliceWfmAudioMode(sliceId, forceMono ? WfmAudioMode::Mono : WfmAudioMode::Stereo); +} + +void RtlSdrBackend::setSliceWfmAudioMode(int sliceId, WfmAudioMode mode) +{ + requestWfmAudioMode(sliceId, mode); +} + +ReceiveDispatch RtlSdrBackend::requestWfmAudioMode(int sliceId, WfmAudioMode mode) +{ + if (!validWfmAudioMode(mode)) { return ReceiveDispatch::Unsupported; } +#ifndef AETHER_ENABLE_NRSC5 + if (mode == WfmAudioMode::HdStereo) { return ReceiveDispatch::Unsupported; } +#endif + const bool hd = mode == WfmAudioMode::HdStereo; + const bool forceMono = mode == WfmAudioMode::Mono; + if (!RtlReceivePipeline::kQualifiedWfmEnabled || !hasAcceptedSlice(sliceId)) { return ReceiveDispatch::Unsupported; } auto desired = m_requested; + if (hd && desired.receivers.size() != 1) { + emit configurationWarning(tr("HD reception currently requires a single configured RTL receiver. Select Mono or Stereo for multiple analog receivers.")); + return ReceiveDispatch::Unsupported; + } const auto receiver = std::ranges::find_if(desired.receivers, [sliceId](const auto& value) { return value.passband.stableId == sliceId; }); - if (receiver == desired.receivers.end() || receiver->mode != RtlCaptureTransaction::Mode::Wfm - || receiver->wfmForceMono == forceMono) { return; } + if (receiver == desired.receivers.end() || receiver->mode != RtlCaptureTransaction::Mode::Wfm) { + return ReceiveDispatch::Unsupported; + } + if (receiver->wfmForceMono == forceMono && receiver->wfmHdStereo == hd) { + return ReceiveDispatch::Dispatched; + } receiver->wfmForceMono = forceMono; + receiver->wfmHdStereo = hd; + if (hd) { desired.followReceiverId = sliceId; } // complete HD sidebands or refuse requestCapture(desired); // Same prepare/adopt/rollback contract as the filter. + return ReceiveDispatch::Dispatched; +} + +void RtlSdrBackend::setSliceHdProgram(int sliceId, int program) +{ + requestHdProgram(sliceId, program); +} + +ReceiveDispatch RtlSdrBackend::requestHdProgram(int sliceId, int program) +{ +#ifdef AETHER_ENABLE_NRSC5 + if (!hasAcceptedSlice(sliceId) || program < 0 || program > 7) { return ReceiveDispatch::Unsupported; } + const HdFmReception& reception = m_hdReception[sliceId]; + const auto now = hdMonotonicMs(); + const auto produced = m_hdObservationProducedMs[sliceId]; + if (!produced || produced > now || now - produced > 500 + || m_hdIdentity[sliceId].expired || !acceptsFrame(reception.sessionId, reception.revision) + || !reception.valid || !reception.synced + || std::ranges::none_of(reception.services, [program](const HdFmService& service) { + return service.program == program && service.audioAvailable; + })) { return ReceiveDispatch::Unsupported; } + auto desired = m_requested; + const auto receiver = std::ranges::find_if(desired.receivers, + [sliceId](const auto& value) { return value.passband.stableId == sliceId; }); + if (receiver == desired.receivers.end() || receiver->mode != RtlCaptureTransaction::Mode::Wfm + || !receiver->wfmHdStereo + || std::abs(receiver->passband.carrierHz - reception.frequencyHz) > 0.5) { return ReceiveDispatch::Unsupported; } + if (receiver->hdProgram != program) { + receiver->hdProgram = program; requestCapture(desired); + } + return ReceiveDispatch::Dispatched; +#else + Q_UNUSED(sliceId); Q_UNUSED(program); + return ReceiveDispatch::Unsupported; +#endif } void RtlSdrBackend::setSliceWfmDeemphasis(int sliceId, int microseconds) @@ -1105,12 +1204,18 @@ ReceiveDispatch RtlSdrBackend::requestSliceWfm(int sliceId, const SliceWfmReques || receiver->mode != RtlCaptureTransaction::Mode::Wfm) { return ReceiveDispatch::Unsupported; } - if (request.field == SliceWfmRequest::Field::ForceMono) { - setSliceWfmForceMono(sliceId, request.value != 0); - } else { + switch (request.field) { + case SliceWfmRequest::Field::ForceMono: + return requestWfmAudioMode(sliceId, request.value ? WfmAudioMode::Mono : WfmAudioMode::Stereo); + case SliceWfmRequest::Field::AudioMode: + return requestWfmAudioMode(sliceId, static_cast(request.value)); + case SliceWfmRequest::Field::HdProgram: + return requestHdProgram(sliceId, request.value); + case SliceWfmRequest::Field::Deemphasis: setSliceWfmDeemphasis(sliceId, request.value); + return ReceiveDispatch::Dispatched; } - return ReceiveDispatch::Dispatched; + return ReceiveDispatch::Unsupported; } ReceiveDispatch RtlSdrBackend::requestSliceSquelch(int sliceId, const SliceSquelchRequest& request) @@ -1299,6 +1404,8 @@ QVector RtlSdrBackend::acceptedSettings() const slice.automaticSquelchMarginDb = receiver.automaticSquelchMarginDb; slice.wfmDeemphasisUs = receiver.wfmDeemphasisUs; slice.wfmForceMono = receiver.wfmForceMono; + slice.wfmHdStereo = receiver.wfmHdStereo; + slice.hdProgram = receiver.hdProgram; output.append(slice); } return output; @@ -1357,18 +1464,22 @@ void RtlSdrBackend::restoreAcceptedSlices() || !isKnownMode(slice.mode) || desired.receivers.size() >= static_cast(m_receiverCapacity)) { continue; } seen.insert(slice.id); +#ifndef AETHER_ENABLE_NRSC5 + if (nativeWfm && slice.wfmHdStereo) { continue; } +#endif if (nativeWfm && (slice.filterLowHz < -100000 || slice.filterHighHz > 100000 || slice.filterLowHz > -15000 || slice.filterHighHz < 15000)) { unsupportedWfmFilter = true; continue; } - const bool bankCompatible = !wide || nativeWfm; + const bool bankCompatible = !wide || (nativeWfm && !slice.wfmHdStereo); if ((!wide && (slice.filterLowHz < -21600 || slice.filterHighHz > 21600 || slice.filterLowHz >= 0 || slice.filterHighHz <= 0)) || (!desired.receivers.empty() && (!bankCompatible || !std::ranges::all_of(desired.receivers, supportsAnalogBank)))) { continue; } desired.receivers.push_back({descriptor, captureMode(slice.mode), slice.audioGain, slice.audioPan, slice.audioMute, !wide && slice.squelchEnabled, slice.squelchLevel, slice.wfmDeemphasisUs, slice.wfmForceMono, + slice.wfmHdStereo, slice.hdProgram, !wide && slice.automaticSquelch, slice.automaticSquelchMarginDb}); } // Preserve a valid configured receiver even when it is parked. The @@ -1698,7 +1809,7 @@ void RtlSdrBackend::drainAudio() const QPointer worker(m_worker.get()); RtlReceivePipeline::RfObservation rf; while (worker && worker.data() == m_worker.get() && worker->takeRfObservation(rf)) { - const auto now = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + const auto now = hdMonotonicMs(); if (!m_receiveMetersEnabled || !acceptsFrame(rf.token.session, rf.token.revision) || !rf.producedMs || rf.producedMs > now || now - rf.producedMs > 500) { continue; } for (int id = 0; id < 8; ++id) { @@ -1710,26 +1821,58 @@ void RtlSdrBackend::drainAudio() } } } + drainHdObservations(); + expireHdObservations(); if (!worker || worker.data() != m_worker.get()) { return; } RtlReceivePipeline::Packet packet; for (int count = 0; count < 128 && worker && worker.data() == m_worker.get() && worker->takeAudio(packet); ++count) { if (!acceptsFrame(packet.token.session, packet.token.revision) || packet.frames == 0 || packet.frames > 1024 || packet.slot < -1 || packet.slot >= 8 - || packet.sampleRateHz != 48000 || packet.channelCount != 2) { continue; } + || (packet.sampleRateHz != 48000 && !(packet.slot >= 0 && packet.sampleRateHz == 44100)) + || packet.channelCount != 2) { continue; } if (!m_lastPublished || m_lastPublished->receivingIds.empty() || (packet.slot >= 0 && std::ranges::find(m_lastPublished->receivingIds, packet.slot) == m_lastPublished->receivingIds.end())) { continue; } + const auto hdReceiver = std::ranges::find_if(m_lastPublished->receivers, [this](const auto& receiver) { + return receiver.mode == RtlCaptureTransaction::Mode::Wfm && receiver.wfmHdStereo + && std::ranges::find(m_lastPublished->receivingIds, receiver.passband.stableId) + != m_lastPublished->receivingIds.end(); + }); + const bool hd = hdReceiver != m_lastPublished->receivers.end(); + if (packet.sampleRateHz == 44100) { + if (!hd || packet.slot != hdReceiver->passband.stableId) { continue; } + const auto& identity = m_hdIdentity[packet.slot]; + const auto now = hdMonotonicMs(); + if (!m_hdReception[packet.slot].audioValid || !packet.producedMonotonicMs + || packet.producedMonotonicMs > now || now - packet.producedMonotonicMs > 500 + || identity.instance != packet.instance || identity.receiverEpoch != packet.receiverEpoch + || identity.audioEpoch != packet.audioEpoch) { continue; } + } else if (hd) { + // Only the explicit playout conversion can supply HD speaker PCM. + if (packet.slot >= 0) { continue; } + const auto& identity = m_hdIdentity[hdReceiver->passband.stableId]; + const auto now = hdMonotonicMs(); + if (identity.expired || packet.audioEpoch == 0 || packet.audioEpoch != identity.audioEpoch + || packet.captureEpoch != identity.captureEpoch + || (packet.producedMonotonicMs && (packet.producedMonotonicMs > now + || now - packet.producedMonotonicMs > 500)) + || (packet.producedMonotonicMs && !m_hdReception[hdReceiver->passband.stableId].audioValid) + || (!packet.producedMonotonicMs && m_hdReception[hdReceiver->passband.stableId].audioValid)) { continue; } + } else if (packet.audioEpoch != 0) { continue; } NativeAudio& output = packet.slot < 0 ? m_speakerAudio : m_sliceAudio[packet.slot]; if (!output.producer || (packet.slot < 0 && output.captureEpoch != packet.captureEpoch) - || output.instance != packet.instance || output.receiverEpoch != packet.receiverEpoch) { + || output.instance != packet.instance || output.receiverEpoch != packet.receiverEpoch + || output.audioEpoch != packet.audioEpoch + || output.sampleRateHz != packet.sampleRateHz) { output = {}; output.producer = std::make_unique(); output.producer->start(packet.slot < 0 ? PcmPurpose::Speaker : PcmPurpose::Slice, packet.slot, {packet.sampleRateHz, PcmLayout::Stereo}, packet.token.session, packet.instance); output.captureEpoch = packet.captureEpoch; output.instance = packet.instance; - output.receiverEpoch = packet.receiverEpoch; + output.receiverEpoch = packet.receiverEpoch; output.audioEpoch = packet.audioEpoch; + output.sampleRateHz = packet.sampleRateHz; } QVector samples(static_cast(2 * packet.frames)); std::copy_n(packet.samples.begin(), samples.size(), samples.begin()); @@ -1772,6 +1915,132 @@ void RtlSdrBackend::drainAudio() } } } +void RtlSdrBackend::drainHdObservations() +{ + const QPointer worker(m_worker.get()); + RtlReceivePipeline::HdFmObservation observation; + for (int count = 0; count < 64 && worker && worker.data() == m_worker.get() + && worker->takeHdObservation(observation); ++count) { + observeHd(observation); + } +} + +void RtlSdrBackend::observeHd(const RtlReceivePipeline::HdFmObservation& observation) +{ + const int id = observation.slot; + const auto& raw = observation.reception; + const auto now = hdMonotonicMs(); + if (id < 0 || id >= 8 || !m_lastPublished + || !acceptsFrame(observation.token.session, observation.token.revision) + || raw.sessionId != observation.token.session || raw.revision != observation.token.revision + || raw.receiverEpoch != observation.receiverEpoch || raw.audioEpoch != observation.audioEpoch + || !observation.instance || !observation.receiverEpoch || !observation.audioEpoch + || !raw.publicationSequence + || (raw.valid && (!raw.observationSequence || !raw.observationMonotonicMs + || raw.observationMonotonicMs > now || now - raw.observationMonotonicMs > 500)) + || std::ranges::find(m_lastPublished->receivingIds, id) == m_lastPublished->receivingIds.end()) { return; } + const auto receiver = std::ranges::find_if(m_lastPublished->receivers, + [id](const auto& value) { return value.passband.stableId == id; }); + if (receiver == m_lastPublished->receivers.end() || receiver->mode != RtlCaptureTransaction::Mode::Wfm + || !receiver->wfmHdStereo || raw.selectedProgram != receiver->hdProgram + || std::abs(receiver->passband.carrierHz - raw.frequencyHz) > 0.5) { return; } + auto& previous = m_hdIdentity[id]; + const bool newReceiver = previous.instance != observation.instance + || previous.receiverEpoch != observation.receiverEpoch; + const bool newAudio = newReceiver || previous.audioEpoch != observation.audioEpoch; + if (!newReceiver && (raw.publicationSequence <= previous.publicationSequence + || observation.audioEpoch < previous.audioEpoch + || (raw.valid && raw.observationSequence < previous.observationSequence))) { return; } + if (!newAudio && raw.valid && raw.audioSequence < previous.audioSequence) { return; } + quint64 producedAudioMs = 0; + if (raw.audioValid && raw.synced && raw.audioSequence && raw.audioMonotonicMs + && raw.audioMonotonicMs <= now && now - raw.audioMonotonicMs <= 500) { + producedAudioMs = raw.audioMonotonicMs; + } + HdFmReception value; + value.valid = raw.valid; value.sessionId = raw.sessionId; + value.receiverEpoch = raw.receiverEpoch; value.revision = raw.revision; + value.frequencyHz = raw.frequencyHz; value.selectedProgram = raw.selectedProgram; + value.synced = raw.synced; + value.audioValid = raw.audioValid && raw.synced && producedAudioMs + && producedAudioMs <= now && now - producedAudioMs <= 500; + const auto text = [](const auto& bytes) { + const auto end = std::find(bytes.begin(), bytes.end(), '\0'); + return QString::fromUtf8(bytes.data(), std::distance(bytes.begin(), end)); + }; + for (const auto& service : raw.services) { + if (service.program < -1) { return; } + if (service.program >= 0) { value.services.append({service.program, text(service.name), service.audioAvailable}); } + } + value.stationName = text(raw.stationName); value.title = text(raw.title); value.artist = text(raw.artist); + value.merLowerDb = raw.merLowerDb; value.merUpperDb = raw.merUpperDb; + value.cber = raw.cber; value.frequencyOffsetHz = raw.frequencyOffsetHz; + value.observationSequence = raw.observationSequence; + value.syncLossCount = raw.syncLossCount; value.reacquisitionCount = raw.reacquisitionCount; + value.syncDurationMs = raw.syncDurationMs; + value = normalizedHdFmReception(std::move(value)); + // Malformed measurements cannot consume an identity or retire accepted PCM. + // A deliberate current-lifetime invalid publication is a withdrawal barrier. + if (raw.valid && !value.valid) { return; } + const std::array counters{raw.iqDrops, raw.pcmDrops, raw.playoutUnderruns}; + auto& priorCounters = m_hdPriorCounters[id]; + if (newReceiver) { priorCounters.fill(0); } + for (std::size_t i = 0; i < counters.size(); ++i) { + const quint64 current = std::max(priorCounters[i], counters[i]); + const quint64 increment = current - priorCounters[i]; + m_hdDiagnosticTotals[i] += std::min(increment, + std::numeric_limits::max() - m_hdDiagnosticTotals[i]); + priorCounters[i] = current; + } + m_hdDiagnosticsObserved = true; + if (newAudio || !raw.valid) { + m_sliceAudio[id] = {}; m_speakerAudio = {}; + m_hdAudioAge[id].invalidate(); + } + m_hdAudioProducedMs[id] = raw.valid ? producedAudioMs : 0; + const bool transition = newAudio || value.valid != m_hdReception[id].valid + || value.synced != m_hdReception[id].synced || value.audioValid != m_hdReception[id].audioValid + || value.services != m_hdReception[id].services || value.stationName != m_hdReception[id].stationName + || value.title != m_hdReception[id].title || value.artist != m_hdReception[id].artist; + if (!value.audioValid && m_hdReception[id].audioValid) { + m_sliceAudio[id] = {}; m_speakerAudio = {}; + } + previous = {observation.instance, observation.receiverEpoch, observation.audioEpoch, + observation.captureEpoch, raw.publicationSequence, + newReceiver ? raw.observationSequence : std::max(previous.observationSequence, raw.observationSequence), + newAudio ? raw.audioSequence : std::max(previous.audioSequence, raw.audioSequence), !raw.valid}; + m_hdObservationProducedMs[id] = raw.observationMonotonicMs; + m_hdObservationAge[id].restart(); + m_hdReception[id] = std::move(value); + if (!transition && m_hdPublicationAge[id].isValid() && m_hdPublicationAge[id].elapsed() < 250) { return; } + m_hdPublicationAge[id].restart(); + SliceDelta delta; delta.hdFmReception = m_hdReception[id]; + emit sliceChanged(id, delta); +} + +void RtlSdrBackend::expireHdObservations() +{ + for (int id = 0; id < 8; ++id) { + const auto now = hdMonotonicMs(); + const bool stale = !m_hdIdentity[id].expired && m_hdObservationProducedMs[id] + && (m_hdObservationProducedMs[id] > now || now - m_hdObservationProducedMs[id] > 500); + const bool staleAudio = m_hdReception[id].audioValid + && (!m_hdAudioProducedMs[id] || m_hdAudioProducedMs[id] > now + || now - m_hdAudioProducedMs[id] > 500); + if (!stale && !staleAudio) { continue; } + m_sliceAudio[id] = {}; m_speakerAudio = {}; + m_hdAudioAge[id].invalidate(); + if (stale) { + m_hdReception[id] = {}; m_hdObservationAge[id].invalidate(); + m_hdAudioProducedMs[id] = 0; + // Keep epoch/order high-water marks while withdrawing publication. + m_hdIdentity[id].expired = true; + } else { m_hdReception[id].audioValid = false; } + SliceDelta delta; delta.hdFmReception = m_hdReception[id]; + emit sliceChanged(id, delta); + } +} + // Decoder observations travel with accepted PCM. A requested mode or a stale // packet can never light the stereo indicator. Silence or a stopped producer // expires the observation; there is no indefinitely retained pilot claim. @@ -1850,6 +2119,10 @@ void RtlSdrBackend::emitSliceState(const RtlCaptureTransaction::Receiver& receiv != m_lastPublished->receivingIds.end(); delta.wfmDeemphasisUs = receiver.wfmDeemphasisUs; delta.wfmForceMono = receiver.wfmForceMono; + delta.wfmAudioMode = receiver.wfmHdStereo ? WfmAudioMode::HdStereo + : (receiver.wfmForceMono ? WfmAudioMode::Mono : WfmAudioMode::Stereo); + delta.hdProgram = receiver.hdProgram; + delta.hdFmReception = m_hdReception[id]; delta.wfmReceptionDiagnostics = m_wfmReception[id]; delta.wfmStereoStatus = m_wfmStatus[id]; delta.modeList = capabilities().receiveModeControl->modes; @@ -1886,7 +2159,9 @@ void RtlSdrBackend::publishCapture() const auto current = std::ranges::find_if(state.receivers, [id](const auto& value) { return value.passband.stableId == id; }); if (current == state.receivers.end() || current->passband != previous.passband || current->mode != previous.mode || current->wfmDeemphasisUs != previous.wfmDeemphasisUs - || current->wfmForceMono != previous.wfmForceMono) { m_sliceAudio[id] = {}; } + || current->wfmForceMono != previous.wfmForceMono + || current->wfmHdStereo != previous.wfmHdStereo + || current->hdProgram != previous.hdProgram) { m_sliceAudio[id] = {}; } } if (std::ranges::any_of(state.receivers, [&state](const auto& value) { return std::ranges::find(state.receivingIds, value.passband.stableId) @@ -1940,6 +2215,10 @@ void RtlSdrBackend::publishCapture() // never carry a pilot claim across that publication boundary. m_wfmStatus[id] = active ? WfmStereoStatus::Acquiring : WfmStereoStatus::Unavailable; m_wfmReception[id] = {}; + m_hdReception[id] = {}; + m_hdObservationAge[id].invalidate(); m_hdAudioAge[id].invalidate(); + m_hdObservationProducedMs[id] = 0; m_hdAudioProducedMs[id] = 0; + m_hdPublicationAge[id].invalidate(); // Retain the last observed decoder identity across control-only // revisions: cached credits must not refresh a stalled decoder. // A new prepared graph differs by instance/epoch even if its local diff --git a/src/core/backends/rtl/RtlSdrBackend.h b/src/core/backends/rtl/RtlSdrBackend.h index a653e946f..7ff8f6f48 100644 --- a/src/core/backends/rtl/RtlSdrBackend.h +++ b/src/core/backends/rtl/RtlSdrBackend.h @@ -84,6 +84,8 @@ class RtlSdrBackend : public IRadioBackend { void setSliceAutoSquelch(int sliceId, bool enabled, int marginDb) override; void setSliceWfmDeemphasis(int sliceId, int microseconds) override; void setSliceWfmForceMono(int sliceId, bool forceMono) override; + void setSliceWfmAudioMode(int sliceId, WfmAudioMode mode) override; + void setSliceHdProgram(int sliceId, int program) override; void setKeying(bool key, const AetherSDR::TxCoordinator::Operation& operation, const AetherSDR::TxCoordinator::Completion& completion = {}) override; void invokeExtension(const QString& ns, const QString& verb, quint64 requestId, const QVariant& arg = {}) override; @@ -109,6 +111,8 @@ class RtlSdrBackend : public IRadioBackend { static constexpr int kQualifiedReceiverCapacity = 1; private: + ReceiveDispatch requestWfmAudioMode(int sliceId, WfmAudioMode mode); + ReceiveDispatch requestHdProgram(int sliceId, int program); bool hasAcceptedSlice(int sliceId) const; friend struct RtlSdrBackendTestAccess; // Relay the current worker's token-qualified outputs to the seam once. @@ -130,6 +134,9 @@ class RtlSdrBackend : public IRadioBackend { void drainAudio(); void observeWfm(const RtlReceivePipeline::Packet& packet); void expireWfmObservations(); + void drainHdObservations(); + void observeHd(const RtlReceivePipeline::HdFmObservation& observation); + void expireHdObservations(); void publishLegacyPcm(const QByteArray& pcm, const QByteArray& preMonitor); void retireNativeAudio(); void emitSliceState(const RtlCaptureTransaction::Receiver& receiver); @@ -208,6 +215,8 @@ class RtlSdrBackend : public IRadioBackend { quint64 captureEpoch = 0; quint64 instance = 0; quint64 receiverEpoch = 0; + quint64 audioEpoch = 0; + int sampleRateHz = 48000; quint64 nextSample = 0; }; NativeAudio m_speakerAudio; @@ -238,6 +247,18 @@ class RtlSdrBackend : public IRadioBackend { std::array m_wfmObservationAge; std::array m_wfmPublicationAge; std::array m_wfmReception; + std::array m_hdReception; + struct HdObservationIdentity { + quint64 instance = 0, receiverEpoch = 0, audioEpoch = 0, captureEpoch = 0; + quint64 publicationSequence = 0, observationSequence = 0, audioSequence = 0; + bool expired = false; + }; + std::array m_hdIdentity; + bool m_hdDiagnosticsObserved = false; + std::array m_hdDiagnosticTotals{}; + std::array, 8> m_hdPriorCounters{}; + std::array m_hdObservationAge, m_hdAudioAge, m_hdPublicationAge; + std::array m_hdObservationProducedMs{}, m_hdAudioProducedMs{}; struct WfmObservationIdentity { std::uint64_t instance = 0; std::uint64_t epoch = 0; diff --git a/src/core/backends/rtl/RtlSdrWorker.h b/src/core/backends/rtl/RtlSdrWorker.h index d9d39ae64..08eaca8ea 100644 --- a/src/core/backends/rtl/RtlSdrWorker.h +++ b/src/core/backends/rtl/RtlSdrWorker.h @@ -51,6 +51,7 @@ class RtlSdrWorker : public QThread { bool rfObservationIsCurrent(const RtlReceivePipeline::RfObservation& value) const { return m_pipeline->rfObservationIsCurrent(value); } void setReceiveMetersEnabled(bool enabled) { m_pipeline->setReceiveMetersEnabled(enabled); } bool takeAudio(RtlReceivePipeline::Packet& packet) { return m_pipeline->takePacket(packet); } + bool takeHdObservation(RtlReceivePipeline::HdFmObservation& value) { return m_pipeline->takeHdObservation(value); } // Independently sampled counters since construction. Timing covers each // valid callback's adoption and processing, not USB transport latency. // The final bucket is unbounded; finite percentile bounds are conservative. diff --git a/src/gui/DxClusterDialog.cpp b/src/gui/DxClusterDialog.cpp index 1acc4d70e..6054717f6 100644 --- a/src/gui/DxClusterDialog.cpp +++ b/src/gui/DxClusterDialog.cpp @@ -47,6 +47,7 @@ #include #include #include "core/ThemeManager.h" +#include "WfmPresentationSettings.h" namespace AetherSDR { @@ -2763,6 +2764,17 @@ void DxClusterDialog::buildDisplayTab(QTabWidget* tabs) }); toggleRow->addWidget(memoriesToggle); + auto* wfmRdsToggle = makeToggle("WFM RDS", + WfmPresentationSettings::instance().broadcastOverlayEnabled(), + "Show current broadcast station and now-playing labels on the panadapter. " + "Digital metadata is labeled Digital. Does not stop decoding or applet text."); + wfmRdsToggle->setObjectName(QStringLiteral("wfmRdsToggle")); + wfmRdsToggle->setAccessibleName(QStringLiteral("WFM RDS")); + wfmRdsToggle->setAccessibleDescription(wfmRdsToggle->toolTip()); + connect(wfmRdsToggle, &QPushButton::toggled, + &WfmPresentationSettings::instance(), &WfmPresentationSettings::setBroadcastOverlayEnabled); + toggleRow->addWidget(wfmRdsToggle); + auto* kiwiDxToggle = makeToggle("Kiwi DX", kiwiDxSpots, "Overlay KiwiSDR Community DX database spots (beacons, utilities, time signals) on the band plan strip."); connect(kiwiDxToggle, &QPushButton::toggled, this, [save](bool on) { diff --git a/src/gui/MainWindow_Wiring.cpp b/src/gui/MainWindow_Wiring.cpp index 4f83e8c8f..4ec814143 100644 --- a/src/gui/MainWindow_Wiring.cpp +++ b/src/gui/MainWindow_Wiring.cpp @@ -5,6 +5,8 @@ #include "MainWindow.h" #include "StatusIndicator.h" + +#include "WfmBroadcastOverlay.h" #include "models/CwDecodeSettings.h" #include "PeripheralAuthStore.h" #include "core/backends/AutoRfGainControl.h" @@ -3718,6 +3720,16 @@ void MainWindow::wirePanadapter(PanadapterApplet* applet) auto* sw = applet->spectrumWidget(); auto* menu = sw->overlayMenu(); const quint64 rangeWiringEpoch = sw->advanceDbmRangeWireEpoch(); + + auto* broadcast = sw->findChild(QStringLiteral("wfmBroadcastOverlay"), + Qt::FindDirectChildrenOnly); + if (!broadcast) { + broadcast = new WfmBroadcastOverlay(sw); + broadcast->setObjectName(QStringLiteral("wfmBroadcastOverlay")); + connect(broadcast, &WfmBroadcastOverlay::overlaysChanged, + sw, &SpectrumWidget::setBroadcastOverlays); + } + broadcast->bind(&m_radioModel, applet->panId()); if (!sw->capturePlacementAction()) { sw->setCapturePlacementAction(new ReceiveCaptureAction(m_radioModel, [applet] { return applet->panId(); }, sw)); diff --git a/src/gui/SpectrumWidget.cpp b/src/gui/SpectrumWidget.cpp index 032fb6503..b5132d50c 100644 --- a/src/gui/SpectrumWidget.cpp +++ b/src/gui/SpectrumWidget.cpp @@ -1958,6 +1958,7 @@ SpectrumWidget::SpectrumWidget(QWidget* parent) theme::setContainer(this, QStringLiteral("spectrum")); m_panStats.clock.start(); // panstats rates are meaningless without an epoch + m_broadcastClock.start(); setMinimumHeight(100); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -8424,6 +8425,9 @@ void SpectrumWidget::updateAccessibleStateDescription() "VFO-locked").arg(aName, bName) : tr("Slice Link %1 to %2 active").arg(aName, bName)); } + for (const WfmBroadcastOverlayRecord& record : m_broadcastOverlays) { + parts << tr("Broadcast metadata for Slice %1: %2").arg(record.sliceId).arg(record.displayText()); + } setAccessibleDescription(parts.join(QStringLiteral("; "))); } @@ -11816,6 +11820,11 @@ void SpectrumWidget::setBackgroundFillColor(const QColor& c) bool SpectrumWidget::event(QEvent* ev) { + if (ev->type() == QEvent::Hide) { + for (WfmBroadcastTicker& ticker : m_broadcastTickers) { + ticker.advance(m_broadcastClock.elapsed(), false); + } + } // Re-assert mouse tracking after native window changes (reparenting into // QSplitter, window recreation). Without this, QRhiWidget's native Metal // surface loses mouse tracking and mouseMoveEvent stops firing. @@ -13870,6 +13879,7 @@ void SpectrumWidget::renderGpuFrame(QRhiCommandBuffer* cb, const int wfH = h - wfY; const QRect specRect(0, 0, w, specH); const QRect wfRect(0, wfY, w, wfH); + updateBroadcastOverlayTicker(specRect, !resizePreview); // The FFT trace, 3DSS surface, and waterfall render into the frequency // canvas only (width minus the right dBm / time strip) so they end at the // tape, matching the contentWidth() mapping used by every marker (#3482). @@ -14437,6 +14447,7 @@ void SpectrumWidget::renderGpuFrame(QRhiCommandBuffer* cb, } drawSwrSweep(frequencyPainter, specRect); drawSliceMarkers(frequencyPainter, specRect, wfRect); + drawBroadcastOverlays(frequencyPainter, specRect); drawOffScreenSlices(frequencyPainter, specRect); m_overlayStatic.fill(Qt::transparent); @@ -15547,6 +15558,7 @@ void SpectrumWidget::paintEvent(QPaintEvent* ev) const int wfY = scaleY + freqScaleH(); const QRect specRect (0, 0, width(), specH); + updateBroadcastOverlayTicker(specRect); const QRect divRect (0, divY, width(), DIVIDER_H); const QRect scaleRect(0, scaleY, width(), freqScaleH()); const QRect wfRect (0, wfY, width(), wfH); @@ -15625,6 +15637,7 @@ void SpectrumWidget::paintEvent(QPaintEvent* ev) if (m_showSpots || m_showSHistory) drawSpotMarkers(p, specRect); drawSwrSweep(p, specRect); drawSliceMarkers(p, specRect, wfRect); + drawBroadcastOverlays(p, specRect); drawSmartMtrValueLabels(p); drawOffScreenSlices(p, specRect); @@ -16498,6 +16511,117 @@ static QString spotMarkerTooltip(const SpectrumWidget::SpotMarker& sm) return tip; } +void SpectrumWidget::setBroadcastOverlays(const QVector& records) +{ + const QVector bounded = records.mid(0, 8); + if (bounded == m_broadcastOverlays) { return; } + QVector next; + next.reserve(bounded.size()); + for (const WfmBroadcastOverlayRecord& record : bounded) { + WfmBroadcastTicker ticker; + for (qsizetype i = 0; i < m_broadcastOverlays.size(); ++i) { + if (m_broadcastOverlays.at(i).sliceId == record.sliceId) { + ticker = m_broadcastTickers.at(i); + break; + } + } + ticker.setContent(record); + next.append(std::move(ticker)); + } + m_broadcastOverlays = bounded; + m_broadcastTickers = std::move(next); + const QString previousDescription = accessibleDescription(); + updateAccessibleStateDescription(); + if (accessibleDescription() != previousDescription) { + QAccessibleEvent accessibleEvent(this, QAccessible::DescriptionChanged); + QAccessible::updateAccessibility(&accessibleEvent); + } + markOverlayDirty(); + update(); +} + +int SpectrumWidget::broadcastOverlayStartY(const QRect& specRect) const +{ + // Match ordinary spots' configured position, while clearing expanded flags. + // The full flag geometry includes whichever control panel is currently open. + int top = specRect.top() + specRect.height() * m_spotStartPct / 100; + for (const VfoWidget* flag : m_vfoWidgets) { + if (flag && flag->isVisible()) { + top = std::max(top, flag->geometry().bottom() + 8); + } + } + return top; +} + +void SpectrumWidget::updateBroadcastOverlayTicker(const QRect& specRect, bool presentPages) +{ + if (m_broadcastTickers.isEmpty()) { return; } + QFont labelFont = font(); + labelFont.setPixelSize(12); + const int textWidth = std::min(420, specRect.width() - 12) - 12; + const int rowHeight = QFontMetrics(labelFont).height() + 6; + const qint64 nowMs = m_broadcastClock.elapsed(); + const int startY = broadcastOverlayStartY(specRect); + bool changed = m_broadcastStartY != startY; + m_broadcastStartY = startY; + int row = 0; + for (qsizetype i = 0; i < m_broadcastTickers.size(); ++i) { + WfmBroadcastTicker& ticker = m_broadcastTickers[i]; + changed = ticker.layout(labelFont, textWidth) || changed; + const int x = mhzToX(m_broadcastOverlays.at(i).frequencyHz / 1.0e6); + const bool inView = x >= specRect.left() && x <= specRect.right(); + const int y = startY + row * (rowHeight + 2); + const bool visible = presentPages && isVisible() && !m_frequencyPreviewActive + && specRect.width() >= 40 && inView && y + rowHeight <= specRect.bottom(); + changed = ticker.advance(nowMs, visible) || changed; + if (inView) { ++row; } + } + // The existing receive/display cadence paints this frame. Page/placement + // changes invalidate the cached overlay; no timer, spot update, or radio write. + if (changed) { markOverlayDirty("broadcastPage", false); } +} + +void SpectrumWidget::drawBroadcastOverlays(QPainter& p, const QRect& specRect) +{ + if (m_broadcastOverlays.isEmpty() || specRect.width() < 40) { return; } + p.save(); + p.setClipRect(specRect, Qt::IntersectClip); + QFont labelFont = font(); + labelFont.setPixelSize(12); + p.setFont(labelFont); + const QFontMetrics metrics(labelFont); + const int rowHeight = metrics.height() + 6; + const int maxWidth = std::min(420, specRect.width() - 12); + ThemeManager& theme = ThemeManager::instance(); + const int startY = broadcastOverlayStartY(specRect); + int row = 0; + for (qsizetype i = 0; i < m_broadcastOverlays.size(); ++i) { + const WfmBroadcastOverlayRecord& record = m_broadcastOverlays.at(i); + const WfmBroadcastTicker& ticker = m_broadcastTickers.at(i); + const int x = mhzToX(record.frequencyHz / 1.0e6); + if (x < specRect.left() || x > specRect.right()) { continue; } + // Below slice flags, in a bounded independent row. Never enters spot + // clustering, hit testing, trigger/remove commands or smart filters. + const int y = startY + row * (rowHeight + 2); + if (y + rowHeight > specRect.bottom()) { break; } + const QString label = ticker.pageText(); + // Size from the whole label so advancing a page never moves its RF anchor. + const int width = std::min(maxWidth, ticker.labelWidth() + 12); + const int left = std::clamp(x - width / 2, specRect.left() + 6, specRect.right() - width - 5); + const QRect box(left, y, width, rowHeight); + p.fillRect(box, theme.brush(this, "color.background.0", box)); + // Slice markers leave their fill brush selected. Draw only the border + // here so that brush cannot repaint the themed label background. + p.setBrush(Qt::NoBrush); + p.setPen(theme.color(this, "color.border.subtle")); + p.drawRect(box.adjusted(0, 0, -1, -1)); + p.setPen(theme.color(this, "color.text.primary")); + p.drawText(box.adjusted(6, 0, -6, 0), Qt::AlignVCenter | Qt::AlignLeft, label); + ++row; + } + p.restore(); +} + void SpectrumWidget::drawSpotMarkers(QPainter& p, const QRect& specRect) { // Merge DX spots, Signal History markers, and QRM History markers. diff --git a/src/gui/SpectrumWidget.h b/src/gui/SpectrumWidget.h index 3fd84ff65..2e53bd063 100644 --- a/src/gui/SpectrumWidget.h +++ b/src/gui/SpectrumWidget.h @@ -4,6 +4,8 @@ #include "DbmRangeTransition.h" #include "core/backends/SquelchLevelScale.h" #include "RfGainPresentation.h" +#include "WfmBroadcastOverlay.h" +#include "WfmBroadcastTicker.h" #include #include @@ -895,6 +897,8 @@ class SpectrumWidget : public SPECTRUM_BASE_CLASS { QString backgroundColor; }; void setSpotMarkers(const QVector& markers); + // Passive local broadcast presentation, independent of ordinary spots. + void setBroadcastOverlays(const QVector& records); struct SpotCluster { QRect rect; @@ -1118,6 +1122,9 @@ class SpectrumWidget : public SPECTRUM_BASE_CLASS { void drawTnfMarkers(QPainter& p, const QRect& specRect, const QRect& wfRect = QRect()); void drawSpotMarkers(QPainter& p, const QRect& specRect); + int broadcastOverlayStartY(const QRect& specRect) const; + void drawBroadcastOverlays(QPainter& p, const QRect& specRect); + void updateBroadcastOverlayTicker(const QRect& specRect, bool presentPages = true); void drawSwrSweep(QPainter& p, const QRect& specRect); void drawAutoSqlFloor(QPainter& p, const QRect& specRect); void drawSquelchLine(QPainter& p, const QRect& specRect); @@ -2168,6 +2175,10 @@ class SpectrumWidget : public SPECTRUM_BASE_CLASS { int m_notchMaxWidthHz{12000}; bool m_tnfGlobalEnabled{true}; QVector m_spotMarkers; + QVector m_broadcastOverlays; + QVector m_broadcastTickers; + int m_broadcastStartY{-1}; + QElapsedTimer m_broadcastClock; QVector m_swrSweepPoints; bool m_swrSweepRunning{false}; double m_swrSweepCurrentFreqMhz{-1.0}; diff --git a/src/gui/WfmApplet.cpp b/src/gui/WfmApplet.cpp index ff3f3499c..1121396c1 100644 --- a/src/gui/WfmApplet.cpp +++ b/src/gui/WfmApplet.cpp @@ -4,7 +4,7 @@ #include "GuardedSlider.h" #include "ModeFilterPresets.h" #include "WfmLockScope.h" -#include "core/AppSettings.h" +#include "WfmPresentationSettings.h" #include "core/ThemeManager.h" #include "models/RadioModel.h" #include "models/PanadapterModel.h" @@ -17,26 +17,80 @@ #include #include #include -#include -#include #include #include +#include +#include +#include #include #include +#include +#include +#include +#include #include +#include +#include #include #include namespace AetherSDR { +// QLabel retains the full plain-text value for accessibility while painting +// a bounded horizontal marquee. Hidden or short content does no timer work. +class WfmMetadataTicker final : public QLabel { +public: + explicit WfmMetadataTicker(QWidget* parent) : QLabel(parent) + { + setTextFormat(Qt::PlainText); + setMinimumHeight(fontMetrics().height() + 8); + setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + m_timer.setInterval(40); + connect(&ThemeManager::instance(), &ThemeManager::themeChanged, + this, qOverload<>(&QLabel::update)); + connect(&m_timer, &QTimer::timeout, this, [this] { + m_offset = (m_offset + 1) % std::max(1, fontMetrics().horizontalAdvance(text()) + 32); + update(); + }); + } + void setContent(const QString& content) + { + if (text() == content) { return; } + setText(content); + setAccessibleName(tr("Broadcast metadata: %1").arg(content)); + m_offset = 0; + refreshTimer(); + QAccessibleEvent event(this, QAccessible::NameChanged); + QAccessible::updateAccessibility(&event); + } +protected: + void paintEvent(QPaintEvent*) override + { + QPainter painter(this); + painter.setClipRect(rect()); + painter.setPen(ThemeManager::instance().color(this, "color.text.primary")); + const int span = fontMetrics().horizontalAdvance(text()) + 32; + painter.drawText(QRect(-m_offset, 0, std::max(width(), span), height()), Qt::AlignVCenter, text()); + if (m_offset > 0) { + painter.drawText(QRect(span - m_offset, 0, span, height()), Qt::AlignVCenter, text()); + } + } + void hideEvent(QHideEvent* event) override { m_timer.stop(); m_offset = 0; QLabel::hideEvent(event); } + void showEvent(QShowEvent* event) override { QLabel::showEvent(event); refreshTimer(); } + void resizeEvent(QResizeEvent* event) override { QLabel::resizeEvent(event); refreshTimer(); } +private: + void refreshTimer() + { + if (isVisible() && fontMetrics().horizontalAdvance(text()) > width()) { m_timer.start(); } + else { m_timer.stop(); m_offset = 0; } + update(); + } + QTimer m_timer; + int m_offset{0}; +}; namespace { -QJsonObject preferences() -{ - return QJsonDocument::fromJson(AppSettings::instance() - .value(QStringLiteral("WfmApplet"), QString{}).toString().toUtf8()).object(); -} -// A popup may outlive its receiver binding. Preserve the binding witness -// after hidePopup(): Qt can deliver activated later. +// A popup may outlive its receiver binding or digital reception/service identity. +// Preserve both witnesses after hidePopup(): Qt can deliver activated later. class WfmReceiverComboBox final : public GuardedComboBox { public: using GuardedComboBox::GuardedComboBox; @@ -50,12 +104,20 @@ class WfmReceiverComboBox final : public GuardedComboBox { ++m_generation; hidePopup(); } + void setReception(const HdFmReception& value) + { + const Context context{value.sessionId, value.receiverEpoch, value.revision, + value.frequencyHz, value.selectedProgram}; + if (context == m_context) { return; } + m_context = context; + invalidateInteraction(); + } quint64 takeActivationBinding() { const Origin origin = m_origin.value_or(Origin{m_binding, m_generation}); m_origin.reset(); // Bound controls use nonzero slice generations. Zero cannot authorize - // an activation whose receiver binding was retired. + // an activation whose digital reception or service membership was retired. return origin.generation == m_generation ? origin.binding : 0; } void showPopup() override @@ -79,6 +141,8 @@ class WfmReceiverComboBox final : public GuardedComboBox { quint64 binding; quint64 generation; }; + using Context = std::tuple; + Context m_context{0, 0, 0, 0, -1}; quint64 m_binding{0}; quint64 m_generation{0}; std::optional m_origin; @@ -175,8 +239,26 @@ WfmApplet::WfmApplet(QWidget* parent) : QWidget(parent) settings->addWidget(bandwidthLabel, 1, 0); settings->addWidget(m_bandwidth, 1, 1); settings->setColumnStretch(1, 1); + m_hdProgram = new WfmReceiverComboBox(this); + m_hdProgram->setObjectName(QStringLiteral("wfmHdProgram")); + m_hdProgram->setAccessibleName(tr("Digital program")); + m_hdProgram->setFocusPolicy(Qt::StrongFocus); + m_hdProgram->setPlaceholderText(tr("Unavailable")); + applyComboStyle(m_hdProgram); + auto* programLabel = new QLabel(tr("Digital program"), this); + programLabel->setBuddy(m_hdProgram); + ThemeManager::instance().applyStyleSheet(programLabel, + "QLabel { color: {{color.text.secondary}}; font-size: 11px; }"); + settings->addWidget(programLabel, 2, 0); + settings->addWidget(m_hdProgram, 2, 1); root->addLayout(settings); + m_metadata = new WfmMetadataTicker(this); + m_metadata->setObjectName(QStringLiteral("wfmBroadcastMetadata")); + m_metadata->setAccessibleDescription(tr("Current Digital station and now-playing text. " + "Independent of the SpotHub WFM RDS overlay toggle. No analog RDS decoder is implied.")); + root->addWidget(m_metadata); + m_settingsToggle = new QPushButton(tr("▸ Settings"), this); m_settingsToggle->setObjectName(QStringLiteral("wfmSettingsToggle")); m_settingsToggle->setCheckable(true); @@ -195,9 +277,8 @@ WfmApplet::WfmApplet(QWidget* parent) : QWidget(parent) m_showDiagnostics = new QCheckBox(tr("Show Diagnostics"), m_settingsDrawer); m_showDiagnostics->setObjectName(QStringLiteral("wfmShowDiagnostics")); m_showDiagnostics->setAccessibleName(tr("Show Broadcast FM diagnostics")); - const QJsonObject ui = preferences().value(QStringLiteral("ui")).toObject(); - m_showScope->setChecked(ui.value(QStringLiteral("showLockScope")).toBool(true)); - m_showDiagnostics->setChecked(ui.value(QStringLiteral("showDiagnostics")).toBool(false)); + m_showScope->setChecked(WfmPresentationSettings::instance().showLockScope()); + m_showDiagnostics->setChecked(WfmPresentationSettings::instance().showDiagnostics()); drawer->addWidget(m_showScope); drawer->addWidget(m_showDiagnostics); root->addWidget(m_settingsDrawer); @@ -216,7 +297,28 @@ WfmApplet::WfmApplet(QWidget* parent) : QWidget(parent) }); connect(m_audioMode, &QPushButton::clicked, this, [this] { if (acceptsControl(m_audioMode, m_audioModeBinding)) { - m_slice->setWfmForceMono(!m_slice->wfmForceMono()); + if (m_caps.broadcastFmReceive->hdStereo) { + const WfmAudioMode current = m_slice->wfmAudioMode(); + m_slice->setWfmAudioMode(current == WfmAudioMode::Mono ? WfmAudioMode::Stereo + : current == WfmAudioMode::Stereo ? WfmAudioMode::HdStereo : WfmAudioMode::Mono); + } else { + m_slice->setWfmForceMono(!m_slice->wfmForceMono()); + } + } + refresh(); + }); + connect(m_hdProgram, &QComboBox::activated, this, [this](int index) { + const quint64 binding = static_cast(m_hdProgram)->takeActivationBinding(); + bool validProgram = false; + const int program = m_hdProgram->itemData(index).toInt(&validProgram); + if (acceptsControl(m_hdProgram, binding) && index >= 0 && index < m_hdProgram->count() + && validProgram && hasCurrentHdReception() + && program != m_slice->hdProgram()) { + const QVector& services = m_slice->hdFmReception().services; + const bool available = std::ranges::any_of(services, [program](const HdFmService& service) { + return service.program == program && service.audioAvailable; + }); + if (available) { m_slice->setHdProgram(program); } } refresh(); // The displayed selection remains backend-confirmed. }); @@ -271,6 +373,7 @@ bool WfmApplet::acceptsControl(const QWidget* control, quint64 binding) const void WfmApplet::setRadioModel(RadioModel* model) { const quint64 binding = ++m_modelBinding; + static_cast(m_hdProgram)->invalidateInteraction(); for (const auto& connection : m_modelConnections) { disconnect(connection); } m_modelConnections.clear(); m_availability.reset(); @@ -319,13 +422,15 @@ void WfmApplet::setRadioModel(RadioModel* model) void WfmApplet::setSlice(SliceModel* slice) { const quint64 binding = ++m_sliceBinding; - for (QComboBox* combo : {m_deemphasis, m_bandwidth}) { + for (QComboBox* combo : {m_deemphasis, m_bandwidth, m_hdProgram}) { static_cast(combo)->setBinding(binding); } m_audioMode->setDown(false); for (const auto& connection : m_sliceConnections) { disconnect(connection); } m_sliceConnections.clear(); m_scope->clear(); + m_hdSession = m_hdEpoch = m_hdRevision = m_hdSequence = 0; + m_hdSelectedProgram = -1; m_slice = slice; if (slice) { const auto current = [this, binding] { @@ -339,6 +444,23 @@ void WfmApplet::setSlice(SliceModel* slice) m_sliceConnections.append(connect(slice, &SliceModel::frequencyReported, this, update)); m_sliceConnections.append(connect(slice, &SliceModel::wfmDeemphasisChanged, this, update)); m_sliceConnections.append(connect(slice, &SliceModel::wfmForceMonoChanged, this, update)); + m_sliceConnections.append(connect(slice, &SliceModel::wfmAudioModeChanged, this, [this, current] { + if (!current()) { return; } + m_hdSequence = 0; + m_scope->clear(); + refresh(); + })); + m_sliceConnections.append(connect(slice, &SliceModel::hdProgramChanged, this, [this, current] { + if (!current()) { return; } + m_hdSequence = 0; + m_scope->clear(); + refresh(); + })); + m_sliceConnections.append(connect(slice, &SliceModel::hdFmReceptionChanged, this, [this, current] { + if (!current()) { return; } + refresh(); + appendHdScopeSample(); + })); m_sliceConnections.append(connect(slice, &SliceModel::wfmStereoStatusChanged, this, update)); m_sliceConnections.append(connect(slice, &SliceModel::wfmReceptionDiagnosticsChanged, this, [this, current] { if (!current()) { return; } @@ -348,7 +470,7 @@ void WfmApplet::setSlice(SliceModel* slice) m_sliceConnections.append(connect(slice, &SliceModel::frequencyChanged, this, [this, current] { if (!current()) { return; } m_scope->clear(); - refreshDiagnostics(); + refresh(); })); m_sliceConnections.append(connect(slice, &SliceModel::inCaptureChanged, this, update)); m_sliceConnections.append(connect(slice, &SliceModel::externalReceiveReplacementChanged, this, update)); @@ -370,7 +492,17 @@ void WfmApplet::registerControls() m_availability->registerWidget(m_audioMode, tr("Mono selection is unavailable for this receiver or replacement audio source"), [supported](bool connected, const RadioCapabilities& caps) { - return supported(connected, caps) && caps.broadcastFmReceive->forceMonoControl; + return supported(connected, caps) && (caps.broadcastFmReceive->forceMonoControl || caps.broadcastFmReceive->hdStereo); + }, [] { return true; }, false); + m_availability->registerWidget(m_hdProgram, + tr("Select Digital on a supported receiver to choose a discovered audio program"), + [this, supported](bool connected, const RadioCapabilities& caps) { + if (!supported(connected, caps) || !caps.broadcastFmReceive->hdStereo + || m_slice->wfmAudioMode() != WfmAudioMode::HdStereo || !hasCurrentHdReception()) { return false; } + for (const HdFmService& service : m_slice->hdFmReception().services) { + if (service.audioAvailable) { return true; } + } + return false; }, [] { return true; }, false); m_availability->registerWidget(m_deemphasis, tr("De-emphasis is unavailable for this receiver or replacement audio source"), @@ -386,7 +518,7 @@ void WfmApplet::registerControls() for (QCheckBox* control : {m_showScope, m_showDiagnostics}) { m_availability->registerWidget(control, tr("This receiver does not provide pilot measurements"), [this](bool connected, const RadioCapabilities& caps) { - return connected && caps.broadcastFmReceive && caps.broadcastFmReceive->receptionDiagnostics + return connected && caps.broadcastFmReceive && (caps.broadcastFmReceive->receptionDiagnostics || caps.broadcastFmReceive->hdStereo) && ownsWfmSlice(); }, [] { return true; }, false); } @@ -409,14 +541,17 @@ void WfmApplet::refresh() m_availability->refreshEngaged(); } else { for (QWidget* control : {static_cast(m_audioMode), static_cast(m_deemphasis), - static_cast(m_bandwidth), static_cast(m_showScope), + static_cast(m_bandwidth), static_cast(m_hdProgram), static_cast(m_showScope), static_cast(m_showDiagnostics)}) { control->setEnabled(false); control->setAccessibleDescription(tr("Connect a broadcast FM receiver and select WFM")); } } - const bool forceMono = m_available && m_slice->wfmForceMono(); - const QString modeText = forceMono ? tr("Mono") : tr("Auto Stereo"); + refreshHd(); + const bool forceMono = m_available && m_slice->wfmAudioMode() == WfmAudioMode::Mono; + const bool hdSelected = m_available && m_slice->wfmAudioMode() == WfmAudioMode::HdStereo; + m_scope->setHdMode(hdSelected); + const QString modeText = hdSelected ? tr("Digital") : forceMono ? tr("Mono") : tr("Auto Stereo"); if (m_audioMode->text() != modeText) { m_audioMode->setText(modeText); m_audioMode->setAccessibleName(tr("Broadcast FM audio mode: %1").arg(modeText)); @@ -424,7 +559,9 @@ void WfmApplet::refresh() QAccessible::updateAccessibility(&event); } if (m_audioMode->isEnabled()) { - m_audioMode->setAccessibleDescription(tr("Cycle between forced Mono and Auto Stereo. Auto Stereo falls back to mono when no stereo pilot is detected.")); + m_audioMode->setAccessibleDescription(m_caps.broadcastFmReceive->hdStereo + ? tr("Cycle Mono, Auto Stereo and Digital. Digital program selection uses discovered audio services.") + : tr("Cycle between forced Mono and Auto Stereo. Auto Stereo falls back to mono when no stereo pilot is detected.")); m_audioMode->setToolTip(m_audioMode->accessibleDescription()); } const QString oldDeemphasis = m_deemphasis->currentText(); @@ -469,9 +606,41 @@ void WfmApplet::refreshDiagnostics() { const bool receiving = m_available && m_slice->inCapture() && !m_slice->externalReceiveReplacementActive(); + if (m_available && m_slice->wfmAudioMode() == WfmAudioMode::HdStereo) { + const bool current = receiving && hasCurrentHdReception(); + const HdFmReception value = current ? m_slice->hdFmReception() : HdFmReception{}; + const bool unstable = value.synced && value.syncLossCount > 0 && value.syncDurationMs < 5000; + QString status = !receiving ? tr("Unavailable") + : !value.valid || !value.synced ? tr("Digital acquiring") + : value.audioValid ? tr("Digital audio valid") : tr("Digital synced · awaiting audio"); + if (unstable) { status += tr(" · Unstable"); } + setReadout(m_status, status, tr("Digital observed reception: %1").arg(status)); + m_status->setAccessibleDescription(tr("Actual digital sync and valid selected-program audio, independent of the requested mode.")); + ThemeManager::instance().setWidgetForegroundToken(m_status, + !receiving ? QStringLiteral("color.text.secondary") + : value.valid && value.synced && value.audioValid && !unstable ? QStringLiteral("color.accent.success") + : QStringLiteral("color.accent.warning")); + const auto metric = [this](const std::optional& number, const QString& unit) { + return number ? QString::number(*number, 'g', 5) + unit : tr("Unavailable"); + }; + QString details = tr("Source: Digital\nSync: %1 · Audio: %2\nProgram: %3\nMER lower / upper: %4 / %5\nCBER: %6\nFrequency offset: %7\nSync duration: %8 s\nSync losses: %9 · Reacquisitions: %10") + .arg(value.synced ? tr("Yes") : tr("No"), value.audioValid ? tr("Valid") : tr("Unavailable")) + .arg(m_slice->hdProgram() + 1) + .arg(metric(value.merLowerDb, QStringLiteral(" dB")), metric(value.merUpperDb, QStringLiteral(" dB")), + metric(value.cber, {}), metric(value.frequencyOffsetHz, QStringLiteral(" Hz"))) + .arg(value.syncDurationMs / 1000.0, 0, 'f', 1).arg(value.syncLossCount).arg(value.reacquisitionCount); + if (m_slice->frequencyReportedKnown()) { + details += tr("\nFrequency: %1 MHz").arg(m_slice->reportedFrequency(), 0, 'f', 6); + } + setReadout(m_diagnostics, details, tr("Digital diagnostics: %1").arg(details)); + m_diagnostics->setAccessibleDescription(tr("Measured NRSC-5 decoder telemetry. Lower and upper MER use the decoder's spectral convention, inverted relative to RF sidebands. Unavailable metrics are not estimated.")); + m_diagnostics->setToolTip(m_diagnostics->accessibleDescription()); + if (!current) { m_scope->clear(); } + return; + } const WfmReceptionDiagnostics diagnostics = receiving && m_caps.broadcastFmReceive->receptionDiagnostics ? m_slice->wfmReceptionDiagnostics() : WfmReceptionDiagnostics{}; - const bool forceMono = m_available && m_slice->wfmForceMono(); + const bool forceMono = m_available && m_slice->wfmAudioMode() == WfmAudioMode::Mono; const WfmStereoStatus status = receiving ? m_slice->wfmStereoStatus() : WfmStereoStatus::Unavailable; QString text = tr("Unavailable"); QString token = QStringLiteral("color.text.secondary"); @@ -530,8 +699,90 @@ void WfmApplet::refreshDiagnostics() m_diagnostics->setToolTip(m_diagnostics->accessibleDescription()); } +bool WfmApplet::hasCurrentHdReception() const +{ + if (!m_available || !m_slice || !m_caps.broadcastFmReceive->hdStereo + || !m_slice->inCapture() || m_slice->externalReceiveReplacementActive() + || m_slice->wfmAudioMode() != WfmAudioMode::HdStereo || !m_slice->frequencyReportedKnown()) { return false; } + const HdFmReception& value = m_slice->hdFmReception(); + return value.valid && value.frequencyHz == std::llround(m_slice->reportedFrequency() * 1.0e6) + && value.selectedProgram == m_slice->hdProgram(); +} + +void WfmApplet::refreshHd() +{ + const QString previous = m_hdProgram->currentText(); + const QSignalBlocker blocker(m_hdProgram); + const bool current = hasCurrentHdReception(); + auto* programCombo = static_cast(m_hdProgram); + programCombo->setReception(current ? m_slice->hdFmReception() : HdFmReception{}); + std::array labels; + QVector programs; + if (current) { + for (const HdFmService& service : m_slice->hdFmReception().services) { + if (!service.audioAvailable) { continue; } + labels[service.program] = service.name.isEmpty() ? tr("P%1").arg(service.program + 1) + : tr("P%1 · %2").arg(service.program + 1).arg(service.name); + } + } + for (int program = 0; program < static_cast(labels.size()); ++program) { + if (!labels[program].isEmpty()) { programs.append(program); } + } + // Decoder telemetry may reorder services or rename them without changing + // their identity. Keep rows and the popup highlight stable in that case. + bool membershipChanged = m_hdProgram->count() != programs.size(); + for (int row = 0; !membershipChanged && row < programs.size(); ++row) { + membershipChanged = m_hdProgram->itemData(row).toInt() != programs[row]; + } + if (membershipChanged) { + programCombo->invalidateInteraction(); + m_hdProgram->clear(); + for (int program : programs) { m_hdProgram->addItem(labels[program], program); } + } else { + for (int row = 0; row < programs.size(); ++row) { + if (m_hdProgram->itemText(row) != labels[programs[row]]) { + m_hdProgram->setItemText(row, labels[programs[row]]); + } + } + } + m_hdProgram->setCurrentIndex(current ? m_hdProgram->findData(m_slice->hdProgram()) : -1); + announceCombo(m_hdProgram, previous); + if (m_availability) { m_availability->refreshEngaged(); } + QString metadata = tr("Digital metadata unavailable"); + if (current && m_slice->hdFmReception().synced) { + const HdFmReception& value = m_slice->hdFmReception(); + QStringList parts{tr("Digital · P%1").arg(value.selectedProgram + 1)}; + if (!value.stationName.isEmpty()) { parts.append(value.stationName); } + if (!value.title.isEmpty()) { parts.append(value.title); } + if (!value.artist.isEmpty()) { parts.append(value.artist); } + metadata = parts.join(QStringLiteral(" · ")); + } + m_metadata->setContent(metadata); +} + +void WfmApplet::appendHdScopeSample() +{ + if (!hasCurrentHdReception()) { m_scope->clear(); m_hdSequence = 0; return; } + const HdFmReception& value = m_slice->hdFmReception(); + if (m_hdSession != value.sessionId || m_hdEpoch != value.receiverEpoch + || m_hdRevision != value.revision || m_hdSelectedProgram != value.selectedProgram) { + m_scope->clear(); + m_hdSequence = 0; + } + m_hdSession = value.sessionId; + m_hdEpoch = value.receiverEpoch; + m_hdRevision = value.revision; + m_hdSelectedProgram = value.selectedProgram; + if (m_hdSequence == value.observationSequence) { return; } + m_hdSequence = value.observationSequence; + if (!m_showScope->isChecked() || !m_scope->isVisible()) { return; } + m_scope->appendHdSample(value.merLowerDb, value.merUpperDb, value.synced, value.audioValid, + value.synced && value.syncLossCount > 0 && value.syncDurationMs < 5000); +} + void WfmApplet::appendScopeSample() { + if (m_available && m_slice->wfmAudioMode() == WfmAudioMode::HdStereo) { return; } if (!m_available || !m_slice || !m_slice->inCapture() || m_slice->externalReceiveReplacementActive() || !m_caps.broadcastFmReceive->receptionDiagnostics || !m_slice->wfmReceptionDiagnostics().valid) { m_scope->clear(); @@ -540,7 +791,7 @@ void WfmApplet::appendScopeSample() if (!m_showScope->isChecked() || !m_scope->isVisible()) { return; } const WfmReceptionDiagnostics& diagnostics = m_slice->wfmReceptionDiagnostics(); m_scope->appendSample(diagnostics.pilotMagnitude, diagnostics.pilotEngageThreshold, - diagnostics.pilotReleaseThreshold, m_slice->wfmStereoStatus(), m_slice->wfmForceMono()); + diagnostics.pilotReleaseThreshold, m_slice->wfmStereoStatus(), m_slice->wfmAudioMode() == WfmAudioMode::Mono); } void WfmApplet::setSettingsExpanded(bool expanded) @@ -565,15 +816,7 @@ void WfmApplet::applyUiPreferences() void WfmApplet::saveUiPreferences() { - // Client presentation only. Accepted receiver controls persist through - // their model/backend path, never through this UI preference object. - QJsonObject document = preferences(); - QJsonObject ui = document.value(QStringLiteral("ui")).toObject(); - ui.insert(QStringLiteral("showLockScope"), m_showScope->isChecked()); - ui.insert(QStringLiteral("showDiagnostics"), m_showDiagnostics->isChecked()); - document.insert(QStringLiteral("ui"), ui); - AppSettings::instance().setValue(QStringLiteral("WfmApplet"), - QString::fromUtf8(QJsonDocument(document).toJson(QJsonDocument::Compact))); - AppSettings::instance().save(); + WfmPresentationSettings::instance().setAppletOptions( + m_showScope->isChecked(), m_showDiagnostics->isChecked()); } } // namespace AetherSDR diff --git a/src/gui/WfmApplet.h b/src/gui/WfmApplet.h index 98a5f5c59..1114ee403 100644 --- a/src/gui/WfmApplet.h +++ b/src/gui/WfmApplet.h @@ -17,6 +17,7 @@ class ControlAvailabilityRegistry; class RadioModel; class SliceModel; class WfmLockScope; +class WfmMetadataTicker; // Selected-slice broadcast FM controls. AppletPanel owns the visibility of // the complete tile and floating window; this widget never hides itself. @@ -37,6 +38,9 @@ class WfmApplet : public QWidget { bool acceptsControl(const QWidget* control, quint64 binding) const; void refresh(); void refreshDiagnostics(); + void refreshHd(); + void appendHdScopeSample(); + bool hasCurrentHdReception() const; void appendScopeSample(); void registerControls(); void setSettingsExpanded(bool expanded); @@ -58,6 +62,13 @@ class WfmApplet : public QWidget { WfmLockScope* m_scope{nullptr}; QPushButton* m_audioMode{nullptr}; QLabel* m_status{nullptr}; + QComboBox* m_hdProgram{nullptr}; + WfmMetadataTicker* m_metadata{nullptr}; + quint64 m_hdSession{0}; + quint64 m_hdEpoch{0}; + quint64 m_hdRevision{0}; + quint64 m_hdSequence{0}; + int m_hdSelectedProgram{-1}; QComboBox* m_deemphasis{nullptr}; QComboBox* m_bandwidth{nullptr}; QPushButton* m_settingsToggle{nullptr}; diff --git a/src/gui/WfmBroadcastOverlay.cpp b/src/gui/WfmBroadcastOverlay.cpp new file mode 100644 index 000000000..0c4ba2336 --- /dev/null +++ b/src/gui/WfmBroadcastOverlay.cpp @@ -0,0 +1,94 @@ +#include "WfmBroadcastOverlay.h" +#include "WfmPresentationSettings.h" +#include "models/RadioModel.h" +#include "models/SliceModel.h" +#include +#include + +namespace AetherSDR { +WfmBroadcastOverlay::WfmBroadcastOverlay(QObject* parent) : QObject(parent) +{ + qRegisterMetaType>(); + connect(&WfmPresentationSettings::instance(), &WfmPresentationSettings::overlayEnabledChanged, + this, &WfmBroadcastOverlay::refresh); +} +void WfmBroadcastOverlay::bind(RadioModel* model, const QString& panId) +{ + for (const auto& connection : m_modelConnections) { disconnect(connection); } + m_modelConnections.clear(); + m_model = model; + m_panId = panId; + if (model) { + m_modelConnections.append(connect(model, &RadioModel::sliceAdded, + this, &WfmBroadcastOverlay::rebindSlices)); + m_modelConnections.append(connect(model, &RadioModel::sliceRemoved, + this, &WfmBroadcastOverlay::rebindSlices)); + // Reconnect can re-adopt the same SliceModel without sliceAdded. + m_modelConnections.append(connect(model, &RadioModel::slotOccupancyChanged, + this, &WfmBroadcastOverlay::rebindSlices)); + m_modelConnections.append(connect(model, &RadioModel::connectionStateChanged, + this, &WfmBroadcastOverlay::refresh)); + m_modelConnections.append(connect(model, &RadioModel::capabilitiesChanged, + this, &WfmBroadcastOverlay::refresh)); + m_modelConnections.append(connect(model, &QObject::destroyed, this, [this] { + m_model = nullptr; + rebindSlices(); + })); + } + rebindSlices(); +} +void WfmBroadcastOverlay::rebindSlices() +{ + for (const auto& connection : m_sliceConnections) { disconnect(connection); } + m_sliceConnections.clear(); + if (m_model) { + for (SliceModel* slice : m_model->slices()) { + m_sliceConnections.append(connect(slice, &SliceModel::hdFmReceptionChanged, + this, &WfmBroadcastOverlay::refresh)); + m_sliceConnections.append(connect(slice, &SliceModel::wfmAudioModeChanged, + this, &WfmBroadcastOverlay::refresh)); + m_sliceConnections.append(connect(slice, &SliceModel::hdProgramChanged, + this, &WfmBroadcastOverlay::refresh)); + m_sliceConnections.append(connect(slice, &SliceModel::frequencyChanged, + this, &WfmBroadcastOverlay::refresh)); + m_sliceConnections.append(connect(slice, &SliceModel::modeChanged, + this, &WfmBroadcastOverlay::refresh)); + m_sliceConnections.append(connect(slice, &SliceModel::panIdChanged, + this, &WfmBroadcastOverlay::refresh)); + m_sliceConnections.append(connect(slice, &SliceModel::inCaptureChanged, + this, &WfmBroadcastOverlay::refresh)); + m_sliceConnections.append(connect(slice, &SliceModel::externalReceiveReplacementChanged, + this, &WfmBroadcastOverlay::refresh)); + } + } + refresh(); +} +void WfmBroadcastOverlay::refresh() +{ + QVector next; + const RadioCapabilities caps = m_model ? m_model->backendCapabilities() : RadioCapabilities{}; + if (m_model && m_model->isConnected() && caps.broadcastFmReceive + && caps.broadcastFmReceive->hdStereo + && WfmPresentationSettings::instance().broadcastOverlayEnabled()) { + for (const SliceModel* slice : m_model->slices()) { + if (next.size() >= 8) { break; } + if (m_model->slice(slice->sliceId()) != slice || slice->panId() != m_panId + || slice->mode() != QLatin1String("WFM") || !slice->inCapture() + || slice->externalReceiveReplacementActive() + || slice->wfmAudioMode() != WfmAudioMode::HdStereo + || !slice->frequencyReportedKnown()) { continue; } + const HdFmReception& value = slice->hdFmReception(); + if (!value.valid || !value.synced || value.selectedProgram != slice->hdProgram() + || value.selectedProgram < 0 || value.selectedProgram > 7 + || value.frequencyHz != std::llround(slice->reportedFrequency() * 1.0e6) + || (value.stationName.isEmpty() && value.title.isEmpty() && value.artist.isEmpty())) { continue; } + next.append({slice->sliceId(), value.sessionId, value.receiverEpoch, value.revision, + value.frequencyHz, value.selectedProgram, value.stationName, value.title, value.artist}); + } + } + std::sort(next.begin(), next.end(), [](const auto& a, const auto& b) { return a.sliceId < b.sliceId; }); + if (next == m_records) { return; } + m_records = next; + emit overlaysChanged(m_records); +} +} diff --git a/src/gui/WfmBroadcastOverlay.h b/src/gui/WfmBroadcastOverlay.h new file mode 100644 index 000000000..bdb1673f8 --- /dev/null +++ b/src/gui/WfmBroadcastOverlay.h @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace AetherSDR { +class RadioModel; + +// Local presentation only: no spot index, cluster publication, lifetime store, +// DXCC lookup, smart filtering or click-to-tune semantics. +struct WfmBroadcastOverlayRecord { + int sliceId{-1}; + quint64 sessionId{0}; + quint64 receiverEpoch{0}; + quint64 revision{0}; + qint64 frequencyHz{0}; + int program{-1}; + QString stationName; + QString title; + QString artist; + QString displayText() const + { + QString text = QStringLiteral("Digital · P%1").arg(program + 1); + for (const QString& part : {stationName, title, artist}) { + if (!part.isEmpty()) { text += QStringLiteral(" · ") + part; } + } + return text; + } + bool operator==(const WfmBroadcastOverlayRecord&) const = default; +}; + +class WfmBroadcastOverlay final : public QObject { + Q_OBJECT +public: + explicit WfmBroadcastOverlay(QObject* parent = nullptr); + void bind(RadioModel* model, const QString& panId); + const QVector& records() const { return m_records; } +signals: + void overlaysChanged(const QVector& records); +private: + void rebindSlices(); + void refresh(); + QPointer m_model; + QString m_panId; + QVector m_modelConnections; + QVector m_sliceConnections; + QVector m_records; +}; +} + +Q_DECLARE_METATYPE(AetherSDR::WfmBroadcastOverlayRecord) diff --git a/src/gui/WfmBroadcastTicker.h b/src/gui/WfmBroadcastTicker.h new file mode 100644 index 000000000..dccb9d8ba --- /dev/null +++ b/src/gui/WfmBroadcastTicker.h @@ -0,0 +1,110 @@ +#pragma once +#include "WfmBroadcastOverlay.h" +#include +#include +#include +#include +#include + +namespace AetherSDR { + +// Presentation clock only: no metadata ownership, callbacks, or background timer. +// Incoming metadata is already bounded by the receiver/model normalization. +class WfmBroadcastTicker final { +public: + bool setContent(const WfmBroadcastOverlayRecord& record) + { + if (m_haveContent && m_record.sliceId == record.sliceId + && m_record.sessionId == record.sessionId && m_record.receiverEpoch == record.receiverEpoch + && m_record.frequencyHz == record.frequencyHz && m_record.program == record.program + && m_record.stationName == record.stationName && m_record.title == record.title + && m_record.artist == record.artist) { + return false; // An accepted control revision alone is not new content. + } + m_record = record; + m_text = record.displayText().simplified(); + m_haveContent = true; + m_haveLayout = false; + m_pages.clear(); + m_page = 0; + m_deadlineMs = -1; + return true; + } + + bool layout(const QFont& font, int availableWidth) + { + const int width = std::max(1, availableWidth); + if (!m_haveContent || (m_haveLayout && m_font == font && m_width == width)) { return false; } + m_font = font; + m_width = width; + m_haveLayout = true; + m_pages.clear(); + const QFontMetrics metrics(font); + m_labelWidth = std::min(width, metrics.horizontalAdvance(m_text)); + QTextBoundaryFinder lines(QTextBoundaryFinder::Line, m_text); + QTextBoundaryFinder graphemes(QTextBoundaryFinder::Grapheme, m_text); + qsizetype offset = 0; + while (offset < m_text.size()) { + qsizetype end = offset; + lines.setPosition(offset); + for (qsizetype next = lines.toNextBoundary(); next >= 0; next = lines.toNextBoundary()) { + if (metrics.horizontalAdvance(m_text.mid(offset, next - offset).trimmed()) > width) { break; } + end = next; + } + // A single unbroken word may exceed the box. Split only at grapheme + // boundaries, preserving surrogate pairs and combining sequences. + if (end == offset) { + graphemes.setPosition(offset); + for (qsizetype next = graphemes.toNextBoundary(); next >= 0; next = graphemes.toNextBoundary()) { + if (metrics.horizontalAdvance(m_text.mid(offset, next - offset)) > width && end > offset) { break; } + end = next; + if (metrics.horizontalAdvance(m_text.mid(offset, end - offset)) > width) { break; } + } + } + if (end <= offset) { break; } + const QString page = m_text.mid(offset, end - offset).trimmed(); + if (!page.isEmpty()) { m_pages.append(page); } + offset = end; + } + m_page = 0; + m_deadlineMs = -1; + return true; + } + + bool advance(qint64 nowMs, bool visible) + { + if (!visible || m_pages.size() <= 1) { + m_deadlineMs = -1; + return false; + } + if (m_deadlineMs < 0) { + m_deadlineMs = nowMs + holdMs(); + return false; + } + if (nowMs < m_deadlineMs) { return false; } + m_page = (m_page + 1) % m_pages.size(); + // A delayed frame advances once, never races through unread pages. + m_deadlineMs = nowMs + holdMs(); + return true; + } + + const QString& fullText() const { return m_text; } + QString pageText() const { return m_pages.isEmpty() ? m_text : m_pages.at(m_page); } + const QStringList& pages() const { return m_pages; } + int labelWidth() const { return m_labelWidth; } + int pageIndex() const { return m_page; } + +private: + qint64 holdMs() const { return m_page == 0 || m_page == m_pages.size() - 1 ? 5000 : 3000; } + WfmBroadcastOverlayRecord m_record; + QString m_text; + QStringList m_pages; + QFont m_font; + int m_width{0}; + int m_labelWidth{0}; + int m_page{0}; + qint64 m_deadlineMs{-1}; + bool m_haveContent{false}; + bool m_haveLayout{false}; +}; +} diff --git a/src/gui/WfmLockScope.cpp b/src/gui/WfmLockScope.cpp index ad790af98..f81ceaeb6 100644 --- a/src/gui/WfmLockScope.cpp +++ b/src/gui/WfmLockScope.cpp @@ -87,6 +87,7 @@ void WfmLockScope::appendSample(double pilotMagnitude, double acquireThreshold, double releaseThreshold, WfmStereoStatus status, bool forceMono) { + if (m_hdMode) { setHdMode(false); } // Hidden scope means no history growth, timer reads or repaint requests. // A show starts with fresh telemetry, never a retained green strip. if (!isVisible()) { @@ -121,11 +122,53 @@ void WfmLockScope::appendSample(double pilotMagnitude, double acquireThreshold, update(); } +void WfmLockScope::setHdMode(bool enabled) +{ + if (m_hdMode == enabled) { return; } + m_hdMode = enabled; + setAccessibleName(enabled ? tr("Digital lock scope") : tr("WFM pilot lock scope")); + setToolTip(enabled + ? tr("Measured Digital MER in dB, using the decoder's lower/upper convention. " + "State shows actual digital sync and valid selected-program audio. " + "No lock threshold is inferred from MER.") + : tr("Measured 19 kHz pilot magnitude and acquire/release thresholds in relative units. " + "Threshold crossings require consecutive decoder blocks; this is not SNR or PLL lock.")); + clear(); +} + +void WfmLockScope::appendHdSample(std::optional lowerMerDb, + std::optional upperMerDb, bool synced, bool audioValid, bool recentlyRecovered) +{ + if (!m_hdMode) { setHdMode(true); } + if (!isVisible()) { return; } + if ((lowerMerDb && !std::isfinite(*lowerMerDb)) + || (upperMerDb && !std::isfinite(*upperMerDb)) || (audioValid && !synced)) { + clear(); + return; + } + if (!m_clock.isValid()) { m_clock.start(); } + const qint64 now = m_clock.elapsed(); + while (!m_hdSamples.isEmpty() && (m_hdSamples.size() >= kCapacity + || now - m_hdSamples.first().milliseconds > kWindowMs)) { m_hdSamples.removeFirst(); } + m_hdSamples.append({now, lowerMerDb, upperMerDb, synced, audioValid, recentlyRecovered}); + const auto metric = [](const std::optional& value) { + return value ? QString::number(*value, 'f', 1) + QStringLiteral(" dB") : QStringLiteral("unavailable"); + }; + QString state = synced ? (audioValid ? tr("Digital audio valid") : tr("Digital synced; awaiting audio")) + : tr("Digital acquiring"); + if (recentlyRecovered) { state += tr(" · Unstable (sync recovered within 5 s)"); } + publishAccessibleValue(this, tr("Digital MER: decoder lower %1; upper %2. %3. %4 current observations.") + .arg(metric(lowerMerDb), metric(upperMerDb), state).arg(m_hdSamples.size())); + update(); +} + void WfmLockScope::clear() { m_samples.clear(); + m_hdSamples.clear(); m_clock.invalidate(); - publishAccessibleValue(this, QStringLiteral("Awaiting current receiver pilot telemetry.")); + publishAccessibleValue(this, m_hdMode ? tr("Awaiting current receiver Digital telemetry.") + : tr("Awaiting current receiver pilot telemetry.")); if (isVisible()) { update(); } @@ -140,6 +183,7 @@ void WfmLockScope::hideEvent(QHideEvent* event) void WfmLockScope::paintEvent(QPaintEvent*) { QPainter painter(this); + if (m_hdMode) { paintHd(painter); return; } ThemeManager& theme = ThemeManager::instance(); const QColor text = theme.color(this, "color.text.secondary"); const QColor trace = theme.color(this, "color.text.primary"); @@ -221,4 +265,74 @@ void WfmLockScope::paintEvent(QPaintEvent*) Qt::AlignRight, QStringLiteral("← up to 40 s · now")); } +void WfmLockScope::paintHd(QPainter& painter) +{ + ThemeManager& theme = ThemeManager::instance(); + painter.fillRect(rect(), theme.brush(this, "color.background.0", rect())); + painter.setPen(theme.color(this, "color.border.subtle")); + painter.drawRect(rect().adjusted(0, 0, -1, -1)); + painter.setPen(theme.color(this, "color.text.secondary")); + const int lineHeight = fontMetrics().height(); + painter.drawText(QRect(5, 3, width() - 10, lineHeight), Qt::AlignLeft, + tr("Digital MER · dB (decoder lower / upper)")); + if (m_hdSamples.isEmpty()) { + painter.drawText(rect().adjusted(5, lineHeight + 5, -5, -5), Qt::AlignCenter, + tr("Awaiting Digital telemetry")); + return; + } + const QRectF plot(6, lineHeight + 7, width() - 12, + std::max(20, height() - 3 * lineHeight - 28)); + bool hasMetric = false; + double minimum = 0.0; + double maximum = 0.0; + for (const HdSample& sample : m_hdSamples) { + for (const auto& value : {sample.lower, sample.upper}) { + if (!value) { continue; } + if (!hasMetric) { minimum = maximum = *value; hasMetric = true; } + minimum = std::min(minimum, *value); + maximum = std::max(maximum, *value); + } + } + const HdSample& latest = m_hdSamples.last(); + const auto xOf = [&](const HdSample& sample) { + return plot.right() - double(latest.milliseconds - sample.milliseconds) / kWindowMs * plot.width(); + }; + if (hasMetric) { + minimum -= 1.0; + maximum += 1.0; + painter.drawText(QPointF(plot.left(), plot.top() + lineHeight), QString::number(maximum, 'f', 1)); + painter.drawText(QPointF(plot.left(), plot.bottom()), QString::number(minimum, 'f', 1)); + for (bool lower : {true, false}) { + painter.setPen(QPen(theme.color(this, lower ? "color.text.primary" : "color.text.secondary"), + 1.5, lower ? Qt::SolidLine : Qt::DashLine)); + QPolygonF points; + for (const HdSample& sample : m_hdSamples) { + const auto& value = lower ? sample.lower : sample.upper; + if (!value) { painter.drawPolyline(points); points.clear(); continue; } + points.append(QPointF(xOf(sample), plot.bottom() + - (*value - minimum) / (maximum - minimum) * plot.height())); + } + painter.drawPolyline(points); + if (!points.isEmpty()) { painter.drawEllipse(points.last(), 2.0, 2.0); } + } + } else { + painter.drawText(plot, Qt::AlignCenter, tr("MER unavailable")); + } + const qreal stripY = plot.bottom() + 4; + for (qsizetype i = 0; i < m_hdSamples.size(); ++i) { + const HdSample& sample = m_hdSamples[i]; + const qreal left = xOf(sample); + const qreal right = i + 1 < m_hdSamples.size() ? xOf(m_hdSamples[i + 1]) : plot.right(); + painter.fillRect(QRectF(left, stripY, std::max(2.0, right - left), 4), + theme.color(this, sample.synced && sample.audioValid && !sample.recentlyRecovered ? "color.accent.success" : "color.accent.warning")); + } + painter.setPen(theme.color(this, "color.text.secondary")); + QString state = latest.synced ? (latest.audioValid ? tr("Digital audio valid") : tr("Synced · awaiting audio")) + : tr("Digital acquiring"); + if (latest.recentlyRecovered) { state += tr(" · Unstable"); } + painter.drawText(QRect(6, int(stripY) + 8, width() - 12, lineHeight), Qt::AlignLeft, state); + painter.drawText(QRect(6, height() - lineHeight - 2, width() - 12, lineHeight), + Qt::AlignRight, tr("Lower — / Upper - - · up to 40 s")); +} + } // namespace AetherSDR diff --git a/src/gui/WfmLockScope.h b/src/gui/WfmLockScope.h index 86e9b7ad1..328c56b2d 100644 --- a/src/gui/WfmLockScope.h +++ b/src/gui/WfmLockScope.h @@ -5,6 +5,9 @@ #include #include #include +#include + +class QPainter; namespace AetherSDR { @@ -22,8 +25,12 @@ class WfmLockScope : public QWidget { void appendSample(double pilotMagnitude, double acquireThreshold, double releaseThreshold, WfmStereoStatus status, bool forceMono); + // HD uses actual decoder MER values, with no invented lock threshold. + void setHdMode(bool enabled); + void appendHdSample(std::optional lowerMerDb, std::optional upperMerDb, + bool synced, bool audioValid, bool recentlyRecovered = false); void clear(); - int sampleCount() const { return int(m_samples.size()); } + int sampleCount() const { return int(m_hdMode ? m_hdSamples.size() : m_samples.size()); } protected: void paintEvent(QPaintEvent* event) override; @@ -38,6 +45,17 @@ class WfmLockScope : public QWidget { WfmStereoStatus status; bool forceMono; }; + struct HdSample { + qint64 milliseconds; + std::optional lower; + std::optional upper; + bool synced; + bool audioValid; + bool recentlyRecovered; + }; + void paintHd(QPainter& painter); + bool m_hdMode{false}; + QList m_hdSamples; static constexpr int kCapacity = 160; static constexpr qint64 kWindowMs = 40000; QList m_samples; diff --git a/src/gui/WfmPresentationSettings.cpp b/src/gui/WfmPresentationSettings.cpp new file mode 100644 index 000000000..57d7a7dc9 --- /dev/null +++ b/src/gui/WfmPresentationSettings.cpp @@ -0,0 +1,58 @@ +#include "WfmPresentationSettings.h" +#include "core/AppSettings.h" +#include +#include + +namespace AetherSDR { +namespace { +QJsonObject document() +{ + return QJsonDocument::fromJson(AppSettings::instance() + .value(QStringLiteral("WfmApplet"), QString{}).toString().toUtf8()).object(); +} +void save(QJsonObject value, const QJsonObject& ui) +{ + value.insert(QStringLiteral("ui"), ui); + AppSettings::instance().setValue(QStringLiteral("WfmApplet"), + QString::fromUtf8(QJsonDocument(value).toJson(QJsonDocument::Compact))); + AppSettings::instance().save(); +} +} +WfmPresentationSettings& WfmPresentationSettings::instance() +{ + static WfmPresentationSettings settings; + return settings; +} +bool WfmPresentationSettings::broadcastOverlayEnabled() const +{ + return document().value(QStringLiteral("ui")).toObject() + .value(QStringLiteral("showBroadcastOverlay")).toBool(true); +} +void WfmPresentationSettings::setBroadcastOverlayEnabled(bool enabled) +{ + const bool changed = broadcastOverlayEnabled() != enabled; + QJsonObject value = document(); + QJsonObject ui = value.value(QStringLiteral("ui")).toObject(); + ui.insert(QStringLiteral("showBroadcastOverlay"), enabled); + save(value, ui); + if (changed) { emit overlayEnabledChanged(enabled); } +} +bool WfmPresentationSettings::showLockScope() const +{ + return document().value(QStringLiteral("ui")).toObject() + .value(QStringLiteral("showLockScope")).toBool(true); +} +bool WfmPresentationSettings::showDiagnostics() const +{ + return document().value(QStringLiteral("ui")).toObject() + .value(QStringLiteral("showDiagnostics")).toBool(false); +} +void WfmPresentationSettings::setAppletOptions(bool scope, bool diagnostics) +{ + QJsonObject value = document(); + QJsonObject ui = value.value(QStringLiteral("ui")).toObject(); + ui.insert(QStringLiteral("showLockScope"), scope); + ui.insert(QStringLiteral("showDiagnostics"), diagnostics); + save(value, ui); +} +} diff --git a/src/gui/WfmPresentationSettings.h b/src/gui/WfmPresentationSettings.h new file mode 100644 index 000000000..17cdb1b5c --- /dev/null +++ b/src/gui/WfmPresentationSettings.h @@ -0,0 +1,21 @@ +#pragma once +#include + +namespace AetherSDR { +// Sole owner of the client-only WFM presentation document. Receiver state is +// persisted by its model/backend, never by these preferences. +class WfmPresentationSettings final : public QObject { + Q_OBJECT +public: + static WfmPresentationSettings& instance(); + bool broadcastOverlayEnabled() const; + void setBroadcastOverlayEnabled(bool enabled); + bool showLockScope() const; + bool showDiagnostics() const; + void setAppletOptions(bool scope, bool diagnostics); +signals: + void overlayEnabledChanged(bool enabled); +private: + WfmPresentationSettings() = default; +}; +} diff --git a/src/models/RadioModel.cpp b/src/models/RadioModel.cpp index fb65ccc49..790e668d1 100644 --- a/src/models/RadioModel.cpp +++ b/src/models/RadioModel.cpp @@ -2069,6 +2069,8 @@ RadioModel::RadioModel(QObject* parent) qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); @@ -7066,6 +7068,7 @@ void RadioModel::stageSessionModelsForReconnect() SliceDelta unavailableWfm; unavailableWfm.wfmStereoStatus = WfmStereoStatus::Unavailable; unavailableWfm.wfmReceptionDiagnostics = WfmReceptionDiagnostics{}; + unavailableWfm.hdFmReception = HdFmReception{}; slice->applyChanges(unavailableWfm); m_staleSlices.insert(slice->sliceId(), slice); } @@ -9983,6 +9986,10 @@ void RadioModel::wireSliceReceiveIntentsToBackend(SliceModel* s) this, &RadioModel::dispatchSliceDsp, type); connect(s, &SliceModel::receiveAudioRequested, this, &RadioModel::dispatchSliceAudio, type); + connect(s, &SliceModel::wfmAudioModeRequested, + this, &RadioModel::dispatchSliceWfmAudioMode, type); + connect(s, &SliceModel::hdProgramRequested, + this, &RadioModel::dispatchSliceHdProgram, type); connect(s, &SliceModel::wfmForceMonoRequested, this, &RadioModel::dispatchSliceWfmForceMono, type); connect(s, &SliceModel::wfmDeemphasisRequested, @@ -10079,6 +10086,16 @@ void RadioModel::dispatchSliceAudio(const SliceAudioRequest& request) } } +void RadioModel::dispatchSliceWfmAudioMode(WfmAudioMode mode) +{ + dispatchSliceWfm({SliceWfmRequest::Field::AudioMode, int(mode)}); +} + +void RadioModel::dispatchSliceHdProgram(int program) +{ + dispatchSliceWfm({SliceWfmRequest::Field::HdProgram, program}); +} + void RadioModel::dispatchSliceWfmForceMono(bool forceMono) { dispatchSliceWfm({SliceWfmRequest::Field::ForceMono, int(forceMono)}); @@ -10093,11 +10110,32 @@ void RadioModel::dispatchSliceWfm(const SliceWfmRequest& request) { if (SliceModel* source = receiveCommandSource()) { const std::optional feature = m_backend->capabilities().broadcastFmReceive; - const bool available = request.valid() && feature + bool available = request.valid() && feature && source->mode() == QLatin1String("WFM") - && !source->externalReceiveReplacementActive() - && (request.field == SliceWfmRequest::Field::ForceMono - ? feature->forceMonoControl : feature->deemphasisUs.contains(request.value)); + && !source->externalReceiveReplacementActive(); + if (available) { + switch (request.field) { + case SliceWfmRequest::Field::ForceMono: + available = feature->forceMonoControl; + break; + case SliceWfmRequest::Field::Deemphasis: + available = feature->deemphasisUs.contains(request.value); + break; + case SliceWfmRequest::Field::AudioMode: + available = (feature->forceMonoControl || feature->hdStereo) + && (request.value != int(WfmAudioMode::HdStereo) || feature->hdStereo); + break; + case SliceWfmRequest::Field::HdProgram: { + const HdFmReception& reception = source->hdFmReception(); + available = feature->hdStereo && source->wfmAudioMode() == WfmAudioMode::HdStereo + && reception.valid && reception.synced + && std::ranges::any_of(reception.services, [&request](const HdFmService& service) { + return service.program == request.value && service.audioAvailable; + }); + break; + } + } + } reportReceiveDispatch(available ? m_backend->requestSliceWfm(source->sliceId(), request) : ReceiveDispatch::Unsupported, QStringLiteral("broadcast FM")); diff --git a/src/models/RadioModel.h b/src/models/RadioModel.h index 5f5261e42..619466c1e 100644 --- a/src/models/RadioModel.h +++ b/src/models/RadioModel.h @@ -1777,6 +1777,8 @@ private slots: void dispatchSliceAgc(const SliceAgcRequest& request); void dispatchSliceDsp(const SliceDspRequest& request); void dispatchSliceAudio(const SliceAudioRequest& request); + void dispatchSliceWfmAudioMode(WfmAudioMode mode); + void dispatchSliceHdProgram(int program); void dispatchSliceWfmForceMono(bool forceMono); void dispatchSliceWfmDeemphasis(int microseconds); void dispatchSliceWfm(const SliceWfmRequest& request); diff --git a/src/models/SliceModel.cpp b/src/models/SliceModel.cpp index 9daadd389..a4f7a3e94 100644 --- a/src/models/SliceModel.cpp +++ b/src/models/SliceModel.cpp @@ -805,6 +805,16 @@ void SliceModel::setAgcOffLevel(int value) SliceAgcRequest::Origin::Operator}); } +void SliceModel::setWfmAudioMode(WfmAudioMode mode) +{ + if (validWfmAudioMode(mode)) { emit wfmAudioModeRequested(mode); } +} + +void SliceModel::setHdProgram(int program) +{ + if (program >= 0 && program < 8) { emit hdProgramRequested(program); } +} + void SliceModel::setWfmForceMono(bool forceMono) { // Selection is intent until the prepared decoder revision is accepted. @@ -1863,6 +1873,22 @@ void SliceModel::applyChanges(const SliceDelta& d) m_wfmForceMono = *d.wfmForceMono; emit wfmForceMonoChanged(m_wfmForceMono); } + const WfmAudioMode nextWfmMode = d.wfmAudioMode.value_or( + d.wfmForceMono && m_wfmAudioMode != WfmAudioMode::HdStereo + ? (*d.wfmForceMono ? WfmAudioMode::Mono : WfmAudioMode::Stereo) : m_wfmAudioMode); + if (validWfmAudioMode(nextWfmMode) && m_wfmAudioMode != nextWfmMode) { + m_wfmAudioMode = nextWfmMode; emit wfmAudioModeChanged(nextWfmMode); + } + if (d.hdProgram && *d.hdProgram >= 0 && *d.hdProgram < 8 && m_hdProgram != *d.hdProgram) { + m_hdProgram = *d.hdProgram; emit hdProgramChanged(m_hdProgram); + } + HdFmReception hdValue = d.hdFmReception ? normalizedHdFmReception(*d.hdFmReception) : m_hdFmReception; + if (m_mode != QLatin1String("WFM") || m_wfmAudioMode != WfmAudioMode::HdStereo + || !std::isfinite(m_frequency) || std::abs(m_frequency * 1e6 - hdValue.frequencyHz) > 0.5 + || hdValue.selectedProgram != m_hdProgram) { hdValue = {}; } + if (hdValue != m_hdFmReception) { + m_hdFmReception = hdValue; emit hdFmReceptionChanged(m_hdFmReception); + } if (d.wfmStereoStatus) { const WfmStereoStatus status = *d.wfmStereoStatus; switch (status) { diff --git a/src/models/SliceModel.h b/src/models/SliceModel.h index e7ca7a356..6db23363a 100644 --- a/src/models/SliceModel.h +++ b/src/models/SliceModel.h @@ -187,6 +187,9 @@ class SliceModel : public QObject { // Zero means not observed; never present a default as adopted DSP state. int wfmDeemphasisUs() const { return m_wfmDeemphasisUs; } bool wfmForceMono() const { return m_wfmForceMono; } + WfmAudioMode wfmAudioMode() const { return m_wfmAudioMode; } + int hdProgram() const { return m_hdProgram; } + const HdFmReception& hdFmReception() const { return m_hdFmReception; } const WfmReceptionDiagnostics& wfmReceptionDiagnostics() const { return m_wfmReceptionDiagnostics; } WfmStereoStatus wfmStereoStatus() const { return m_wfmStereoStatus; } int flexSquelchLevel() const { return m_squelchLevel; } @@ -345,6 +348,8 @@ class SliceModel : public QObject { void setSquelch(bool on, int level); void setWfmDeemphasis(int microseconds); void setWfmForceMono(bool forceMono); + void setWfmAudioMode(WfmAudioMode mode); + void setHdProgram(int program); // For genuine operator-driven manual squelch input only (a VFO flag's // own SQL controls, a controller-mapped squelch knob) — setSquelch() // plus recording the level as the operator's manual choice, in one @@ -526,6 +531,11 @@ class SliceModel : public QObject { void wfmDeemphasisChanged(int microseconds); void wfmForceMonoChanged(bool forceMono); void wfmForceMonoRequested(bool forceMono); + void wfmAudioModeChanged(AetherSDR::WfmAudioMode mode); + void wfmAudioModeRequested(AetherSDR::WfmAudioMode mode); + void hdProgramChanged(int program); + void hdProgramRequested(int program); + void hdFmReceptionChanged(const AetherSDR::HdFmReception& reception); void wfmReceptionDiagnosticsChanged(const AetherSDR::WfmReceptionDiagnostics& diagnostics); void wfmStereoStatusChanged(AetherSDR::WfmStereoStatus status); void wfmDeemphasisRequested(int microseconds); @@ -721,6 +731,9 @@ class SliceModel : public QObject { int m_squelchLevel{20}; int m_wfmDeemphasisUs{0}; bool m_wfmForceMono{false}; + WfmAudioMode m_wfmAudioMode{WfmAudioMode::Stereo}; + int m_hdProgram{0}; + HdFmReception m_hdFmReception; WfmReceptionDiagnostics m_wfmReceptionDiagnostics; WfmStereoStatus m_wfmStereoStatus{WfmStereoStatus::Unavailable}; int m_manualSquelchLevel{20}; diff --git a/tests/broadcast_overlay_test.cpp b/tests/broadcast_overlay_test.cpp new file mode 100644 index 000000000..e11b87b4a --- /dev/null +++ b/tests/broadcast_overlay_test.cpp @@ -0,0 +1,396 @@ +// Socket-free current-reception presentation. No receiver, cluster, audio or RF. +#include "TestSettingsProfile.h" +#include "core/AppSettings.h" +#include "core/backends/SliceDelta.h" +#include "gui/WfmBroadcastOverlay.h" +#include "gui/WfmBroadcastTicker.h" +#include "gui/WfmPresentationSettings.h" +#include "models/RadioModel.h" +#include "models/SliceModel.h" +#include +#include +#include +#include +#include + +using namespace AetherSDR; +namespace { +class Backend final : public IRadioBackend { +public: + RadioCapabilities caps; + bool connected{true}; + int commands{0}; + RadioCapabilities capabilities() const override { return caps; } + bool isConnected() const override { return connected; } + ReceiveControlPolicy receiveControlPolicy() const override { return ReceiveControlPolicy::Confirmed; } + void connectRadio(const RadioConnectRequest&) override {} + void disconnectRadio() override { connected = false; } + void setSliceFrequency(int, double) override { ++commands; } + void setSliceMode(int, const QString&) override { ++commands; } + void setSliceFilter(int, int, int) override { ++commands; } + void setSliceAgc(int, const QString&, int) override { ++commands; } + void setPanCenter(const QString&, double, PanCenterIntent) override { ++commands; } + void setKeying(bool, const TxCoordinator::Operation&, const TxCoordinator::Completion&) override { ++commands; } + void invokeExtension(const QString&, const QString&, quint64, const QVariant&) override { ++commands; } +}; +HdFmReception reception(int program = 0, qint64 frequency = 100300000) +{ + HdFmReception value; + value.valid = true; + value.sessionId = 11; + value.receiverEpoch = 22; + value.revision = 33; + value.frequencyHz = frequency; + value.selectedProgram = program; + value.observationSequence = 1; + value.synced = value.audioValid = true; + value.services = {{0, QStringLiteral("Main"), true}, {1, QStringLiteral("Second"), true}}; + value.stationName = QStringLiteral("Station"); + value.title = QStringLiteral("First song"); + value.artist = QStringLiteral("Artist"); + return value; +} +SliceDelta hdSlice(int program = 0, qint64 frequency = 100300000) +{ + SliceDelta value; + value.mode = QStringLiteral("WFM"); + value.panId = QStringLiteral("test-pan"); + value.frequency = frequency / 1.0e6; + value.inCapture = true; + value.wfmAudioMode = WfmAudioMode::HdStereo; + value.hdProgram = program; + value.hdFmReception = reception(program, frequency); + return value; +} +Backend* attach(RadioModel& model) +{ + auto backend = std::make_unique(); + backend->caps.broadcastFmReceive = BroadcastFmReceive{{50, 75}, true, true, true}; + Backend* source = backend.get(); + model.setBackendForTest(std::move(backend), QStringLiteral("test")); + emit source->sliceChanged(3, hdSlice()); + return source; +} +} +class BroadcastOverlayTest : public QObject { + Q_OBJECT +private slots: + void init() { AppSettings::instance().remove(QStringLiteral("WfmApplet")); } + void tickerPagesUseWordBoundariesAndReadableEndpointPauses() + { + WfmBroadcastOverlayRecord record{3, 11, 22, 33, 100300000, 0, + QStringLiteral("Station"), QStringLiteral("One two three four five six seven eight nine ten eleven twelve"), {}}; + const WfmBroadcastOverlayRecord original = record; + QFont font; font.setPixelSize(12); + const QFontMetrics metrics(font); + const int width = metrics.horizontalAdvance(QStringLiteral("One two three")); + WfmBroadcastTicker ticker; + QVERIFY(ticker.setContent(record)); + QVERIFY(ticker.layout(font, width)); + QVERIFY(ticker.pages().size() > 3); + QCOMPARE(ticker.pages().join(QLatin1Char(' ')), record.displayText().simplified()); + for (const QString& page : ticker.pages()) { QVERIFY(metrics.horizontalAdvance(page) <= width); } + const int stableWidth = ticker.labelWidth(); + QVERIFY(!ticker.advance(100, true)); + QVERIFY(!ticker.advance(5099, true)); + QVERIFY(ticker.advance(5100, true)); + QCOMPARE(ticker.pageIndex(), 1); + QVERIFY(!ticker.advance(8099, true)); + QVERIFY(ticker.advance(8100, true)); + qint64 now = 8100; + while (ticker.pageIndex() < ticker.pages().size() - 1) { + now += 3000; + QVERIFY(ticker.advance(now, true)); + } + QVERIFY(!ticker.advance(now + 4999, true)); + QVERIFY(ticker.advance(now + 5000, true)); + QCOMPARE(ticker.pageIndex(), 0); + QCOMPARE(ticker.labelWidth(), stableWidth); + QVERIFY(record == original); // Paging never mutates/reinserts metadata. + } + void tickerIgnoresControlRevisionsButResetsContentServiceAndGeneration() + { + WfmBroadcastOverlayRecord record{3, 11, 22, 33, 100300000, 0, + QStringLiteral("Station"), QStringLiteral("A longer station announcement with many words for paging"), {}}; + QFont font; font.setPixelSize(12); + WfmBroadcastTicker ticker; + ticker.setContent(record); ticker.layout(font, 100); + ticker.advance(0, true); QVERIFY(ticker.advance(5000, true)); + const int page = ticker.pageIndex(); + ++record.revision; + QVERIFY(!ticker.setContent(record)); + QVERIFY(!ticker.layout(font, 100)); + QCOMPARE(ticker.pageIndex(), page); + QVERIFY(!ticker.advance(6000, false)); + QVERIFY(!ticker.advance(90000, true)); // Hidden time is not catch-up work. + QCOMPARE(ticker.pageIndex(), page); + QVERIFY(!ticker.advance(92999, true)); + QVERIFY(ticker.advance(93000, true)); + record.title = QStringLiteral("Replacement text at the same frequency"); + QVERIFY(ticker.setContent(record)); ticker.layout(font, 100); + QCOMPARE(ticker.pageIndex(), 0); + QVERIFY(!ticker.fullText().contains(QStringLiteral("announcement"))); + for (int change = 0; change < 4; ++change) { + ticker.advance(0, true); QVERIFY(ticker.advance(5000, true)); + if (change == 0) { ++record.program; } + if (change == 1) { ++record.receiverEpoch; } + if (change == 2) { ++record.sessionId; } + if (change == 3) { ++record.frequencyHz; } + QVERIFY(ticker.setContent(record)); ticker.layout(font, 100); + QCOMPARE(ticker.pageIndex(), 0); + } + record.title.clear(); record.stationName.clear(); record.artist.clear(); + QVERIFY(ticker.setContent(record)); ticker.layout(font, 400); + QCOMPARE(ticker.pages().size(), 1); + QVERIFY(!ticker.advance(0, true)); QVERIFY(!ticker.advance(100000, true)); + QCOMPARE(ticker.pageText(), record.displayText()); + ticker = {}; // Overlay off/loss removes presentation state entirely. + QVERIFY(ticker.fullText().isEmpty()); QVERIFY(ticker.pages().isEmpty()); + QVERIFY(!ticker.advance(200000, true)); + ticker.setContent(record); ticker.layout(font, 400); + QCOMPARE(ticker.pageIndex(), 0); + } + void tickerLongWordsPreserveUnicodeGraphemesAndAlwaysAdvance() + { + QFont font; font.setPixelSize(12); + const QString cluster = QString::fromUtf8("👩🏽‍🚀"); + const QString combining = QString::fromUtf8("é"); + WfmBroadcastOverlayRecord record{3, 11, 22, 33, 100300000, 0, + {}, cluster.repeated(20) + combining.repeated(20), {}}; + WfmBroadcastTicker ticker; + ticker.setContent(record); + const int width = std::max(QFontMetrics(font).horizontalAdvance(cluster), + QFontMetrics(font).horizontalAdvance(QStringLiteral("Digital"))); + ticker.layout(font, width); + QVERIFY(ticker.pages().size() > 2); + QString reconstructed; + for (const QString& page : ticker.pages()) { QVERIFY(!page.isEmpty()); reconstructed += page; } + QString expected = ticker.fullText(); expected.remove(QLatin1Char(' ')); + reconstructed.remove(QLatin1Char(' ')); + QCOMPARE(reconstructed, expected); + for (const QString& page : ticker.pages()) { + QString remainder = page; + remainder.remove(cluster); remainder.remove(combining); + QVERIFY(!remainder.contains(QChar(0x200d))); // No partial joiner sequence. + QVERIFY(!remainder.contains(QChar(0x0301))); // No detached combining mark. + for (const QChar ch : remainder) { QVERIFY(!ch.isSurrogate()); } + } + ticker.layout(font, 1); // Even an oversized glyph beside a space must progress. + reconstructed.clear(); + for (const QString& page : ticker.pages()) { QVERIFY(!page.isEmpty()); reconstructed += page; } + reconstructed.remove(QLatin1Char(' ')); + QCOMPARE(reconstructed, expected); + } + void freshDefaultAndExplicitFalseHaveOnePersistentOwner() + { + auto& settings = WfmPresentationSettings::instance(); + QVERIFY(settings.broadcastOverlayEnabled()); + AppSettings::instance().setValue(QStringLiteral("WfmApplet"), + QStringLiteral(R"({"other":17,"ui":{"retained":"yes","showLockScope":false}})")); + QSignalSpy changed(&settings, &WfmPresentationSettings::overlayEnabledChanged); + settings.setBroadcastOverlayEnabled(false); + QCOMPARE(changed.count(), 1); + QVERIFY(!settings.broadcastOverlayEnabled()); + QVERIFY(!settings.showLockScope()); + settings.setAppletOptions(true, true); + AppSettings::instance().load(); + QVERIFY(!settings.broadcastOverlayEnabled()); + QVERIFY(settings.showLockScope()); + QVERIFY(settings.showDiagnostics()); + const QJsonObject document = QJsonDocument::fromJson(AppSettings::instance() + .value(QStringLiteral("WfmApplet")).toString().toUtf8()).object(); + QCOMPARE(document.value(QStringLiteral("other")).toInt(), 17); + QCOMPARE(document.value(QStringLiteral("ui")).toObject().value(QStringLiteral("retained")).toString(), QStringLiteral("yes")); + settings.setBroadcastOverlayEnabled(false); + QCOMPARE(changed.count(), 1); + } + void sameLocalEntryReplacesSongWithoutSpotOrCommandSideEffects() + { + RadioModel model; + Backend* source = attach(model); + WfmBroadcastOverlay overlay; + overlay.bind(&model, model.slice(3)->panId()); + QCOMPARE(overlay.records().size(), 1); + QCOMPARE(overlay.records().first().sliceId, 3); + QVERIFY(overlay.records().first().displayText().startsWith(QStringLiteral("Digital · P1"))); + QSignalSpy changed(&overlay, &WfmBroadcastOverlay::overlaysChanged); + for (int i = 0; i < 30; ++i) { + HdFmReception value = reception(); + value.observationSequence = i + 2; + value.title = QStringLiteral("Song %1 ").arg(i); + SliceDelta delta; delta.hdFmReception = value; + emit source->sliceChanged(3, delta); + QCOMPARE(overlay.records().size(), 1); + QCOMPARE(overlay.records().first().title, value.title); + } + QCOMPARE(changed.count(), 30); + QCOMPARE(source->commands, 0); + QVERIFY(model.spotModel().spots().isEmpty()); + WfmPresentationSettings::instance().setBroadcastOverlayEnabled(false); + QVERIFY(overlay.records().isEmpty()); + QVERIFY(model.slice(3)->hdFmReception().valid); + QCOMPARE(model.slice(3)->hdFmReception().title, QStringLiteral("Song 29 ")); + WfmPresentationSettings::instance().setBroadcastOverlayEnabled(true); + QCOMPARE(overlay.records().size(), 1); + QCOMPARE(overlay.records().first().title, QStringLiteral("Song 29 ")); + QCOMPARE(source->commands, 0); + } + void simultaneousMetadataRemainsWithItsOriginAcrossFocusParkingAndReuse() + { + RadioModel model; + Backend* source = attach(model); + SliceDelta second = hdSlice(1, 100900000); + second.hdFmReception->receiverEpoch = 44; + second.hdFmReception->stationName = QStringLiteral("Other station"); + second.hdFmReception->title = QStringLiteral("Other song"); + emit source->sliceChanged(4, second); + WfmBroadcastOverlay overlay; + overlay.bind(&model, model.slice(3)->panId()); + QCOMPARE(overlay.records().size(), 2); + const WfmBroadcastOverlayRecord firstRecord = overlay.records().at(0); + const WfmBroadcastOverlayRecord secondRecord = overlay.records().at(1); + QCOMPARE(firstRecord.sliceId, 3); + QCOMPARE(secondRecord.sliceId, 4); + QCOMPARE(secondRecord.frequencyHz, qint64{100900000}); + QCOMPARE(secondRecord.program, 1); + for (int pass = 0; pass < 20; ++pass) { + model.slice(3)->setActive(pass % 2 == 0); + model.slice(4)->setActive(pass % 2 != 0); + QCOMPARE(overlay.records().at(0), firstRecord); + QCOMPARE(overlay.records().at(1), secondRecord); + } + SliceDelta update; + update.hdFmReception = reception(); + update.hdFmReception->title = QStringLiteral("First station new song"); + emit source->sliceChanged(3, update); + QCOMPARE(overlay.records().at(0).title, QStringLiteral("First station new song")); + QCOMPARE(overlay.records().at(1), secondRecord); + WfmPresentationSettings::instance().setBroadcastOverlayEnabled(false); + QVERIFY(overlay.records().isEmpty()); + QCOMPARE(model.slice(3)->hdFmReception().title, QStringLiteral("First station new song")); + QCOMPARE(model.slice(4)->hdFmReception().title, QStringLiteral("Other song")); + WfmPresentationSettings::instance().setBroadcastOverlayEnabled(true); + QCOMPARE(overlay.records().size(), 2); + QCOMPARE(overlay.records().at(1), secondRecord); + update = {}; update.inCapture = false; + emit source->sliceChanged(3, update); + QCOMPARE(overlay.records().size(), 1); + QCOMPARE(overlay.records().first(), secondRecord); + emit source->sliceRemoved(3); + QCOMPARE(overlay.records().first(), secondRecord); + SliceDelta replacement = hdSlice(); + replacement.hdFmReception = HdFmReception{}; + emit source->sliceChanged(3, replacement); + QCOMPARE(overlay.records().size(), 1); + QCOMPARE(overlay.records().first(), secondRecord); + replacement.hdFmReception = reception(); + replacement.hdFmReception->receiverEpoch = 99; + replacement.hdFmReception->stationName = QStringLiteral("Replacement station"); + emit source->sliceChanged(3, replacement); + QCOMPARE(overlay.records().size(), 2); + QCOMPARE(overlay.records().at(0).receiverEpoch, quint64{99}); + QCOMPARE(overlay.records().at(0).stationName, QStringLiteral("Replacement station")); + QCOMPARE(overlay.records().at(1), secondRecord); + QCOMPARE(source->commands, 0); + QVERIFY(model.spotModel().spots().isEmpty()); + } + + void acceptedIdentityAndPanOwnershipClearOldPresentation() + { + RadioModel model; + Backend* source = attach(model); + WfmBroadcastOverlay overlay; + const QString pan = model.slice(3)->panId(); + overlay.bind(&model, QStringLiteral("foreign-pan")); + QVERIFY(overlay.records().isEmpty()); + overlay.bind(&model, pan); + QCOMPARE(overlay.records().size(), 1); + model.slice(3)->setHdProgram(1); // intent must not clear the accepted entry + QCOMPARE(overlay.records().first().program, 0); + SliceDelta change; + change.hdProgram = 1; + emit source->sliceChanged(3, change); + QVERIFY(overlay.records().isEmpty()); + change = {}; + change.hdFmReception = reception(1); + emit source->sliceChanged(3, change); + QCOMPARE(overlay.records().first().program, 1); + change = {}; change.frequency = 101.1; + emit source->sliceChanged(3, change); + QVERIFY(overlay.records().isEmpty()); + change = {}; change.hdFmReception = reception(1); // old frequency is rejected + emit source->sliceChanged(3, change); + QVERIFY(overlay.records().isEmpty()); + change.hdFmReception = reception(1, 101100000); + emit source->sliceChanged(3, change); + QCOMPARE(overlay.records().size(), 1); + change = {}; change.inCapture = false; + emit source->sliceChanged(3, change); + QVERIFY(overlay.records().isEmpty()); + change.inCapture = true; + emit source->sliceChanged(3, change); + QCOMPARE(overlay.records().size(), 1); + change = {}; change.mode = QStringLiteral("AM"); + emit source->sliceChanged(3, change); + QVERIFY(overlay.records().isEmpty()); + } + void disconnectCapabilityRetirementAndRebindCannotRetainLabels() + { + RadioModel model; + Backend* source = attach(model); + WfmBroadcastOverlay overlay; + overlay.bind(&model, model.slice(3)->panId()); + source->connected = false; + emit model.connectionStateChanged(false); + QVERIFY(overlay.records().isEmpty()); + source->connected = true; + emit model.connectionStateChanged(true); + QCOMPARE(overlay.records().size(), 1); + source->caps.broadcastFmReceive->hdStereo = false; + emit model.capabilitiesChanged(true, source->caps); + QVERIFY(overlay.records().isEmpty()); + source->caps.broadcastFmReceive->hdStereo = true; + emit model.capabilitiesChanged(true, source->caps); + QCOMPARE(overlay.records().size(), 1); + emit source->sliceRemoved(3); + QVERIFY(overlay.records().isEmpty()); + emit source->sliceChanged(3, hdSlice()); + QCOMPARE(overlay.records().size(), 1); + overlay.bind(nullptr, {}); + QVERIFY(overlay.records().isEmpty()); + emit source->sliceChanged(3, hdSlice()); + QVERIFY(overlay.records().isEmpty()); + auto other = std::make_unique(); + attach(*other); + overlay.bind(other.get(), other->slice(3)->panId()); + QCOMPARE(overlay.records().size(), 1); + other.reset(); + QVERIFY(overlay.records().isEmpty()); + } + void lossOfSyncClearsTextAndBoundedSlicesNeverAccumulateHistory() + { + RadioModel model; + Backend* source = attach(model); + WfmBroadcastOverlay overlay; + overlay.bind(&model, model.slice(3)->panId()); + HdFmReception lost = reception(); + lost.synced = false; + ++lost.observationSequence; + SliceDelta delta; delta.hdFmReception = lost; + emit source->sliceChanged(3, delta); + QVERIFY(overlay.records().isEmpty()); + for (int i = 0; i < 12; ++i) { emit source->sliceChanged(i, hdSlice()); } + QCOMPARE(overlay.records().size(), 8); + QVERIFY(model.spotModel().spots().isEmpty()); + } +}; +int main(int argc, char** argv) +{ + TestSettingsProfile profile(QStringLiteral("broadcast-overlay")); + if (!profile.isValid()) { return 1; } + QApplication app(argc, argv); + AppSettings::instance().load(); + BroadcastOverlayTest test; + return QTest::qExec(&test, argc, argv); +} +#include "broadcast_overlay_test.moc" diff --git a/tests/dax_receive_applet_test.cpp b/tests/dax_receive_applet_test.cpp index 503b9d090..3232cd34e 100644 --- a/tests/dax_receive_applet_test.cpp +++ b/tests/dax_receive_applet_test.cpp @@ -38,7 +38,7 @@ int main(int argc,char** argv) ReceiveBackend* source=backend.get(); source->caps.canTransmit=false; source->caps.maxSlices=8; - source->caps.receiveAudioExport=ReceiveAudioExport{{24000,48000},8}; + source->caps.receiveAudioExport=ReceiveAudioExport{{24000,44100,48000},8}; model.setBackendForTest(std::move(backend),QStringLiteral("test")); DaxApplet widget; widget.setRadioModel(&model); diff --git a/tests/decoder_pcm_adapter_test.cpp b/tests/decoder_pcm_adapter_test.cpp index 3b10b80c4..233faae5f 100644 --- a/tests/decoder_pcm_adapter_test.cpp +++ b/tests/decoder_pcm_adapter_test.cpp @@ -411,6 +411,16 @@ int main(int argc, char** argv) passthroughAndAdmission(receiver); sharedRxDemodLane(); continuousConversion(); + const auto hdInput = tone(44100, 1100, 44100); + const auto hdRegular = convert(hdInput, 44100, PcmLayout::Stereo, {4096}); + const auto hdSplit = convert(hdInput, 44100, PcmLayout::Stereo, {1, 17, 257, 511}); + check(hdRegular == hdSplit && hdRegular.size() > 23800 && hdRegular.size() <= 24000, + "HD 44.1-to24 conversion preserves rational duration and arbitrary chunk equivalence"); + check(hdRegular.size() > 2048 && std::abs(rms(hdRegular, 2048) - std::sqrt(0.5)) < 0.005, + "HD one-sided stereo is properly averaged before conversion"); + const auto hdRejected = convert(tone(44100, 16000, 44100), 44100, PcmLayout::Stereo, {257, 4095}); + check(hdRejected.size() > 2048 && rms(hdRejected, 2048) < 0.0001, + "HD 44.1-to24 conversion rejects out-of-band energy before aliasing"); transitionHistoryAndBounds(); independentConsumers(); extremeInputAndOrdering(); diff --git a/tests/hd_fm_iq_adapter_test.cpp b/tests/hd_fm_iq_adapter_test.cpp new file mode 100644 index 000000000..7555bd844 --- /dev/null +++ b/tests/hd_fm_iq_adapter_test.cpp @@ -0,0 +1,92 @@ +#include "core/backends/rtl/HdFmIqAdapter.h" +#include +#include +#include +#include +#include +#include +#include + +using Adapter = AetherSDR::rtl::HdFmIqAdapter; +namespace Policy = AetherSDR::SharedCapturePolicy; +static int failures = 0; +static void check(bool value, const char* text) +{ if (!value) { ++failures; std::fprintf(stderr, "FAIL: %s\n", text); } } +struct Sink final : Adapter::Sink { + std::vector> data; + bool hdIq(std::span input) noexcept override + { + if (input.empty() || input.size() % 2 || input.size() > 32768) { return false; } + for (std::size_t n = 0; n < input.size(); n += 2) { + if (!std::isfinite(input[n]) || !std::isfinite(input[n + 1])) { return false; } + data.emplace_back(input[n], input[n + 1]); + } + return true; + } +}; +static double magnitude(std::span> values, double hz) +{ + std::complex sum{}; + for (std::size_t n = 0; n < values.size(); ++n) { + const double phase = -2 * std::numbers::pi * hz * n / Adapter::kOutputRate; + sum += std::complex(values[n]) * std::complex(std::cos(phase), std::sin(phase)); + } + return std::abs(sum) / values.size(); +} +int main() +{ + static_assert(Adapter::kOutputRate == 744187.5); + constexpr std::array rates{900001, 1000000, 1536000, 1843200, 2000000, 2400000, 3000000}; + for (const int rate : rates) { + const Policy::CaptureDescriptor capture{1, 1, 100e6, double(rate), 0.45 * rate, 0.45 * rate}; + const Policy::SliceDescriptor footprint{0, 100e6 + 65000, -225000, 225000, 0, 3000, 3000}; + std::vector> input(static_cast(rate / 10)); + for (std::size_t n = 0; n < input.size(); ++n) { + const double first = 2 * std::numbers::pi * (65000 + 195000) * n / rate; + const double second = 2 * std::numbers::pi * (65000 - 195000) * n / rate; + input[n] = std::complex(0.2f * std::cos(first) + 0.3f * std::cos(second), + 0.2f * std::sin(first) + 0.3f * std::sin(second)); + } + std::array sinks; + for (std::size_t run = 0; run < 2; ++run) { + Adapter adapter(capture, footprint); + check(adapter.valid(), "full HD footprint admitted at every supported achieved rate"); + sinks[run].data.reserve(100000); + constexpr std::array chunks{1, 17, 263, 997, 8192}; + bool accepted = true; + for (std::size_t at = 0, block = 0; at < input.size(); ++block) { + const std::size_t size = std::min(input.size() - at, run ? chunks[block % chunks.size()] : 8192); + accepted = adapter.process(at, std::span(input).subspan(at, size), sinks[run]); + if (!accepted) { break; } + at += size; + } + check(accepted && adapter.valid(), "continuous arbitrary input partitions succeed"); + const double expected = input.size() * Adapter::kOutputRate / rate; + check(adapter.outputFrames() == sinks[run].data.size() + && adapter.outputFrames() <= expected + 1 && expected - adapter.outputFrames() < 4096, + "exact fractional conversion count differs only by bounded converter staging"); + } + check(sinks[0].data == sinks[1].data, "USB partitioning cannot alter the exact IQ sequence"); + if (sinks[0].data.size() > 10000) { + const auto settled = std::span(sinks[0].data).subspan(4096); + check(magnitude(settled, 195000) > 0.19 && magnitude(settled, -195000) > 0.29, + "both outer HD sidebands survive translation before analog filtering"); + check(magnitude(settled, 100000) < 0.003, "adapter does not substitute in-band analog energy"); + } else { check(false, "converter produces nonzero observable output"); } + std::printf("HD_IQ rate=%d output=%zu exactRate=744187.5 partitionEqual=%d\n", rate, + sinks[0].data.size(), sinks[0].data == sinks[1].data); + } + const Policy::CaptureDescriptor capture{1, 1, 100e6, 1000000, 450000, 450000}; + const Policy::SliceDescriptor footprint{0, 100e6, -225000, 225000, 0, 3000, 3000}; + std::array, 256> input{}; Sink sink; + Adapter gap(capture, footprint); + check(gap.process(100, input, sink) && !gap.process(357, input, sink) && !gap.valid(), + "a single missing capture sample terminally withdraws the adapter"); + Adapter nonfinite(capture, footprint); input[3] = {std::numeric_limits::quiet_NaN(), 0}; + check(!nonfinite.process(0, input, sink), "nonfinite RF is refused before decoder submission"); + auto outside = footprint; outside.carrierHz += 230000; + Adapter invalid(capture, outside); + check(!invalid.valid(), "full sideband footprint plus guards must fit, regardless of analog width"); + std::printf("ALL PASS — %d failure(s)\n", failures); + return failures ? 1 : 0; +} diff --git a/tests/hd_fm_receiver_test.cpp b/tests/hd_fm_receiver_test.cpp new file mode 100644 index 000000000..839762b1f --- /dev/null +++ b/tests/hd_fm_receiver_test.cpp @@ -0,0 +1,366 @@ +#include "core/backends/rtl/HdFmReceiver.h" +#include "core/backends/rtl/RtlReceivePipeline.h" +#include +#include +#include "core/Resampler.h" +// The pinned C API header does not declare C++ linkage guards. +extern "C" { +#include +} +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Registry = AetherSDR::rtl::RtlReceiverRegistry; +using Decoder = AetherSDR::rtl::Nrsc5FmDecoder; +using Raw = AetherSDR::rtl::HdFmRawReception; +using Identity = AetherSDR::rtl::HdFmAudioIdentity; +using AetherSDR::rtl::prepareHdFmReceiver; +static int failures = 0; +static void check(bool value, const char* text) +{ if (!value) { ++failures; std::fprintf(stderr, "FAIL: %s\n", text); } } +struct Control { + std::mutex mutex; + std::condition_variable changed; + unsigned requested = 0, done = 0; + int blocks = 0; + bool pauseAfter = false, paused = false, release = false, retireOnRelease = false; + void command(int count, bool pause = false) + { + const std::lock_guard lock(mutex); + blocks = count; pauseAfter = pause; paused = false; release = false; ++requested; + } + bool wait(bool forPause) + { + std::unique_lock lock(mutex); + return changed.wait_for(lock, std::chrono::seconds(3), [&] { return forPause ? paused : done == requested; }); + } + bool releaseWithLoss() + { + std::unique_lock lock(mutex); + retireOnRelease = true; release = true; changed.notify_all(); + return changed.wait_for(lock, std::chrono::seconds(3), [&] { return done == requested; }); + } + void releaseQuietly() + { const std::lock_guard lock(mutex); retireOnRelease = false; release = true; changed.notify_all(); } +}; +// Injects documented callback events into the production wrapper on its real +// private decoder thread. This is queue/lifecycle evidence, not RF decoding. +struct Pipe final : Decoder::Pipe { + Control& control; + std::array pcm{}; + explicit Pipe(Control& value) : control(value) + { + for (std::size_t i = 0; i < 2048; ++i) { + pcm[2 * i] = std::int16_t(11000 * std::sin(0.031 * i)); + pcm[2 * i + 1] = std::int16_t(8000 * std::cos(0.057 * i)); + } + } + bool valid() const noexcept override { return true; } + void audio(int blocks, Callback callback, void* context) + { + nrsc5_event_t sync{}; sync.event = NRSC5_EVENT_SYNC; callback(&sync, context); + nrsc5_event_t event{}; event.event = NRSC5_EVENT_AUDIO; event.audio.program = 0; + event.audio.data = pcm.data(); event.audio.count = pcm.size(); + for (int i = 0; i < blocks; ++i) { callback(&event, context); } + } + bool feed(std::span, Callback callback, void* context) noexcept override + { + std::unique_lock lock(control.mutex); + if (control.done == control.requested) { return true; } + const unsigned request = control.requested; + const int count = control.blocks; const bool pause = control.pauseAfter; + lock.unlock(); + if (count > 0) { audio(count, callback, context); } + lock.lock(); + if (pause) { + control.paused = true; control.changed.notify_all(); + control.changed.wait(lock, [&] { return control.release; }); + const bool loss = control.retireOnRelease; + lock.unlock(); + if (loss) { + nrsc5_event_t event{}; event.event = NRSC5_EVENT_LOST_SYNC; callback(&event, context); + audio(1, callback, context); + } + lock.lock(); + } + control.done = request; control.changed.notify_all(); + return true; + } +}; +struct Sink final : Registry::AudioSink { + Raw last; + std::uint64_t publication = 0, tapFrames = 0, speakerFrames = 0, nonzeroSpeaker = 0; + std::uint64_t tapEpoch = 0, tapNext = 0, producedSpeakerFrames = 0; + bool speakerPaired = false; + bool ordered = true, paired = false, zeroWhileAcquiring = true, releasePassed = false; + Identity lastSpeaker; + Control* retireAtReady = nullptr; + std::vector observations; + Sink() { observations.reserve(1024); } + void audioBlock(const Registry::ReceiverSpec&, std::uint64_t, std::span, std::span, bool) noexcept override + { check(false, "HD never impersonates the analog 48 kHz tap"); } + void hdObservation(const Registry::ReceiverSpec&, const Raw& raw) noexcept override + { + ordered &= raw.publicationSequence > publication; + publication = raw.publicationSequence; last = raw; observations.push_back(raw); + if (raw.audioValid && retireAtReady) { + auto* control = retireAtReady; retireAtReady = nullptr; + releasePassed = control->releaseWithLoss(); + } + } + void hdDecodedAudio(const Registry::ReceiverSpec&, const Identity& identity, std::uint64_t first, + std::span left, std::span right, bool discontinuity) noexcept override + { + if (identity.audioEpoch != tapEpoch) { tapEpoch = identity.audioEpoch; tapNext = 0; ordered &= discontinuity; } + ordered &= last.audioValid && identity.audioEpoch == last.audioEpoch && first == tapNext + && identity.producedMonotonicMs > 0 && left.size() == right.size(); + tapNext += left.size(); tapFrames += left.size(); + for (std::size_t i = 0; i < left.size(); ++i) { paired |= left[i] != right[i]; } + } + void hdSpeakerAudio(const Registry::ReceiverSpec&, const Identity& identity, std::uint64_t, + std::span left, std::span right, bool) noexcept override + { + lastSpeaker = identity; speakerFrames += left.size(); + if (identity.producedMonotonicMs) { producedSpeakerFrames += left.size(); } + ordered &= left.size() == right.size() && left.size() % 128 == 0; + for (std::size_t i = 0; i < left.size(); ++i) { + if (left[i] != 0 || right[i] != 0) { ++nonzeroSpeaker; } + speakerPaired |= left[i] != right[i]; + if (!last.audioValid) { zeroWhileAcquiring &= left[i] == 0 && right[i] == 0; } + ordered &= std::isfinite(left[i]) && std::isfinite(right[i]); + } + } +}; +static Registry::ReceiverSpec spec() +{ + Registry::ReceiverSpec value; + value.handle = {1, 2, 0}; value.passband = {0, 100e6, -90000, 90000, 0, 1000, 1000}; + value.capture = {1, 1, 100e6, 1000000, 450000, 450000}; value.extractRf = true; value.epoch = 7; + value.hdFm = AetherSDR::rtl::HdFmRecipe{0}; + return value; +} +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + // Query the actual converter before starting the worker or its freshness clock. + const int speakerDelayInput = AetherSDR::Resampler(44100, 48000, 2048).groupDelayInputFrames(); + const auto recipe = spec(); Control control; Sink sink; std::string error; + auto receiver = prepareHdFmReceiver(recipe, error, std::make_unique(control)); + check(bool(receiver), "injected pipe prepares the production bounded HD receiver"); + if (!receiver) { return 1; } + std::array, 8192> iq{}; + std::uint64_t captureFirst = 0, revision = 9; + const auto step = [&] { + const bool result = receiver->processCapture({1, recipe.capture, captureFirst, captureFirst == 0, iq, 8, revision}, sink); + captureFirst += iq.size(); return result; + }; + check(step() && sink.speakerFrames > 0 && sink.tapFrames == 0 && !sink.last.audioValid, + "fresh graph accepts the initial discontinuity and supplies intentional-zero speaker quanta without a decoded tap"); + // Four native frames fill playout. Hold the worker after its callbacks so + // loss can occur exactly during the readiness publication, before taps. + control.command(4, true); check(step() && control.wait(true), "worker reached deterministic post-audio hold"); + sink.retireAtReady = &control; + check(step() && sink.releasePassed, "loss injected between ready speaker buffer and tap dispatch"); + check(sink.tapFrames == 0 && !sink.last.audioValid && sink.zeroWhileAcquiring, + "replacement audio epoch cannot inherit old prefill or leak queued native tap"); + // The loss callback completed one replacement AUDIO block before returning. + // Drain that epoch's speaker queue before submitting any further AUDIO so + // its own insufficient prefill is observed independently of the reset. + check(step() && !sink.last.audioValid && sink.tapFrames == 0 + && sink.producedSpeakerFrames == 0 && sink.lastSpeaker.producedMonotonicMs == 0, + "partial replacement prefill stays acquiring with no native tap or produced speaker PCM"); + // Three more complete its own prebuffer, with no lost initial tap. + control.command(3); check(step() && control.wait(false), "new epoch produced its remaining prebuffer"); + const bool prefillStep = step(); + const bool prefillPassed = prefillStep && sink.last.audioValid && sink.tapFrames == 8192 + && sink.paired && sink.ordered; + if (!prefillPassed) { + const auto ms = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); + std::fprintf(stderr, + "HD prefill diagnostic: step=%d valid=%d synced=%d audioValid=%d tapFrames=%llu paired=%d " + "speakerFrames=%llu nonzeroSpeaker=%llu tapEpoch=%llu tapNext=%llu rawEpoch=%llu " + "speakerEpoch=%llu speakerRevision=%llu speakerProducedMs=%llu nowMs=%lld " + "publication=%llu observation=%llu audioSequence=%llu iqDrops=%llu pcmDrops=%llu underruns=%llu " + "ordered=%d zeroWhileAcquiring=%d observations=%zu\n", + prefillStep, sink.last.valid, sink.last.synced, sink.last.audioValid, + static_cast(sink.tapFrames), sink.paired, + static_cast(sink.speakerFrames), static_cast(sink.nonzeroSpeaker), + static_cast(sink.tapEpoch), static_cast(sink.tapNext), + static_cast(sink.last.audioEpoch), static_cast(sink.lastSpeaker.audioEpoch), + static_cast(sink.lastSpeaker.revision), static_cast(sink.lastSpeaker.producedMonotonicMs), + static_cast(ms), static_cast(sink.last.publicationSequence), + static_cast(sink.last.observationSequence), static_cast(sink.last.audioSequence), + static_cast(sink.last.iqDrops), static_cast(sink.last.pcmDrops), + static_cast(sink.last.playoutUnderruns), sink.ordered, sink.zeroWhileAcquiring, + sink.observations.size()); + } + check(prefillPassed, + "new-epoch prefill publishes all 8192 ordered original paired 44100 frames"); + // prewarm() consumes r8brain's no-output interval, but its acoustic delay + // remains. Advance only capture time: no extra decoder AUDIO event can + // hide a lost prefix, reset, or insufficient replacement-epoch prefill. + const std::uint64_t audioEpoch = sink.last.audioEpoch; + const std::uint64_t delayOutput = (static_cast(std::max(0, speakerDelayInput)) * 48000 + 44099) / 44100; + const std::uint64_t captureRate = static_cast(recipe.capture.achievedSampleRateHz); + const std::uint64_t maximumStepFrames = ((iq.size() * 48000 + captureRate - 1) / captureRate + 127) / 128 * 128; + const std::uint64_t minimumStepFrames = iq.size() * 48000 / captureRate / 128 * 128; + // Readiness can precede the final fixture step. Subtract all audio already + // consumed, and reserve one callback for the revision-preservation check. + const std::uint64_t remainingFrames = 8192 - std::min(8192, sink.producedSpeakerFrames); + const std::uint64_t availableSteps = remainingFrames / maximumStepFrames; + const std::uint64_t maximumDrainSteps = availableSteps > 0 ? availableSteps - 1 : 0; + const bool delayFits = speakerDelayInput >= 0 + && delayOutput + 128 <= sink.producedSpeakerFrames + maximumDrainSteps * minimumStepFrames; + bool drainSucceeded = true; + std::uint64_t drainSteps = 0; + if (prefillPassed && delayFits) { + while (drainSteps < maximumDrainSteps + && (sink.producedSpeakerFrames < delayOutput + 128 || !sink.nonzeroSpeaker || !sink.speakerPaired)) { + ++drainSteps; + if (!step()) { drainSucceeded = false; break; } + } + } + const bool speakerPassed = prefillPassed && delayFits && drainSucceeded + && sink.producedSpeakerFrames >= delayOutput + 128 && sink.nonzeroSpeaker > 0 && sink.speakerPaired + && sink.tapFrames == 8192 && sink.tapNext == 8192 && sink.tapEpoch == audioEpoch && sink.ordered + && sink.last.audioValid && sink.last.audioEpoch == audioEpoch && sink.lastSpeaker.audioEpoch == audioEpoch + && sink.lastSpeaker.revision == 9 && sink.lastSpeaker.producedMonotonicMs > 0 + && sink.last.iqDrops == 0 && sink.last.pcmDrops == 0 && sink.last.playoutUnderruns == 0; + if (!speakerPassed) { + std::fprintf(stderr, + "HD speaker diagnostic: delayInput=%d delayOutput=%llu steps=%llu/%llu producedFrames=%llu " + "nonzero=%llu paired=%d tapFrames=%llu epoch=%llu rawEpoch=%llu valid=%d ordered=%d " + "iqDrops=%llu pcmDrops=%llu underruns=%llu\n", + speakerDelayInput, static_cast(delayOutput), + static_cast(drainSteps), static_cast(maximumDrainSteps), + static_cast(sink.producedSpeakerFrames), static_cast(sink.nonzeroSpeaker), + sink.speakerPaired, static_cast(sink.tapFrames), static_cast(audioEpoch), + static_cast(sink.last.audioEpoch), sink.last.audioValid, sink.ordered, + static_cast(sink.last.iqDrops), static_cast(sink.last.pcmDrops), + static_cast(sink.last.playoutUnderruns)); + } + check(speakerPassed, "bounded capture-only playout produces nonzero distinct 48000 channels after converter delay without losing native taps"); + const std::uint64_t measuredSequence = sink.last.observationSequence; + check(sink.ordered && sink.zeroWhileAcquiring && sink.last.publicationSequence > 0, + "measurement and delivery transitions preserve order without false valid audio"); + revision = 10; + check(step() && sink.lastSpeaker.revision == 9 && sink.lastSpeaker.producedMonotonicMs > 0, + "buffered speaker prefix retains original token/time across a monitor-only revision"); + check(sink.last.observationSequence >= measuredSequence, "control changes do not invent decoder measurements"); + check(!receiver->processCapture({1, recipe.capture, captureFirst, true, iq, 8, revision}, sink) + && !sink.last.audioValid, + "later explicit discontinuity withdraws even at the expected contiguous sample position"); + control.releaseQuietly(); receiver.reset(); + + Control gap; Sink gapSink; + auto gapReceiver = prepareHdFmReceiver(recipe, error, std::make_unique(gap)); + check(gapReceiver && gapReceiver->processCapture({1, recipe.capture, 0, true, iq, 8, 9}, gapSink), + "independent gap fixture accepts its first marked boundary"); + check(gapReceiver && !gapReceiver->processCapture({1, recipe.capture, iq.size() + 1, false, iq, 8, 9}, gapSink) + && !gapSink.last.audioValid, + "later unmarked sample-position gap still withdraws"); + gapReceiver.reset(); + + // Global resident reservation covers offered + active decoder workers. + Control one, two, three; + auto a = prepareHdFmReceiver(recipe, error, std::make_unique(one)); + auto b = prepareHdFmReceiver(recipe, error, std::make_unique(two)); + auto c = prepareHdFmReceiver(recipe, error, std::make_unique(three)); + check(a && b && !c, "hard process-wide two-worker bound includes old/new resident sessions"); + a.reset(); b.reset(); + + Control retained; + auto reservation = WdspChannel::reserveChannels(1); + check(reservation.has_value(), "HD conservatively reserves the shared DSP pool"); + std::shared_ptr original = prepareHdFmReceiver(recipe, error, + std::make_unique(retained), std::move(reservation)); + auto reused = original; + reservation.reset(); original.reset(); + check(reused && !WdspChannel::reserveChannels(32), + "unused reservation follows shared HD receiver after original bank ownership retires"); + reused.reset(); + check(WdspChannel::reserveChannels(32).has_value(), "HD teardown returns the unused pool reservation"); + + Control overload; Sink overflowSink; + auto bounded = prepareHdFmReceiver(recipe, error, std::make_unique(overload)); + std::uint64_t first = 0; + const auto push = [&] { + const bool ok = bounded->processCapture({1, recipe.capture, first, false, iq, 8, 9}, overflowSink); + first += iq.size(); return ok; + }; + overload.command(0, true); check(push() && overload.wait(true), "worker held before bounded queue overflow"); + bool refused = false; + for (int i = 0; i < 34 && !refused; ++i) { refused = !push(); } + check(refused && !overflowSink.last.audioValid && overflowSink.last.iqDrops == 1, + "acquisition never blocks on a full IQ queue and withdraws truthfully"); + overload.releaseQuietly(); bounded.reset(); + + // Exercise the real Pipeline -> SampleReader -> native receiver dispatch. + // SampleReader marks this first delivery discontinuous even though the + // public pipeline caller passes only an ordinary first IQ block. + using Pipeline = AetherSDR::rtl::RtlReceivePipeline; + Pipeline::Transaction::State state; + state.token = {81, 4}; state.capture = recipe.capture; + state.hardware.centerHz = 100000000; state.hardware.sampleRateHz = 1000000; + Pipeline::Transaction::Receiver selected{recipe.passband, Pipeline::Transaction::Mode::Wfm}; + selected.passband.guardLowHz = WdspChannel::WbfmReceive::kRfTransitionGuardHz; + selected.passband.guardHighHz = WdspChannel::WbfmReceive::kRfTransitionGuardHz; + selected.wfmHdStereo = true; + state.receivers = {selected}; state.receivingIds = {0}; + auto pipeline = std::make_unique(1, true); + const bool submitted = pipeline->prepare(state, true); + bool adopted = false; + if (submitted) { + const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); + while (std::chrono::steady_clock::now() < deadline) { + const auto status = pipeline->service(); + if (status == Pipeline::Preparation::Failed) { break; } + if (status == Pipeline::Preparation::Ready) { adopted = pipeline->adopt(); break; } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } + check(adopted, "native HD pipeline prepares and adopts without hardware or an injected decoder"); + if (adopted) { + check(pipeline->process(0, iq) && !pipeline->needsRepair(), + "registry-marked first capture block starts the native HD graph without repair"); + Pipeline::HdFmObservation observation; + bool observed = false, observationsValid = true; + std::uint64_t audioEpoch = 0; + while (pipeline->takeHdObservation(observation)) { + observed = true; audioEpoch = observation.audioEpoch; + observationsValid &= observation.token == state.token && observation.slot == 0 + && observation.instance > 0 && observation.receiverEpoch > 0 && observation.captureEpoch > 0 + && audioEpoch > 0 && observation.reception.sessionId == state.token.session + && observation.reception.revision == state.token.revision && !observation.reception.audioValid; + } + check(observed && observationsValid, "initial acquiring observation carries the accepted publication identity"); + Pipeline::Packet packet; + std::uint64_t speakerFrames = 0; + bool packetsValid = true; + while (pipeline->takePacket(packet)) { + packetsValid &= packet.token == state.token && packet.slot == -1 && packet.sampleRateHz == 48000 + && packet.channelCount == 2 && packet.audioEpoch == audioEpoch && packet.producedMonotonicMs == 0 + && packet.frames > 0 && packet.frames <= 1024; + if (packet.frames <= 1024) { + packetsValid &= std::all_of(packet.samples.begin(), packet.samples.begin() + 2 * packet.frames, + [](float value) { return value == 0; }); + } + speakerFrames += packet.frames; + } + check(speakerFrames > 0 && packetsValid, "initial native pipeline supplies complete intentional-zero 48000 speaker PCM"); + check(pipeline->process(iq.size(), iq) && !pipeline->needsRepair(), + "native HD pipeline continues after its initial registry boundary"); + } + pipeline->stop(); pipeline.reset(); + check(QThreadPool::globalInstance()->waitForDone(5000), "native pipeline worker retires within the bounded test teardown"); + std::printf("%s — %d failure(s)\n", failures ? "FAIL" : "ALL PASS", failures); + return failures ? 1 : 0; +} diff --git a/tests/nrsc5_fm_decoder_test.cpp b/tests/nrsc5_fm_decoder_test.cpp new file mode 100644 index 000000000..6aaf08d52 --- /dev/null +++ b/tests/nrsc5_fm_decoder_test.cpp @@ -0,0 +1,98 @@ +#include "core/backends/rtl/Nrsc5FmDecoder.h" +// The pinned C API header does not declare C++ linkage guards. +extern "C" { +#include +} +#include +#include +#include +#include +#include +#include + +using Decoder = AetherSDR::rtl::Nrsc5FmDecoder; +using Raw = AetherSDR::rtl::HdFmRawReception; +using Identity = AetherSDR::rtl::HdFmAudioIdentity; +static int failures = 0; +static void check(bool value, const char* text) +{ if (!value) { ++failures; std::fprintf(stderr, "FAIL: %s\n", text); } } +struct Events final : Decoder::Pipe { + std::vector events; + bool good = true; + bool valid() const noexcept override { return true; } + bool feed(std::span, Callback callback, void* context) noexcept override + { for (const auto& event : events) { callback(&event, context); } events.clear(); return good; } +}; +struct Sink final : Decoder::Sink { + Decoder* decoder = nullptr; + std::vector identities; + std::vector firsts, retired; + std::vector left, right; + bool accept = true, statusFirst = true; + bool decoded(const Identity& identity, std::uint64_t first, std::span l, + std::span r) noexcept override + { + const auto& state = decoder->reception(); + statusFirst &= state.audioValid && state.audioEpoch == identity.audioEpoch + && state.audioMonotonicMs == identity.producedMonotonicMs && state.audioSequence > 0; + identities.push_back(identity); firsts.push_back(first); + left.insert(left.end(), l.begin(), l.end()); right.insert(right.end(), r.begin(), r.end()); + return accept; + } + void audioRetired(std::uint64_t epoch) noexcept override { retired.push_back(epoch); } +}; +int main() +{ + Sink sink; + auto pipe = std::make_unique(); auto* events = pipe.get(); + Raw initial; initial.receiverEpoch = 9; initial.frequencyHz = 100100000; initial.selectedProgram = 2; + Decoder decoder(initial, sink, std::move(pipe)); sink.decoder = &decoder; + std::array iq{}; + const auto submit = [&](nrsc5_event_t event, std::uint64_t revision = 3) { + events->events.push_back(event); return decoder.feed(iq, 7, revision); + }; + nrsc5_event_t event{}; event.event = NRSC5_EVENT_STATION_NAME; event.station_name.name = "KTEST\nFM"; + check(submit(event) && !decoder.reception().audioValid && sink.left.empty(), "station identity alone cannot establish valid audio"); + check(std::strcmp(decoder.reception().stationName.data(), "KTEST FM") == 0, "borrowed text is copied and controls bounded"); + event = {}; event.event = NRSC5_EVENT_SYNC; event.sync.freq_offset = 125.5f; + check(submit(event) && decoder.reception().synced && !decoder.reception().audioValid, "RF sync and selected audio validity are independent"); + event = {}; event.event = NRSC5_EVENT_AUDIO_SERVICE; event.audio_service.program = 2; + check(submit(event) && decoder.reception().services[2].audioAvailable, "audio service declaration enables discovery before audio decodes"); + std::array pcm{}; + for (std::size_t i = 0; i < 2048; ++i) { pcm[2 * i] = std::int16_t(i - 1024); pcm[2 * i + 1] = std::int16_t(1024 - i); } + event = {}; event.event = NRSC5_EVENT_AUDIO; event.audio.program = 1; event.audio.data = pcm.data(); event.audio.count = pcm.size(); + check(submit(event) && sink.left.empty() && !decoder.reception().audioValid, "another program never drives selected PCM or green state"); + event.audio.program = 2; event.audio.flags = NRSC5_AUDIO_FLAGS_DECODING_ERROR; + check(submit(event) && sink.left.empty() && decoder.reception().services[2].audioAvailable, + "concealed audio does not decode or remove the discovered service"); + event.audio.flags = NRSC5_AUDIO_FLAGS_NONE; + check(submit(event) && submit(event) && sink.left.size() == 4096 && sink.firsts == std::vector{0, 2048}, + "selected 44100 stereo preserves ordered native sample counts"); + check(sink.statusFirst && sink.left[0] == -1024 / 32768.0f && sink.right[0] == 1024 / 32768.0f + && sink.identities.back().sessionId == 7 && sink.identities.back().revision == 3 + && sink.identities.back().producedMonotonicMs > 0, "status precedes distinct paired PCM with original publication identity/time"); + const auto epoch = decoder.reception().audioEpoch; + event.audio.flags = NRSC5_AUDIO_FLAGS_UNAVAILABLE; + check(submit(event) && !decoder.reception().audioValid && decoder.reception().audioEpoch == epoch + 1, + "missing selected audio retires the complete PCM epoch"); + event.audio.flags = NRSC5_AUDIO_FLAGS_NONE; + check(submit(event, 4) && sink.firsts.back() == 0 && sink.identities.back().revision == 4, + "reacquired audio starts its own sequence with the actual new token"); + event = {}; event.event = NRSC5_EVENT_MER; event.mer.lower = std::numeric_limits::infinity(); event.mer.upper = 12.0f; + check(submit(event) && !decoder.reception().merLowerDb && decoder.reception().merUpperDb == 12.0, + "nonfinite diagnostics do not enter observed models"); + event = {}; event.event = NRSC5_EVENT_LOST_SYNC; + check(submit(event) && !decoder.reception().synced && !decoder.reception().audioValid + && decoder.reception().stationName[0] == 0 && !decoder.reception().services[2].audioAvailable, + "loss clears station/program observations and retires queued audio"); + event = {}; event.event = NRSC5_EVENT_SYNC; + check(submit(event) && decoder.reception().syncLossCount == 1 && decoder.reception().reacquisitionCount == 1, + "loss/reacquisition counts report actual decoder events"); + event = {}; event.event = NRSC5_EVENT_AUDIO; event.audio.program = 2; event.audio.data = pcm.data(); event.audio.count = 3; + check(submit(event) && !decoder.reception().audioValid, "malformed odd stereo audio is refused"); + event.audio.count = pcm.size(); sink.accept = false; + check(!submit(event) && !decoder.valid() && !decoder.reception().audioValid, + "downstream bounded-queue refusal fails closed and retires partially published audio"); + std::printf("ALL PASS — %d failure(s)\n", failures); + return failures ? 1 : 0; +} diff --git a/tests/nrsc5_windows_compat_test.c b/tests/nrsc5_windows_compat_test.c new file mode 100644 index 000000000..050dcdbc1 --- /dev/null +++ b/tests/nrsc5_windows_compat_test.c @@ -0,0 +1,137 @@ +// Native behavior of the private nrsc5 Windows C adapters. No radio or socket. +#include +#include +#include +#include + +static int failures = 0; + +static void check(int value, const char *message) +{ + if (!value) { + ++failures; + fprintf(stderr, "FAIL: %s\n", message); + } +} + +static int nearValue(float value, double expected) +{ + return isfinite(value) && fabs((double)value - expected) < 0.00001; +} + +static void complexArithmetic(void) +{ + volatile float complex inputA = CMPLXF(3.0f, 4.0f); + volatile float complex inputB = CMPLXF(-2.0f, 0.5f); + const float complex a = inputA; + const float complex b = inputB; + const float complex product = a * b; + const float complex quotient = a / b; + check(nearValue(crealf(product), -8.0) && nearValue(cimagf(product), -6.5), + "native C complex multiplication preserves both components"); + check(nearValue(crealf(quotient), -4.0 / 4.25) && nearValue(cimagf(quotient), -9.5 / 4.25), + "native C complex division preserves both components"); + check(nearValue(cabsf(a), 5.0) && nearValue(cargf(a), atan2(4.0, 3.0)), + "CRT magnitude and argument agree with independent real arithmetic"); + const float complex conjugate = conjf(a); + check(nearValue(crealf(conjugate), 3.0) && nearValue(cimagf(conjugate), -4.0), + "conjugation preserves real and negates imaginary components"); + const float complex exponential = cexpf(CMPLXF(0.3f, -0.6f)); + check(nearValue(crealf(exponential), exp(0.3) * cos(-0.6)) + && nearValue(cimagf(exponential), exp(0.3) * sin(-0.6)), + "CRT exponential preserves amplitude and signed phase"); + const float complex imaginaryUnit = I * I; + check(nearValue(crealf(imaginaryUnit), -1.0) && nearValue(cimagf(imaginaryUnit), 0.0), + "imaginary unit participates in compiler complex arithmetic"); + const float complex zeros = conjf(CMPLXF(-0.0f, 0.0f)); + check(signbit(crealf(zeros)) && signbit(cimagf(zeros)), + "component construction and conjugation preserve signed zero"); + const double pi = acos(-1.0); + check(nearValue(cargf(CMPLXF(-1.0f, 0.0f)), pi) + && nearValue(cargf(CMPLXF(-1.0f, -0.0f)), -pi), + "CRT argument preserves the signed-zero branch boundary"); + const float complex independent = CMPLXF(INFINITY, 2.0f); + check(isinf(crealf(independent)) && cimagf(independent) == 2.0f, + "component construction does not contaminate the other component"); +} + +enum { kWorkers = 4, kIterations = 20000 }; +static pthread_mutex_t plannerMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t changed; +static int ready = 0; +static int start = 0; +static int count = 0; +static volatile LONG workerFailures = 0; + +static void *contend(void *argument) +{ + (void)argument; + if (pthread_mutex_lock(&plannerMutex) != 0) { + InterlockedIncrement(&workerFailures); + return NULL; + } + ++ready; + pthread_cond_broadcast(&changed); + while (!start) { + if (pthread_cond_wait(&changed, &plannerMutex) != 0) { + InterlockedIncrement(&workerFailures); + pthread_mutex_unlock(&plannerMutex); + return NULL; + } + } + pthread_mutex_unlock(&plannerMutex); + for (int iteration = 0; iteration < kIterations; ++iteration) { + if (pthread_mutex_lock(&plannerMutex) != 0) { + InterlockedIncrement(&workerFailures); + return NULL; + } + ++count; + if (pthread_mutex_unlock(&plannerMutex) != 0) { + InterlockedIncrement(&workerFailures); + return NULL; + } + } + return NULL; +} + +static int mutexAndCondition(void) +{ + pthread_t workers[kWorkers]; + check(pthread_cond_init(&changed, NULL) == 0, "condition initializes"); + for (int worker = 0; worker < kWorkers; ++worker) { + if (pthread_create(&workers[worker], NULL, contend, NULL) != 0) { + check(0, "native CRT worker starts"); + return 0; + } + } + check(pthread_mutex_lock(&plannerMutex) == 0, "static planner mutex locks"); + while (ready != kWorkers) { + check(pthread_cond_wait(&changed, &plannerMutex) == 0, + "condition releases and reacquires the static mutex"); + } + start = 1; + check(pthread_cond_broadcast(&changed) == 0, "broadcast wakes waiting workers"); + check(pthread_mutex_unlock(&plannerMutex) == 0, "static planner mutex unlocks"); + for (int worker = 0; worker < kWorkers; ++worker) { + check(pthread_join(workers[worker], NULL) == 0, "native CRT worker joins and closes"); + } + check(workerFailures == 0 && count == kWorkers * kIterations, + "contended static planner mutex protects every update"); + pthread_mutex_t initialized; + check(pthread_mutex_init(&initialized, NULL) == 0 + && pthread_mutex_lock(&initialized) == 0 + && pthread_mutex_unlock(&initialized) == 0, + "dynamically initialized mutex locks and unlocks"); + return 1; +} + +int main(void) +{ + complexArithmetic(); + if (!mutexAndCondition()) { + return 1; + } + printf("%s - %d failure(s), %d protected updates\n", + failures ? "FAIL" : "ALL PASS", failures, count); + return failures ? 1 : 0; +} diff --git a/tests/pcm_frame_test.cpp b/tests/pcm_frame_test.cpp index 51d74d002..7741b15a7 100644 --- a/tests/pcm_frame_test.cpp +++ b/tests/pcm_frame_test.cpp @@ -56,7 +56,7 @@ void formatsAndOwnership() check(!producer.produce({0.0f, std::numeric_limits::infinity()}), "infinity rejected"); const auto next = producer.produce({0.25f, -0.25f}); check(next->firstSample() == 6, "malformed input never advances the cursor"); - check(!producer.setFormat({44100, PcmLayout::Stereo}), "unsupported producer rate refused"); + check(!producer.setFormat({44100, PcmLayout::Stereo}), "unsupported speaker rate refused"); check(!producer.setFormat({24000, static_cast(9)}), "unknown layout refused"); check(next->current(), "refused format change preserves valid stream"); check(producer.setFormat({48000, PcmLayout::Stereo}), "48 kHz contract supported"); @@ -220,6 +220,19 @@ int main(int argc, char** argv) qRegisterMetaType(); check(QMetaType::fromName("AetherSDR::PcmFrame").isValid(), "name-based metatype registered"); formatsAndOwnership(); + PcmProducer hd; + check(hd.start(PcmPurpose::Slice, 0, {44100, PcmLayout::Stereo}), "native HD slice keeps actual 44100 stereo format"); + const auto hdFrame = hd.produce({0.25f, -0.5f, 0.75f, -0.125f}); + check(hdFrame && hdFrame->frameCount() == 2 && hdFrame->samples()[1] == -0.5f + && hdFrame->stream().format.sampleRateHz == 44100 && hdFrame->legacyStereo24().isEmpty(), + "HD tap preserves independent LR and never masquerades as legacy24"); + PcmFrameGate hdGate; + check(hdFrame && hdGate.accept(*hdFrame), "HD native slice reaches typed consumer gate"); + check(hd.setFormat({48000, PcmLayout::Stereo}) && !hdFrame->current(), "HD format transition revokes queued tap"); + PcmProducer wrongDomain; + check(!wrongDomain.start(PcmPurpose::Speaker, -1, {44100, PcmLayout::Stereo}) + && !wrongDomain.start(PcmPurpose::Auxiliary, -1, {44100, PcmLayout::Stereo}), + "HD tap support cannot silently expand speaker or auxiliary DSP domains"); identityContinuityAndQueueing(app); boundedIndependentStreams(); concurrentRevocation(); diff --git a/tests/rtl_capture_transaction_test.cpp b/tests/rtl_capture_transaction_test.cpp index 007d94ed1..a003b402b 100644 --- a/tests/rtl_capture_transaction_test.cpp +++ b/tests/rtl_capture_transaction_test.cpp @@ -453,6 +453,90 @@ int main() "DC placement does not switch automatic front-end mode across 24 MHz"); check(job->target.receivers == next.receivers, "band-edge placement never rewrites RF or filters"); } + // HD admission uses the complete digital sidebands, without rewriting the + // analog passband that the operator will recover on returning to Stereo. + { + T tx({8, 1}); Device usb; if (!establish(tx, usb)) { return 1; } + auto next = desired(); + next.receivers[0].passband.carrierHz = 100'900'000; + check(bool(tx.submit(next)), "analog edge receiver fits the current capture"); + auto job = tx.takeWork(); + if (job) { tx.complete(T::execute(*job, usb)); } + check(tx.confirmed()->receivingIds == std::vector{0}, "analog edge receiver is receiving"); + const auto analog = tx.confirmed()->receivers[0].passband; + const int writesBefore = usb.writes; + next.receivers[0].wfmHdStereo = true; + next.receivers[0].hdProgram = 7; + check(bool(tx.submit(next)), "HD edge receiver remains configured while parked"); + job = tx.takeWork(); + check(job && !job->hardwareChanged && job->target.receivingIds.empty(), + "digital sidebands that do not fit are parked even though analog audio would fit"); + check(!tx.confirmed()->receivers[0].wfmHdStereo && tx.confirmed()->receivers[0].hdProgram == 0, + "HD policy and program remain unaccepted before decoder acknowledgment"); + if (job) { + check(tx.complete(T::execute(*job, usb)) == T::Completion::Published, + "parked HD selection is accepted only with its matching acknowledgment"); + } + check(usb.writes == writesBefore && tx.confirmed()->receivers[0].passband == analog, + "parking HD preserves USB center and the saved analog passband"); + next.followReceiverId = 0; + check(bool(tx.submit(next)), "explicit HD selection can follow its full RF sidebands"); + job = tx.takeWork(); + check(job && job->hardwareChanged && job->target.receivingIds == std::vector{0}, + "HD follow recenters capture to admit the complete digital receiver"); + if (job) { + check(tx.complete(T::execute(*job, usb)) == T::Completion::Published, + "HD follow publishes after verified readback"); + } + check(tx.confirmed()->receivers[0].passband == analog + && tx.confirmed()->receivers[0].wfmHdStereo && tx.confirmed()->receivers[0].hdProgram == 7, + "HD capture relocation preserves receiver identity and independent settings"); + next.followReceiverId.reset(); next.hardware.centerHz = 105'000'000; + check(bool(tx.submit(next)), "free RF pan can leave an HD receiver behind"); + job = tx.takeWork(); + check(job && job->target.receivingIds.empty() && job->target.receivers == next.receivers, + "free pan parks HD without deleting or changing its configured recipe"); + if (job) { tx.complete(T::execute(*job, usb)); } + } + for (double carrier : {100'852'000.0, 100'852'001.0}) { + T tx({8, 1}); Device usb; if (!establish(tx, usb)) { return 1; } + auto next = desired(); next.receivers[0].wfmHdStereo = true; + next.receivers[0].passband.carrierHz = carrier; + check(bool(tx.submit(next)), "HD capture-edge configuration admitted"); + const auto job = tx.takeWork(); + check(job && (job->target.receivingIds == std::vector{0}) == (carrier == 100'852'000.0), + "HD admission includes exactly the 225 kHz sideband plus 3 kHz guard"); + } + { + T tx({8, 1}); Device usb; tx.beginSession(); + auto next = desired(); next.hardware.sampleRateHz = 225'001; + check(bool(tx.submit(next)), "narrow capture accommodates the saved analog passband"); + auto job = tx.takeWork(); + if (job) { tx.complete(T::execute(*job, usb)); } + const auto before = *tx.confirmed(); const int writesBefore = usb.writes; + next.receivers[0].wfmHdStereo = true; next.followReceiverId = 0; + check(!tx.submit(next) && !tx.takeWork() && usb.writes == writesBefore + && tx.confirmed()->token == before.token && tx.confirmed()->receivers == before.receivers, + "capture too narrow for HD refuses the request and retains accepted analog state"); + } + for (int invalid : {-1, 8}) { + T tx({8, 1}); Device usb; if (!establish(tx, usb)) { return 1; } + auto next = desired(); next.receivers[0].wfmHdStereo = true; + next.receivers[0].hdProgram = invalid; + const auto before = tx.confirmed()->token; + check(!tx.submit(next) && !tx.takeWork() && tx.confirmed()->token == before, + "out-of-range HD program never enters a receiver transaction"); + } + for (int invalid = 0; invalid < 3; ++invalid) { + T tx({8, 1}); Device usb; if (!establish(tx, usb)) { return 1; } + auto next = desired(); next.receivers[0].wfmHdStereo = true; + auto& band = next.receivers[0].passband; + if (invalid == 0) { band.filterLowHz = std::numeric_limits::quiet_NaN(); } + if (invalid == 1) { band.filterLowHz = band.filterHighHz; } + if (invalid == 2) { band.guardLowHz = -1; } + check(!tx.submit(next) && !tx.takeWork(), + "HD effective width cannot hide malformed saved analog filters or guards"); + } std::fprintf(stderr, "rtl_capture_transaction_test: %d checks, %d failures\n", checks, failures); return failures ? 1 : 0; } diff --git a/tests/rtl_model_acceptance_test.cpp b/tests/rtl_model_acceptance_test.cpp index 43324c102..9a889b4b7 100644 --- a/tests/rtl_model_acceptance_test.cpp +++ b/tests/rtl_model_acceptance_test.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,49 @@ struct RtlCaptureBackendTestAccess { QThread::msleep(510); backend.expireWfmObservations(); } + static void hd(RtlSdrBackend& backend, const RtlReceivePipeline::HdFmObservation& observation) + { backend.observeHd(observation); } + static void expireHd(RtlSdrBackend& backend) { backend.expireHdObservations(); } + static HdFmReception hdReception(const RtlSdrBackend& backend, int id) + { return backend.m_hdReception[id]; } + static bool refusesProgramDuringPendingTune(RtlSdrBackend& backend, SliceModel& slice, + double frequencyHz, int program) + { + if (!idle(backend)) { return false; } + auto desired = backend.m_requested; + const auto receiver = std::ranges::find_if(desired.receivers, + [&slice](const auto& value) { return value.passband.stableId == slice.sliceId(); }); + if (receiver == desired.receivers.end()) { return false; } + receiver->passband.carrierHz = frequencyHz; + desired.followReceiverId = slice.sliceId(); + const auto savedCapture = backend.m_capture; + const auto savedRequested = backend.m_requested; + const auto savedStatus = backend.m_captureStatus; + const auto savedDrag = backend.m_dragCapture; + const bool savedPendingDrag = backend.m_pendingDrag; + const auto savedViewport = backend.m_pendingViewport; + const auto savedExtensions = backend.m_pendingExtensionRequests; + const auto submitted = backend.m_capture.submit(desired); + // Hold the production transaction after dispatch ownership is claimed, + // but never hand its work to the injected device. A missing refusal + // can only coalesce intent here; it cannot advance hardware or adoption. + const auto heldWork = backend.m_capture.takeWork(); + bool refused = false; + if (submitted && heldWork) { + backend.m_requested = desired; + slice.setHdProgram(program); + refused = backend.m_capture.requested() == submitted.token + && backend.m_requested.receivers == desired.receivers; + } + backend.m_capture = savedCapture; + backend.m_requested = savedRequested; + backend.m_captureStatus = savedStatus; + backend.m_dragCapture = savedDrag; + backend.m_pendingDrag = savedPendingDrag; + backend.m_pendingViewport = savedViewport; + backend.m_pendingExtensionRequests = savedExtensions; + return refused; + } static void spectrum(RtlSdrBackend& backend, const QByteArray& frame, T::Token token) { emit backend.m_worker->spectrumFrameReady(token.session, token.revision, 0, frame); } }; @@ -127,6 +171,242 @@ static std::uint64_t monotonicMs() std::chrono::steady_clock::now().time_since_epoch()).count(); } +static void hdModelBoundary() +{ + SliceModel model(3); + int choices = 0, programs = 0; + QObject::connect(&model, &SliceModel::wfmAudioModeRequested, &model, [&](WfmAudioMode) { ++choices; }); + QObject::connect(&model, &SliceModel::hdProgramRequested, &model, [&](int) { ++programs; }); + model.setWfmAudioMode(WfmAudioMode::HdStereo); model.setHdProgram(7); + model.setWfmAudioMode(static_cast(99)); model.setHdProgram(-1); model.setHdProgram(8); + check(choices == 1 && programs == 1 && model.wfmAudioMode() == WfmAudioMode::Stereo + && model.hdProgram() == 0, "typed HD setters validate intent without publishing accepted state"); + HdFmReception reception; + reception.valid = true; reception.sessionId = 1; reception.receiverEpoch = 2; reception.revision = 3; + reception.frequencyHz = 100'000'000; reception.selectedProgram = 0; + reception.synced = true; reception.audioValid = true; reception.observationSequence = 1; + reception.services = {{0, QStringLiteral("News\nHD"), true}}; + reception.stationName = QString(100, QLatin1Char('x')); + reception.title = QStringLiteral("Title\n") + QChar(0x202e) + QStringLiteral("Artist"); + reception.cber = 0.01; reception.merLowerDb = 12.0; + SliceDelta delta; delta.mode = QStringLiteral("WFM"); delta.frequency = 100.0; + delta.wfmAudioMode = WfmAudioMode::HdStereo; delta.hdProgram = 0; delta.hdFmReception = reception; + model.applyChanges(delta); + check(model.hdFmReception().valid && model.hdFmReception().audioValid + && model.hdFmReception().stationName.size() == 64 + && model.hdFmReception().services.front().name == QStringLiteral("News HD") + && model.hdFmReception().title == QStringLiteral("Title Artist"), + "accepted HD snapshot bounds and normalizes broadcast text before model publication"); + delta = {}; reception.cber = std::numeric_limits::quiet_NaN(); delta.hdFmReception = reception; + model.applyChanges(delta); + check(!model.hdFmReception().valid, "nonfinite reception metric cannot enter the model"); + reception.cber = 0.01; reception.services.push_back(reception.services.front()); + delta.hdFmReception = reception; model.applyChanges(delta); + check(!model.hdFmReception().valid, "duplicate discovered programs cannot enter the model"); + reception.services.resize(1); reception.synced = false; + delta.hdFmReception = reception; model.applyChanges(delta); + check(model.hdFmReception().valid && !model.hdFmReception().audioValid + && model.hdFmReception().services.isEmpty() && model.hdFmReception().title.isEmpty() + && !model.hdFmReception().merLowerDb, + "sync loss clears decoded metadata, service choices and audio claims"); + reception.synced = true; delta.hdFmReception = reception; model.applyChanges(delta); + delta = {}; delta.frequency = 100.1; model.applyChanges(delta); + check(!model.hdFmReception().valid, "retuning invalidates HD reception without waiting for new metadata"); + delta = {}; delta.frequency = 100.0; delta.hdFmReception = reception; model.applyChanges(delta); + delta = {}; delta.hdProgram = 1; model.applyChanges(delta); + check(!model.hdFmReception().valid, "program change invalidates the old program's reception"); +} + +static void hdAcceptedControls(RadioModel& model, rtl::RtlSdrBackend& backend, + SliceModel& slice, CaptureClock& captureClock, + const RadioSettingsScope& scope) +{ + using Access = rtl::RtlCaptureBackendTestAccess; + using Observation = rtl::RtlReceivePipeline::HdFmObservation; + const auto feature = backend.capabilities().broadcastFmReceive; + RadioModelSliceLifecycleTestAccess::flush(model); + const auto before = scope.featureExact("RtlSlices"); + const auto beforeState = Access::state(backend); + slice.setWfmAudioMode(static_cast(99)); slice.setHdProgram(-1); slice.setHdProgram(8); + check(Access::idle(backend) && Access::state(backend).token == beforeState.token, + "invalid HD controls never reach a transaction"); + slice.setWfmAudioMode(WfmAudioMode::HdStereo); + check(slice.wfmAudioMode() == WfmAudioMode::Stereo && scope.featureExact("RtlSlices") == before, + "HD mode request neither changes accepted selection nor persists pending intent"); + if (!feature || !feature->hdStereo) { + check(Access::idle(backend) && Access::state(backend).token == beforeState.token, + "build without qualified native HD refuses HD mode without queuing work"); + RadioModelSliceLifecycleTestAccess::flush(model); + check(scope.featureExact("RtlSlices") == before, "unavailable HD selection cannot overwrite analog settings"); + return; + } + check(waitFor([&] { captureClock.advance(); return Access::idle(backend) + && slice.wfmAudioMode() == WfmAudioMode::HdStereo; }), "native HD selection waits for receiver adoption"); + RadioModelSliceLifecycleTestAccess::flush(model); + const auto hdSaved = RtlSliceSettings(scope).load().document.slices.value(3); + check(hdSaved.wfmHdStereo && !hdSaved.wfmForceMono && hdSaved.hdProgram == 0 + && hdSaved.filterLowHz == -90000 && hdSaved.filterHighHz == 90000 && hdSaved.wfmDeemphasisUs == 50, + "only adopted HD selection persists while the analog filter and deemphasis remain intact"); + slice.setHdProgram(1); + check(Access::idle(backend) && slice.hdProgram() == 0, + "program choice requires a currently discovered audio service"); + + Observation current; + current.slot = 3; current.token = Access::state(backend).token; + current.instance = 9001; current.receiverEpoch = 9002; current.audioEpoch = 9003; current.captureEpoch = 9004; + auto& raw = current.reception; + raw.valid = true; raw.sessionId = current.token.session; raw.revision = current.token.revision; + raw.receiverEpoch = current.receiverEpoch; raw.audioEpoch = current.audioEpoch; + raw.frequencyHz = static_cast(std::llround(slice.frequency() * 1e6)); + raw.selectedProgram = 0; raw.synced = true; raw.audioValid = true; + raw.publicationSequence = 1000; raw.observationSequence = 1000; raw.audioSequence = 1000; + raw.observationMonotonicMs = monotonicMs(); raw.audioMonotonicMs = raw.observationMonotonicMs; + raw.services[0].program = 0; raw.services[0].audioAvailable = true; + raw.services[1].program = 1; raw.services[1].audioAvailable = true; + raw.services[2].program = 2; raw.services[2].audioAvailable = false; + raw.cber = 0.01; raw.merLowerDb = 12.0; + std::copy_n("Station", 7, raw.stationName.begin()); + Access::hd(backend, current); + check(slice.hdFmReception().valid && slice.hdFmReception().synced && slice.hdFmReception().audioValid + && slice.hdFmReception().stationName == QStringLiteral("Station"), + "current accepted decoder observation publishes real HD service and audio state"); + const auto accepted = Access::hdReception(backend, 3); + for (int invalid = 0; invalid < 12; ++invalid) { + auto stale = current; + ++stale.reception.publicationSequence; ++stale.reception.observationSequence; + if (invalid == 0) { --stale.token.revision; --stale.reception.revision; } + if (invalid == 1) { ++stale.token.session; ++stale.reception.sessionId; } + if (invalid == 2) { ++stale.reception.frequencyHz; } + if (invalid == 3) { stale.reception.selectedProgram = 1; } + if (invalid == 4) { stale.reception.observationMonotonicMs = monotonicMs() - 501; } + if (invalid == 5) { stale.reception.observationMonotonicMs = monotonicMs() + 10000; } + if (invalid == 6) { stale.reception.observationMonotonicMs = 0; } + if (invalid == 7) { stale.reception.cber = std::numeric_limits::quiet_NaN(); } + if (invalid == 8) { stale.reception.services[2].program = 1; } + if (invalid == 9) { --stale.audioEpoch; --stale.reception.audioEpoch; } + if (invalid == 10) { stale.reception.observationSequence = raw.observationSequence - 1; } + if (invalid == 11) { stale.reception.services[3].program = -2; } + Access::hd(backend, stale); + check(Access::hdReception(backend, 3) == accepted, + "stale, mismatched or malformed HD observations cannot replace accepted reception"); + } + slice.setHdProgram(2); slice.setHdProgram(7); + check(Access::idle(backend) && slice.hdProgram() == 0, + "data-only and undiscovered services cannot be selected as audio programs"); + std::thread foreign([&] { slice.setWfmAudioMode(WfmAudioMode::Mono); slice.setHdProgram(1); }); + foreign.join(); + check(Access::idle(backend) && slice.wfmAudioMode() == WfmAudioMode::HdStereo && slice.hdProgram() == 0, + "HD controls from a foreign thread cannot dispatch through the model"); + + // Acquisition readiness can change between decoder measurements. A new + // publication may report that change without inventing a new source time. + auto notReady = current; + ++notReady.reception.publicationSequence; notReady.reception.audioValid = false; + Access::hd(backend, notReady); + check(slice.hdFmReception().valid && !slice.hdFmReception().audioValid, + "new playout publication can withdraw audio with the same decoder observation"); + raw.publicationSequence = notReady.reception.publicationSequence + 1; + Access::hd(backend, current); + check(slice.hdFmReception().audioValid, + "ready publication uses existing fresh producer evidence without requiring a new decoder block"); + + for (const auto invalidAudioTime : {quint64{0}, quint64{monotonicMs() + 10000}}) { + ++raw.publicationSequence; + raw.audioMonotonicMs = invalidAudioTime; + Access::hd(backend, current); + check(!slice.hdFmReception().audioValid, + "invalid current PCM timestamp cannot borrow an earlier fresh audio claim"); + ++raw.publicationSequence; + raw.audioMonotonicMs = monotonicMs(); + Access::hd(backend, current); + check(slice.hdFmReception().audioValid, "valid source timestamp restores current selected PCM"); + } + + // No event processing while time advances: exercise the producer clock, + // including a service request before the owner expiry timer gets a turn. + QThread::msleep(510); + slice.setHdProgram(1); + check(Access::idle(backend) && slice.hdProgram() == 0, + "old discovered service cannot authorize a program request before expiry timer delivery"); + ++raw.publicationSequence; ++raw.observationSequence; raw.observationMonotonicMs = monotonicMs(); + Access::hd(backend, current); + check(Access::hdReception(backend, 3).valid && Access::hdReception(backend, 3).synced + && !Access::hdReception(backend, 3).audioValid, + "fresh metadata cannot renew old PCM producer time or retain Audio valid"); + ++raw.publicationSequence; ++raw.observationSequence; ++raw.audioSequence; + raw.observationMonotonicMs = monotonicMs(); raw.audioMonotonicMs = raw.observationMonotonicMs; + Access::hd(backend, current); + check(slice.hdFmReception().audioValid, "new selected-program PCM restores the audio observation"); + QThread::msleep(510); + Access::expireHd(backend); + check(!slice.hdFmReception().valid, "stopped HD producer expires all reception and service claims"); + auto cached = current; + cached.reception.observationMonotonicMs = monotonicMs(); cached.reception.audioMonotonicMs = monotonicMs(); + Access::hd(backend, cached); + check(!slice.hdFmReception().valid, "duplicate decoder observation cannot become fresh by later receipt"); + ++raw.publicationSequence; ++raw.observationSequence; ++raw.audioSequence; + raw.observationMonotonicMs = monotonicMs(); raw.audioMonotonicMs = raw.observationMonotonicMs; + Access::hd(backend, current); + check(slice.hdFmReception().valid && slice.hdFmReception().audioValid, + "new producer evidence can restore HD reception after timeout"); + + auto barrier = current; + ++barrier.reception.publicationSequence; + barrier.reception.valid = false; barrier.reception.synced = false; barrier.reception.audioValid = false; + barrier.reception.observationSequence = 0; barrier.reception.observationMonotonicMs = 0; + barrier.reception.audioSequence = 0; barrier.reception.audioMonotonicMs = 0; + barrier.reception.iqDrops = 2; barrier.reception.pcmDrops = 3; barrier.reception.playoutUnderruns = 4; + Access::hd(backend, barrier); + const auto hdHealth = backend.healthSnapshot(); + check(hdHealth.values.value(QStringLiteral("rtlHdIqDrops")).toULongLong() == 2 + && hdHealth.values.value(QStringLiteral("rtlHdPcmDrops")).toULongLong() == 3 + && hdHealth.values.value(QStringLiteral("rtlHdPlayoutUnderruns")).toULongLong() == 4, + "withdrawal retains observed HD processing faults for runtime diagnostics"); + check(!slice.hdFmReception().valid, + "current lifetime barrier clears reception even without decoder measurement time"); + Access::hd(backend, current); + check(!slice.hdFmReception().valid, "old acquisition publication cannot revive reception after its barrier"); + raw.publicationSequence = barrier.reception.publicationSequence + 1; + ++raw.observationSequence; ++raw.audioSequence; + raw.observationMonotonicMs = monotonicMs(); raw.audioMonotonicMs = raw.observationMonotonicMs; + Access::hd(backend, current); + check(slice.hdFmReception().valid && slice.hdFmReception().audioValid, + "new producer measurement can restore reception after a lifetime barrier"); + check(backend.healthSnapshot().values.value(QStringLiteral("rtlHdPlayoutUnderruns")).toULongLong() == 4, + "HD diagnostic totals cannot regress on a later receiver observation"); + + const auto beforeProgram = scope.featureExact("RtlSlices"); + const auto beforePendingTune = Access::state(backend); + check(Access::refusesProgramDuringPendingTune(backend, slice, + raw.frequencyHz + 200'000.0, 1), + "a discovered program at the accepted station cannot alter a pending tune to another station"); + RadioModelSliceLifecycleTestAccess::flush(model); + check(Access::idle(backend) && Access::state(backend).token == beforePendingTune.token + && std::abs(slice.frequency() * 1e6 - raw.frequencyHz) <= 0.5 && slice.hdProgram() == 0 + && slice.hdFmReception().valid && scope.featureExact("RtlSlices") == beforeProgram, + "pending-tune program refusal preserves accepted reception and persistent settings"); + slice.setHdProgram(1); + check(slice.hdProgram() == 0 && scope.featureExact("RtlSlices") == beforeProgram, + "discovered program request remains unaccepted and unpersisted until adoption"); + check(waitFor([&] { captureClock.advance(); return Access::idle(backend) && slice.hdProgram() == 1; }), + "matching HD receiver adoption confirms the selected program"); + RadioModelSliceLifecycleTestAccess::flush(model); + check(RtlSliceSettings(scope).load().document.slices.value(3).hdProgram == 1, + "only adopted program reaches persistent receiver settings"); + Access::hd(backend, current); + check(!slice.hdFmReception().valid || slice.hdFmReception().selectedProgram == 1, + "previous program revision cannot restore old service or title state"); + slice.setWfmAudioMode(WfmAudioMode::Mono); + check(slice.wfmAudioMode() == WfmAudioMode::HdStereo, "leaving HD remains pending until decoder adoption"); + check(waitFor([&] { captureClock.advance(); return Access::idle(backend) + && slice.wfmAudioMode() == WfmAudioMode::Mono; }), "accepted Mono leaves HD through the normal receiver transaction"); + check(slice.wfmForceMono() && !slice.hdFmReception().valid, + "accepted analog Mono clears HD reception and applies the real Mono policy"); + slice.setWfmAudioMode(WfmAudioMode::Stereo); + check(waitFor([&] { captureClock.advance(); return Access::idle(backend) + && slice.wfmAudioMode() == WfmAudioMode::Stereo; }), "Auto Stereo restored after HD lifecycle checks"); +} + static void analogBankRestoreAndSharedCapture() { using Access = rtl::RtlCaptureBackendTestAccess; @@ -273,26 +553,27 @@ static void analogBankSelection(int capacity = 4) model.slice(2)->setMode("WFM"); check(waitFor([&] { return settled() && model.slice(2)->mode() == "WFM"; }), "second WFM receiver independently adopted"); - model.slice(1)->setWfmForceMono(true); + model.slice(1)->setWfmAudioMode(WfmAudioMode::Mono); model.slice(1)->setWfmDeemphasis(50); model.slice(1)->setFilterWidth(-90000, 90000); - model.slice(2)->setWfmForceMono(false); + model.slice(2)->setWfmAudioMode(WfmAudioMode::Stereo); model.slice(2)->setWfmDeemphasis(75); model.slice(2)->setFilterWidth(-80000, 80000); check(waitFor([&] { return settled() && model.slice(1)->filterLow() == -90000 && model.slice(2)->filterLow() == -80000; }), "two independent WFM recipes adopt"); - check(model.slice(1)->wfmForceMono() + check(model.slice(1)->wfmAudioMode() == WfmAudioMode::Mono && model.slice(1)->wfmDeemphasisUs() == 50 - && !model.slice(2)->wfmForceMono() + && model.slice(2)->wfmAudioMode() == WfmAudioMode::Stereo && model.slice(2)->wfmDeemphasisUs() == 75 && selected(3), "WFM recipes and focus survive sibling publications"); const auto beforeRefusal = Access::state(backend); int warnings = 0; QObject::connect(&backend, &IRadioBackend::configurationWarning, &model, [&](const QString&) { ++warnings; }); + model.slice(1)->setWfmAudioMode(WfmAudioMode::HdStereo); model.slice(0)->setMode("AM"); check(Access::idle(backend) && Access::state(backend).token == beforeRefusal.token - && model.slice(0)->mode() == "FM" && model.slice(1)->wfmForceMono() - && warnings >= 1, "unsupported legacy combinations refuse without altering recipes"); + && model.slice(0)->mode() == "FM" && model.slice(1)->wfmAudioMode() == WfmAudioMode::Mono + && warnings >= 1, "unsupported HD and legacy combinations refuse without altering recipes"); model.slice(2)->setActive(true); auto* pan = model.panadapter(model.slice(2)->panId()); check(pan != nullptr, "multi receiver pan materialized"); @@ -348,6 +629,7 @@ int main(int argc, char** argv) TestSettingsProfile profile(QStringLiteral("rtl-model-acceptance")); if (!profile.isValid()) { return 1; } QCoreApplication app(argc, argv); AppSettings::instance().load(); + hdModelBoundary(); analogBankSelection(); analogBankSelection(8); analogBankRestoreAndSharedCapture(); @@ -709,6 +991,7 @@ int main(int argc, char** argv) RadioModelSliceLifecycleTestAccess::flush(model); check(!RtlSliceSettings(scope).load().document.slices.value(3).wfmForceMono, "accepted Auto Stereo replaces only the chosen persistent policy"); + hdAcceptedControls(model, backend, *slice, captureClock, scope); } else { const int wfmLow = slice->filterLow(); const int wfmHigh = slice->filterHigh(); diff --git a/tests/rtl_receive_pipeline_test.cpp b/tests/rtl_receive_pipeline_test.cpp index 7ccd731dd..83933a19d 100644 --- a/tests/rtl_receive_pipeline_test.cpp +++ b/tests/rtl_receive_pipeline_test.cpp @@ -370,6 +370,7 @@ struct RtlReceivePipelineTestAccess { RtlReceivePipeline::Packet packet; packet.token = {7, 11}; packet.slot = 3; packet.captureEpoch = 19; packet.receiverEpoch = 23; packet.frames = 256; packet.samples[0] = .375f; + packet.producedMonotonicMs = 1234; // decoded time must not become mailbox time if (pipeline.diagnostics().packetQueueCapacity != expectedCapacity) { return false; } // Repeated fills cross the physical wrap. Drop-new must retain every // accepted packet and preserve a genuine position gap on resumption. @@ -388,7 +389,7 @@ struct RtlReceivePipelineTestAccess { || packet.token != RtlReceivePipeline::Transaction::Token{7, 11} || packet.slot != 3 || packet.captureEpoch != 19 || packet.receiverEpoch != 23 || packet.frames != 256 || packet.samples[0] != .375f || packet.discontinuity - || packet.enqueuedMonotonicNs == 0) { return false; } + || packet.enqueuedMonotonicNs == 0 || packet.producedMonotonicMs != 1234) { return false; } } if (pipeline.takePacket(packet)) { return false; } } diff --git a/tests/rtl_slice_settings_test.cpp b/tests/rtl_slice_settings_test.cpp index 5c51c7fd2..2962c6d54 100644 --- a/tests/rtl_slice_settings_test.cpp +++ b/tests/rtl_slice_settings_test.cpp @@ -245,6 +245,57 @@ int main(int argc, char** argv) && wfmScope.featureExact(feature) == beforeInvalid, "invalid deemphasis cannot overwrite accepted settings"); } + { + const RadioSettingsScope hdScope("rtl", "hd-selection"); + RtlSliceSettings hd(hdScope); + auto selected = slice(3); + selected.mode = QStringLiteral("WFM"); + selected.filterLowHz = -90000; selected.filterHighHz = 90000; + selected.wfmDeemphasisUs = 50; selected.wfmHdStereo = true; + for (int program : {0, 7}) { + selected.hdProgram = program; + check(hd.patch(100'000'000, 2'400'000, {selected}), "HD program endpoint persists"); + const auto restored = hd.load().document.slices.value(3); + check(restored.wfmHdStereo && restored.hdProgram == program + && restored.filterLowHz == -90000 && restored.filterHighHz == 90000 + && restored.wfmDeemphasisUs == 50 && !restored.wfmForceMono, + "HD selection and program preserve the independent analog recipe"); + } + selected.mode = QStringLiteral("FM"); + check(hd.patch(100'000'000, 2'400'000, {selected}) + && hd.load().document.slices.value(3).wfmHdStereo + && hd.load().document.slices.value(3).hdProgram == 7, + "leaving broadcast FM remembers the selected HD policy and program"); + QJsonObject old = hdScope.featureExact(feature); + QJsonObject oldEntries = old.value("slices").toObject(); + QJsonObject oldEntry = oldEntries.value("3").toObject(); + oldEntry.remove("wfmHdStereo"); oldEntry.remove("hdProgram"); + oldEntries.insert("3", oldEntry); old.insert("slices", oldEntries); + RtlSliceSettings::Document restored; + check(RtlSliceSettings::decode(old, restored, reason) + && !restored.slices.value(3).wfmHdStereo && restored.slices.value(3).hdProgram == 0, + "older settings default to analog and the first HD program without migration"); + const auto rejects = [&](const QString& key, const QJsonValue& invalid) { + QJsonObject malformed = old, items = oldEntries, item = oldEntry; + item.insert(key, invalid); items.insert("3", item); malformed.insert("slices", items); + restored.captureCenterHz = 42; + check(!RtlSliceSettings::decode(malformed, restored, reason) + && restored.captureCenterHz == 42, + "malformed HD choice refuses the complete document without partial adoption"); + }; + for (const QJsonValue& invalid : {QJsonValue(0), QJsonValue(1), QJsonValue("true"), + QJsonValue(QJsonValue::Null), QJsonValue(QJsonObject{})}) { + rejects(QStringLiteral("wfmHdStereo"), invalid); + } + for (const QJsonValue& invalid : {QJsonValue(-1), QJsonValue(8), QJsonValue(0.5), + QJsonValue(true), QJsonValue("1"), QJsonValue(QJsonValue::Null)}) { + rejects(QStringLiteral("hdProgram"), invalid); + } + const auto accepted = hdScope.featureExact(feature); + selected.hdProgram = 8; + check(!hd.patch(100'000'000, 2'400'000, {selected}) && hdScope.featureExact(feature) == accepted, + "invalid program cannot overwrite accepted persistent receiver settings"); + } QJsonObject bad = valid; entries = bad.value("slices").toObject(); entries.insert("00", entries.take("0")); diff --git a/tests/rtl_wfm_pipeline_test.cpp b/tests/rtl_wfm_pipeline_test.cpp index 96b7c7d6b..cf0809187 100644 --- a/tests/rtl_wfm_pipeline_test.cpp +++ b/tests/rtl_wfm_pipeline_test.cpp @@ -960,6 +960,12 @@ void admission() check(!multiple->prepare(invalid), "configured legacy singleton refusal is independent of receiver order"); } + invalid = current; + invalid.receivers[0].wfmHdStereo = true; + invalid.receivers.push_back({{6, 103'000'000, -8000, 8000, 0, 3000, 3000}, Transaction::Mode::Fmn}); + check(!multiple->prepare(invalid), "HD singleton refusal includes parked configured siblings"); + invalid.receivingIds.clear(); + check(!multiple->prepare(invalid), "all-parked membership cannot hide an unsupported HD combination"); multiple->stop(); } } diff --git a/tests/spectrum_confirmed_geometry_test.cpp b/tests/spectrum_confirmed_geometry_test.cpp index 411fbc3ca..cdd2ab26c 100644 --- a/tests/spectrum_confirmed_geometry_test.cpp +++ b/tests/spectrum_confirmed_geometry_test.cpp @@ -6,6 +6,8 @@ #include "gui/MainWindowHelpers.h" #include "RtlInjectedDevice.h" #include "core/backends/flex/FlexBackend.h" +#include "core/ThemeManager.h" +#include #include "core/backends/hl2/Hl2Backend.h" #include "core/backends/rtl/RtlSdrBackend.h" #include "models/SliceModel.h" @@ -52,6 +54,30 @@ struct SpectrumOffscreenTestAccess { static int freshFrames(const SpectrumWidget& widget) { return widget.m_noiseFloorFreshFrameCount; } static float baseline(const SpectrumWidget& widget) { return widget.m_noiseFloorBaselineDbm; } static bool baselineValid(const SpectrumWidget& widget) { return widget.m_noiseFloorBaselineValid; } + + static QImage broadcast(SpectrumWidget& widget, const QColor& field, const QBrush& inherited, + bool& brushRestored, QPoint& backgroundSample, QRect* paintedBounds = nullptr) { + widget.updateBroadcastOverlayTicker(QRect(0, 0, widget.width(), widget.height())); + QImage image(widget.size(), QImage::Format_ARGB32_Premultiplied); + image.fill(field); + QPainter painter(&image); + painter.setBrush(inherited); // Same state left by the real slice marker. + widget.drawBroadcastOverlays(painter, QRect(0, 0, widget.width(), widget.height())); + brushRestored = painter.brush() == inherited; + painter.end(); + QRect bounds; + for (int y = 0; y < image.height(); ++y) { + for (int x = 0; x < image.width(); ++x) { + if (image.pixelColor(x, y) != field) { + bounds = bounds.united(QRect(x, y, 1, 1)); + } + } + } + backgroundSample = QPoint(bounds.left() + 2, bounds.center().y()); + if (paintedBounds) { *paintedBounds = bounds; } + return image; + } + static const QVector& trace(const SpectrumWidget& widget) { return widget.displaySpectrumBins(); } static QVector supplemental(const SpectrumWidget& widget, const QVector& bins, double low, double high, bool sameScale) { @@ -383,6 +409,73 @@ int main(int argc, char** argv) check(requests == 0 && SpectrumOffscreenTestAccess::baselineValid(trace), "a floor already at -180 dBm is not held for a recovery that cannot come"); } + { + ThemeManager& theme = ThemeManager::instance(); + const QString originalTheme = theme.activeTheme(); + SpectrumWidget broadcast; + broadcast.resize(800, 200); + broadcast.observeFrequencyRange(100.3, 2.4); + const WfmBroadcastOverlayRecord record{3, 11, 22, 33, 100300000, 0, + QStringLiteral("Station"), QStringLiteral("A complete broadcast title"), QStringLiteral("Artist")}; + broadcast.setBroadcastOverlays({record}); + check(broadcast.accessibleDescription().contains(record.displayText()), + "broadcast paging retains full metadata in the composed accessible description"); + for (const QString& name : {QStringLiteral("Default Dark"), QStringLiteral("Default Light")}) { + check(theme.setActiveTheme(name), "bundled broadcast contrast theme loads"); + for (const QString& fieldToken : {QStringLiteral("color.background.0"), QStringLiteral("color.text.primary")}) { + const QBrush inherited(theme.color(QStringLiteral("color.slice.a"))); + bool restored = false; QPoint sample; + const QImage image = SpectrumOffscreenTestAccess::broadcast( + broadcast, theme.color(fieldToken), inherited, restored, sample); + check(image.pixelColor(sample) == theme.color(QStringLiteral("color.background.0")), + "broadcast border does not repaint themed background with inherited slice brush"); + check(restored, "broadcast painting restores the caller brush"); + const QString output = qEnvironmentVariable("AETHER_WFM_APPLET_SCREENSHOT_DIR"); + if (!output.isEmpty()) { + const QString file = QStringLiteral("broadcast-fixture-%1-%2.png") + .arg(name.endsWith(QStringLiteral("Dark")) ? QStringLiteral("dark") : QStringLiteral("light")) + .arg(fieldToken.endsWith(QStringLiteral("primary")) ? QStringLiteral("bright-field") : QStringLiteral("dark-field")); + check(image.save(QDir(output).filePath(file)), "broadcast contrast fixture image saved"); + } + } + } + // Real expanded/collapsed flags must never cover the station label. + broadcast.resize(800, 600); + broadcast.setSpotStartPct(10); + broadcast.show(); + VfoWidget* flag = broadcast.addVfoWidget(3); + flag->setCollapsed(true); + flag->setCollapsed(false); + flag->move(250, 20); + QApplication::processEvents(); + bool restored = false; QPoint sample; QRect bounds; + const QColor field = theme.color(QStringLiteral("color.text.primary")); + QImage expanded = SpectrumOffscreenTestAccess::broadcast( + broadcast, field, Qt::NoBrush, restored, sample, &bounds); + check(!bounds.isEmpty() && !bounds.intersects(flag->geometry()) + && bounds.top() > flag->geometry().bottom(), + "Digital station label clears the real expanded slice flag"); + const QString output = qEnvironmentVariable("AETHER_WFM_APPLET_SCREENSHOT_DIR"); + if (!output.isEmpty()) { + QPainter painter(&expanded); + flag->render(&painter, flag->pos()); + painter.end(); + check(expanded.save(QDir(output).filePath(QStringLiteral("broadcast-expanded-flag.png"))), + "expanded flag placement fixture image saved"); + } + flag->setCollapsed(true); + broadcast.setSpotStartPct(25); + SpectrumOffscreenTestAccess::broadcast(broadcast, field, Qt::NoBrush, restored, sample, &bounds); + check(bounds.top() == 150, "Digital label follows the ordinary spot start percentage"); + broadcast.setSpotStartPct(70); + SpectrumOffscreenTestAccess::broadcast(broadcast, field, Qt::NoBrush, restored, sample, &bounds); + check(bounds.top() == 420, "Digital label follows spot placement changes"); + broadcast.hide(); + check(theme.setActiveTheme(originalTheme), "original isolated theme restored after broadcast fixture"); + broadcast.setBroadcastOverlays({}); + check(!broadcast.accessibleDescription().contains(record.displayText()), + "overlay off removes full metadata from accessible presentation"); + } { SpectrumWidget trace; trace.updateSpectrum(QVector(32,-80)); diff --git a/tests/tci_rx_audio_test.cpp b/tests/tci_rx_audio_test.cpp index f0dc84014..5b56425ba 100644 --- a/tests/tci_rx_audio_test.cpp +++ b/tests/tci_rx_audio_test.cpp @@ -84,7 +84,7 @@ class InjectedBackend final : public IRadioBackend { public: RadioCapabilities capabilities() const override { RadioCapabilities caps; - if (exportAvailable) { caps.receiveAudioExport = ReceiveAudioExport{{24000,48000},8}; } + if (exportAvailable) { caps.receiveAudioExport = ReceiveAudioExport{{24000,44100,48000},8}; } return caps; } bool exportAvailable = true; @@ -186,7 +186,7 @@ class TciRxAudioTest { { // Catches forced 24k producer interpretation, // stereo downmix, unbounded packets and last-client-only accounting. - for (int sourceRate : {24000,48000}) { + for (int sourceRate : {24000,44100,48000}) { Fixture f; f.backend->add(3); PcmProducer producer; @@ -846,7 +846,7 @@ class TciRxAudioTest { { const std::array ids{3,17,29,41,57,63,80,99}; const std::array rates{8000,12000,24000,48000,8000,12000,24000,48000}; - for (int sourceRate : {24000,48000}) { + for (int sourceRate : {24000,44100,48000}) { Fixture f; DaxReceiveModel dax(f.model, f.server); std::array received; @@ -966,14 +966,14 @@ class TciRxAudioTest { f.feed(17,frame(producer,128,0.8f,0.8f)); check(audio.isEmpty(),"DAX conversion retains a bounded partial source block"); const PcmFrame stale=frame(producer,128,0.8f,0.8f); - producer.setFormat({24000,PcmLayout::Stereo}); + producer.setFormat({44100,PcmLayout::Stereo}); f.feed(17,stale); check(audio.isEmpty(),"revoked queued epoch cannot complete old DAX converter staging"); f.feed(17,frame(producer,4096,0,0)); check(!audio.isEmpty() && std::all_of(audio.cbegin(),audio.cend(),[](char byte) {return byte==0;}), - "24k epoch starts silent with no retained 48k signal history"); + "44.1k epoch starts silent with no retained 48k signal history"); const qsizetype beforeCompeting=audio.size(); - competing.start(PcmPurpose::Slice,17,{24000,PcmLayout::Stereo}); + competing.start(PcmPurpose::Slice,17,{44100,PcmLayout::Stereo}); f.feed(17,frame(competing,4096)); check(audio.size()==beforeCompeting,"a competing live producer cannot replace a pinned native DAX source"); audio.clear(); diff --git a/tests/tci_rx_converter_test.cpp b/tests/tci_rx_converter_test.cpp index 1f30cc7b3..a94f37166 100644 --- a/tests/tci_rx_converter_test.cpp +++ b/tests/tci_rx_converter_test.cpp @@ -82,7 +82,7 @@ double magnitude(const QVector& samples, int rate, int channel, double hz void nativeIsExact() { // Catches native-rate filtering, stereo collapse, clipping and borrowed output. - for (const int rate : {24000, 48000}) { + for (const int rate : {24000, 44100, 48000}) { TciRxConverter converter({rate, PcmLayout::Stereo}, rate); QVector input{0.0f, -0.0f, 1.25f, -1.5f, 0.123f, -0.456f}; const QVector expected = input; @@ -115,7 +115,7 @@ void rateMatrixAndPartitioning() // channel intermixing and rate conversion dependent on producer chunk size. constexpr int kFrames = 49373; constexpr std::array kChunks{1, 255, 7, 511, 3, 1024, 31}; - for (const int sourceRate : {24000, 48000}) { + for (const int sourceRate : {24000, 44100, 48000}) { for (const int outputRate : {8000, 12000, 24000, 44100, 48000}) { for (const PcmLayout layout : {PcmLayout::Mono, PcmLayout::Stereo}) { std::printf("MATRIX: %d -> %d, %s\n", sourceRate, outputRate, @@ -177,7 +177,7 @@ void rateMatrixAndPartitioning() void antiphaseAndWideAudio() { // Existing processStereoToStereo() averages LR and turns this into silence. - for (const int sourceRate : {24000, 48000}) { + for (const int sourceRate : {24000, 44100, 48000}) { for (const int outputRate : {8000, 12000, 24000, 44100, 48000}) { TciRxConverter converter({sourceRate, PcmLayout::Stereo}, outputRate); QVector output; @@ -206,7 +206,7 @@ void antiphaseAndWideAudio() void discardRetiresEverything() { // Catches resampler history or partial input surviving disconnect/restart. - for (const int rate : {24000, 48000}) { + for (const int rate : {24000, 44100, 48000}) { for (const int outputRate : {8000, 12000, 24000, 44100, 48000}) { TciRxConverter converter({rate, PcmLayout::Stereo}, outputRate); QVector output; @@ -230,7 +230,7 @@ void declaredLatencyMatchesWaveform() { // Catches a delay getter that describes only no-output warmup rather than // the acoustic filter delay, or samples assigned to the wrong clock. - for (const int sourceRate : {24000, 48000}) { + for (const int sourceRate : {24000, 44100, 48000}) { for (const int outputRate : {8000, 12000, 24000, 44100, 48000}) { TciRxConverter converter({sourceRate, PcmLayout::Stereo}, outputRate); QVector impulse(49152 * 2, 0.0f); @@ -264,7 +264,7 @@ void boundsAndRejection() // Catches malformed or poisoned data reaching a sink; invalid calls discard // old buffered audio instead of making it available to a later stream. for (const PcmFormat format : {PcmFormat{0, PcmLayout::Stereo}, - PcmFormat{44100, PcmLayout::Stereo}, + PcmFormat{32000, PcmLayout::Stereo}, PcmFormat{24000, static_cast(9)}}) { TciRxConverter converter(format, 48000); int calls = 0; diff --git a/tests/tests.cmake b/tests/tests.cmake index efb3caa26..96309e184 100644 --- a/tests/tests.cmake +++ b/tests/tests.cmake @@ -1680,6 +1680,26 @@ add_executable(wdsp_allocation_scope_test tests/wdsp_allocation_scope_test.cpp) target_link_libraries(wdsp_allocation_scope_test PRIVATE aether_wdsp Threads::Threads) add_test(NAME wdsp_allocation_scope_test COMMAND wdsp_allocation_scope_test) +# Exact fractional IQ conversion is socket-free and independent of decoder availability. +add_executable(hd_fm_iq_adapter_test tests/hd_fm_iq_adapter_test.cpp) +target_link_libraries(hd_fm_iq_adapter_test PRIVATE aethercore Qt6::Core) +add_test(NAME hd_fm_iq_adapter_test COMMAND hd_fm_iq_adapter_test) +set_tests_properties(hd_fm_iq_adapter_test PROPERTIES TIMEOUT 60) + +# Native event reduction and bounded worker lifetime via injected documented +# callbacks, without a firmware peer or socket. This opt-in lane requires HD. +if(ENABLE_HD_FM) + add_executable(nrsc5_fm_decoder_test tests/nrsc5_fm_decoder_test.cpp) + target_link_libraries(nrsc5_fm_decoder_test PRIVATE aethercore aether_nrsc5 Qt6::Core) + add_test(NAME nrsc5_fm_decoder_test COMMAND nrsc5_fm_decoder_test) + set_tests_properties(nrsc5_fm_decoder_test PROPERTIES TIMEOUT 30) + + add_executable(hd_fm_receiver_test tests/hd_fm_receiver_test.cpp) + target_link_libraries(hd_fm_receiver_test PRIVATE aethercore aether_nrsc5 Qt6::Core) + add_test(NAME hd_fm_receiver_test COMMAND hd_fm_receiver_test) + set_tests_properties(hd_fm_receiver_test PROPERTIES TIMEOUT 45) +endif() + add_executable(rtl_wfm_pipeline_test tests/rtl_wfm_pipeline_test.cpp) target_include_directories(rtl_wfm_pipeline_test PRIVATE src) target_link_libraries(rtl_wfm_pipeline_test PRIVATE aethercore Qt6::Core) @@ -7812,6 +7832,7 @@ set_tests_properties(fm_filter_controls_test PROPERTIES add_executable(wfm_controls_test tests/wfm_controls_test.cpp src/gui/WfmApplet.cpp + src/gui/WfmPresentationSettings.cpp src/gui/WfmLockScope.cpp src/gui/RxApplet.cpp src/gui/ControlAvailabilityRegistry.cpp @@ -7839,6 +7860,17 @@ add_test(NAME wfm_controls_test set_tests_properties(wfm_controls_test PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") +# Local broadcast presentation has no SpotModel, radio command or network peer. +add_executable(broadcast_overlay_test + tests/broadcast_overlay_test.cpp + src/gui/WfmPresentationSettings.cpp + src/gui/WfmBroadcastOverlay.cpp +) +target_include_directories(broadcast_overlay_test PRIVATE src) +target_link_libraries(broadcast_overlay_test PRIVATE aethercore Qt6::Widgets Qt6::Test) +add_test(NAME broadcast_overlay_test COMMAND broadcast_overlay_test) +set_tests_properties(broadcast_overlay_test PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") + # The VFO flag's AetherRX / AetherTX launchers stay the same width on every # mode's DSP grid. Same socket-free build as the squelch test above. add_executable(vfo_dsp_launcher_width_test @@ -8173,6 +8205,7 @@ set(AETHER_SETTINGS_CONSUMERS vfo_meter_overlay_invalidation_test fm_filter_controls_test wfm_controls_test + broadcast_overlay_test spectrum_confirmed_geometry_test panadapter_dbm_range_test flex_slice_mode_intent_test @@ -8914,6 +8947,20 @@ if(APPLE) endif() add_test(NAME sleep_inhibitor_backend_test COMMAND sleep_inhibitor_backend_test) +# The native Windows C adapter owns no sockets or radio transport. Exercise +# its actual complex arithmetic and mutex/condition subset with clang-cl. +if(WIN32 AND ENABLE_HD_FM AND TARGET aether_nrsc5) + aether_hd_windows_c_objects(hd_compat_objects compat-test + "${CMAKE_CURRENT_SOURCE_DIR}/tests/nrsc5_windows_compat_test.c" + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/nrsc5/compat/msvc" "") + add_executable(nrsc5_windows_compat_test ${hd_compat_objects}) + set_target_properties(nrsc5_windows_compat_test PROPERTIES LINKER_LANGUAGE C + AUTOMOC OFF AUTOUIC OFF AUTORCC OFF) + target_link_libraries(nrsc5_windows_compat_test PRIVATE "${AETHER_HD_COMPILER_RT}") + add_test(NAME nrsc5_windows_compat_test COMMAND nrsc5_windows_compat_test) + set_tests_properties(nrsc5_windows_compat_test PROPERTIES TIMEOUT 30) +endif() + # Anonymous-pipe output injection: no sockets, audio devices or helper processes. if(HAVE_PIPEWIRE) add_executable(pipewire_rx_bridge_test tests/pipewire_rx_bridge_test.cpp) diff --git a/tests/wfm_controls_test.cpp b/tests/wfm_controls_test.cpp index fa8250044..ed2a25953 100644 --- a/tests/wfm_controls_test.cpp +++ b/tests/wfm_controls_test.cpp @@ -9,6 +9,7 @@ #include "gui/VfoWidget.h" #include "gui/WfmApplet.h" #include "gui/WfmLockScope.h" +#include "gui/WfmPresentationSettings.h" #include "core/ThemeManager.h" #include "models/RadioModel.h" #include "models/SliceModel.h" @@ -41,6 +42,8 @@ class WfmBackend final : public IRadioBackend { bool refuseWfm = false; QVector> requests; QVector> monoRequests; + QVector> audioModeRequests; + QVector> programRequests; RadioCapabilities capabilities() const override { return caps; } bool isConnected() const override { return connected; } ReceiveControlPolicy receiveControlPolicy() const override { return ReceiveControlPolicy::Confirmed; } @@ -55,8 +58,12 @@ class WfmBackend final : public IRadioBackend { if (refuseWfm || !request.valid()) { return ReceiveDispatch::Unsupported; } if (request.field == SliceWfmRequest::Field::ForceMono) { setSliceWfmForceMono(id, request.value != 0); - } else { + } else if (request.field == SliceWfmRequest::Field::Deemphasis) { setSliceWfmDeemphasis(id, request.value); + } else if (request.field == SliceWfmRequest::Field::AudioMode) { + setSliceWfmAudioMode(id, static_cast(request.value)); + } else { + setSliceHdProgram(id, request.value); } return ReceiveDispatch::Dispatched; } @@ -64,6 +71,10 @@ class WfmBackend final : public IRadioBackend { { requests.append({id, microseconds}); } void setSliceWfmForceMono(int id, bool mono) override { monoRequests.append({id, mono}); } + void setSliceWfmAudioMode(int id, WfmAudioMode mode) override + { audioModeRequests.append({id, mode}); } + void setSliceHdProgram(int id, int program) override + { programRequests.append({id, program}); } void setPanCenter(const QString&, double, PanCenterIntent) override {} void setKeying(bool, const TxCoordinator::Operation&, const TxCoordinator::Completion&) override {} @@ -86,6 +97,29 @@ WfmReceptionDiagnostics measuredPilot(bool locked = false) return value; } +HdFmReception measuredHd(int program = 0) +{ + HdFmReception value; + value.valid = true; + value.sessionId = 1; + value.receiverEpoch = 2; + value.revision = 3; + value.frequencyHz = 100300000; + value.selectedProgram = program; + value.observationSequence = 1; + value.synced = value.audioValid = true; + value.services = {{0, QStringLiteral("Main"), true}, {1, QStringLiteral("Second"), true}, + {2, QStringLiteral("Data only"), false}}; + value.stationName = QStringLiteral("Fixture digital station"); + value.title = QStringLiteral("Title "); + value.artist = QStringLiteral("Artist"); + value.merLowerDb = 16.5; + value.merUpperDb = 17.0; + value.cber = 0.0001; + value.syncDurationMs = 5000; + return value; +} + SliceDelta initialWfm() { SliceDelta delta; @@ -483,6 +517,7 @@ private slots: WfmBackend* source = attachBackend(model); SliceDelta first = initialWfm(); first.letter = QStringLiteral("A"); + first.wfmAudioMode = WfmAudioMode::Mono; first.wfmForceMono = true; emit source->sliceChanged(3, first); SliceDelta second = initialWfm(); @@ -518,7 +553,7 @@ private slots: } QVERIFY(source->requests.isEmpty()); QVERIFY(source->monoRequests.isEmpty()); - QVERIFY(source->monoRequests.isEmpty()); + QVERIFY(source->audioModeRequests.isEmpty()); QTest::keyClick(deemphasis, Qt::Key_Up); QCOMPARE(source->requests.size(), 1); QCOMPARE(source->requests.last(), qMakePair(3, 50)); @@ -554,16 +589,20 @@ private slots: { RadioModel model; WfmBackend* source = attachBackend(model); - SliceDelta analog = initialWfm(); - emit source->sliceChanged(3, analog); - emit source->sliceChanged(4, analog); + source->caps.broadcastFmReceive->hdStereo = true; + SliceDelta hd = initialWfm(); + hd.wfmAudioMode = WfmAudioMode::HdStereo; + hd.hdFmReception = measuredHd(); + emit source->sliceChanged(3, hd); + emit source->sliceChanged(4, hd); WfmApplet applet; applet.setRadioModel(&model); applet.setSlice(model.slice(3)); applet.show(); QSignalSpy firstFilter(model.slice(3), &SliceModel::filterCommandIssued); QSignalSpy secondFilter(model.slice(4), &SliceModel::filterCommandIssued); - for (const QString& name : {QStringLiteral("wfmDeemphasis"), QStringLiteral("wfmBandwidth")}) { + for (const QString& name : {QStringLiteral("wfmDeemphasis"), QStringLiteral("wfmBandwidth"), + QStringLiteral("wfmHdProgram")}) { auto* combo = applet.findChild(name); QVERIFY(combo && combo->isEnabled()); applet.setSlice(model.slice(3)); @@ -580,6 +619,7 @@ private slots: QCOMPARE(combo->currentIndex(), acceptedIndex); } QVERIFY(source->requests.isEmpty()); + QVERIFY(source->programRequests.isEmpty()); QVERIFY(firstFilter.isEmpty()); QVERIFY(secondFilter.isEmpty()); auto* cycle = applet.findChild(QStringLiteral("wfmAudioMode")); @@ -588,17 +628,17 @@ private slots: QTest::mousePress(cycle, Qt::LeftButton); applet.setSlice(model.slice(4)); QTest::mouseRelease(cycle, Qt::LeftButton); - QVERIFY(source->monoRequests.isEmpty()); + QVERIFY(source->audioModeRequests.isEmpty()); cycle->click(); - QCOMPARE(source->monoRequests.size(), 1); - QCOMPARE(source->monoRequests.last().first, 4); + QCOMPARE(source->audioModeRequests.size(), 1); + QCOMPARE(source->audioModeRequests.last().first, 4); auto* deemphasis = applet.findChild(QStringLiteral("wfmDeemphasis")); applet.setSlice(model.slice(3)); deemphasis->showPopup(); std::thread removalNotice([&model] { emit model.sliceRemoved(3); }); removalNotice.join(); emit source->sliceRemoved(3); - emit source->sliceChanged(3, analog); + emit source->sliceChanged(3, hd); applet.setSlice(model.slice(3)); QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); QVERIFY(applet.isAvailable()); @@ -624,7 +664,8 @@ private slots: QVERIFY(scope && scope->isVisible()); std::thread queued([oldSlice] { emit oldSlice->frequencyChanged(100.7); - emit oldSlice->wfmForceMonoChanged(true); + emit oldSlice->wfmAudioModeChanged(WfmAudioMode::Mono); + emit oldSlice->hdProgramChanged(1); emit oldSlice->wfmReceptionDiagnosticsChanged(measuredPilot()); }); queued.join(); @@ -674,6 +715,54 @@ private slots: QCOMPARE(identity->text(), QStringLiteral("Slice D · 100.300000 MHz")); } + void selectedHdMetadataNeverUsesASiblingsStationOrService() + { + RadioModel model; + WfmBackend* source = attachBackend(model); + source->caps.broadcastFmReceive->hdStereo = true; + SliceDelta first = initialWfm(); + first.wfmAudioMode = WfmAudioMode::HdStereo; + first.hdProgram = 0; + first.hdFmReception = measuredHd(); + emit source->sliceChanged(3, first); + SliceDelta second = first; + second.frequency = 100.9; + second.hdProgram = 1; + second.hdFmReception = measuredHd(1); + second.hdFmReception->frequencyHz = 100900000; + second.hdFmReception->receiverEpoch = 7; + second.hdFmReception->stationName = QStringLiteral("Other station"); + second.hdFmReception->title = QStringLiteral("Other song"); + emit source->sliceChanged(4, second); + WfmApplet applet; + applet.setRadioModel(&model); + applet.setSlice(model.slice(3)); + auto* metadata = applet.findChild(QStringLiteral("wfmBroadcastMetadata")); + auto* program = applet.findChild(QStringLiteral("wfmHdProgram")); + QVERIFY(metadata && program); + QVERIFY(metadata->text().contains(QStringLiteral("Fixture digital station"))); + applet.setSlice(model.slice(4)); + const QString secondText = metadata->text(); + QVERIFY(secondText.contains(QStringLiteral("Other station"))); + QVERIFY(secondText.contains(QStringLiteral("P2"))); + QCOMPARE(program->currentData().toInt(), 1); + first.hdFmReception->title = QStringLiteral("Sibling song changed"); + emit source->sliceChanged(3, first); + QCOMPARE(metadata->text(), secondText); + SliceDelta parked; + parked.inCapture = false; + emit source->sliceChanged(4, parked); + QVERIFY(!metadata->text().contains(QStringLiteral("Other station"))); + QCOMPARE(model.slice(4)->hdProgram(), 1); + applet.setSlice(model.slice(3)); + QVERIFY(metadata->text().contains(QStringLiteral("Sibling song changed"))); + emit source->sliceRemoved(4); + QVERIFY(metadata->text().contains(QStringLiteral("Sibling song changed"))); + QVERIFY(source->requests.isEmpty()); + QVERIFY(source->programRequests.isEmpty()); + QVERIFY(source->audioModeRequests.isEmpty()); + } + void audioCycleWaitsForAcceptanceAndPreservesObservedPilot() { RadioModel model; @@ -973,6 +1062,415 @@ private slots: checkBroadcastFmVisibility(*host.applet, false); } + void hdCycleProgramAndMetadataFollowAcceptedStateOnly() + { + RadioModel model; + WfmBackend* source = attachBackend(model); + source->caps.broadcastFmReceive->hdStereo = true; + emit model.capabilitiesChanged(true, source->caps); + WfmApplet applet; + applet.setRadioModel(&model); + applet.setSlice(model.slice(3)); + auto* cycle = applet.findChild(QStringLiteral("wfmAudioMode")); + auto* program = applet.findChild(QStringLiteral("wfmHdProgram")); + auto* status = applet.findChild(QStringLiteral("wfmStereoStatus")); + auto* metadata = applet.findChild(QStringLiteral("wfmBroadcastMetadata")); + QVERIFY(cycle && program && status && metadata); + QVERIFY(!program->isEnabled()); + cycle->click(); + QCOMPARE(source->audioModeRequests.size(), 1); + QCOMPARE(source->audioModeRequests.last().second, WfmAudioMode::HdStereo); + QCOMPARE(cycle->text(), QStringLiteral("Auto Stereo")); + QCOMPARE(model.slice(3)->wfmAudioMode(), WfmAudioMode::Stereo); + SliceDelta accepted; + accepted.wfmAudioMode = WfmAudioMode::HdStereo; + emit source->sliceChanged(3, accepted); + QCOMPARE(cycle->text(), QStringLiteral("Digital")); + QCOMPARE(status->text(), QStringLiteral("Digital acquiring")); + QVERIFY(!program->isEnabled()); + accepted = {}; + accepted.hdFmReception = measuredHd(); + emit source->sliceChanged(3, accepted); + QCOMPARE(status->text(), QStringLiteral("Digital audio valid")); + const QString healthyStyle = status->styleSheet(); + QCOMPARE(program->count(), 2); // data-only services never masquerade as audio + QCOMPARE(program->currentData().toInt(), 0); + QVERIFY(program->isEnabled()); + QCOMPARE(metadata->textFormat(), Qt::PlainText); + QVERIFY(metadata->text().contains(QStringLiteral("Digital · P1"))); + QVERIFY(metadata->text().contains(QStringLiteral(""))); + const QString text = metadata->text(); + WfmPresentationSettings::instance().setBroadcastOverlayEnabled(false); + QCOMPARE(metadata->text(), text); // overlay preference does not stop applet text + QVERIFY(model.slice(3)->hdFmReception().audioValid); + program->setCurrentIndex(1); + QVERIFY(QMetaObject::invokeMethod(program, "activated", Q_ARG(int, 1))); + QCOMPARE(source->programRequests.size(), 1); + QCOMPARE(source->programRequests.last().second, 1); + QCOMPARE(program->currentData().toInt(), 0); // pending intent is not readback + accepted = {}; accepted.hdProgram = 1; + emit source->sliceChanged(3, accepted); + QVERIFY(!metadata->text().contains(QStringLiteral("Fixture digital station"))); + QVERIFY(status->styleSheet() != healthyStyle); + accepted = {}; accepted.hdFmReception = measuredHd(1); + emit source->sliceChanged(3, accepted); + QCOMPARE(program->currentData().toInt(), 1); + QCOMPARE(status->styleSheet(), healthyStyle); + QVERIFY(metadata->text().contains(QStringLiteral("P2"))); + cycle->click(); + QCOMPARE(source->audioModeRequests.last().second, WfmAudioMode::Mono); + QCOMPARE(cycle->text(), QStringLiteral("Digital")); + accepted = {}; accepted.wfmAudioMode = WfmAudioMode::Mono; accepted.wfmForceMono = true; + emit source->sliceChanged(3, accepted); + QCOMPARE(cycle->text(), QStringLiteral("Mono")); + QVERIFY(!program->isEnabled()); + cycle->click(); + QCOMPARE(source->audioModeRequests.last().second, WfmAudioMode::Stereo); + QCOMPARE(cycle->text(), QStringLiteral("Mono")); + WfmPresentationSettings::instance().setBroadcastOverlayEnabled(true); + } + + void hdPopupTelemetryPreservesPendingChoice_data() + { + QTest::addColumn("mouse"); + QTest::addColumn("serviceChange"); + QTest::newRow("mouse-press-telemetry-release") << true << 0; + QTest::newRow("keyboard-highlight-telemetry-return") << false << 0; + QTest::newRow("mouse-service-rename") << true << 1; + QTest::newRow("keyboard-service-rename") << false << 1; + QTest::newRow("mouse-service-reorder") << true << 2; + QTest::newRow("keyboard-service-reorder") << false << 2; + } + + void hdPopupTelemetryPreservesPendingChoice() + { + QFETCH(bool, mouse); + QFETCH(int, serviceChange); + RadioModel model; + WfmBackend* source = attachBackend(model); + source->caps.broadcastFmReceive->hdStereo = true; + emit model.capabilitiesChanged(true, source->caps); + WfmHarness host; + WfmApplet& applet = *host.applet; + applet.setRadioModel(&model); + applet.setSlice(model.slice(3)); + SliceDelta observation; + observation.wfmAudioMode = WfmAudioMode::HdStereo; + observation.hdFmReception = measuredHd(); + emit source->sliceChanged(3, observation); + auto* program = applet.findChild(QStringLiteral("wfmHdProgram")); + auto* settings = applet.findChild(QStringLiteral("wfmSettingsToggle")); + QVERIFY(program && settings && program->isEnabled()); + host.resize(400, 600); + host.show(); + QVERIFY(QTest::qWaitForWindowExposed(&host)); + if (!settings->isChecked()) { QTest::mouseClick(settings, Qt::LeftButton); } + QTRY_VERIFY(program->isVisible()); + QCOMPARE(program->currentData().toInt(), 0); + QTest::mouseClick(program, Qt::LeftButton); + QAbstractItemView* view = program->view(); + QTRY_VERIFY(view->isVisible()); + QVERIFY(QTest::qWaitForWindowExposed(view->window())); + // Qt ignores the opening click's release for a short interval. Wait + // before the distinct selection click; telemetry still interleaves + // strictly between its press and release below. + if (mouse) { QTest::qWait(QApplication::doubleClickInterval() + 20); } + const QModelIndex second = program->model()->index(program->findData(1), 0); + const QPoint position = view->visualRect(second).center(); + QVERIFY(view->viewport()->rect().contains(position)); + if (mouse) { + QTest::mouseMove(view->viewport(), position); + QTest::mousePress(view->viewport(), Qt::LeftButton, Qt::NoModifier, position); + } else { + QTest::keyClick(view, Qt::Key_Down); + } + QCOMPARE(view->currentIndex().data(Qt::UserRole).toInt(), 1); + QVERIFY(source->programRequests.isEmpty()); + for (int sequence = 2; sequence < 18; ++sequence) { + observation.hdFmReception->observationSequence = sequence; + observation.hdFmReception->syncDurationMs += 250; + observation.hdFmReception->title = QStringLiteral("Telemetry %1").arg(sequence); + if (serviceChange == 1) { + observation.hdFmReception->services[1].name = QStringLiteral("Renamed %1").arg(sequence); + } else if (serviceChange == 2) { + observation.hdFmReception->services.swapItemsAt(0, 1); + } + emit source->sliceChanged(3, observation); + QApplication::processEvents(); + } + const bool popupStayedOpen = view->isVisible(); + const int highlightedProgram = view->currentIndex().data(Qt::UserRole).toInt(); + QVERIFY(source->programRequests.isEmpty()); // telemetry cannot activate a choice + if (mouse) { + QTest::mouseRelease(view->viewport(), Qt::LeftButton, Qt::NoModifier, position); + } else { + QTest::keyClick(view, Qt::Key_Return); + } + QApplication::processEvents(); + qInfo("Digital popup input=%s openAfterTelemetry=%d highlightedProgram=%d requests=%lld displayedProgram=%d", + mouse ? "mouse" : "keyboard", popupStayedOpen, highlightedProgram, + static_cast(source->programRequests.size()), program->currentData().toInt()); + QCOMPARE(source->programRequests.size(), 1); + QCOMPARE(source->programRequests.first(), (QPair{3, 1})); + QVERIFY(popupStayedOpen); + QCOMPARE(highlightedProgram, 1); + QCOMPARE(model.slice(3)->hdProgram(), 0); + QCOMPARE(program->currentData().toInt(), 0); // intent is not confirmed state + if (serviceChange == 1) { + QVERIFY(program->itemText(program->findData(1)).contains(QStringLiteral("Renamed 17"))); + } + emit source->sliceChanged(3, observation); // unchanged backend state is not an acknowledgement + QCOMPARE(program->currentData().toInt(), 0); + QCOMPARE(source->programRequests.size(), 1); + observation.hdProgram = 1; + observation.hdFmReception = measuredHd(1); + emit source->sliceChanged(3, observation); + QCOMPARE(program->currentData().toInt(), 1); + QCOMPARE(source->programRequests.size(), 1); + QTest::mouseClick(program, Qt::LeftButton); + QTRY_VERIFY(view->isVisible()); + QTest::keyClick(view, Qt::Key_Return); // the accepted service is a no-op + QCOMPARE(source->programRequests.size(), 1); + } + + void hdPopupCancelsWhenReceiverOrServiceIdentityChanges_data() + { + QTest::addColumn("change"); + QTest::addColumn("mouse"); + const QStringList changes = {QStringLiteral("service-removed"), QStringLiteral("service-added"), + QStringLiteral("audio-unavailable"), QStringLiteral("session"), QStringLiteral("epoch"), + QStringLiteral("revision"), QStringLiteral("frequency"), QStringLiteral("slice"), + QStringLiteral("reused-slice-id"), QStringLiteral("radio-model"), QStringLiteral("confirmed-program"), + QStringLiteral("reception-unavailable")}; + for (const QString& change : changes) { + for (bool mouse : {false, true}) { + const QByteArray name = (change + (mouse ? QStringLiteral("-mouse") : QStringLiteral("-keyboard"))).toUtf8(); + QTest::newRow(name.constData()) << change << mouse; + } + } + } + + void hdPopupCancelsWhenReceiverOrServiceIdentityChanges() + { + QFETCH(QString, change); + QFETCH(bool, mouse); + RadioModel model; + WfmBackend* source = attachBackend(model); + source->caps.broadcastFmReceive->hdStereo = true; + emit model.capabilitiesChanged(true, source->caps); + RadioModel replacementModel; + WfmBackend* replacementSource = attachBackend(replacementModel); + replacementSource->caps.broadcastFmReceive->hdStereo = true; + emit replacementModel.capabilitiesChanged(true, replacementSource->caps); + WfmHarness host; + WfmApplet& applet = *host.applet; + applet.setRadioModel(&model); + applet.setSlice(model.slice(3)); + SliceDelta observation = initialWfm(); + observation.wfmAudioMode = WfmAudioMode::HdStereo; + observation.hdFmReception = measuredHd(); + emit source->sliceChanged(3, observation); + auto* program = applet.findChild(QStringLiteral("wfmHdProgram")); + auto* settings = applet.findChild(QStringLiteral("wfmSettingsToggle")); + QVERIFY(program && settings && program->isEnabled()); + host.resize(400, 600); + host.show(); + QVERIFY(QTest::qWaitForWindowExposed(&host)); + if (!settings->isChecked()) { QTest::mouseClick(settings, Qt::LeftButton); } + QTRY_VERIFY(program->isVisible()); + QTest::mouseClick(program, Qt::LeftButton); + QAbstractItemView* view = program->view(); + QTRY_VERIFY(view->isVisible()); + QVERIFY(QTest::qWaitForWindowExposed(view->window())); + if (mouse) { QTest::qWait(QApplication::doubleClickInterval() + 20); } + const QModelIndex second = program->model()->index(program->findData(1), 0); + const QPoint position = view->visualRect(second).center(); + QVERIFY(view->viewport()->rect().contains(position)); + if (mouse) { + QTest::mouseMove(view->viewport(), position); + QTest::mousePress(view->viewport(), Qt::LeftButton, Qt::NoModifier, position); + } else { + QTest::keyClick(view, Qt::Key_Down); + } + QCOMPARE(view->currentIndex().data(Qt::UserRole).toInt(), 1); + QVERIFY(source->programRequests.isEmpty()); + if (change == QStringLiteral("slice")) { + emit source->sliceChanged(4, observation); + applet.setSlice(model.slice(4)); + } else if (change == QStringLiteral("reused-slice-id")) { + SliceModel* original = model.slice(3); + emit source->sliceRemoved(3); + emit source->sliceChanged(3, observation); + QVERIFY(model.slice(3) != original); + applet.setSlice(model.slice(3)); + } else if (change == QStringLiteral("radio-model")) { + emit replacementSource->sliceChanged(3, observation); + applet.setRadioModel(&replacementModel); + applet.setSlice(replacementModel.slice(3)); + } else { + if (change == QStringLiteral("service-removed")) { + observation.hdFmReception->services.removeAt(1); + } else if (change == QStringLiteral("service-added")) { + observation.hdFmReception->services.append({4, QStringLiteral("New service"), true}); + } else if (change == QStringLiteral("audio-unavailable")) { + observation.hdFmReception->services[1].audioAvailable = false; + } else if (change == QStringLiteral("session")) { + ++observation.hdFmReception->sessionId; + } else if (change == QStringLiteral("epoch")) { + ++observation.hdFmReception->receiverEpoch; + } else if (change == QStringLiteral("revision")) { + ++observation.hdFmReception->revision; + } else if (change == QStringLiteral("frequency")) { + observation.frequency = 101.1; + observation.hdFmReception->frequencyHz = 101100000; + } else if (change == QStringLiteral("confirmed-program")) { + observation.hdProgram = 1; + observation.hdFmReception->selectedProgram = 1; + } else if (change == QStringLiteral("reception-unavailable")) { + observation.hdFmReception->valid = false; + } + emit source->sliceChanged(3, observation); + } + QApplication::processEvents(); + QTRY_VERIFY(!view->isVisible()); + QVERIFY(source->programRequests.isEmpty()); + QVERIFY(replacementSource->programRequests.isEmpty()); + // A completion already aimed at the old popup must never select a + // same-numbered row in its replacement receiver or service list. + if (mouse) { + QTest::mouseRelease(view->viewport(), Qt::LeftButton, Qt::NoModifier, position); + } else { + QTest::keyClick(view, Qt::Key_Return); + } + QApplication::processEvents(); + QVERIFY(source->programRequests.isEmpty()); + QVERIFY(replacementSource->programRequests.isEmpty()); + if (program->isEnabled() && program->count() > 1) { + const int acceptedProgram = program->currentData().toInt(); + const int targetRow = program->currentIndex() == 0 ? program->count() - 1 : 0; + const int targetProgram = program->itemData(targetRow).toInt(); + QTest::mouseClick(program, Qt::LeftButton); + QTRY_VERIFY(view->isVisible()); + QTest::keyClick(view, targetRow == 0 ? Qt::Key_Home : Qt::Key_End); + QCOMPARE(view->currentIndex().data(Qt::UserRole).toInt(), targetProgram); + QTest::keyClick(view, Qt::Key_Return); + const bool replacement = change == QStringLiteral("radio-model"); + WfmBackend* activeSource = replacement ? replacementSource : source; + WfmBackend* inactiveSource = replacement ? source : replacementSource; + const int sliceId = change == QStringLiteral("slice") ? 4 : 3; + QCOMPARE(activeSource->programRequests, (QVector>{{sliceId, targetProgram}})); + QVERIFY(inactiveSource->programRequests.isEmpty()); + QCOMPARE(program->currentData().toInt(), acceptedProgram); + } + } + + void hdTelemetryScopeIsMeasuredBoundedAndRetiresOnIdentityChanges() + { + AppSettings::instance().remove(QStringLiteral("WfmApplet")); + RadioModel model; + WfmBackend* source = attachBackend(model); + source->caps.broadcastFmReceive->hdStereo = true; + WfmApplet applet; + applet.setRadioModel(&model); + applet.setSlice(model.slice(3)); + applet.resize(300, 460); + applet.show(); + QCoreApplication::processEvents(); + auto* scope = applet.findChild(); + auto* status = applet.findChild(QStringLiteral("wfmStereoStatus")); + auto* metadata = applet.findChild(QStringLiteral("wfmBroadcastMetadata")); + QVERIFY(scope && status && metadata); + SliceDelta accepted; + accepted.wfmAudioMode = WfmAudioMode::HdStereo; + accepted.hdFmReception = measuredHd(); + emit source->sliceChanged(3, accepted); + QCOMPARE(scope->sampleCount(), 1); + auto* accessible = QAccessible::queryAccessibleInterface(scope); + QVERIFY(accessible); + QVERIFY(accessible->text(QAccessible::Value).contains(QStringLiteral("16.5 dB"))); + QVERIFY(accessible->text(QAccessible::Value).contains(QStringLiteral("Digital audio valid"))); + const QString healthyStyle = status->styleSheet(); + const QString fixtureDirectory = qEnvironmentVariable("AETHER_WFM_APPLET_SCREENSHOT_DIR"); + if (!fixtureDirectory.isEmpty()) { + QVERIFY(QDir().mkpath(fixtureDirectory)); + auto* fixtureLabel = new QLabel(QStringLiteral("TEST FIXTURE · synthetic Digital telemetry"), &applet); + fixtureLabel->setWordWrap(true); + qobject_cast(applet.layout())->insertWidget(0, fixtureLabel); + auto* diagnostics = applet.findChild(QStringLiteral("wfmShowDiagnostics")); + diagnostics->setChecked(true); + applet.findChild(QStringLiteral("wfmSettingsToggle"))->setChecked(true); + applet.resize(300, applet.sizeHint().height()); + QCoreApplication::processEvents(); + QVERIFY(applet.grab().save(QDir(fixtureDirectory).filePath(QStringLiteral("wfm-test-fixture-hd.png")))); + diagnostics->setChecked(false); + applet.findChild(QStringLiteral("wfmSettingsToggle"))->setChecked(false); + } + HdFmReception next = measuredHd(); + next.title = QStringLiteral("Updated title"); + accepted = {}; accepted.hdFmReception = next; + emit source->sliceChanged(3, accepted); + QCOMPARE(scope->sampleCount(), 1); // same observation never grows history + QCOMPARE(metadata->text().contains(QStringLiteral("Updated title")), true); + next.observationSequence = 2; + next.audioValid = false; + accepted.hdFmReception = next; + emit source->sliceChanged(3, accepted); + QCOMPARE(scope->sampleCount(), 2); + QCOMPARE(status->text(), QStringLiteral("Digital synced · awaiting audio")); + QVERIFY(status->styleSheet() != healthyStyle); + next.audioValid = true; + next.syncLossCount = 1; + next.syncDurationMs = 100; + accepted.hdFmReception = next; + emit source->sliceChanged(3, accepted); + QVERIFY(status->text().contains(QStringLiteral("Unstable"))); + QVERIFY(status->styleSheet() != healthyStyle); + next.observationSequence = 3; + next.receiverEpoch = 4; + accepted.hdFmReception = next; + emit source->sliceChanged(3, accepted); + QCOMPARE(scope->sampleCount(), 1); + scope->hide(); + next.observationSequence = 4; + accepted.hdFmReception = next; + emit source->sliceChanged(3, accepted); + QCOMPARE(scope->sampleCount(), 0); + scope->show(); + QCOMPARE(scope->sampleCount(), 0); + next.observationSequence = 5; + accepted.hdFmReception = next; + emit source->sliceChanged(3, accepted); + QCOMPARE(scope->sampleCount(), 1); + accepted = {}; accepted.frequency = 101.1; + emit source->sliceChanged(3, accepted); + QCOMPARE(scope->sampleCount(), 0); + QVERIFY(!metadata->text().contains(QStringLiteral("Updated title"))); + QVERIFY(accessible->text(QAccessible::Value).contains(QStringLiteral("Awaiting"))); + for (int i = 0; i < 180; ++i) { scope->appendHdSample(16.5, 17.0, true, true); } + QCOMPARE(scope->sampleCount(), 160); + scope->appendHdSample(std::numeric_limits::quiet_NaN(), 17.0, true, true); + QCOMPARE(scope->sampleCount(), 0); + scope->appendHdSample({}, {}, false, false); + QCOMPARE(scope->sampleCount(), 1); + QVERIFY(accessible->text(QAccessible::Value).contains(QStringLiteral("unavailable"))); + scope->setHdMode(false); + QCOMPARE(scope->sampleCount(), 0); + QVERIFY(accessible->text(QAccessible::Value).contains(QStringLiteral("pilot"))); + SliceDelta another = initialWfm(); + another.wfmAudioMode = WfmAudioMode::HdStereo; + another.hdFmReception = measuredHd(); + emit source->sliceChanged(4, another); + applet.setSlice(model.slice(4)); + QVERIFY(metadata->text().contains(QStringLiteral("Fixture digital station"))); + const QString selectedText = metadata->text(); + next = measuredHd(); next.frequencyHz = 101100000; next.title = QStringLiteral("Stale old slice song"); + accepted = {}; accepted.hdFmReception = next; + emit source->sliceChanged(3, accepted); + QCOMPARE(metadata->text(), selectedText); + applet.setSlice(nullptr); + QVERIFY(!metadata->text().contains(QStringLiteral("Fixture digital station"))); + } + void broadcastFilterPresetsPreserveSavedEdgesInBothWidgets() { RadioModel model; diff --git a/third_party/faad_hdc/README.aethersdr.md b/third_party/faad_hdc/README.aethersdr.md new file mode 100644 index 000000000..7a1faea36 --- /dev/null +++ b/third_party/faad_hdc/README.aethersdr.md @@ -0,0 +1,45 @@ +# FAAD2 HDC decoder in AetherSDR + +This source subset comes from [FAAD2](https://github.com/knik0/faad2) tag +`2.11.2`, commit `673a22a3c7c33e96e2ff7aae7c4d2bc190dfbf92`, with exactly the +HDC-support patch shipped by nrsc5 commit +`0225922b6f68109df39d07391f4d855464598ab8` applied. No other vendor edits are made. + +Patch: `../nrsc5/upstream/support/faad2-hdc-support.patch` + +SHA-256: `e25e8d69c20269757c66ba38c730fbde4588393d981d45d86472beea5cffc117` + +The patch's original header credits Clayton Smith and patch commit +`813725c185276045641e7df90329dbeab5029434`. `SOURCE-MANIFEST.json` records each +included file's pristine and patched SHA-256 plus all omitted upstream paths. +It retains the original `COPYING`, `README`, `AUTHORS`, `ChangeLog`, public +headers, libfaad sources, version metadata and both non-library files touched +by the patch (`CMakeLists.txt`, `frontend/main.c`). Those last two are retained +for exact patch reproduction and are never evaluated or compiled by Aether. + +## License and acknowledgment + +FAAD2 declares GPL-2.0-or-later; AetherSDR distributes this combined work under +GPLv3. Preserve `upstream/COPYING`, the original README, and every per-file +copyright notice. The required acknowledgment is: + +**Code from FAAD2 is copyright (c) Nero AG, www.nero.com** + +The original notices discuss separate commercial licensing and possible patent +royalties. Vendoring does not provide patent clearance. No proprietary binary, +decoder decompilation, or dynamically downloaded runtime is included. + +## Build and reproduction + +`cmake/AetherHdFm.cmake` builds only static `aether_faad_hdc`, with +`HDC_SUPPORT`, upstream's floating-point configuration and DRC setting, and the +GNU `-ffloat-store` setting. The ordinary, DRM and fixed-point variants and the +FAAD CLI are not targets. This decoder is linked privately by `aether_nrsc5`. + +To reproduce, obtain the exact FAAD revision, copy the upstream paths in the +manifest into a fresh directory, and verify each `upstreamSha256`. From that +directory run `git apply --check /absolute/path/to/faad2-hdc-support.patch` +followed by `git apply /absolute/path/to/faad2-hdc-support.patch`. Every +`vendoredSha256` must then match. Patch failure must fail the import; no fuzz, +partial application or ignored error is acceptable. Builds never apply patches +or download sources. The checked-in patched files are the build inputs. diff --git a/third_party/faad_hdc/SOURCE-MANIFEST.json b/third_party/faad_hdc/SOURCE-MANIFEST.json new file mode 100644 index 000000000..98ae37b5f --- /dev/null +++ b/third_party/faad_hdc/SOURCE-MANIFEST.json @@ -0,0 +1,692 @@ +{ + "schemaVersion": 1, + "project": "faad_hdc", + "sourceUrl": "https://github.com/knik0/faad2", + "sourceRevision": "673a22a3c7c33e96e2ff7aae7c4d2bc190dfbf92", + "upstreamLicense": "GPL-2.0-or-later", + "omittedUpstreamPaths": [ + ".bazelrc", + ".github/workflows/build.yaml", + ".github/workflows/fuzz.yaml", + ".gitignore", + "BUILD.bazel", + "WORKSPACE.bazel", + "docs/Ahead AAC Decoder library documentation.doc", + "docs/Ahead AAC Decoder library documentation.pdf", + "docs/libfaad.3", + "frontend/audio.c", + "frontend/audio.h", + "frontend/faad.man", + "frontend/getopt.c", + "frontend/getopt.h", + "frontend/mp4read.c", + "frontend/mp4read.h", + "frontend/unicode_support.c", + "frontend/unicode_support.h", + "fuzz/fuzz_config.c", + "fuzz/fuzz_decode.c", + "libfaad2.def", + "workspace.bzl" + ], + "patches": [ + { + "pathFromRepositoryRoot": "third_party/nrsc5/upstream/support/faad2-hdc-support.patch", + "sourceRevision": "0225922b6f68109df39d07391f4d855464598ab8", + "sha256": "e25e8d69c20269757c66ba38c730fbde4588393d981d45d86472beea5cffc117", + "application": "git apply --check, then git apply; no fuzz or ignored failure" + } + ], + "files": [ + { + "upstreamPath": "AUTHORS", + "vendoredPath": "upstream/AUTHORS", + "upstreamSha256": "dd70437637d86b05ffa5cf1d0cccd35bba11bb8f64b523ebe28ce9a130b774f1", + "vendoredSha256": "dd70437637d86b05ffa5cf1d0cccd35bba11bb8f64b523ebe28ce9a130b774f1" + }, + { + "upstreamPath": "CMakeLists.txt", + "vendoredPath": "upstream/CMakeLists.txt", + "upstreamSha256": "40be17908fadf397ea2ce0857f541cfaad4f50814e3f3eb0a538bac4299c9670", + "vendoredSha256": "4ddfffdc99f37a0b3ef24986fa26c01c1d19fe639e71031e9b16c4720e08aa6c" + }, + { + "upstreamPath": "COPYING", + "vendoredPath": "upstream/COPYING", + "upstreamSha256": "d3baf3a54943cf12a994c85867a18dec84f810901b2f2878ddfd77efcc3c150f", + "vendoredSha256": "d3baf3a54943cf12a994c85867a18dec84f810901b2f2878ddfd77efcc3c150f" + }, + { + "upstreamPath": "ChangeLog", + "vendoredPath": "upstream/ChangeLog", + "upstreamSha256": "1894c29072dde62862f241e2385446025569c9fefd69191282c72370e7338e0e", + "vendoredSha256": "1894c29072dde62862f241e2385446025569c9fefd69191282c72370e7338e0e" + }, + { + "upstreamPath": "README", + "vendoredPath": "upstream/README", + "upstreamSha256": "1f4c7ff64ad2a330f5609471d583f88c6d573b71f23547e38f6f88d32170d918", + "vendoredSha256": "1f4c7ff64ad2a330f5609471d583f88c6d573b71f23547e38f6f88d32170d918" + }, + { + "upstreamPath": "frontend/main.c", + "vendoredPath": "upstream/frontend/main.c", + "upstreamSha256": "f956fdc4bab32eabafdf12aff5044a34affaa330704a7d4f923ff9919efb328b", + "vendoredSha256": "aad7f17d0651f40f5da7153024118d270ac643fc59e42db4c45bbbbb4d1a1fd8" + }, + { + "upstreamPath": "include/faad.h.in", + "vendoredPath": "upstream/include/faad.h.in", + "upstreamSha256": "c763b75c247add3756cdff0ba1e580a60b9b7e0c7d8d78f5a18c8c93bfceebf9", + "vendoredSha256": "c763b75c247add3756cdff0ba1e580a60b9b7e0c7d8d78f5a18c8c93bfceebf9" + }, + { + "upstreamPath": "include/neaacdec.h", + "vendoredPath": "upstream/include/neaacdec.h", + "upstreamSha256": "798e82aff75aaf896f8fe9df26a2191d16817aea838a0d33b5b128e46337ad84", + "vendoredSha256": "08e0da4060e83df26691a8f6f2e1eb4ced1f4d867fe0da5d19dfa046e77e5dcd" + }, + { + "upstreamPath": "libfaad/analysis.h", + "vendoredPath": "upstream/libfaad/analysis.h", + "upstreamSha256": "98b74d724f0df92528d5ea8912489ac01f62bebe110078ebc347ca8f2ccc634b", + "vendoredSha256": "98b74d724f0df92528d5ea8912489ac01f62bebe110078ebc347ca8f2ccc634b" + }, + { + "upstreamPath": "libfaad/bits.c", + "vendoredPath": "upstream/libfaad/bits.c", + "upstreamSha256": "304b2e034bf4f9d2dbfbd727b431dd38da46ae8fa6aea75d07d147c54b359d34", + "vendoredSha256": "e5e84379959b2b9fb7e334389693da34badde5c923418e7d74632714a445ec57" + }, + { + "upstreamPath": "libfaad/bits.h", + "vendoredPath": "upstream/libfaad/bits.h", + "upstreamSha256": "81e8aacc42ff85e2b25191d241be9d946c9c71a729ad3deef8aecec53d603c57", + "vendoredSha256": "1f3170a4e7bb3a7bc6b71edcb809cb97422ec908372f48812535ee2a2c862d02" + }, + { + "upstreamPath": "libfaad/cfft.c", + "vendoredPath": "upstream/libfaad/cfft.c", + "upstreamSha256": "0ea7766f49fb137d033dd57e6bd2548d9eb2701a948529c328976bb1fa531465", + "vendoredSha256": "0ea7766f49fb137d033dd57e6bd2548d9eb2701a948529c328976bb1fa531465" + }, + { + "upstreamPath": "libfaad/cfft.h", + "vendoredPath": "upstream/libfaad/cfft.h", + "upstreamSha256": "14761ba5aaa4ecaa1308975cf3c062780142c27029bf509ea1c8e1e418331b36", + "vendoredSha256": "14761ba5aaa4ecaa1308975cf3c062780142c27029bf509ea1c8e1e418331b36" + }, + { + "upstreamPath": "libfaad/cfft_tab.h", + "vendoredPath": "upstream/libfaad/cfft_tab.h", + "upstreamSha256": "81cd09c19c00fd2b8055cd348daf4e6b55c52bdecb90e7aadb490a9054266e4e", + "vendoredSha256": "81cd09c19c00fd2b8055cd348daf4e6b55c52bdecb90e7aadb490a9054266e4e" + }, + { + "upstreamPath": "libfaad/codebook/hcb.h", + "vendoredPath": "upstream/libfaad/codebook/hcb.h", + "upstreamSha256": "c29b747115cdb5e63208cbaf4340b0b406cb5ab31f84d508487f4142e11afaa9", + "vendoredSha256": "c29b747115cdb5e63208cbaf4340b0b406cb5ab31f84d508487f4142e11afaa9" + }, + { + "upstreamPath": "libfaad/codebook/hcb_1.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_1.h", + "upstreamSha256": "812786639afdf9bd1577ddd10ddce3bea79370c4a6a5446277535707fba0a635", + "vendoredSha256": "812786639afdf9bd1577ddd10ddce3bea79370c4a6a5446277535707fba0a635" + }, + { + "upstreamPath": "libfaad/codebook/hcb_10.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_10.h", + "upstreamSha256": "b97e36bdc34cadc84b7af13106849bf4efa2179e387b8cc380ddb915b6223e67", + "vendoredSha256": "b97e36bdc34cadc84b7af13106849bf4efa2179e387b8cc380ddb915b6223e67" + }, + { + "upstreamPath": "libfaad/codebook/hcb_11.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_11.h", + "upstreamSha256": "74cbea5f948917bd6cbaf514b7c222f9d02d443b0f3783d1fd17de2960273de1", + "vendoredSha256": "74cbea5f948917bd6cbaf514b7c222f9d02d443b0f3783d1fd17de2960273de1" + }, + { + "upstreamPath": "libfaad/codebook/hcb_2.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_2.h", + "upstreamSha256": "cf1392f536e917656a2aca3bf4bf438dd4b24beebd7ab319cc6cd40ea4733688", + "vendoredSha256": "cf1392f536e917656a2aca3bf4bf438dd4b24beebd7ab319cc6cd40ea4733688" + }, + { + "upstreamPath": "libfaad/codebook/hcb_3.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_3.h", + "upstreamSha256": "6f3289cf4bdbfb6801a3cf277a453dc1be8240f0e51518cb44735d257e3b06b1", + "vendoredSha256": "6f3289cf4bdbfb6801a3cf277a453dc1be8240f0e51518cb44735d257e3b06b1" + }, + { + "upstreamPath": "libfaad/codebook/hcb_4.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_4.h", + "upstreamSha256": "e69b22f2062239f19a04fc5f6136f89d48a432b298313641727b584e252facd3", + "vendoredSha256": "e69b22f2062239f19a04fc5f6136f89d48a432b298313641727b584e252facd3" + }, + { + "upstreamPath": "libfaad/codebook/hcb_5.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_5.h", + "upstreamSha256": "b1583b6ebf35764aaf3cbf952f697d0619a7a858dfd31a3886c9257526cd4b03", + "vendoredSha256": "b1583b6ebf35764aaf3cbf952f697d0619a7a858dfd31a3886c9257526cd4b03" + }, + { + "upstreamPath": "libfaad/codebook/hcb_6.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_6.h", + "upstreamSha256": "1a0939bafdde97cb3fb62bb8554aeacb66d104a1a74b0d3b024584de8d132c4f", + "vendoredSha256": "1a0939bafdde97cb3fb62bb8554aeacb66d104a1a74b0d3b024584de8d132c4f" + }, + { + "upstreamPath": "libfaad/codebook/hcb_7.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_7.h", + "upstreamSha256": "035acec6e4d28ab791fe53f8ab7450ddf1e7138b829a1839d903ec7614b16a26", + "vendoredSha256": "035acec6e4d28ab791fe53f8ab7450ddf1e7138b829a1839d903ec7614b16a26" + }, + { + "upstreamPath": "libfaad/codebook/hcb_8.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_8.h", + "upstreamSha256": "8e514b723d66661177ea00127ae1db401c93de087d586b43fffafdc4a0cc4025", + "vendoredSha256": "8e514b723d66661177ea00127ae1db401c93de087d586b43fffafdc4a0cc4025" + }, + { + "upstreamPath": "libfaad/codebook/hcb_9.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_9.h", + "upstreamSha256": "5f57474baef72243dbcecedfbecd28768dec41e651d606336aeab96047fe892e", + "vendoredSha256": "5f57474baef72243dbcecedfbecd28768dec41e651d606336aeab96047fe892e" + }, + { + "upstreamPath": "libfaad/codebook/hcb_sf.h", + "vendoredPath": "upstream/libfaad/codebook/hcb_sf.h", + "upstreamSha256": "75df9f00c22d23285f681620f8ea8d97922df7546a5d741e0243207a648cc74b", + "vendoredSha256": "75df9f00c22d23285f681620f8ea8d97922df7546a5d741e0243207a648cc74b" + }, + { + "upstreamPath": "libfaad/common.c", + "vendoredPath": "upstream/libfaad/common.c", + "upstreamSha256": "23bc661c85dee7821f7771a7313563f72c590740cb30a84fcdb605f301f757d4", + "vendoredSha256": "4ad73e881102f7b0509b34d144bcf74ca1b20ed5adb368a7dbb68b4ed5ef4151" + }, + { + "upstreamPath": "libfaad/common.h", + "vendoredPath": "upstream/libfaad/common.h", + "upstreamSha256": "ab3becc31870a0eade97f8569c8e24ff69a887fff3dd960bb0fda833a9c4ab7a", + "vendoredSha256": "7068477b90964402a0a69d0e8f360b669a71722f9b2cf0219511317850e9afdb" + }, + { + "upstreamPath": "libfaad/decoder.c", + "vendoredPath": "upstream/libfaad/decoder.c", + "upstreamSha256": "64c9b4502e7551eee11417fdb364b89d9e53728ccde4e45176a2f9e835172825", + "vendoredSha256": "78ef694355400f94e505b326a034df9e2a7b38705bf64ee03aa54f65e99680dc" + }, + { + "upstreamPath": "libfaad/drc.c", + "vendoredPath": "upstream/libfaad/drc.c", + "upstreamSha256": "d8cad0825b6a051d5ad04076f514d2ae72e235debdb8177189d0c7905c92df79", + "vendoredSha256": "d8cad0825b6a051d5ad04076f514d2ae72e235debdb8177189d0c7905c92df79" + }, + { + "upstreamPath": "libfaad/drc.h", + "vendoredPath": "upstream/libfaad/drc.h", + "upstreamSha256": "dc331e3fcf499304650f22b8db5288440be25a1ebe5a6e166a4c2611a42f3ac0", + "vendoredSha256": "dc331e3fcf499304650f22b8db5288440be25a1ebe5a6e166a4c2611a42f3ac0" + }, + { + "upstreamPath": "libfaad/drm_dec.c", + "vendoredPath": "upstream/libfaad/drm_dec.c", + "upstreamSha256": "fd4ddf48b006ab4ab4c1f758609404708f42540e8abd0b9b5936bc0f5bf9abb8", + "vendoredSha256": "0261aafd59369c358374a2fd03c44e55c82c7d2b9473922c1c6186be5edf84d9" + }, + { + "upstreamPath": "libfaad/drm_dec.h", + "vendoredPath": "upstream/libfaad/drm_dec.h", + "upstreamSha256": "2d7fbc506a6d6a3134bfd52029bb87f7eacb13b8a968a540d5a3b0ee56b6411c", + "vendoredSha256": "2e5d76bfc41d09b10bf749afdad972d367c36de12e6933bd559110838afdc168" + }, + { + "upstreamPath": "libfaad/error.c", + "vendoredPath": "upstream/libfaad/error.c", + "upstreamSha256": "77287cfbc0759219be6af289bbfba5ae6eeee51a60ebbc21223be2d8f3515fa9", + "vendoredSha256": "77287cfbc0759219be6af289bbfba5ae6eeee51a60ebbc21223be2d8f3515fa9" + }, + { + "upstreamPath": "libfaad/error.h", + "vendoredPath": "upstream/libfaad/error.h", + "upstreamSha256": "4abe243fcd032387b172ce394ef82ba0faa574c4bec00f1349eb1841807acb0b", + "vendoredSha256": "4abe243fcd032387b172ce394ef82ba0faa574c4bec00f1349eb1841807acb0b" + }, + { + "upstreamPath": "libfaad/faad2.pc.in", + "vendoredPath": "upstream/libfaad/faad2.pc.in", + "upstreamSha256": "44c590599905a6b2b67dabe6bcadc4347b94f700ffb3086471d3724f1693c7f5", + "vendoredSha256": "44c590599905a6b2b67dabe6bcadc4347b94f700ffb3086471d3724f1693c7f5" + }, + { + "upstreamPath": "libfaad/filtbank.c", + "vendoredPath": "upstream/libfaad/filtbank.c", + "upstreamSha256": "f1948147427c5c0b249ae989f61b6f5d72be51c72724e3a747fc10c08db84eee", + "vendoredSha256": "f1948147427c5c0b249ae989f61b6f5d72be51c72724e3a747fc10c08db84eee" + }, + { + "upstreamPath": "libfaad/filtbank.h", + "vendoredPath": "upstream/libfaad/filtbank.h", + "upstreamSha256": "90e81e11cac10d565117010751acc6b15254bc4396b0e7987e47420548a79962", + "vendoredSha256": "90e81e11cac10d565117010751acc6b15254bc4396b0e7987e47420548a79962" + }, + { + "upstreamPath": "libfaad/fixed.h", + "vendoredPath": "upstream/libfaad/fixed.h", + "upstreamSha256": "fcc639af56c3e9681bb3934ff7b695ae010dff5c5762dc5f213dcb3b82695985", + "vendoredSha256": "fcc639af56c3e9681bb3934ff7b695ae010dff5c5762dc5f213dcb3b82695985" + }, + { + "upstreamPath": "libfaad/hcr.c", + "vendoredPath": "upstream/libfaad/hcr.c", + "upstreamSha256": "1fae10ed88f09c38b9030eff9f607bfed61023089c25d5c62aa494e263bc3e82", + "vendoredSha256": "1fae10ed88f09c38b9030eff9f607bfed61023089c25d5c62aa494e263bc3e82" + }, + { + "upstreamPath": "libfaad/huffman.c", + "vendoredPath": "upstream/libfaad/huffman.c", + "upstreamSha256": "1e17914eae08260014cc02cd834bc7ec38de322a4abd27a04ff66d8ef32a27b7", + "vendoredSha256": "1e17914eae08260014cc02cd834bc7ec38de322a4abd27a04ff66d8ef32a27b7" + }, + { + "upstreamPath": "libfaad/huffman.h", + "vendoredPath": "upstream/libfaad/huffman.h", + "upstreamSha256": "eec86c53f227b9d03308b21684da63dfeca1fbcc885d419202a3dbf8f1789a35", + "vendoredSha256": "eec86c53f227b9d03308b21684da63dfeca1fbcc885d419202a3dbf8f1789a35" + }, + { + "upstreamPath": "libfaad/ic_predict.c", + "vendoredPath": "upstream/libfaad/ic_predict.c", + "upstreamSha256": "b36661e9b2f6e3d38507d25b482c003096b59801adfaa4685c2a7830f320e515", + "vendoredSha256": "b36661e9b2f6e3d38507d25b482c003096b59801adfaa4685c2a7830f320e515" + }, + { + "upstreamPath": "libfaad/ic_predict.h", + "vendoredPath": "upstream/libfaad/ic_predict.h", + "upstreamSha256": "9f176652e5c63c7927831b33d797139fbd17e465a6df359b5e1e7c589962c652", + "vendoredSha256": "9f176652e5c63c7927831b33d797139fbd17e465a6df359b5e1e7c589962c652" + }, + { + "upstreamPath": "libfaad/iq_table.h", + "vendoredPath": "upstream/libfaad/iq_table.h", + "upstreamSha256": "136063089b6e9319fab1aab0d42eba58b0a42c07afc9e394fa441b28ffa583bc", + "vendoredSha256": "136063089b6e9319fab1aab0d42eba58b0a42c07afc9e394fa441b28ffa583bc" + }, + { + "upstreamPath": "libfaad/is.c", + "vendoredPath": "upstream/libfaad/is.c", + "upstreamSha256": "0eb29ed74ae3ebce2aba27460bd3c8ec2f9ec28fad8339c93de7c63f1fcb4870", + "vendoredSha256": "0eb29ed74ae3ebce2aba27460bd3c8ec2f9ec28fad8339c93de7c63f1fcb4870" + }, + { + "upstreamPath": "libfaad/is.h", + "vendoredPath": "upstream/libfaad/is.h", + "upstreamSha256": "4d6b1c58bc115dd86f4d5ba7c61ff275a07763e4c9f706198f79d1201142f9da", + "vendoredSha256": "4d6b1c58bc115dd86f4d5ba7c61ff275a07763e4c9f706198f79d1201142f9da" + }, + { + "upstreamPath": "libfaad/kbd_win.h", + "vendoredPath": "upstream/libfaad/kbd_win.h", + "upstreamSha256": "70540aa3de9aea352b014506d2f285c32da7c6065f0f8c5b50ef321448588e57", + "vendoredSha256": "70540aa3de9aea352b014506d2f285c32da7c6065f0f8c5b50ef321448588e57" + }, + { + "upstreamPath": "libfaad/lt_predict.c", + "vendoredPath": "upstream/libfaad/lt_predict.c", + "upstreamSha256": "3d25f428111c47e3458edf8cc53b083988512ba91974c3c24966a5d3b4d81100", + "vendoredSha256": "3d25f428111c47e3458edf8cc53b083988512ba91974c3c24966a5d3b4d81100" + }, + { + "upstreamPath": "libfaad/lt_predict.h", + "vendoredPath": "upstream/libfaad/lt_predict.h", + "upstreamSha256": "0b9784a45a2eb23fa432b6c1ac9d15ec021be6cbe596a786349e2c39f4532c21", + "vendoredSha256": "0b9784a45a2eb23fa432b6c1ac9d15ec021be6cbe596a786349e2c39f4532c21" + }, + { + "upstreamPath": "libfaad/mdct.c", + "vendoredPath": "upstream/libfaad/mdct.c", + "upstreamSha256": "481e4674ef4f9ff5b8bf29f735c7f101a20954e378b891073dc2104aa9a9d588", + "vendoredSha256": "481e4674ef4f9ff5b8bf29f735c7f101a20954e378b891073dc2104aa9a9d588" + }, + { + "upstreamPath": "libfaad/mdct.h", + "vendoredPath": "upstream/libfaad/mdct.h", + "upstreamSha256": "d1a4dadf95dbc3940013e3ba7910ec38403a448ec72771dbc1261f54a7a68bad", + "vendoredSha256": "d1a4dadf95dbc3940013e3ba7910ec38403a448ec72771dbc1261f54a7a68bad" + }, + { + "upstreamPath": "libfaad/mdct_tab.h", + "vendoredPath": "upstream/libfaad/mdct_tab.h", + "upstreamSha256": "bf4022ebfddf5969cd065a5c2cf42332e09d6825d015a87551796576d74f4892", + "vendoredSha256": "bf4022ebfddf5969cd065a5c2cf42332e09d6825d015a87551796576d74f4892" + }, + { + "upstreamPath": "libfaad/mp4.c", + "vendoredPath": "upstream/libfaad/mp4.c", + "upstreamSha256": "700c6752183075a9cee6176fd5defc7dba7a51ff20ef55721fd5b203793b7996", + "vendoredSha256": "700c6752183075a9cee6176fd5defc7dba7a51ff20ef55721fd5b203793b7996" + }, + { + "upstreamPath": "libfaad/mp4.h", + "vendoredPath": "upstream/libfaad/mp4.h", + "upstreamSha256": "0b543f88bbedec3245a52388677dded05823483339ce2c25efc8c5f2b7e1918c", + "vendoredSha256": "0b543f88bbedec3245a52388677dded05823483339ce2c25efc8c5f2b7e1918c" + }, + { + "upstreamPath": "libfaad/ms.c", + "vendoredPath": "upstream/libfaad/ms.c", + "upstreamSha256": "158b5c1bc9777c4adf67d17e8da0c95bf0cac395c1d887da8e45931d1f63bc0f", + "vendoredSha256": "158b5c1bc9777c4adf67d17e8da0c95bf0cac395c1d887da8e45931d1f63bc0f" + }, + { + "upstreamPath": "libfaad/ms.h", + "vendoredPath": "upstream/libfaad/ms.h", + "upstreamSha256": "913977cc6e5b37335ff8c494d93fe8f882a0d05ccbe34106a04123e7412d76e2", + "vendoredSha256": "913977cc6e5b37335ff8c494d93fe8f882a0d05ccbe34106a04123e7412d76e2" + }, + { + "upstreamPath": "libfaad/output.c", + "vendoredPath": "upstream/libfaad/output.c", + "upstreamSha256": "df55151d19a021bd12ba256d95ad326757b36dcdb2b01d5b55f117bc60108502", + "vendoredSha256": "df55151d19a021bd12ba256d95ad326757b36dcdb2b01d5b55f117bc60108502" + }, + { + "upstreamPath": "libfaad/output.h", + "vendoredPath": "upstream/libfaad/output.h", + "upstreamSha256": "4fc4291a6e9207a081aa3610d9b5ad70da8d95175a6dce316b17e5579ab7f9ff", + "vendoredSha256": "4fc4291a6e9207a081aa3610d9b5ad70da8d95175a6dce316b17e5579ab7f9ff" + }, + { + "upstreamPath": "libfaad/pns.c", + "vendoredPath": "upstream/libfaad/pns.c", + "upstreamSha256": "ed4d47afdafcc8f325e1b4790ebfddfdc09e899f555fc4a657cfa0ee5d5b7136", + "vendoredSha256": "ed4d47afdafcc8f325e1b4790ebfddfdc09e899f555fc4a657cfa0ee5d5b7136" + }, + { + "upstreamPath": "libfaad/pns.h", + "vendoredPath": "upstream/libfaad/pns.h", + "upstreamSha256": "fed99c5b14d404c879d31e31d29e23b6e9e7b97bba7d1b509c65f9db02b1e47c", + "vendoredSha256": "fed99c5b14d404c879d31e31d29e23b6e9e7b97bba7d1b509c65f9db02b1e47c" + }, + { + "upstreamPath": "libfaad/ps_dec.c", + "vendoredPath": "upstream/libfaad/ps_dec.c", + "upstreamSha256": "ba5e0ab56fa8afff78c815031524a452523e9380d6fa060f3abac75ddd17e681", + "vendoredSha256": "ba5e0ab56fa8afff78c815031524a452523e9380d6fa060f3abac75ddd17e681" + }, + { + "upstreamPath": "libfaad/ps_dec.h", + "vendoredPath": "upstream/libfaad/ps_dec.h", + "upstreamSha256": "bdf72abfcb9bc3e957ad4d2905e984df009371a1cc062859d485918e67777479", + "vendoredSha256": "bdf72abfcb9bc3e957ad4d2905e984df009371a1cc062859d485918e67777479" + }, + { + "upstreamPath": "libfaad/ps_syntax.c", + "vendoredPath": "upstream/libfaad/ps_syntax.c", + "upstreamSha256": "fb11a927956413ff12355238b6dbf5a98def2da4d9bca51deb936c0d337375d0", + "vendoredSha256": "fb11a927956413ff12355238b6dbf5a98def2da4d9bca51deb936c0d337375d0" + }, + { + "upstreamPath": "libfaad/ps_tables.h", + "vendoredPath": "upstream/libfaad/ps_tables.h", + "upstreamSha256": "f93cb99806729b56d2a439cbccf3bb2cc039fae5d2cc285206a22ac7ac4fe17a", + "vendoredSha256": "f93cb99806729b56d2a439cbccf3bb2cc039fae5d2cc285206a22ac7ac4fe17a" + }, + { + "upstreamPath": "libfaad/pulse.c", + "vendoredPath": "upstream/libfaad/pulse.c", + "upstreamSha256": "b9072dd68f62791c228e2346c4fd5c0ffcebd5d3432bd23493ff733b16607fa8", + "vendoredSha256": "b9072dd68f62791c228e2346c4fd5c0ffcebd5d3432bd23493ff733b16607fa8" + }, + { + "upstreamPath": "libfaad/pulse.h", + "vendoredPath": "upstream/libfaad/pulse.h", + "upstreamSha256": "abf0d9823322d87c47ff8c688faf45c1627e875d5e0af21d836489e35778938b", + "vendoredSha256": "abf0d9823322d87c47ff8c688faf45c1627e875d5e0af21d836489e35778938b" + }, + { + "upstreamPath": "libfaad/rvlc.c", + "vendoredPath": "upstream/libfaad/rvlc.c", + "upstreamSha256": "d90e7b0faa73cc8e5717c31ecc037e7cfa6c8e7e0200b2823a1b0f62470142c9", + "vendoredSha256": "d90e7b0faa73cc8e5717c31ecc037e7cfa6c8e7e0200b2823a1b0f62470142c9" + }, + { + "upstreamPath": "libfaad/rvlc.h", + "vendoredPath": "upstream/libfaad/rvlc.h", + "upstreamSha256": "262ede8409646833fd48e1fb4a35df1f2fed9d8a57ad913d1bd52fc704b0e3c2", + "vendoredSha256": "262ede8409646833fd48e1fb4a35df1f2fed9d8a57ad913d1bd52fc704b0e3c2" + }, + { + "upstreamPath": "libfaad/sbr_dct.c", + "vendoredPath": "upstream/libfaad/sbr_dct.c", + "upstreamSha256": "ef70916bf8eace403ab7a3475b688cbef57257c551ebb91b54c4c981709eead4", + "vendoredSha256": "ef70916bf8eace403ab7a3475b688cbef57257c551ebb91b54c4c981709eead4" + }, + { + "upstreamPath": "libfaad/sbr_dct.h", + "vendoredPath": "upstream/libfaad/sbr_dct.h", + "upstreamSha256": "cf3027abd54ff02cca8cb53eef225a0f1addc349a4d55aee4856a2185fe1b1b5", + "vendoredSha256": "cf3027abd54ff02cca8cb53eef225a0f1addc349a4d55aee4856a2185fe1b1b5" + }, + { + "upstreamPath": "libfaad/sbr_dec.c", + "vendoredPath": "upstream/libfaad/sbr_dec.c", + "upstreamSha256": "f5c5ae679772a12ea98f502e813eaa04da77ffbd463716891733ceabb7946165", + "vendoredSha256": "272f1fff88067388f72ba5690fffb7849d5b8a57b930248cd17039de98bd6f12" + }, + { + "upstreamPath": "libfaad/sbr_dec.h", + "vendoredPath": "upstream/libfaad/sbr_dec.h", + "upstreamSha256": "906b0d68b3ca6676d75a285f3237be7dfa5192e176870a1037806c1659a0350f", + "vendoredSha256": "c9b49b2a1adaada7b7fc665e6fe3d18c6521323c0d13c78e2ea9e08a49b54e88" + }, + { + "upstreamPath": "libfaad/sbr_e_nf.c", + "vendoredPath": "upstream/libfaad/sbr_e_nf.c", + "upstreamSha256": "b69fabde5cb4721bc48d7539c691d8fb570c0d408c9f950a72f6f97999c639ed", + "vendoredSha256": "b69fabde5cb4721bc48d7539c691d8fb570c0d408c9f950a72f6f97999c639ed" + }, + { + "upstreamPath": "libfaad/sbr_e_nf.h", + "vendoredPath": "upstream/libfaad/sbr_e_nf.h", + "upstreamSha256": "0ec89825151ba5c3439aebce9974500633ecd2e2192bde0b64791238989973e7", + "vendoredSha256": "0ec89825151ba5c3439aebce9974500633ecd2e2192bde0b64791238989973e7" + }, + { + "upstreamPath": "libfaad/sbr_fbt.c", + "vendoredPath": "upstream/libfaad/sbr_fbt.c", + "upstreamSha256": "32453532480bdadd008b127810ac9864ce3faa73bb8139427ef3cb8215328d14", + "vendoredSha256": "32453532480bdadd008b127810ac9864ce3faa73bb8139427ef3cb8215328d14" + }, + { + "upstreamPath": "libfaad/sbr_fbt.h", + "vendoredPath": "upstream/libfaad/sbr_fbt.h", + "upstreamSha256": "416d55f49dd10da3c1664785d312e5f9c65cc60fa488366b5e7aa017918a59e3", + "vendoredSha256": "416d55f49dd10da3c1664785d312e5f9c65cc60fa488366b5e7aa017918a59e3" + }, + { + "upstreamPath": "libfaad/sbr_hfadj.c", + "vendoredPath": "upstream/libfaad/sbr_hfadj.c", + "upstreamSha256": "403db23a2d248afbd29bf0703319c4e18974be900693659a919ef0a8165bea8e", + "vendoredSha256": "403db23a2d248afbd29bf0703319c4e18974be900693659a919ef0a8165bea8e" + }, + { + "upstreamPath": "libfaad/sbr_hfadj.h", + "vendoredPath": "upstream/libfaad/sbr_hfadj.h", + "upstreamSha256": "4a53c7e9423f4ffd3a72a2ce1a02e0fe88c5ddf4860bcc8dd084a2cb98dda71d", + "vendoredSha256": "4a53c7e9423f4ffd3a72a2ce1a02e0fe88c5ddf4860bcc8dd084a2cb98dda71d" + }, + { + "upstreamPath": "libfaad/sbr_hfgen.c", + "vendoredPath": "upstream/libfaad/sbr_hfgen.c", + "upstreamSha256": "12e6351d78c509ab8d7a43e626bf871f562c150a300cb0aba1f25a771a3bfe04", + "vendoredSha256": "12e6351d78c509ab8d7a43e626bf871f562c150a300cb0aba1f25a771a3bfe04" + }, + { + "upstreamPath": "libfaad/sbr_hfgen.h", + "vendoredPath": "upstream/libfaad/sbr_hfgen.h", + "upstreamSha256": "38fb6ac458d6f637fd1ff8c7d88005945aa25acd82b4691c5b341b6f18bb89bc", + "vendoredSha256": "38fb6ac458d6f637fd1ff8c7d88005945aa25acd82b4691c5b341b6f18bb89bc" + }, + { + "upstreamPath": "libfaad/sbr_huff.c", + "vendoredPath": "upstream/libfaad/sbr_huff.c", + "upstreamSha256": "7e7e420891bd04bfd86945f6d1fbf6ef8ef3db601a3ad5f92e1b900c6d92469c", + "vendoredSha256": "7e7e420891bd04bfd86945f6d1fbf6ef8ef3db601a3ad5f92e1b900c6d92469c" + }, + { + "upstreamPath": "libfaad/sbr_huff.h", + "vendoredPath": "upstream/libfaad/sbr_huff.h", + "upstreamSha256": "c7394a14a2dd6878be53b81b3d2079a9f28320d89640bd6517cca9d8d527082b", + "vendoredSha256": "c7394a14a2dd6878be53b81b3d2079a9f28320d89640bd6517cca9d8d527082b" + }, + { + "upstreamPath": "libfaad/sbr_noise.h", + "vendoredPath": "upstream/libfaad/sbr_noise.h", + "upstreamSha256": "ebf972f09441c2a607b16fc8ac8b7e830a5b8f5529595daba972ccc27bc97170", + "vendoredSha256": "ebf972f09441c2a607b16fc8ac8b7e830a5b8f5529595daba972ccc27bc97170" + }, + { + "upstreamPath": "libfaad/sbr_qmf.c", + "vendoredPath": "upstream/libfaad/sbr_qmf.c", + "upstreamSha256": "e227134d98dc175741734f0eeaf42fabfa75e99ecd71fa24381f0805c1432d3f", + "vendoredSha256": "e227134d98dc175741734f0eeaf42fabfa75e99ecd71fa24381f0805c1432d3f" + }, + { + "upstreamPath": "libfaad/sbr_qmf.h", + "vendoredPath": "upstream/libfaad/sbr_qmf.h", + "upstreamSha256": "b2932c1aba63f33948504a1e2786df965b7b0eedffdb869b9914fb6aa8d0992d", + "vendoredSha256": "b2932c1aba63f33948504a1e2786df965b7b0eedffdb869b9914fb6aa8d0992d" + }, + { + "upstreamPath": "libfaad/sbr_qmf_c.h", + "vendoredPath": "upstream/libfaad/sbr_qmf_c.h", + "upstreamSha256": "81a7c0b46f31eb80b674b4caa16a81ed4ed48ae6f1f93f37178815188aef2a83", + "vendoredSha256": "81a7c0b46f31eb80b674b4caa16a81ed4ed48ae6f1f93f37178815188aef2a83" + }, + { + "upstreamPath": "libfaad/sbr_syntax.c", + "vendoredPath": "upstream/libfaad/sbr_syntax.c", + "upstreamSha256": "af9740680d0655bcebdcb53f8302d50d68603e0d9d285c1d43d8b1f892c87e2f", + "vendoredSha256": "0622799683e3dcdef555cf08d125cae73876e35b673f5415fc7487bd6c38594b" + }, + { + "upstreamPath": "libfaad/sbr_syntax.h", + "vendoredPath": "upstream/libfaad/sbr_syntax.h", + "upstreamSha256": "b99a1012f124eac127c56aa6c91b7239a5348938d8becab92d99d112e27752d3", + "vendoredSha256": "b99a1012f124eac127c56aa6c91b7239a5348938d8becab92d99d112e27752d3" + }, + { + "upstreamPath": "libfaad/sbr_tf_grid.c", + "vendoredPath": "upstream/libfaad/sbr_tf_grid.c", + "upstreamSha256": "7105328e3220116e1d5d3c3ced52d0fc172360f818ff7cd4c13e7de9791b5e22", + "vendoredSha256": "7105328e3220116e1d5d3c3ced52d0fc172360f818ff7cd4c13e7de9791b5e22" + }, + { + "upstreamPath": "libfaad/sbr_tf_grid.h", + "vendoredPath": "upstream/libfaad/sbr_tf_grid.h", + "upstreamSha256": "d4d0be2de8298e9249774e7303c9cc492523f959ba561b7222c2f805222501b0", + "vendoredSha256": "d4d0be2de8298e9249774e7303c9cc492523f959ba561b7222c2f805222501b0" + }, + { + "upstreamPath": "libfaad/sine_win.h", + "vendoredPath": "upstream/libfaad/sine_win.h", + "upstreamSha256": "c633d41a59845923df5694bd14c8e9f1905e4fc7648a2fb5f0f25dbfc67bcb51", + "vendoredSha256": "c633d41a59845923df5694bd14c8e9f1905e4fc7648a2fb5f0f25dbfc67bcb51" + }, + { + "upstreamPath": "libfaad/specrec.c", + "vendoredPath": "upstream/libfaad/specrec.c", + "upstreamSha256": "84f1907e2f8089fde43bddd18e33f87567481c9864161e909c3d5d395e6d16c3", + "vendoredSha256": "84f1907e2f8089fde43bddd18e33f87567481c9864161e909c3d5d395e6d16c3" + }, + { + "upstreamPath": "libfaad/specrec.h", + "vendoredPath": "upstream/libfaad/specrec.h", + "upstreamSha256": "e7d3b845c16f4c19320480b35046d1c3d0cf1351d5487571df57f8494b3d52c1", + "vendoredSha256": "e7d3b845c16f4c19320480b35046d1c3d0cf1351d5487571df57f8494b3d52c1" + }, + { + "upstreamPath": "libfaad/ssr.c", + "vendoredPath": "upstream/libfaad/ssr.c", + "upstreamSha256": "0fdfc615585e42715fc750de7c1a4e006959537d616ab3051f81bf6bf42e9973", + "vendoredSha256": "0fdfc615585e42715fc750de7c1a4e006959537d616ab3051f81bf6bf42e9973" + }, + { + "upstreamPath": "libfaad/ssr.h", + "vendoredPath": "upstream/libfaad/ssr.h", + "upstreamSha256": "b0354a5f8b89b536bd15bf8709b917289702b8a7cfe71cee883e8a0b32c8fc7f", + "vendoredSha256": "b0354a5f8b89b536bd15bf8709b917289702b8a7cfe71cee883e8a0b32c8fc7f" + }, + { + "upstreamPath": "libfaad/ssr_fb.c", + "vendoredPath": "upstream/libfaad/ssr_fb.c", + "upstreamSha256": "bbcf4e804f78acac1689c4d10a0327d2aed3d9c6724bb01dda16a04a53a5e9c3", + "vendoredSha256": "bbcf4e804f78acac1689c4d10a0327d2aed3d9c6724bb01dda16a04a53a5e9c3" + }, + { + "upstreamPath": "libfaad/ssr_fb.h", + "vendoredPath": "upstream/libfaad/ssr_fb.h", + "upstreamSha256": "43a600bb33427a9cd27b333435a0f35d2cf6c662df5950e5ea801bda5fca8a28", + "vendoredSha256": "43a600bb33427a9cd27b333435a0f35d2cf6c662df5950e5ea801bda5fca8a28" + }, + { + "upstreamPath": "libfaad/ssr_ipqf.c", + "vendoredPath": "upstream/libfaad/ssr_ipqf.c", + "upstreamSha256": "3209f62e35ec7228c106185cce97b5d905714b8f5bc3af417e14e2cb04005dfe", + "vendoredSha256": "3209f62e35ec7228c106185cce97b5d905714b8f5bc3af417e14e2cb04005dfe" + }, + { + "upstreamPath": "libfaad/ssr_ipqf.h", + "vendoredPath": "upstream/libfaad/ssr_ipqf.h", + "upstreamSha256": "bb9ca4ba6e70808d72808cd01449ecf1aacbeb311b7902d362fc3dbbff14e466", + "vendoredSha256": "bb9ca4ba6e70808d72808cd01449ecf1aacbeb311b7902d362fc3dbbff14e466" + }, + { + "upstreamPath": "libfaad/ssr_win.h", + "vendoredPath": "upstream/libfaad/ssr_win.h", + "upstreamSha256": "35645021a29c425e3b6b9a049ee22068be535b371ade4a1dd3041d956d627d25", + "vendoredSha256": "35645021a29c425e3b6b9a049ee22068be535b371ade4a1dd3041d956d627d25" + }, + { + "upstreamPath": "libfaad/structs.h", + "vendoredPath": "upstream/libfaad/structs.h", + "upstreamSha256": "cd547bbf59a112b81add8e1ef5d9d30282e97030400def577d9f447157b44ac6", + "vendoredSha256": "cd547bbf59a112b81add8e1ef5d9d30282e97030400def577d9f447157b44ac6" + }, + { + "upstreamPath": "libfaad/syntax.c", + "vendoredPath": "upstream/libfaad/syntax.c", + "upstreamSha256": "62146abb3a64a1949bd5a9bf834d737b6c5e7927a588de13e30125bb798931a1", + "vendoredSha256": "56bec534b01c0d55bc47fd302f052034ad4b212e52cf17541ae1e1d2d5aea07d" + }, + { + "upstreamPath": "libfaad/syntax.h", + "vendoredPath": "upstream/libfaad/syntax.h", + "upstreamSha256": "74cca2de950fda47a7e65361478e67125d8f68eb61607d87c4e62c764ee8fb59", + "vendoredSha256": "fe964c48d6599b27f92584b43811fa7ef4c444dd6defe557667c68aa24a78017" + }, + { + "upstreamPath": "libfaad/tns.c", + "vendoredPath": "upstream/libfaad/tns.c", + "upstreamSha256": "3ea9dce050eefb0eaab0000c15427652fb94b1e7564797f0e1cdb9e88e2c6cd9", + "vendoredSha256": "3ea9dce050eefb0eaab0000c15427652fb94b1e7564797f0e1cdb9e88e2c6cd9" + }, + { + "upstreamPath": "libfaad/tns.h", + "vendoredPath": "upstream/libfaad/tns.h", + "upstreamSha256": "2fb5b5ebf36b00e80109b23c9e06ed3a2f3e4a582b969126e877a813d1235272", + "vendoredSha256": "2fb5b5ebf36b00e80109b23c9e06ed3a2f3e4a582b969126e877a813d1235272" + }, + { + "upstreamPath": "properties.json", + "vendoredPath": "upstream/properties.json", + "upstreamSha256": "345e2577fdd5443b73bc5c74d1a54725afb56f952aba432cb071f12dc8d0c710", + "vendoredSha256": "345e2577fdd5443b73bc5c74d1a54725afb56f952aba432cb071f12dc8d0c710" + } + ], + "upstreamTag": "2.11.2", + "fileCount": 108, + "vendoredBytes": 2478750 +} diff --git a/third_party/faad_hdc/upstream/AUTHORS b/third_party/faad_hdc/upstream/AUTHORS new file mode 100644 index 000000000..4f8d7660e --- /dev/null +++ b/third_party/faad_hdc/upstream/AUTHORS @@ -0,0 +1,17 @@ + +M. Bakker (mbakker(at)nero.com) + - complete library + +Alexander Kurpiers (a.kurpiers(at)nt.tu-darmstadt.de) + - HCR code + - DRM stuff + - lot's of bug fixes + +Volker Fischer (v.fischer(at)nt.tu-darmstadt.de) + - DRM code + - lot's of bug fixes + +Gian-Carlo Pascutto (gpascutto(at)nero.com) + - DRM PS code + - bugfixes + \ No newline at end of file diff --git a/third_party/faad_hdc/upstream/CMakeLists.txt b/third_party/faad_hdc/upstream/CMakeLists.txt new file mode 100644 index 000000000..595641847 --- /dev/null +++ b/third_party/faad_hdc/upstream/CMakeLists.txt @@ -0,0 +1,304 @@ +# Available CMake versions: +# - Ubuntu 20.04 LTS : 3.16.3 +# - Solaris 11.4 SRU 15 : 3.15 +cmake_minimum_required(VERSION 3.15) + +include(CheckLibraryExists) +include(CheckSymbolExists) + +project(LIBFAAD2 LANGUAGES C) + +# Set a default build type if none was specified +set(default_build_type "RelWithDebInfo") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE + STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + +# If FAAD is being bundled in another project, we don't want to +# install anything. However, we want to let people override this, so +# we'll use the FAAD_BUNDLED_MODE variable to let them do that; just +# set it to OFF in your project before you add_subdirectory(faad2). +get_directory_property(FAAD_PARENT_DIRECTORY PARENT_DIRECTORY) +if(NOT DEFINED FAAD_BUNDLED_MODE) + # Bundled mode hasn't been set one way or the other, set the default + # depending on whether or not we are the top-level project. + if(FAAD_PARENT_DIRECTORY) + set(FAAD_BUNDLED_MODE ON) + if(NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS OFF) + endif() + else() + set(FAAD_BUNDLED_MODE OFF) + if(NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON) + endif() + endif() +endif() +mark_as_advanced(FAAD_BUNDLED_MODE) + +if(BUILD_SHARED_LIBS) + if(NOT DEFINED CMAKE_C_VISIBILITY_PRESET) + set(CMAKE_C_VISIBILITY_PRESET hidden) + endif() + if (WIN32 AND NOT DEFINED CMAKE_DLL_NAME_WITH_SOVERSION) + set(CMAKE_DLL_NAME_WITH_SOVERSION ON) + endif() +endif() + +find_library(MATH_LIBRARY m) + +include(GNUInstallDirs) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(CMAKE_MACOS_RPATH TRUE) + set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib") +endif() + +# Extract version information + +set(CAPTURE_PACKAGE_VERSION "[ \\t]*\"PACKAGE_VERSION\"[ \\t]*:[ \\t]\"(.*)\"") +file(STRINGS "properties.json" _faad_version_line REGEX "${CAPTURE_PACKAGE_VERSION}") +string(REGEX REPLACE "${CAPTURE_PACKAGE_VERSION}" "\\1" FAAD_VERSION "${_faad_version_line}") +string(REPLACE "\." ";" FAAD_VERSION_PARTS ${FAAD_VERSION}) +list(GET FAAD_VERSION_PARTS 0 FAAD_VERSION_MAJOR) +list(GET FAAD_VERSION_PARTS 1 FAAD_VERSION_MINOR) +list(GET FAAD_VERSION_PARTS 2 FAAD_VERSION_PATCH) +# Semantic -> library version +# NB(eustas): likely that will be always OK; if not, we could read "overrides" from properties.json +math(EXPR FAAD_ABI_VERSION_CURRENT "${FAAD_VERSION_MAJOR} + ${FAAD_VERSION_MINOR}") +set(FAAD_ABI_VERSION_AGE "${FAAD_VERSION_MINOR}") +set(FAAD_ABI_VERSION_REVISION "${FAAD_VERSION_PATCH}") +set(FAAD_ABI_COMPATIBILITY "${FAAD_VERSION_MAJOR}") + +message(STATUS "FAAD PACKAGE_VERSION: ${FAAD_VERSION}") +mark_as_advanced(FAAD_VERSION FAAD_ABI_COMPATIBILITY FAAD_ABI_VERSION_AGE FAAD_ABI_VERSION_REVISION) + +file(READ include/faad.h.in FAAD_H_SRC) +string(REGEX REPLACE "@VERSION@" "${FAAD_VERSION}" FAAD_H_SRC ${FAAD_H_SRC}) +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/include/faad.h ${FAAD_H_SRC}) + +# Read sources list + +file(GLOB_RECURSE LIBFAAD_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} libfaad/*.c libfaad/*.h) +mark_as_advanced(LIBFAAD_SOURCES) + +file(GLOB_RECURSE FAAD_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} frontend/*.c frontend/*.h) +if (NOT MSVC) + list(FILTER FAAD_SOURCES EXCLUDE REGEX ".*getopt\\.[ch]$") +endif() + +# Build + +# Lets stick to predefined config, until more flexibility is actually requested. +set(FAAD_DEFINES + HAVE_INTTYPES_H=1 + HAVE_MEMCPY=1 + HAVE_STRING_H=1 + HAVE_STRINGS_H=1 + HAVE_SYS_STAT_H=1 + HAVE_SYS_TYPES_H=1 + PACKAGE_VERSION=\"${FAAD_VERSION}\" +) + +option(FAAD_APPLY_DRC "Apply dynamic range control" ON) +if(FAAD_APPLY_DRC) + list(APPEND FAAD_DEFINES + APPLY_DRC + ) +endif() + +check_library_exists(m lrintf "" HAVE_LIBM) +if(HAVE_LIBM) + list(APPEND CMAKE_REQUIRED_LIBRARIES m) +endif() + +check_symbol_exists(lrintf "math.h" HAVE_LRINTF) +if(HAVE_LRINTF) + list(APPEND FAAD_DEFINES + HAVE_LRINTF=1 + ) +endif() + +set(FAAD_FLAGS + -Wall +) +if(MSVC) + list(APPEND FAAD_FLAGS + /wd4702 # unreachable code; there is a false-positive report + /wd4820 # implicit padding in structs + /wd5045 # Spectre nonsense + ) +else() + list(APPEND FAAD_FLAGS + -pedantic + ) +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + list(APPEND FAAD_FLAGS -ffloat-store) +endif() # GCC + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/include/neaacdec.h + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include) + +foreach(LIB faad faad_drm faad_hdc faad_fixed faad_drm_fixed) + add_library(${LIB} ${LIBFAAD_SOURCES}) + if(MATH_LIBRARY) + target_link_libraries(${LIB} PUBLIC ${MATH_LIBRARY}) + endif() + target_include_directories(${LIB} PUBLIC + ${CMAKE_CURRENT_BINARY_DIR}/include + ) + target_include_directories(${LIB} PRIVATE + libfaad + ) + target_compile_definitions(${LIB} PRIVATE + ${FAAD_DEFINES} + ) + target_compile_options(${LIB} PRIVATE + ${FAAD_FLAGS} + ) +endforeach() + +foreach(LIB faad_drm faad_drm_fixed) +target_compile_definitions(${LIB} PRIVATE + DRM_SUPPORT +) +endforeach() + +foreach(LIB faad_hdc) + target_compile_definitions(${LIB} PRIVATE + HDC_SUPPORT + ) +endforeach() + +foreach(LIB faad_fixed faad_drm_fixed) +target_compile_definitions(${LIB} PRIVATE + FIXED_POINT +) +endforeach() + +# Generate a pkg-config files + +function(generate_pkg_config_path outvar path) + string(LENGTH "${path}" path_length) + + set(path_args ${ARGV}) + list(REMOVE_AT path_args 0 1) + list(LENGTH path_args path_args_remaining) + + set("${outvar}" "${path}") + + while(path_args_remaining GREATER 1) + list(GET path_args 0 name) + list(GET path_args 1 value) + + get_filename_component(value_full "${value}" ABSOLUTE) + string(LENGTH "${value}" value_length) + + if(path_length EQUAL value_length AND path STREQUAL value) + set("${outvar}" "\${${name}}") + break() + elseif(path_length GREATER value_length) + # We might be in a subdirectory of the value, but we have to be + # careful about a prefix matching but not being a subdirectory + # (for example, /usr/lib64 is not a subdirectory of /usr/lib). + # We'll do this by making sure the next character is a directory + # separator. + string(SUBSTRING "${path}" ${value_length} 1 sep) + if(sep STREQUAL "/") + string(SUBSTRING "${path}" 0 ${value_length} s) + if(s STREQUAL value) + string(SUBSTRING "${path}" "${value_length}" -1 suffix) + set("${outvar}" "\${${name}}${suffix}") + break() + endif() + endif() + endif() + + list(REMOVE_AT path_args 0 1) + list(LENGTH path_args path_args_remaining) + endwhile() + + set("${outvar}" "${${outvar}}" PARENT_SCOPE) +endfunction(generate_pkg_config_path) + +function(transform_pc_file INPUT_FILE OUTPUT_FILE VERSION) + file(READ ${INPUT_FILE} TEXT) + + set(PREFIX "${CMAKE_INSTALL_PREFIX}") + string(REGEX REPLACE "@prefix@" "${PREFIX}" TEXT ${TEXT}) + string(REGEX REPLACE "@exec_prefix@" "\${prefix}" TEXT ${TEXT}) + + generate_pkg_config_path(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}" exec_prefix "${PREFIX}") + string(REGEX REPLACE "@libdir@" "${LIBDIR}" TEXT ${TEXT}) + + generate_pkg_config_path(INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}" prefix "${PREFIX}") + string(REGEX REPLACE "@includedir@" "${INCLUDEDIR}" TEXT ${TEXT}) + + string(REGEX REPLACE "@VERSION@" "${VERSION}" TEXT ${TEXT}) + + file(WRITE ${OUTPUT_FILE} ${TEXT}) +endfunction() + +transform_pc_file("libfaad/faad2.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/faad2.pc" "${FAAD_VERSION}") + +foreach(LIB faad faad_drm faad_fixed faad_drm_fixed) + set_target_properties(${LIB} PROPERTIES + VERSION "${FAAD_ABI_COMPATIBILITY}.${FAAD_ABI_VERSION_AGE}.${FAAD_ABI_VERSION_REVISION}" + SOVERSION "${FAAD_ABI_COMPATIBILITY}" + ) +endforeach() + +# CLI +option(FAAD_BUILD_CLI "Build faad_cli executable" ON) +if (FAAD_BUILD_CLI) + add_executable(faad_cli ${FAAD_SOURCES}) + target_link_libraries(faad_cli faad) + target_compile_definitions(faad_cli PRIVATE ${FAAD_DEFINES}) + target_compile_options(faad_cli PRIVATE ${FAAD_FLAGS}) + + if (MSVC) + target_include_directories(faad_cli PRIVATE + frontend + ) + else() + set_target_properties(faad_cli PROPERTIES OUTPUT_NAME faad) + endif() +endif() +# Installation + +if(NOT FAAD_BUNDLED_MODE) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/faad2.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + + set(INSTALL_TARGETS faad faad_drm faad_hdc) + if (FAAD_BUILD_CLI) + list(APPEND INSTALL_TARGETS faad_cli) + endif() + + install( + TARGETS ${INSTALL_TARGETS} + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) + + # exclude faad.h.in + install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/include/faad.h + FILES ${CMAKE_CURRENT_BINARY_DIR}/include/neaacdec.h + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + ) + + install( + FILES frontend/faad.man + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 + RENAME faad.1 + ) +endif() # FAAD_BUNDLED_MODE diff --git a/third_party/faad_hdc/upstream/COPYING b/third_party/faad_hdc/upstream/COPYING new file mode 100644 index 000000000..920c4e674 --- /dev/null +++ b/third_party/faad_hdc/upstream/COPYING @@ -0,0 +1,350 @@ + +Any non-GPL usage of this software or parts of this software is strictly +forbidden. + +Commercial non-GPL licensing of this software is possible. +For more info contact Ahead Software through Mpeg4AAClicense@nero.com. + + + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/third_party/faad_hdc/upstream/ChangeLog b/third_party/faad_hdc/upstream/ChangeLog new file mode 100644 index 000000000..1b3aefc99 --- /dev/null +++ b/third_party/faad_hdc/upstream/ChangeLog @@ -0,0 +1,237 @@ +2.11.2 (2025-03-04): + [ Armin Novak ] + * Add option BUILD_FAAD_CLI + + [ Johannes Kauffmann ] + * Add conditional build with DRC + + [ Roman Artiukhin ] + * Use adts_frame for adts header detection + + [ gavtroy ] + * Fix gapless calculation in frontend + + [ DingHao ] + * Fix write_audio_* function heap buffer overflow + +2.11.1 (2023-11-14): + [ Fabian Greffrath ] + * Build shared libraries and hide symbols by default. + * Install man page by default. + * Check for `lrintf()` availability, link with `-lm` and define `HAVE_LRINTF` accordingly. + * Set a default build type if none was specified. + * Build DLL name with SOVERSION by default on Windows. + * Fix inlined `lrintf()` function signatures. + +2.11.0 (2023-11-07): + [ Eugène Filin ] + * Fix incorrect variable initialization + + [ Eugene Kliuchnikov ] + * CI/CD, build, etc + + - setup GitHub workflows; test build under MSVC, OSX, MSYS2, Linux + - add CMake build system + - additionally add Bazel build + - remove automake and MSVC project files + - add fuzzers that cover almost all decoder code + - setup fuzzing for various builds: (no-)FIXED_POINT / (no-)DRM + - remove dead code + - address differes compilers warnings + - move version to distingished place that different build systems can read + + * "Safe" bugs + + "Safe" means that it is unlikely to be exploited; those affect the decoded + result for (most likely) extreme inputs. Some fixes are useful only for + "FIXED_POINT" build, since it has more restrictions on intermediate values. + + - "negative range" in estimate_current_envelope + - integer overflow in channel downmixing + - integer overflow in estimate_envelope + - integer overflows caused by "practical infinite" gain + - integer overflows in HF adjustment code + - several "left shift of negative value" + - priming RNG to avoid using values that does not look random at all + - do not drop the first frame of output; other decoders don't do this + - touching uninitialized values in lt_update_state + - touching uninitialized values in bit-reader buffers + + * "Almost Safe" bugs + + "Almost safe" means that those are unlinkly to be exploited; if those surface + depends on build options / environment. + + - division by zero in HF (noise?) generator and scale factor adjustment + - division by zero gen_rand_vector + + * "Unsafe" bugs + + "Unsafe" means that those can cause crash, or could somehow else be exploited. + + - CLI: accessing unallocated memory in mp4info (corrupted / zero-samples input) (CVE-2023-38857) + - CLI: out-of-bounds when parsing mp4 header + - CLI: crash because of wrong mp4 frame offset calculation (CVE-2023-38857) + - error handling rvlc_decode_scale_factors (CPU bomb?) + - null pointer dereference (in DRM + PS build) + - index-out-of-bounds / stack-buffer-overflow in decode_sce_lfe + (for streams with PCE) + - stack-buffer-overflow in pns_decode + - null pointer derefernce (when channels change their type in the middle + of the stream) + - infinite loop on currupted stream + - add practial limits for scale factors; otherwise calculated NaN/Inf values + could confuse further logic, resulting in access-out-of-bounds + - check sf_index in window_grouping_info to avoid access-out-of-bounds + - clamp bs_pointer values to avoid access-out-of-bounds + - infinite loop in fill_element + - sanitize input values in ps_mix_phase to avoid access-out-of-bounds + - fix internal decoder buffer size calculation to avoid heap-out-of-bounds + - calculate channel length multiplier even if main channel is already allocated + to avoid heap-out-of-bounds + - reserve enough slots for channels in decode_sce_lfe + to avoid heap-out-of-bounds + + [ David Korczynski ] + * Fuzzing integration with oss-fuzz + + [ Steveice10 ] + * Add define option to disable SBR/PS support + * Fix coefficient table selection in tns_decode_coef + +2.10.1 (2022-10-20): + [David Korczynski] + * Reject buffers of zero size. + + [François Cartegnie] + * Fix 7.1 with PCE mapping. + * Have proper version string in `faad.h`. + * Add conditional build with DRC. + +2.10.0 (2020-10-20): + [ tatsuz ] + * updated Visual Studio projects to VS 2019 (#54) + + [ Fabian Greffrath ] + * mp4read.c: fix stack-buffer-overflow in stringin()/ftypin() + * fix heap-buffer-overflow in mp4read.c + + [ Clayton Smith ] + * Remove non-ASCII characters + * Remove trailing whitespace + + [ Andrew Wesie ] + * Check return value of ltp_data. + * Restrict SBR frame length to 960 and 1024 samples. + * Support object type 29. + * Support implicit SBR signaling in frontend. + * Fix PNS decoding when only right channel is noise. + * Initialize element_id array with an invalid id. + * Fix NULL pointer dereferences. + * Fix infinite loop in adts_parse. + * Fix infinite loop in huffman_getescape. + * Check for error after each channel decode. + * Check for inconsistent number of channels. + +2.9.2 (2020-05-04): + [ Michał Janiszewski ] + * Only use x86-assembly when explicitly on x86 + * Use unsigned integers correctly + * Initialize pointers that might otherwise not be + + [ Fabian Greffrath ] + * update README esp. WRT directory structure + + [ Rosen Penev ] + * fix compilation without SBR/PS_DEC (#48) + * fix compilation with LC_ONLY_DECODER (#47) + + [ Fabian Greffrath ] + * fix "inline function 'cfftf1' declared but never defined" compiler warning + * fix some inconsistencies in the frontend output + * mp4read_open: add check for failed frame buffer allocation + * stszin: add check for allocation error and integer overflow + * add a pkg-config file + + [ Stefan Pöschel ] + * frontend: address compile warning + add missing LF (#50) + + [ François Cartegnie ] + * library name is faad (#52) + * Unbreak PS audio (#51) + +2.9.1 (2019-11-04): + [ Fabian Greffrath ] + * Include stdio.h in libfaad/ps_dec.c for stderr (Michael Fink) + * Fix Tille -> Title typo in frontend/mp4read.c (Alexander Thomas) + +2.9.0 (2019-09-09): + [ Krzysztof Nikiel ] + * Build system fixes and code clean-up + + [ LoRd_MuldeR ] + * Fix compiler warnings and code indentation + * Fix compilation with GCC <= 4.7.3 + * MSVC solution file clean-up + + [ Cameron Cawley ] + * Fix compilation with GCC 4.7.4 + * Fix compilation with MinGW + + [ Michael Fink ] + * MSVC 2017 project file update + + [ Hugo Lefeuvre ] + * Fix crash with unsupported MP4 files (NULL pointer dereference, + division by zero) + * CVE-2019-6956: ps_dec: sanitize iid_index before mixing + * CVE-2018-20196: sbr_fbt: sanitize sbr->M (should not exceed MAX_M) + * CVE-2018-20199, CVE-2018-20360: specrec: better handle unexpected + parametric stereo (PS) + * CVE-2018-20362, CVE-2018-19504, CVE-2018-20195, CVE-2018-20198, + CVE-2018-20358: syntax.c: check for syntax element inconsistencies + * CVE-2018-20194, CVE-2018-19503, CVE-2018-20197, CVE-2018-20357, + CVE-2018-20359, CVE-2018-20361: sbr_hfadj: sanitize frequency band + borders + + [ Hugo Beauzée-Luyssen ] + * CVE-2019-15296, CVE-2018-19502: Fix a couple buffer overflows + + [ Filip Roséen ] + * Prevent crash on SCE followed by CPE + + [ Gianfranco Costamagna ] + * Fix linking with GCC 9 and "-Wl,--as-needed" + + [ Fabian Greffrath ] + * Enable the frontend to be built reproducibly + +2.8.8: +2.8.7: + - MSVC build fixes + - fixed a coulple bugs +2.8.6: +2.8.5: + - another package fix +2.8.4: + - minor fix to released packages +2.8.3 + - better autotools support +2.8.2 + - PNS bug fixed + - New MP4 input module + - NeAACDecGetVersion: + new api function to get version and copyright strings + +2.8.1: + - seeking support for MP4 files +2.8.0: + - patches and fixed bugs + +2009-02-02 - Version 2.7 + * DAB+ support + * Use public headers internally to prevent duplicate declarations + * Explicitly typedef all types as signed + * Made sure MAIN prediction can't be started after the first frame + * Lot's of compilation issues solved + * Bugfix in SBR envelope border calculation diff --git a/third_party/faad_hdc/upstream/README b/third_party/faad_hdc/upstream/README new file mode 100644 index 000000000..c9b6cddbc --- /dev/null +++ b/third_party/faad_hdc/upstream/README @@ -0,0 +1,66 @@ + +Freeware Advanced Audio (AAC) Decoder including SBR decoding + +FAAD2 is a HE, LC, MAIN and LTP profile, MPEG2 and MPEG-4 AAC decoder. +FAAD2 includes code for SBR (HE AAC) decoding. +FAAD2 is licensed under the GPL. + + +__________ +COPYRIGHTS + +For FAAD2 the following license applies: + +****************************************************************************** +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +****************************************************************************** + + +Please note that the use of this software may require the payment of +patent royalties. You need to consider this issue before you start +building derivative works. We are not warranting or indemnifying you in +any way for patent royalities! YOU ARE SOLELY RESPONSIBLE FOR YOUR OWN +ACTIONS! + + +___________________ +DIRECTORY STRUCTURE + +faad2 - top level directory. + + docs - API documentation. + + frontend - command line frontend to the FAAD2 library, also supports + MPEG-4 file decoding. + + include - inlude file for the FAAD2 library. + + libfaad - the FAAD2 AAC decoder library including SBR. + + codebook - Huffman codebooks. + + project/msvc - Visual Studio 2017 project files. diff --git a/third_party/faad_hdc/upstream/frontend/main.c b/third_party/faad_hdc/upstream/frontend/main.c new file mode 100644 index 000000000..18aefb798 --- /dev/null +++ b/third_party/faad_hdc/upstream/frontend/main.c @@ -0,0 +1,1379 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: main.c,v 1.89 2015/01/19 09:46:12 knik Exp $ +**/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS +#define WIN32_LEAN_AND_MEAN +#include +#include +#ifndef __MINGW32__ +#define off_t __int64 +#endif +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "unicode_support.h" +#include "audio.h" +#include "mp4read.h" + +#ifndef min +#define min(a,b) ( (a) < (b) ? (a) : (b) ) +#endif + +#define MAX_CHANNELS 6 /* make this higher to support files with + more channels */ + +#define MAX_PERCENTS 384 + +static int quiet = 0; + +static void faad_fprintf(FILE *stream, const char *fmt, ...) +{ + va_list ap; + + if (!quiet) + { + va_start(ap, fmt); + vfprintf(stream, fmt, ap); + va_end(ap); + } + +#ifdef _WIN32 + if (!_isatty(_fileno(stream))) + { + fflush(stream); /*ensure real-time progress output on Win32*/ + } +#endif +} + +/* FAAD file buffering routines */ +typedef struct { + unsigned long bytes_into_buffer; + unsigned long bytes_consumed; + unsigned long file_offset; + unsigned char *buffer; + FILE *infile; + int at_eof; +} aac_buffer; + + +static int fill_buffer(aac_buffer *b) +{ + unsigned long bread; + + if (b->bytes_consumed > 0) + { + if (b->bytes_into_buffer) + { + memmove((void*)b->buffer, (void*)(b->buffer + b->bytes_consumed), + b->bytes_into_buffer*sizeof(unsigned char)); + } + + if (!b->at_eof) + { + bread = (unsigned long)fread((void*)(b->buffer + b->bytes_into_buffer), 1, + b->bytes_consumed, b->infile); + + if (bread != b->bytes_consumed) + b->at_eof = 1; + + b->bytes_into_buffer += bread; + } + + b->bytes_consumed = 0; + + if (b->bytes_into_buffer > 3) + { + if (memcmp(b->buffer, "TAG", 3) == 0) + b->bytes_into_buffer = 0; + } + if (b->bytes_into_buffer > 11) + { + if (memcmp(b->buffer, "LYRICSBEGIN", 11) == 0) + b->bytes_into_buffer = 0; + } + if (b->bytes_into_buffer > 8) + { + if (memcmp(b->buffer, "APETAGEX", 8) == 0) + b->bytes_into_buffer = 0; + } + } + + return 1; +} + +static void advance_buffer(aac_buffer *b, unsigned int bytes) +{ + while ((b->bytes_into_buffer > 0) && (bytes > 0)) + { + unsigned int chunk = bytes; + if (b->bytes_into_buffer < chunk) + chunk = (unsigned int)b->bytes_into_buffer; + + bytes -= chunk; + b->file_offset += chunk; + b->bytes_consumed = chunk; + b->bytes_into_buffer -= chunk; + + if (b->bytes_into_buffer == 0) + fill_buffer(b); + } +} + +static void lookforheader(aac_buffer *b) +{ + int i = 0; + while (!b->at_eof ) + { + if (b->bytes_into_buffer > 4) + { + if( ((b->buffer[0+i] == 0xff) && ((b->buffer[1+i] & 0xf6) == 0xf0)) || + (b->buffer[0+i] == 'A' && b->buffer[1+i] == 'D' && b->buffer[2+i] == 'I' && b->buffer[3+i] == 'F')) + { + fill_buffer(b); + break; + } else { + i++; + b->file_offset += 1; + b->bytes_consumed += 1; + b->bytes_into_buffer -= 1; + } + } + else + { + fill_buffer(b); + i = 0; + } + } +} + +static int adts_sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,7350,0,0,0}; + +static int adts_parse(aac_buffer *b, int *bitrate, float *length) +{ + size_t frames; + unsigned int frame_length; + size_t t_framelength = 0; + int samplerate = 0; + float frames_per_sec, bytes_per_frame; + + /* Read all frames to ensure correct time and bitrate */ + for (frames = 0; /* */; frames++) + { + fill_buffer(b); + + if (b->bytes_into_buffer > 7) + { + /* check syncword */ + if (!((b->buffer[0] == 0xFF)&&((b->buffer[1] & 0xF6) == 0xF0))) + break; + + if (frames == 0) + samplerate = adts_sample_rates[(b->buffer[2]&0x3c)>>2]; + + frame_length = ((((unsigned int)b->buffer[3] & 0x3)) << 11) + | (((unsigned int)b->buffer[4]) << 3) | (b->buffer[5] >> 5); + if (frame_length == 0) + break; + + t_framelength += frame_length; + + if (frame_length > b->bytes_into_buffer) + break; + + advance_buffer(b, frame_length); + } else { + break; + } + } + + frames_per_sec = (float)samplerate/1024.0f; + if (frames != 0) + bytes_per_frame = (float)t_framelength/(float)(frames*1000); + else + bytes_per_frame = 0; + *bitrate = (int)(8. * bytes_per_frame * frames_per_sec + 0.5); + if (frames_per_sec != 0) + *length = (float)frames/frames_per_sec; + else + *length = 1; + + return 1; +} + +/* MicroSoft channel definitions */ +#define SPEAKER_FRONT_LEFT 0x1 +#define SPEAKER_FRONT_RIGHT 0x2 +#define SPEAKER_FRONT_CENTER 0x4 +#define SPEAKER_LOW_FREQUENCY 0x8 +#define SPEAKER_BACK_LEFT 0x10 +#define SPEAKER_BACK_RIGHT 0x20 +#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 +#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 +#define SPEAKER_BACK_CENTER 0x100 +#define SPEAKER_SIDE_LEFT 0x200 +#define SPEAKER_SIDE_RIGHT 0x400 +#define SPEAKER_TOP_CENTER 0x800 +#define SPEAKER_TOP_FRONT_LEFT 0x1000 +#define SPEAKER_TOP_FRONT_CENTER 0x2000 +#define SPEAKER_TOP_FRONT_RIGHT 0x4000 +#define SPEAKER_TOP_BACK_LEFT 0x8000 +#define SPEAKER_TOP_BACK_CENTER 0x10000 +#define SPEAKER_TOP_BACK_RIGHT 0x20000 +#define SPEAKER_RESERVED 0x80000000 + +static long aacChannelConfig2wavexChannelMask(NeAACDecFrameInfo *hInfo) +{ + if (hInfo->channels == 6 && hInfo->num_lfe_channels) + { + return SPEAKER_FRONT_LEFT + SPEAKER_FRONT_RIGHT + + SPEAKER_FRONT_CENTER + SPEAKER_LOW_FREQUENCY + + SPEAKER_BACK_LEFT + SPEAKER_BACK_RIGHT; + } else { + return 0; + } +} + +static char *position2string(int position) +{ + switch (position) + { + case FRONT_CHANNEL_CENTER: return "Center front"; + case FRONT_CHANNEL_LEFT: return "Left front"; + case FRONT_CHANNEL_RIGHT: return "Right front"; + case SIDE_CHANNEL_LEFT: return "Left side"; + case SIDE_CHANNEL_RIGHT: return "Right side"; + case BACK_CHANNEL_LEFT: return "Left back"; + case BACK_CHANNEL_RIGHT: return "Right back"; + case BACK_CHANNEL_CENTER: return "Center back"; + case LFE_CHANNEL: return "LFE"; + case UNKNOWN_CHANNEL: return "Unknown"; + default: return ""; + } + + // return ""; +} + +static void print_channel_info(NeAACDecFrameInfo *frameInfo) +{ + /* print some channel info */ + int i; + long channelMask = aacChannelConfig2wavexChannelMask(frameInfo); + + faad_fprintf(stderr, " ---------------------\n"); + if (frameInfo->num_lfe_channels > 0) + { + faad_fprintf(stderr, " | Config: %2d.%d Ch |", frameInfo->channels-frameInfo->num_lfe_channels, frameInfo->num_lfe_channels); + } else { + faad_fprintf(stderr, " | Config: %2d Ch |", frameInfo->channels); + } + if (channelMask) + faad_fprintf(stderr, " WARNING: channels are reordered according to\n"); + else + faad_fprintf(stderr, "\n"); + faad_fprintf(stderr, " ---------------------"); + if (channelMask) + faad_fprintf(stderr, " MS defaults defined in WAVE_FORMAT_EXTENSIBLE\n"); + else + faad_fprintf(stderr, "\n"); + faad_fprintf(stderr, " | Ch | Position |\n"); + faad_fprintf(stderr, " ---------------------\n"); + for (i = 0; i < frameInfo->channels; i++) + { + faad_fprintf(stderr, " | %.2d | %-14s |\n", i, position2string((int)frameInfo->channel_position[i])); + } + faad_fprintf(stderr, " ---------------------\n"); + faad_fprintf(stderr, "\n"); +} + +static int FindAdtsSRIndex(int sr) +{ + int i; + + for (i = 0; i < 16; i++) + { + if (sr == adts_sample_rates[i]) + return i; + } + return 16 - 1; +} + +static unsigned char *MakeAdtsHeader(int *dataSize, NeAACDecFrameInfo *hInfo, unsigned char old_format) +{ + unsigned char *data; + int profile = (hInfo->object_type - 1) & 0x3; + int sr_index = ((hInfo->sbr == SBR_UPSAMPLED) || (hInfo->sbr == NO_SBR_UPSAMPLED)) ? + FindAdtsSRIndex(hInfo->samplerate / 2) : FindAdtsSRIndex(hInfo->samplerate); + int skip = (old_format) ? 8 : 7; + int framesize = skip + hInfo->bytesconsumed; + + if (hInfo->header_type == ADTS) + framesize -= skip; + + *dataSize = 7; + + data = malloc(*dataSize * sizeof(unsigned char)); + memset(data, 0, *dataSize * sizeof(unsigned char)); + + data[0] += 0xFF; /* 8b: syncword */ + + data[1] += 0xF0; /* 4b: syncword */ + /* 1b: mpeg id = 0 */ + /* 2b: layer = 0 */ + data[1] += 1; /* 1b: protection absent */ + + data[2] += ((profile << 6) & 0xC0); /* 2b: profile */ + data[2] += ((sr_index << 2) & 0x3C); /* 4b: sampling_frequency_index */ + /* 1b: private = 0 */ + data[2] += ((hInfo->channels >> 2) & 0x1); /* 1b: channel_configuration */ + + data[3] += ((hInfo->channels << 6) & 0xC0); /* 2b: channel_configuration */ + /* 1b: original */ + /* 1b: home */ + /* 1b: copyright_id */ + /* 1b: copyright_id_start */ + data[3] += ((framesize >> 11) & 0x3); /* 2b: aac_frame_length */ + + data[4] += ((framesize >> 3) & 0xFF); /* 8b: aac_frame_length */ + + data[5] += ((framesize << 5) & 0xE0); /* 3b: aac_frame_length */ + data[5] += ((0x7FF >> 6) & 0x1F); /* 5b: adts_buffer_fullness */ + + data[6] += ((0x7FF << 2) & 0x3F); /* 6b: adts_buffer_fullness */ + /* 2b: num_raw_data_blocks */ + + return data; +} + +/* globals */ +char *progName; + +static const char *file_ext[] = +{ + NULL, + ".wav", + ".aif", + ".au", + ".au", + ".pcm", + NULL +}; + +static void usage(void) +{ + faad_fprintf(stdout, "\nUsage:\n"); + faad_fprintf(stdout, "%s [options] infile.aac\n", progName); + faad_fprintf(stdout, "Options:\n"); + faad_fprintf(stdout, " -h Shows this help screen.\n"); + faad_fprintf(stdout, " -i Shows info about the input file.\n"); + faad_fprintf(stdout, " -a X Write MPEG-4 AAC ADTS output file.\n"); + faad_fprintf(stdout, " -t Assume old ADTS format.\n"); + faad_fprintf(stdout, " -o X Set output filename.\n"); + faad_fprintf(stdout, " -f X Set output format. Valid values for X are:\n"); + faad_fprintf(stdout, " 1: Microsoft WAV format (default).\n"); + faad_fprintf(stdout, " 2: RAW PCM data.\n"); + faad_fprintf(stdout, " -b X Set output sample format. Valid values for X are:\n"); + faad_fprintf(stdout, " 1: 16 bit PCM data (default).\n"); + faad_fprintf(stdout, " 2: 24 bit PCM data.\n"); + faad_fprintf(stdout, " 3: 32 bit PCM data.\n"); + faad_fprintf(stdout, " 4: 32 bit floating point data.\n"); + faad_fprintf(stdout, " 5: 64 bit floating point data.\n"); + faad_fprintf(stdout, " -s X Force the samplerate to X (for RAW files).\n"); + faad_fprintf(stdout, " -l X Set object type. Supported object types:\n"); + faad_fprintf(stdout, " 1: Main object type.\n"); + faad_fprintf(stdout, " 2: LC (Low Complexity) object type.\n"); + faad_fprintf(stdout, " 4: LTP (Long Term Prediction) object type.\n"); + faad_fprintf(stdout, " 23: LD (Low Delay) object type.\n"); + faad_fprintf(stdout, " -d Down matrix 5.1 to 2 channels\n"); + faad_fprintf(stdout, " -w Write output to stdio instead of a file.\n"); + faad_fprintf(stdout, " -g Disable gapless decoding.\n"); + faad_fprintf(stdout, " -q Quiet - suppresses status messages.\n"); + faad_fprintf(stdout, " -j X Jump - start output X seconds into track (MP4 files only).\n"); + faad_fprintf(stdout, "Example:\n"); + faad_fprintf(stdout, " %s infile.aac\n", progName); + faad_fprintf(stdout, " %s infile.mp4\n", progName); + faad_fprintf(stdout, " %s -o outfile.wav infile.aac\n", progName); + faad_fprintf(stdout, " %s -w infile.aac > outfile.wav\n", progName); + faad_fprintf(stdout, " %s -a outfile.aac infile.aac\n", progName); + return; +} + +static int decodeAACfile(char *aacfile, char *sndfile, char *adts_fn, int to_stdout, + int def_srate, unsigned char object_type, unsigned char outputFormat, int fileType, + unsigned char downMatrix, int infoOnly, int adts_out, unsigned char old_format, + float *song_length) +{ + int tagsize; + unsigned long samplerate; + unsigned char channels; + void *sample_buffer; + + audio_file *aufile = NULL; + + FILE *adtsFile = NULL; + unsigned char *adtsData; + int adtsDataSize; + + NeAACDecHandle hDecoder; + NeAACDecFrameInfo frameInfo; + NeAACDecConfigurationPtr config; + + char percents[MAX_PERCENTS]; + int percent, old_percent = -1; + int fileread; + unsigned long bread; + long bused; + int header_type = 0; + int bitrate = 0; + float length = 0; + + int first_time = 1; + int retval; + int streaminput = 0; + + aac_buffer b; + + memset(&b, 0, sizeof(aac_buffer)); + + if (adts_out) + { + adtsFile = faad_fopen(adts_fn, "wb"); + if (adtsFile == NULL) + { + faad_fprintf(stderr, "Error opening file: %s\n", adts_fn); + return 1; + } + } + + if (0 == strcmp(aacfile, "-")) + { + b.infile = stdin; +#ifdef _WIN32 + _setmode(_fileno(stdin), O_BINARY); +#endif + + } + else + { + b.infile = faad_fopen(aacfile, "rb"); + if (b.infile == NULL) + { + /* unable to open file */ + faad_fprintf(stderr, "Error opening file: %s\n", aacfile); + return 1; + } + } + + retval = fseek(b.infile, 0, SEEK_END); +#ifdef _WIN32 + if (0 == strcmp(aacfile, "-")) { + retval = -1; + } +#endif + if (retval ) + { + faad_fprintf(stderr, "Input not seekable %s\n", aacfile); + fileread = -1; + streaminput = 1; + } else { + fileread = ftell(b.infile); + fseek(b.infile, 0, SEEK_SET); + }; + + b.buffer = (unsigned char*)malloc(FAAD_MIN_STREAMSIZE*MAX_CHANNELS); + if (!b.buffer) + { + faad_fprintf(stderr, "Memory allocation error\n"); + return 0; + } + memset(b.buffer, 0, FAAD_MIN_STREAMSIZE*MAX_CHANNELS); + + bread = (unsigned long)fread(b.buffer, 1, FAAD_MIN_STREAMSIZE*MAX_CHANNELS, b.infile); + b.bytes_into_buffer = bread; + b.bytes_consumed = 0; + b.file_offset = 0; + + if (bread != FAAD_MIN_STREAMSIZE*MAX_CHANNELS) + b.at_eof = 1; + + tagsize = 0; + if (!memcmp(b.buffer, "ID3", 3)) + { + /* high bit is not used */ + tagsize = (b.buffer[6] << 21) | (b.buffer[7] << 14) | + (b.buffer[8] << 7) | (b.buffer[9] << 0); + + tagsize += 10; + advance_buffer(&b, tagsize); + fill_buffer(&b); + } + + hDecoder = NeAACDecOpen(); + + /* Set the default object type and samplerate */ + /* This is useful for RAW AAC files */ + config = NeAACDecGetCurrentConfiguration(hDecoder); + if (def_srate) + config->defSampleRate = def_srate; + config->defObjectType = object_type; + config->outputFormat = outputFormat; + config->downMatrix = downMatrix; + config->useOldADTSFormat = old_format; + //config->dontUpSampleImplicitSBR = 1; + NeAACDecSetConfiguration(hDecoder, config); + + /* get AAC infos for printing */ + header_type = 0; + if (streaminput == 1) + lookforheader(&b); + + if ((b.buffer[0] == 0xFF) && ((b.buffer[1] & 0xF6) == 0xF0)) + { + if (streaminput == 1) + { + int /*frames,*/ frame_length; + float frames_per_sec, bytes_per_frame; + channels = 2; + samplerate = adts_sample_rates[(b.buffer[2]&0x3c)>>2]; + frame_length = ((((unsigned int)b.buffer[3] & 0x3)) << 11) + | (((unsigned int)b.buffer[4]) << 3) | (b.buffer[5] >> 5); + frames_per_sec = (float)samplerate/1024.0f; + bytes_per_frame = (float)frame_length/(float)(1000); + bitrate = (int)(8. * bytes_per_frame * frames_per_sec + 0.5); + length = 1; + faad_fprintf(stderr, "Streamed input format samplerate %d channels %d.\n", samplerate, channels); + } else { + adts_parse(&b, &bitrate, &length); + fseek(b.infile, tagsize, SEEK_SET); + + bread = (unsigned long)fread(b.buffer, 1, FAAD_MIN_STREAMSIZE*MAX_CHANNELS, b.infile); + if (bread != FAAD_MIN_STREAMSIZE*MAX_CHANNELS) + b.at_eof = 1; + else + b.at_eof = 0; + b.bytes_into_buffer = bread; + b.bytes_consumed = 0; + b.file_offset = tagsize; + } + + header_type = 1; + } + else if (memcmp(b.buffer, "ADIF", 4) == 0) + { + int skip_size = (b.buffer[4] & 0x80) ? 9 : 0; + bitrate = ((unsigned int)(b.buffer[4 + skip_size] & 0x0F)<<19) | + ((unsigned int)b.buffer[5 + skip_size]<<11) | + ((unsigned int)b.buffer[6 + skip_size]<<3) | + ((unsigned int)b.buffer[7 + skip_size] & 0xE0); + + length = (float)fileread; + if (length != 0) + { + length = ((float)length*8.f)/((float)bitrate) + 0.5f; + } + + bitrate = (int)((float)bitrate/1000.0f + 0.5f); + + header_type = 2; + } + + *song_length = length; + + fill_buffer(&b); + bused = NeAACDecInit(hDecoder, b.buffer, b.bytes_into_buffer, &samplerate, &channels); + if (bused < 0) + { + /* If some error initializing occured, skip the file */ + faad_fprintf(stderr, "Error initializing decoder library.\n"); + if (b.buffer) + free(b.buffer); + NeAACDecClose(hDecoder); + if (b.infile != stdin) + fclose(b.infile); + return 1; + } + advance_buffer(&b, bused); + fill_buffer(&b); + + /* print AAC file info */ + faad_fprintf(stderr, "%s file info:\n", aacfile); + switch (header_type) + { + case 0: + faad_fprintf(stderr, "RAW\n\n"); + break; + case 1: + faad_fprintf(stderr, "ADTS, %.3f sec, %d kbps, %d Hz\n\n", + length, bitrate, samplerate); + break; + case 2: + faad_fprintf(stderr, "ADIF, %.3f sec, %d kbps, %d Hz\n\n", + length, bitrate, samplerate); + break; + } + + // Override the logic of skipping 0-th output frame. + NeAACDecPostSeekReset(hDecoder, 1); + + if (infoOnly) + { + NeAACDecClose(hDecoder); + if (b.infile != stdin) + fclose(b.infile); + if (b.buffer) + free(b.buffer); + return 0; + } + + do + { + sample_buffer = NeAACDecDecode(hDecoder, &frameInfo, + b.buffer, b.bytes_into_buffer); + + if (adts_out == 1) + { + int skip = (old_format) ? 8 : 7; + adtsData = MakeAdtsHeader(&adtsDataSize, &frameInfo, old_format); + + /* write the adts header */ + fwrite(adtsData, 1, adtsDataSize, adtsFile); + + /* write the frame data */ + if (frameInfo.header_type == ADTS) + fwrite(b.buffer + skip, 1, frameInfo.bytesconsumed - skip, adtsFile); + else + fwrite(b.buffer, 1, frameInfo.bytesconsumed, adtsFile); + } + + /* update buffer indices */ + advance_buffer(&b, frameInfo.bytesconsumed); + + /* check if the inconsistent number of channels */ + if (aufile != NULL && frameInfo.channels != aufile->channels) + frameInfo.error = 12; + + if (frameInfo.error > 0) + { + faad_fprintf(stderr, "Error: %s\n", + NeAACDecGetErrorMessage(frameInfo.error)); + } + + /* open the sound file now that the number of channels are known */ + if (first_time && !frameInfo.error) + { + /* print some channel info */ + print_channel_info(&frameInfo); + + if (!adts_out) + { + /* open output file */ + if (!to_stdout) + { + aufile = open_audio_file(sndfile, frameInfo.samplerate, frameInfo.channels, + (int)outputFormat, fileType, aacChannelConfig2wavexChannelMask(&frameInfo)); + } else { + aufile = open_audio_file("-", frameInfo.samplerate, frameInfo.channels, + (int)outputFormat, fileType, aacChannelConfig2wavexChannelMask(&frameInfo)); + } + if (aufile == NULL) + { + if (b.buffer) + free(b.buffer); + NeAACDecClose(hDecoder); + if (b.infile != stdin) + fclose(b.infile); + return 0; + } + } else { + faad_fprintf(stderr, "Writing output MPEG-4 AAC ADTS file.\n\n"); + } + first_time = 0; + } + + percent = min((int)(b.file_offset*100)/fileread, 100); + if (percent > old_percent) + { + old_percent = percent; + snprintf(percents, MAX_PERCENTS, "%d%% decoding %s.", percent, aacfile); + faad_fprintf(stderr, "%s\r", percents); +#ifdef _WIN32 + SetConsoleTitle(percents); +#endif + } + + if ((frameInfo.error == 0) && (frameInfo.samples > 0) && (!adts_out)) + { + if (write_audio_file(aufile, sample_buffer, frameInfo.samples) == 0) + break; + } + + /* fill buffer */ + fill_buffer(&b); + + if (b.bytes_into_buffer == 0) + sample_buffer = NULL; /* to make sure it stops now */ + + } while (sample_buffer != NULL); + + NeAACDecClose(hDecoder); + + if (adts_out == 1) + { + fclose(adtsFile); + } + + if (b.infile != stdin) + fclose(b.infile); + + if (!first_time && !adts_out) + close_audio_file(aufile); + + if (b.buffer) + free(b.buffer); + + return frameInfo.error; +} + +static int decodeMP4file(char *mp4file, char *sndfile, char *adts_fn, int to_stdout, + unsigned char outputFormat, int fileType, unsigned char downMatrix, int noGapless, + int infoOnly, int adts_out, float *song_length, float seek_to) +{ + /*int track;*/ + unsigned long samplerate; + unsigned char channels; + void *sample_buffer; + + uint32_t sampleId, startSampleId; + + audio_file *aufile = NULL; + + FILE *adtsFile = NULL; + unsigned char *adtsData; + int adtsDataSize; + + NeAACDecHandle hDecoder; + NeAACDecConfigurationPtr config; + NeAACDecFrameInfo frameInfo = {0}; // should no-frames situation be flagged as an error? + mp4AudioSpecificConfig mp4ASC = {0}; + + char percents[MAX_PERCENTS]; + int percent, old_percent = -1; + + int first_time = 1; + + /* for gapless decoding */ + unsigned int useAacLength = 1; + unsigned int framesize; + unsigned decoded; + + if (strcmp(mp4file, "-") == 0 ) { + faad_fprintf(stderr, "Cannot open stdin for MP4 input \n"); + return 1; + } + + if (!quiet) + { + mp4config.verbose.header = 1; + mp4config.verbose.tags = 1; + } + if (mp4read_open(mp4file)) + { + /* unable to open file */ + faad_fprintf(stderr, "Error opening file: %s\n", mp4file); + return 1; + } + + hDecoder = NeAACDecOpen(); + + /* Set configuration */ + config = NeAACDecGetCurrentConfiguration(hDecoder); + config->outputFormat = outputFormat; + config->downMatrix = downMatrix; + //config->dontUpSampleImplicitSBR = 1; + NeAACDecSetConfiguration(hDecoder, config); + + if (adts_out) + { + adtsFile = faad_fopen(adts_fn, "wb"); + if (adtsFile == NULL) + { + faad_fprintf(stderr, "Error opening file: %s\n", adts_fn); + return 1; + } + } + + if(NeAACDecInit2(hDecoder, mp4config.asc.buf, mp4config.asc.size, + &samplerate, &channels) < 0) + { + /* If some error initializing occured, skip the file */ + faad_fprintf(stderr, "Error initializing decoder library.\n"); + NeAACDecClose(hDecoder); + mp4read_close(); + return 1; + } + + framesize = 1024; + useAacLength = 0; + decoded = 0; + + if (mp4config.asc.size) + { + if (NeAACDecAudioSpecificConfig(mp4config.asc.buf, mp4config.asc.size, &mp4ASC) >= 0) + { + if (mp4ASC.frameLengthFlag == 1) framesize = 960; + if (mp4ASC.sbr_present_flag == 1 || mp4ASC.forceUpSampling) framesize *= 2; + } + } + + /* print some mp4 file info */ + faad_fprintf(stderr, "%s file info:\n\n", mp4file); + { + /*int k, j;*/ + char *ot[6] = { "NULL", "MAIN AAC", "LC AAC", "SSR AAC", "LTP AAC", "HE AAC" }; + float seconds; + seconds = (float)mp4config.samples/(float)mp4ASC.samplingFrequency; + + *song_length = seconds; + + faad_fprintf(stderr, "%s\t%.3f secs, %d ch, %d Hz\n\n", ot[(mp4ASC.objectTypeIndex > 5)?0:mp4ASC.objectTypeIndex], + seconds, mp4ASC.channelsConfiguration, mp4ASC.samplingFrequency); + } + + if (infoOnly) + { + NeAACDecClose(hDecoder); + mp4read_close(); + return 0; + } + + startSampleId = 0; + if (seek_to > 0.1) { + int64_t sample = (int64_t)(seek_to * mp4config.samplerate / framesize); + uint32_t limit = ~0u; + startSampleId = sample < limit ? (uint32_t)sample : limit; + } + + mp4read_seek(startSampleId); + for (sampleId = startSampleId; sampleId < mp4config.frame.nsamples; sampleId++) + { + /*int rc;*/ + unsigned long dur; + unsigned int sample_count; + + if (mp4read_frame()) + break; + + sample_buffer = NeAACDecDecode(hDecoder, &frameInfo, mp4config.bitbuf.data, mp4config.bitbuf.size); + + if (!sample_buffer) { + /* unable to decode file, abort */ + break; + } + + if (adts_out == 1) + { + adtsData = MakeAdtsHeader(&adtsDataSize, &frameInfo, 0); + + /* write the adts header */ + fwrite(adtsData, 1, adtsDataSize, adtsFile); + + fwrite(mp4config.bitbuf.data, 1, frameInfo.bytesconsumed, adtsFile); + } + + dur = frameInfo.samples / frameInfo.channels; + decoded += dur; + + if (decoded > mp4config.samples) + dur -= decoded - mp4config.samples; + + if (dur > framesize) + { + faad_fprintf(stderr, "Warning: excess frame detected in MP4 file.\n"); + dur = framesize; + } + + if (!noGapless) + { + if (useAacLength || (mp4config.samplerate != samplerate)) { + sample_count = frameInfo.samples; + } else { + sample_count = (unsigned int)(dur * frameInfo.channels); + if (sample_count > frameInfo.samples) + sample_count = frameInfo.samples; + } + } else { + sample_count = frameInfo.samples; + } + + /* open the sound file now that the number of channels are known */ + if (first_time && !frameInfo.error) + { + /* print some channel info */ + print_channel_info(&frameInfo); + + if (!adts_out) + { + /* open output file */ + if(!to_stdout) + { + aufile = open_audio_file(sndfile, frameInfo.samplerate, frameInfo.channels, + (int)outputFormat, fileType, aacChannelConfig2wavexChannelMask(&frameInfo)); + } else { +#ifdef _WIN32 + _setmode(_fileno(stdout), O_BINARY); +#endif + aufile = open_audio_file("-", frameInfo.samplerate, frameInfo.channels, + (int)outputFormat, fileType, aacChannelConfig2wavexChannelMask(&frameInfo)); + } + if (aufile == NULL) + { + NeAACDecClose(hDecoder); + mp4read_close(); + return 0; + } + } + first_time = 0; + } + + percent = min((int)(sampleId*100)/mp4config.frame.nsamples, 100); + if (percent > old_percent) + { + old_percent = percent; + snprintf(percents, MAX_PERCENTS, "%d%% decoding %s.", percent, mp4file); + faad_fprintf(stderr, "%s\r", percents); +#ifdef _WIN32 + SetConsoleTitle(percents); +#endif + } + + if ((frameInfo.error == 0) && (sample_count > 0) && (!adts_out)) + { + if (write_audio_file(aufile, sample_buffer, sample_count) == 0) + break; + } + + if (frameInfo.error > 0) + { + faad_fprintf(stderr, "Warning: %s\n", + NeAACDecGetErrorMessage(frameInfo.error)); + } + } + + NeAACDecClose(hDecoder); + + if (adts_out == 1) + { + fclose(adtsFile); + } + + mp4read_close(); + + if (!first_time && !adts_out) + close_audio_file(aufile); + + return frameInfo.error; +} + +static int faad_main(int argc, char *argv[]) +{ + int result; + int infoOnly = 0; + int writeToStdio = 0; + int readFromStdin = 0; + unsigned char object_type = LC; + int def_srate = 0; + unsigned char downMatrix = 0; + int format = 1; + unsigned char outputFormat = FAAD_FMT_16BIT; + int outfile_set = 0; + int adts_out = 0; + unsigned char old_format = 0; + int showHelp = 0; + int mp4file = 0; + int noGapless = 0; + char *fnp; + char *aacFileName = NULL; + char *audioFileName = NULL; + char *adtsFileName = NULL; + float seekTo = 0; + unsigned char header[8]; + size_t bread; + float length = 0; + FILE *hMP4File; + char *faad_id_string; + char *faad_copyright_string; + +/* System dependant types */ +#ifdef _WIN32 + long begin; +#else + clock_t begin; +#endif + + unsigned long cap = NeAACDecGetCapabilities(); + + + /* begin process command line */ + progName = argv[0]; + while (1) { + int c = -1; + int option_index = 0; + static struct option long_options[] = { + { "quiet", 0, 0, 'q' }, + { "outfile", 0, 0, 'o' }, + { "adtsout", 0, 0, 'a' }, + { "oldformat", 0, 0, 't' }, + { "format", 0, 0, 'f' }, + { "bits", 0, 0, 'b' }, + { "samplerate", 0, 0, 's' }, + { "objecttype", 0, 0, 'l' }, + { "downmix", 0, 0, 'd' }, + { "info", 0, 0, 'i' }, + { "stdio", 0, 0, 'w' }, + { "stdio", 0, 0, 'g' }, + { "seek", 1, 0, 'j' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "o:a:s:f:b:l:j:wgdhitq", + long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + case 'o': + if (optarg) + { + outfile_set = 1; + audioFileName = (char *) malloc(sizeof(char) * (strlen(optarg) + 1)); + if (audioFileName == NULL) + { + faad_fprintf(stderr, "Error allocating memory for audioFileName.\n"); + return 1; + } + strcpy(audioFileName, optarg); + } + break; + case 'a': + if (optarg) + { + adts_out = 1; + adtsFileName = (char *) malloc(sizeof(char) * (strlen(optarg) + 1)); + if (adtsFileName == NULL) + { + faad_fprintf(stderr, "Error allocating memory for adtsFileName.\n"); + return 1; + } + strcpy(adtsFileName, optarg); + } + break; + case 's': + if (optarg) + { + char dr[10]; + if (sscanf(optarg, "%s", dr) < 1) { + def_srate = 0; + } else { + def_srate = atoi(dr); + } + } + break; + case 'f': + if (optarg) + { + char dr[10]; + if (sscanf(optarg, "%s", dr) < 1) + { + format = 1; + } else { + format = atoi(dr); + if ((format < 1) || (format > 2)) + showHelp = 1; + } + } + break; + case 'b': + if (optarg) + { + char dr[10]; + if (sscanf(optarg, "%s", dr) < 1) + { + outputFormat = FAAD_FMT_16BIT; /* just use default */ + } else { + int val = atoi(dr); + if ((val < 1) || (val > 5)) + showHelp = 1; + if (val == 5) // Not yet unsupported by "audio". + showHelp = 1; + outputFormat = (unsigned char)val; + } + } + break; + case 'l': + if (optarg) + { + char dr[10]; + if (sscanf(optarg, "%s", dr) < 1) + { + object_type = LC; /* default */ + } else { + int val = atoi(dr); + if ((val != LC) && + (val != MAIN) && + (val != LTP) && + (val != LD) && + (val != HDC_LC)) + { + showHelp = 1; + } + object_type = (unsigned char)val; + } + } + break; + case 'j': + if (optarg) + { + seekTo = (float)atof(optarg); + } + break; + case 't': + old_format = 1; + break; + case 'd': + downMatrix = 1; + break; + case 'w': + writeToStdio = 1; + break; + case 'g': + noGapless = 1; + break; + case 'i': + infoOnly = 1; + break; + case 'h': + showHelp = 1; + break; + case 'q': + quiet = 1; + break; + default: + break; + } + } + + NeAACDecGetVersion(&faad_id_string, &faad_copyright_string); + + faad_fprintf(stderr, " *********** Ahead Software MPEG-4 AAC Decoder V%s ******************\n\n", faad_id_string); +#ifndef BUILD_DATE +#define BUILD_DATE __DATE__ +#endif + faad_fprintf(stderr, " Build: %s\n", BUILD_DATE); +#undef BUILD_DATE + faad_fprintf(stderr, "%s", faad_copyright_string); + if (cap & FIXED_POINT_CAP) + faad_fprintf(stderr, " Fixed point version\n"); + else + faad_fprintf(stderr, " Floating point version\n"); + faad_fprintf(stderr, "\n"); + faad_fprintf(stderr, " This program is free software; you can redistribute it and/or modify\n"); + faad_fprintf(stderr, " it under the terms of the GNU General Public License.\n"); + faad_fprintf(stderr, "\n"); + faad_fprintf(stderr, " **************************************************************************\n\n"); + + + /* check that we have at least two non-option arguments */ + /* Print help if requested */ + if (((argc - optind) < 1) || showHelp) + { + usage(); + return 1; + } + +#if 0 + /* only allow raw data on stdio */ + if (writeToStdio == 1) + { + format = 2; + } +#endif + + /* point to the specified file name */ + aacFileName = (char *) malloc(sizeof(char) * (strlen(argv[optind]) + 1)); + if (aacFileName == NULL) + { + faad_fprintf(stderr, "Error allocating memory for aacFileName.\n"); + return 1; + } + strcpy(aacFileName, argv[optind]); + +#ifdef _WIN32 + begin = GetTickCount(); +#else + begin = clock(); +#endif + + /* Only calculate the path and open the file for writing if + we are not writing to stdout. + */ + if(!writeToStdio && !outfile_set) + { + audioFileName = (char *) malloc(sizeof(char) * (strlen(aacFileName) + strlen(file_ext[format]) + 1)); + if (audioFileName == NULL) + { + faad_fprintf(stderr, "Error allocating memory for audioFileName.\n"); + return 1; + } + strcpy(audioFileName, aacFileName); + + fnp = (char *)strrchr(audioFileName,'.'); + + if (fnp) + fnp[0] = '\0'; + + strcat(audioFileName, file_ext[format]); + } + + /* check for mp4 file */ + if (0 == strcmp(aacFileName, "-")) { + faad_fprintf(stderr, "Reading from stdin: %s\n", aacFileName); + readFromStdin = 1; + hMP4File = stdin; +#ifdef _WIN32 + _setmode(_fileno(stdin), O_BINARY); +#endif + + } else { + + mp4file = 0; + hMP4File = faad_fopen(aacFileName, "rb"); + if (!hMP4File) + { + faad_fprintf(stderr, "Error opening file: %s\n", aacFileName); + return 1; + } + } + + bread = fread(header, 1, 8, hMP4File); + + if (! readFromStdin ) + fclose(hMP4File); + + if (bread != 8) { + faad_fprintf(stderr, "Error reading file.\n"); + return 1; + } + + if (header[4] == 'f' && header[5] == 't' && header[6] == 'y' && header[7] == 'p') + mp4file = 1; + + if (!mp4file && seekTo != 0) { + faad_fprintf(stderr, "Warning: can only seek in MP4 files\n"); + } + + if (mp4file) + { + result = decodeMP4file(aacFileName, audioFileName, adtsFileName, writeToStdio, + outputFormat, format, downMatrix, noGapless, infoOnly, adts_out, &length, seekTo); + } else { + + if (readFromStdin == 1) { + ungetc(header[7],hMP4File); + ungetc(header[6],hMP4File); + ungetc(header[5],hMP4File); + ungetc(header[4],hMP4File); + ungetc(header[3],hMP4File); + ungetc(header[2],hMP4File); + ungetc(header[1],hMP4File); + ungetc(header[0],hMP4File); + } + + result = decodeAACfile(aacFileName, audioFileName, adtsFileName, writeToStdio, + def_srate, object_type, outputFormat, format, downMatrix, infoOnly, adts_out, + old_format, &length); + } + + if (audioFileName != NULL) + free (audioFileName); + if (adtsFileName != NULL) + free (adtsFileName); + + if (!result && !infoOnly) + { +#ifdef _WIN32 + float dec_length = (float)(GetTickCount()-begin)/1000.0f; + SetConsoleTitle("FAAD"); +#else + /* clock() grabs time since the start of the app but when we decode + multiple files, each file has its own starttime (begin). + */ + float dec_length = (float)(clock() - begin)/(float)CLOCKS_PER_SEC; +#endif + faad_fprintf(stderr, "Decoding %s took: %5.2f sec. %5.2fx real-time.\n", aacFileName, + dec_length, (dec_length > 0.01) ? (length/dec_length) : 0.); + } + + free (aacFileName); + return 0; +} + +int main(int argc, char *argv[]) +{ +#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64 + int argc_utf8, exit_code; + char **argv_utf8; + (void)argc; + (void)argv; + init_console_utf8(stderr); + init_commandline_arguments_utf8(&argc_utf8, &argv_utf8); + exit_code = faad_main(argc_utf8, argv_utf8); + free_commandline_arguments_utf8(&argc_utf8, &argv_utf8); + uninit_console_utf8(); + return exit_code; +#else + return faad_main(argc, argv); +#endif +} diff --git a/third_party/faad_hdc/upstream/include/faad.h.in b/third_party/faad_hdc/upstream/include/faad.h.in new file mode 100644 index 000000000..b1c184d3c --- /dev/null +++ b/third_party/faad_hdc/upstream/include/faad.h.in @@ -0,0 +1,36 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +**/ + +/* warn people for update */ +#pragma message("please update faad2 include filename and function names!") + +#define FAAD2_VERSION "@VERSION@" + +/* Backwards compatible link */ +#include "neaacdec.h" diff --git a/third_party/faad_hdc/upstream/include/neaacdec.h b/third_party/faad_hdc/upstream/include/neaacdec.h new file mode 100644 index 000000000..dbe1354ef --- /dev/null +++ b/third_party/faad_hdc/upstream/include/neaacdec.h @@ -0,0 +1,266 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: neaacdec.h,v 1.14 2012/03/02 15:29:47 knik Exp $ +**/ + +#ifndef __NEAACDEC_H__ +#define __NEAACDEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#if 1 +/* MACROS FOR BACKWARDS COMPATIBILITY */ +/* structs */ +#define faacDecHandle NeAACDecHandle +#define faacDecConfiguration NeAACDecConfiguration +#define faacDecConfigurationPtr NeAACDecConfigurationPtr +#define faacDecFrameInfo NeAACDecFrameInfo +/* functions */ +#define faacDecGetErrorMessage NeAACDecGetErrorMessage +#define faacDecSetConfiguration NeAACDecSetConfiguration +#define faacDecGetCurrentConfiguration NeAACDecGetCurrentConfiguration +#define faacDecInit NeAACDecInit +#define faacDecInit2 NeAACDecInit2 +#define faacDecInitDRM NeAACDecInitDRM +#define faacDecPostSeekReset NeAACDecPostSeekReset +#define faacDecOpen NeAACDecOpen +#define faacDecClose NeAACDecClose +#define faacDecDecode NeAACDecDecode +#define AudioSpecificConfig NeAACDecAudioSpecificConfig +#endif + + +#ifdef _WIN32 + #pragma pack(push, 8) + #define NEAACDECAPI +#elif defined(__GNUC__) && __GNUC__ >= 4 + #define NEAACDECAPI __attribute__((visibility("default"))) +#else + #define NEAACDECAPI +#endif + +#ifndef FAAD2_VERSION + #define FAAD2_VERSION "unknown" +#endif + +/* object types for AAC */ +#define MAIN 1 +#define LC 2 +#define SSR 3 +#define LTP 4 +#define HE_AAC 5 +#define ER_LC 17 +#define ER_LTP 19 +#define LD 23 +#define DRM_ER_LC 27 /* special object type for DRM */ +#define HDC_LC 127 /* non standard type for HDC */ + +/* header types */ +#define RAW 0 +#define ADIF 1 +#define ADTS 2 +#define LATM 3 + +/* SBR signalling */ +#define NO_SBR 0 +#define SBR_UPSAMPLED 1 +#define SBR_DOWNSAMPLED 2 +#define NO_SBR_UPSAMPLED 3 + +/* library output formats */ +#define FAAD_FMT_16BIT 1 +#define FAAD_FMT_24BIT 2 +#define FAAD_FMT_32BIT 3 +#define FAAD_FMT_FLOAT 4 +#define FAAD_FMT_FIXED FAAD_FMT_FLOAT +#define FAAD_FMT_DOUBLE 5 + +/* Capabilities */ +#define LC_DEC_CAP (1<<0) /* Can decode LC */ +#define MAIN_DEC_CAP (1<<1) /* Can decode MAIN */ +#define LTP_DEC_CAP (1<<2) /* Can decode LTP */ +#define LD_DEC_CAP (1<<3) /* Can decode LD */ +#define ERROR_RESILIENCE_CAP (1<<4) /* Can decode ER */ +#define FIXED_POINT_CAP (1<<5) /* Fixed point */ + +/* Channel definitions */ +#define FRONT_CHANNEL_CENTER (1) +#define FRONT_CHANNEL_LEFT (2) +#define FRONT_CHANNEL_RIGHT (3) +#define SIDE_CHANNEL_LEFT (4) +#define SIDE_CHANNEL_RIGHT (5) +#define BACK_CHANNEL_LEFT (6) +#define BACK_CHANNEL_RIGHT (7) +#define BACK_CHANNEL_CENTER (8) +#define LFE_CHANNEL (9) +#define UNKNOWN_CHANNEL (0) + +/* DRM channel definitions */ +#define DRMCH_MONO 1 +#define DRMCH_STEREO 2 +#define DRMCH_SBR_MONO 3 +#define DRMCH_SBR_STEREO 4 +#define DRMCH_SBR_PS_STEREO 5 + + +/* A decode call can eat up to FAAD_MIN_STREAMSIZE bytes per decoded channel, + so at least so much bytes per channel should be available in this stream */ +#define FAAD_MIN_STREAMSIZE 768 /* 6144 bits/channel */ + + +typedef void *NeAACDecHandle; + +typedef struct mp4AudioSpecificConfig +{ + /* Audio Specific Info */ + unsigned char objectTypeIndex; + unsigned char samplingFrequencyIndex; + unsigned long samplingFrequency; + unsigned char channelsConfiguration; + + /* GA Specific Info */ + unsigned char frameLengthFlag; + unsigned char dependsOnCoreCoder; + unsigned short coreCoderDelay; + unsigned char extensionFlag; + unsigned char aacSectionDataResilienceFlag; + unsigned char aacScalefactorDataResilienceFlag; + unsigned char aacSpectralDataResilienceFlag; + unsigned char epConfig; + + char sbr_present_flag; + char forceUpSampling; + char downSampledSBR; +} mp4AudioSpecificConfig; + +typedef struct NeAACDecConfiguration +{ + unsigned char defObjectType; + unsigned long defSampleRate; + unsigned char outputFormat; + unsigned char downMatrix; + unsigned char useOldADTSFormat; + unsigned char dontUpSampleImplicitSBR; +} NeAACDecConfiguration, *NeAACDecConfigurationPtr; + +typedef struct NeAACDecFrameInfo +{ + unsigned long bytesconsumed; + unsigned long samples; + unsigned char channels; + unsigned char error; + unsigned long samplerate; + + /* SBR: 0: off, 1: on; upsample, 2: on; downsampled, 3: off; upsampled */ + unsigned char sbr; + + /* MPEG-4 ObjectType */ + unsigned char object_type; + + /* AAC header type; MP4 will be signalled as RAW also */ + unsigned char header_type; + + /* multichannel configuration */ + unsigned char num_front_channels; + unsigned char num_side_channels; + unsigned char num_back_channels; + unsigned char num_lfe_channels; + unsigned char channel_position[64]; + + /* PS: 0: off, 1: on */ + unsigned char ps; +} NeAACDecFrameInfo; + +NEAACDECAPI char* NeAACDecGetErrorMessage(unsigned char errcode); + +NEAACDECAPI unsigned long NeAACDecGetCapabilities(void); + +NEAACDECAPI NeAACDecHandle NeAACDecOpen(void); + +NEAACDECAPI NeAACDecConfigurationPtr NeAACDecGetCurrentConfiguration(NeAACDecHandle hDecoder); + +NEAACDECAPI unsigned char NeAACDecSetConfiguration(NeAACDecHandle hDecoder, + NeAACDecConfigurationPtr config); + +/* Init the library based on info from the AAC file (ADTS/ADIF) */ +NEAACDECAPI long NeAACDecInit(NeAACDecHandle hDecoder, + unsigned char *buffer, + unsigned long buffer_size, + unsigned long *samplerate, + unsigned char *channels); + +/* Init the library using a DecoderSpecificInfo */ +NEAACDECAPI char NeAACDecInit2(NeAACDecHandle hDecoder, + unsigned char *pBuffer, + unsigned long SizeOfDecoderSpecificInfo, + unsigned long *samplerate, + unsigned char *channels); + +/* Init the library for DRM */ +NEAACDECAPI char NeAACDecInitDRM(NeAACDecHandle *hDecoder, unsigned long samplerate, + unsigned char channels); + +/* Init the library for HDC */ +NEAACDECAPI char NeAACDecInitHDC(NeAACDecHandle *hDecoder); + +NEAACDECAPI void NeAACDecPostSeekReset(NeAACDecHandle hDecoder, long frame); + +NEAACDECAPI void NeAACDecClose(NeAACDecHandle hDecoder); + +NEAACDECAPI void* NeAACDecDecode(NeAACDecHandle hDecoder, + NeAACDecFrameInfo *hInfo, + unsigned char *buffer, + unsigned long buffer_size); + +NEAACDECAPI void* NeAACDecDecode2(NeAACDecHandle hDecoder, + NeAACDecFrameInfo *hInfo, + unsigned char *buffer, + unsigned long buffer_size, + void **sample_buffer, + unsigned long sample_buffer_size); + +NEAACDECAPI char NeAACDecAudioSpecificConfig(unsigned char *pBuffer, + unsigned long buffer_size, + mp4AudioSpecificConfig *mp4ASC); + +/* Get version and copyright strings */ +NEAACDECAPI int NeAACDecGetVersion(char **faad_id_string, + char **faad_copyright_string); + +#ifdef _WIN32 + #pragma pack(pop) +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/analysis.h b/third_party/faad_hdc/upstream/libfaad/analysis.h new file mode 100644 index 000000000..a0a98e4f2 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/analysis.h @@ -0,0 +1,52 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: analysis.h,v 1.18 2007/11/01 12:33:29 menno Exp $ +**/ + +#ifndef __ANALYSIS_H__ +#define __ANALYSIS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef ANALYSIS +#define DEBUGDEC ,uint8_t print,uint16_t var,uint8_t *dbg +#define DEBUGVAR(A,B,C) ,A,B,C +extern uint16_t dbg_count; +#else +#define DEBUGDEC +#define DEBUGVAR(A,B,C) +#endif + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/bits.c b/third_party/faad_hdc/upstream/libfaad/bits.c new file mode 100644 index 000000000..ee37affbf --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/bits.c @@ -0,0 +1,292 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: bits.c,v 1.44 2007/11/01 12:33:29 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include +#include "bits.h" + +/* reads only n bytes from the stream instead of the standard 4 */ +static /*INLINE*/ uint32_t getdword_n(void *mem, int n) +{ + uint8_t* m8 = (uint8_t*)mem; + switch (n) + { + case 3: + return ((uint32_t)m8[2] << 8) | ((uint32_t)m8[1] << 16) | ((uint32_t)m8[0] << 24); + case 2: + return ((uint32_t)m8[1] << 16) | ((uint32_t)m8[0] << 24); + case 1: + return (uint32_t)m8[0] << 24; + default: + return 0; + } +} + +/* initialize buffer, call once before first getbits or showbits */ +void faad_initbits(bitfile *ld, const void *_buffer, const uint32_t buffer_size) +{ + uint32_t tmp; + + if (ld == NULL) + return; + + if (buffer_size == 0 || _buffer == NULL) + { + ld->error = 1; + return; + } + + ld->buffer = _buffer; + + ld->buffer_size = buffer_size; + ld->bytes_left = buffer_size; + + if (ld->bytes_left >= 4) + { + tmp = getdword((uint32_t*)ld->buffer); + ld->bytes_left -= 4; + } else { + tmp = getdword_n((uint32_t*)ld->buffer, ld->bytes_left); + ld->bytes_left = 0; + } + ld->bufa = tmp; + + if (ld->bytes_left >= 4) + { + tmp = getdword((uint32_t*)ld->buffer + 1); + ld->bytes_left -= 4; + } else { + tmp = getdword_n((uint32_t*)ld->buffer + 1, ld->bytes_left); + ld->bytes_left = 0; + } + ld->bufb = tmp; + + ld->start = (uint32_t*)ld->buffer; + ld->tail = ((uint32_t*)ld->buffer + 2); + + ld->bits_left = 32; + + ld->error = 0; +} + +void faad_endbits(bitfile *ld) +{ + (void)ld; +} + +uint32_t faad_get_processed_bits(bitfile *ld) +{ + return (uint32_t)(8 * (4*(ld->tail - ld->start) - 4) - (ld->bits_left)); +} + +uint8_t faad_byte_align(bitfile *ld) +{ + int remainder = (32 - ld->bits_left) & 0x7; + + if (remainder) + { + faad_flushbits(ld, 8 - remainder); + return (uint8_t)(8 - remainder); + } + return 0; +} + +void faad_flushbits_ex(bitfile *ld, uint32_t bits) +{ + uint32_t tmp; + + ld->bufa = ld->bufb; + if (ld->bytes_left >= 4) + { + tmp = getdword(ld->tail); + ld->bytes_left -= 4; + } else { + tmp = getdword_n(ld->tail, ld->bytes_left); + ld->bytes_left = 0; + } + ld->bufb = tmp; + ld->tail++; + ld->bits_left += (32 - bits); + //ld->bytes_left -= 4; +// if (ld->bytes_left == 0) +// ld->no_more_reading = 1; +// if (ld->bytes_left < 0) +// ld->error = 1; +} + +#ifdef DRM +/* rewind to beginning */ +void faad_rewindbits(bitfile *ld) +{ + uint32_t tmp; + + ld->bytes_left = ld->buffer_size; + + if (ld->bytes_left >= 4) + { + tmp = getdword((uint32_t*)&ld->start[0]); + ld->bytes_left -= 4; + } else { + tmp = getdword_n((uint32_t*)&ld->start[0], ld->bytes_left); + ld->bytes_left = 0; + } + ld->bufa = tmp; + + if (ld->bytes_left >= 4) + { + tmp = getdword((uint32_t*)&ld->start[1]); + ld->bytes_left -= 4; + } else { + tmp = getdword_n((uint32_t*)&ld->start[1], ld->bytes_left); + ld->bytes_left = 0; + } + ld->bufb = tmp; + + ld->bits_left = 32; + ld->tail = &ld->start[2]; +} +#endif + +/* reset to a certain point */ +void faad_resetbits(bitfile *ld, uint32_t bits) +{ + uint32_t tmp; + uint32_t words = bits >> 5; + uint32_t remainder = bits & 0x1F; + + if (ld->buffer_size < words * 4) + ld->bytes_left = 0; + else + ld->bytes_left = ld->buffer_size - words*4; + + if (ld->bytes_left >= 4) + { + tmp = getdword(&ld->start[words]); + ld->bytes_left -= 4; + } else { + tmp = getdword_n(&ld->start[words], ld->bytes_left); + ld->bytes_left = 0; + } + ld->bufa = tmp; + + if (ld->bytes_left >= 4) + { + tmp = getdword(&ld->start[words+1]); + ld->bytes_left -= 4; + } else { + tmp = getdword_n(&ld->start[words+1], ld->bytes_left); + ld->bytes_left = 0; + } + ld->bufb = tmp; + + ld->bits_left = 32 - remainder; + ld->tail = &ld->start[words+2]; + + /* recheck for reading too many bytes */ + ld->error = 0; +// if (ld->bytes_left == 0) +// ld->no_more_reading = 1; +// if (ld->bytes_left < 0) +// ld->error = 1; +} + +uint8_t *faad_getbitbuffer(bitfile *ld, uint32_t bits + DEBUGDEC) +{ + int i; + unsigned int temp; + int bytes = bits >> 3; + int remainder = bits & 0x7; + + uint8_t *buffer = (uint8_t*)faad_malloc((bytes+1)*sizeof(uint8_t)); + + for (i = 0; i < bytes; i++) + { + buffer[i] = (uint8_t)faad_getbits(ld, 8 DEBUGVAR(print,var,dbg)); + } + + if (remainder) + { + temp = faad_getbits(ld, remainder DEBUGVAR(print,var,dbg)) << (8-remainder); + + buffer[bytes] = (uint8_t)temp; + } + + return buffer; +} + +#if defined(DRM) || defined(HDC) +/* return the original data buffer */ +void *faad_origbitbuffer(bitfile *ld) +{ + return (void*)ld->start; +} + +/* return the original data buffer size */ +uint32_t faad_origbitbuffer_size(bitfile *ld) +{ + return ld->buffer_size; +} +#endif + +#if 0 +/* reversed bit reading routines, used for RVLC and HCR */ +void faad_initbits_rev(bitfile *ld, void *buffer, + uint32_t bits_in_buffer) +{ + uint32_t tmp; + int32_t index; + + ld->buffer_size = bit2byte(bits_in_buffer); + + index = (bits_in_buffer+31)/32 - 1; + + ld->start = (uint32_t*)buffer + index - 2; + + tmp = getdword((uint32_t*)buffer + index); + ld->bufa = tmp; + + tmp = getdword((uint32_t*)buffer + index - 1); + ld->bufb = tmp; + + ld->tail = (uint32_t*)buffer + index; + + ld->bits_left = bits_in_buffer % 32; + if (ld->bits_left == 0) + ld->bits_left = 32; + + ld->bytes_left = ld->buffer_size; + ld->error = 0; +} +#endif + +/* EOF */ diff --git a/third_party/faad_hdc/upstream/libfaad/bits.h b/third_party/faad_hdc/upstream/libfaad/bits.h new file mode 100644 index 000000000..44ea3fa6d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/bits.h @@ -0,0 +1,422 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: bits.h,v 1.45 2007/11/01 12:33:29 menno Exp $ +**/ + +#ifndef __BITS_H__ +#define __BITS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "analysis.h" +#ifdef ANALYSIS +#include +#endif + +#define BYTE_NUMBIT 8 +#define BYTE_NUMBIT_LD 3 +//#define bit2byte(a) ((a+7)/BYTE_NUMBIT) +#define bit2byte(a) ((a+7)>>BYTE_NUMBIT_LD) + +typedef struct _bitfile +{ + const void *buffer; + uint32_t *tail; + uint32_t *start; + /* bit input */ + uint32_t bufa; + uint32_t bufb; + uint32_t bits_left; + uint32_t buffer_size; /* size of the buffer in bytes */ + uint32_t bytes_left; + uint8_t error; +} bitfile; + + +#if 0 +static uint32_t const bitmask[] = { + 0x0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, + 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, + 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, + 0xFFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF + /* added bitmask 32, correct?!?!?! */ + , 0xFFFFFFFF +}; +#endif + +void faad_initbits(bitfile *ld, const void *buffer, const uint32_t buffer_size); +void faad_endbits(bitfile *ld); +#if 0 +void faad_initbits_rev(bitfile *ld, void *buffer, + uint32_t bits_in_buffer); +#endif +uint8_t faad_byte_align(bitfile *ld); +uint32_t faad_get_processed_bits(bitfile *ld); +void faad_flushbits_ex(bitfile *ld, uint32_t bits); +#ifdef DRM +void faad_rewindbits(bitfile *ld); +#endif +void faad_resetbits(bitfile *ld, uint32_t bits); +uint8_t *faad_getbitbuffer(bitfile *ld, uint32_t bits + DEBUGDEC); +#if defined(DRM) || defined(HDC) +void *faad_origbitbuffer(bitfile *ld); +uint32_t faad_origbitbuffer_size(bitfile *ld); +#endif + +/* circumvent memory alignment errors on ARM */ +static INLINE uint32_t getdword(void *mem) +{ + uint8_t* m8 = (uint8_t*)mem; + return (uint32_t)m8[3] | ((uint32_t)m8[2] << 8) | ((uint32_t)m8[1] << 16) | ((uint32_t)m8[0] << 24); +} + +static INLINE uint32_t faad_showbits(bitfile *ld, uint32_t bits) +{ + if (bits <= ld->bits_left) + { + //return (ld->bufa >> (ld->bits_left - bits)) & bitmask[bits]; + return (ld->bufa << (32 - ld->bits_left)) >> (32 - bits); + } + + bits -= ld->bits_left; + //return ((ld->bufa & bitmask[ld->bits_left]) << bits) | (ld->bufb >> (32 - bits)); + return ((ld->bufa & ((1u<bits_left)-1)) << bits) | (ld->bufb >> (32 - bits)); +} + +static INLINE void faad_flushbits(bitfile *ld, uint32_t bits) +{ + /* do nothing if error */ + if (ld->error != 0) + return; + + if (bits < ld->bits_left) + { + ld->bits_left -= bits; + } else { + faad_flushbits_ex(ld, bits); + } +} + +/* return next n bits (right adjusted) */ +static /*INLINE*/ uint32_t faad_getbits(bitfile *ld, uint32_t n DEBUGDEC) +{ + uint32_t ret; + + if (n == 0) + return 0; + + ret = faad_showbits(ld, n); + faad_flushbits(ld, n); + +#ifdef ANALYSIS + if (print) + fprintf(stdout, "%4d %2d bits, val: %4d, variable: %d %s\n", dbg_count++, n, ret, var, dbg); +#endif + + return ret; +} + +static INLINE uint8_t faad_get1bit(bitfile *ld DEBUGDEC) +{ + uint8_t r; + + if (ld->bits_left > 0) + { + ld->bits_left--; + r = (uint8_t)((ld->bufa >> ld->bits_left) & 1); + return r; + } + + /* bits_left == 0 */ +#if 0 + r = (uint8_t)(ld->bufb >> 31); + faad_flushbits_ex(ld, 1); +#else + r = (uint8_t)faad_getbits(ld, 1); +#endif + return r; +} + +#if 0 +/* reversed bitreading routines */ +static INLINE uint32_t faad_showbits_rev(bitfile *ld, uint32_t bits) +{ + uint8_t i; + uint32_t B = 0; + + if (bits <= ld->bits_left) + { + for (i = 0; i < bits; i++) + { + if (ld->bufa & (1u << (i + (32 - ld->bits_left)))) + B |= (1u << (bits - i - 1)); + } + return B; + } else { + for (i = 0; i < ld->bits_left; i++) + { + if (ld->bufa & (1u << (i + (32 - ld->bits_left)))) + B |= (1u << (bits - i - 1)); + } + for (i = 0; i < bits - ld->bits_left; i++) + { + if (ld->bufb & (1u << (i + (32-ld->bits_left)))) + B |= (1u << (bits - ld->bits_left - i - 1)); + } + return B; + } +} + +static INLINE void faad_flushbits_rev(bitfile *ld, uint32_t bits) +{ + /* do nothing if error */ + if (ld->error != 0) + return; + + if (bits < ld->bits_left) + { + ld->bits_left -= bits; + } else { + uint32_t tmp; + + ld->bufa = ld->bufb; + tmp = getdword(ld->start); + ld->bufb = tmp; + ld->start--; + ld->bits_left += (32 - bits); + + if (ld->bytes_left < 4) + { + ld->error = 1; + ld->bytes_left = 0; + } else { + ld->bytes_left -= 4; + } +// if (ld->bytes_left == 0) +// ld->no_more_reading = 1; + } +} + +static /*INLINE*/ uint32_t faad_getbits_rev(bitfile *ld, uint32_t n + DEBUGDEC) +{ + uint32_t ret; + + if (n == 0) + return 0; + + ret = faad_showbits_rev(ld, n); + faad_flushbits_rev(ld, n); + +#ifdef ANALYSIS + if (print) + fprintf(stdout, "%4d %2d bits, val: %4d, variable: %d %s\n", dbg_count++, n, ret, var, dbg); +#endif + + return ret; +} +#endif + +#ifdef DRM +/* CRC lookup table for G8 polynome in DRM standard */ +static const uint8_t crc_table_G8[256] = { + 0x0, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, + 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb, + 0xcd, 0xd0, 0xf7, 0xea, 0xb9, 0xa4, 0x83, 0x9e, + 0x25, 0x38, 0x1f, 0x2, 0x51, 0x4c, 0x6b, 0x76, + 0x87, 0x9a, 0xbd, 0xa0, 0xf3, 0xee, 0xc9, 0xd4, + 0x6f, 0x72, 0x55, 0x48, 0x1b, 0x6, 0x21, 0x3c, + 0x4a, 0x57, 0x70, 0x6d, 0x3e, 0x23, 0x4, 0x19, + 0xa2, 0xbf, 0x98, 0x85, 0xd6, 0xcb, 0xec, 0xf1, + 0x13, 0xe, 0x29, 0x34, 0x67, 0x7a, 0x5d, 0x40, + 0xfb, 0xe6, 0xc1, 0xdc, 0x8f, 0x92, 0xb5, 0xa8, + 0xde, 0xc3, 0xe4, 0xf9, 0xaa, 0xb7, 0x90, 0x8d, + 0x36, 0x2b, 0xc, 0x11, 0x42, 0x5f, 0x78, 0x65, + 0x94, 0x89, 0xae, 0xb3, 0xe0, 0xfd, 0xda, 0xc7, + 0x7c, 0x61, 0x46, 0x5b, 0x8, 0x15, 0x32, 0x2f, + 0x59, 0x44, 0x63, 0x7e, 0x2d, 0x30, 0x17, 0xa, + 0xb1, 0xac, 0x8b, 0x96, 0xc5, 0xd8, 0xff, 0xe2, + 0x26, 0x3b, 0x1c, 0x1, 0x52, 0x4f, 0x68, 0x75, + 0xce, 0xd3, 0xf4, 0xe9, 0xba, 0xa7, 0x80, 0x9d, + 0xeb, 0xf6, 0xd1, 0xcc, 0x9f, 0x82, 0xa5, 0xb8, + 0x3, 0x1e, 0x39, 0x24, 0x77, 0x6a, 0x4d, 0x50, + 0xa1, 0xbc, 0x9b, 0x86, 0xd5, 0xc8, 0xef, 0xf2, + 0x49, 0x54, 0x73, 0x6e, 0x3d, 0x20, 0x7, 0x1a, + 0x6c, 0x71, 0x56, 0x4b, 0x18, 0x5, 0x22, 0x3f, + 0x84, 0x99, 0xbe, 0xa3, 0xf0, 0xed, 0xca, 0xd7, + 0x35, 0x28, 0xf, 0x12, 0x41, 0x5c, 0x7b, 0x66, + 0xdd, 0xc0, 0xe7, 0xfa, 0xa9, 0xb4, 0x93, 0x8e, + 0xf8, 0xe5, 0xc2, 0xdf, 0x8c, 0x91, 0xb6, 0xab, + 0x10, 0xd, 0x2a, 0x37, 0x64, 0x79, 0x5e, 0x43, + 0xb2, 0xaf, 0x88, 0x95, 0xc6, 0xdb, 0xfc, 0xe1, + 0x5a, 0x47, 0x60, 0x7d, 0x2e, 0x33, 0x14, 0x9, + 0x7f, 0x62, 0x45, 0x58, 0xb, 0x16, 0x31, 0x2c, + 0x97, 0x8a, 0xad, 0xb0, 0xe3, 0xfe, 0xd9, 0xc4, +}; + +static INLINE uint8_t faad_check_CRC(bitfile *ld, uint16_t len) +{ + int bytes, rem; + unsigned int CRC; + unsigned int r=255; /* Initialize to all ones */ + + /* CRC polynome used x^8 + x^4 + x^3 + x^2 +1 */ +#define GPOLY 0435 + + faad_rewindbits(ld); + + CRC = (unsigned int) ~faad_getbits(ld, 8 + DEBUGVAR(1,999,"faad_check_CRC(): CRC")) & 0xFF; /* CRC is stored inverted */ + + bytes = len >> 3; + rem = len & 0x7; + + for (; bytes > 0; bytes--) + { + r = crc_table_G8[( r ^ faad_getbits(ld, 8 DEBUGVAR(1,998,"")) ) & 0xFF]; + } + for (; rem > 0; rem--) + { + r = ( (r << 1) ^ (( ( faad_get1bit(ld + DEBUGVAR(1,998,"")) & 1) ^ ((r >> 7) & 1)) * GPOLY )) & 0xFF; + } + + if (r != CRC) + // if (0) + { + return 28; + } else { + return 0; + } +} + +#ifdef SBR_DEC +static uint8_t tabFlipbits[256] = { + 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240, + 8,136,72,200,40,168,104,232,24,152,88,216,56,184,120,248, + 4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244, + 12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252, + 2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242, + 10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250, + 6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246, + 14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254, + 1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241, + 9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249, + 5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245, + 13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253, + 3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243, + 11,139,75,203,43,171,107,235,27,155,91,219,59,187,123,251, + 7,135,71,199,39,167,103,231,23,151,87,215,55,183,119,247, + 15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255 +}; +static INLINE uint8_t reverse_byte(uint8_t b) +{ + return tabFlipbits[b]; +} +#endif +#endif + +#ifdef ERROR_RESILIENCE + +/* Modified bit reading functions for HCR */ + +typedef struct +{ + /* bit input */ + uint32_t bufa; + uint32_t bufb; + int8_t len; +} bits_t; + +/* PRECONDITION: bits <= 32 */ +static INLINE uint32_t showbits_hcr(bits_t *ld, uint8_t bits) +{ + uint32_t mask; + int8_t tail; + if (bits == 0) return 0; + if (ld->len == 0) return 0; + tail = ld->len - bits; + mask = 0xFFFFFFFF >> (32 - bits); + if (ld->len <= 32) + { + /* huffman_spectral_data_2 might request more than available (tail < 0), + pad with zeroes then. */ + if (tail >= 0) + return (ld->bufa >> tail) & mask; /* tail is 0..31 */ + else + return (ld->bufa << -tail) & mask; /* -tail is 1..31 */ + } else { + /* tail is 1..63 */ + if (tail < 32) + return ((ld->bufb << (32 - tail)) | (ld->bufa >> tail)) & mask; + else + return (ld->bufb >> (tail - 32)) & mask; + } +} + +/* return 1 if position is outside of buffer, 0 otherwise */ +static INLINE int8_t flushbits_hcr( bits_t *ld, uint8_t bits) +{ + ld->len -= bits; + + if (ld->len <0) + { + ld->len = 0; + return 1; + } else { + return 0; + } +} + +static INLINE int8_t getbits_hcr(bits_t *ld, uint8_t n, uint32_t *result) +{ + *result = showbits_hcr(ld, n); + return flushbits_hcr(ld, n); +} + +static INLINE int8_t get1bit_hcr(bits_t *ld, uint8_t *result) +{ + uint32_t res; + int8_t ret; + + ret = getbits_hcr(ld, 1, &res); + *result = (int8_t)(res & 1); + return ret; +} + +#endif + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/cfft.c b/third_party/faad_hdc/upstream/libfaad/cfft.c new file mode 100644 index 000000000..d0fd9d565 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/cfft.c @@ -0,0 +1,1050 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: cfft.c,v 1.35 2007/11/01 12:33:29 menno Exp $ +**/ + +/* + * Algorithmically based on Fortran-77 FFTPACK + * by Paul N. Swarztrauber(Version 4, 1985). + * + * Does even sized fft only + */ + +/* isign is +1 for backward and -1 for forward transforms */ + +#include "common.h" +#include "structs.h" + +#include + +#include "cfft.h" +#include "cfft_tab.h" + + +/* static function declarations */ +static void passf2pos(const uint16_t ido, const uint16_t l1, const complex_t *cc, + complex_t *ch, const complex_t *wa); +static void passf2neg(const uint16_t ido, const uint16_t l1, const complex_t *cc, + complex_t *ch, const complex_t *wa); +static void passf3(const uint16_t ido, const uint16_t l1, const complex_t *cc, + complex_t *ch, const complex_t *wa1, const complex_t *wa2, const int8_t isign); +static void passf4pos(const uint16_t ido, const uint16_t l1, const complex_t *cc, complex_t *ch, + const complex_t *wa1, const complex_t *wa2, const complex_t *wa3); +static void passf4neg(const uint16_t ido, const uint16_t l1, const complex_t *cc, complex_t *ch, + const complex_t *wa1, const complex_t *wa2, const complex_t *wa3); +static void passf5(const uint16_t ido, const uint16_t l1, const complex_t *cc, complex_t *ch, + const complex_t *wa1, const complex_t *wa2, const complex_t *wa3, + const complex_t *wa4, const int8_t isign); +static void cffti1(uint16_t n, complex_t *wa, uint16_t *ifac); + + +/*---------------------------------------------------------------------- + passf2, passf3, passf4, passf5. Complex FFT passes fwd and bwd. + ----------------------------------------------------------------------*/ + +static void passf2pos(const uint16_t ido, const uint16_t l1, const complex_t *cc, + complex_t *ch, const complex_t *wa) +{ + uint16_t i, k, ah, ac; + + if (ido == 1) + { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // TODO: remove this code once fuzzer proves it is totally unreahable + // For supported frame lengths that have odd number of factor 2 it is + // never the last factor; consequently `ido` should never be 1. + __builtin_trap(); + /* +#endif + for (k = 0; k < l1; k++) + { + ah = 2*k; + ac = 4*k; + + RE(ch[ah]) = RE(cc[ac]) + RE(cc[ac+1]); + RE(ch[ah+l1]) = RE(cc[ac]) - RE(cc[ac+1]); + IM(ch[ah]) = IM(cc[ac]) + IM(cc[ac+1]); + IM(ch[ah+l1]) = IM(cc[ac]) - IM(cc[ac+1]); + } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + */ +#endif + } else { + for (k = 0; k < l1; k++) + { + ah = k*ido; + ac = 2*k*ido; + + for (i = 0; i < ido; i++) + { + complex_t t2; + + RE(ch[ah+i]) = RE(cc[ac+i]) + RE(cc[ac+i+ido]); + RE(t2) = RE(cc[ac+i]) - RE(cc[ac+i+ido]); + + IM(ch[ah+i]) = IM(cc[ac+i]) + IM(cc[ac+i+ido]); + IM(t2) = IM(cc[ac+i]) - IM(cc[ac+i+ido]); + +#if 1 + ComplexMult(&IM(ch[ah+i+l1*ido]), &RE(ch[ah+i+l1*ido]), + IM(t2), RE(t2), RE(wa[i]), IM(wa[i])); +#else + ComplexMult(&RE(ch[ah+i+l1*ido]), &IM(ch[ah+i+l1*ido]), + RE(t2), IM(t2), RE(wa[i]), IM(wa[i])); +#endif + } + } + } +} + +static void passf2neg(const uint16_t ido, const uint16_t l1, const complex_t *cc, + complex_t *ch, const complex_t *wa) +{ + uint16_t i, k, ah, ac; + + if (ido == 1) + { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // TODO: remove this code once fuzzer proves it is totally unreahable + // For supported frame lengths that have odd number of factor 2 it is + // never the last factor; consequently `ido` should never be 1. + __builtin_trap(); + /* +#endif + for (k = 0; k < l1; k++) + { + ah = 2*k; + ac = 4*k; + + RE(ch[ah]) = RE(cc[ac]) + RE(cc[ac+1]); + RE(ch[ah+l1]) = RE(cc[ac]) - RE(cc[ac+1]); + IM(ch[ah]) = IM(cc[ac]) + IM(cc[ac+1]); + IM(ch[ah+l1]) = IM(cc[ac]) - IM(cc[ac+1]); + } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + */ +#endif + } else { + for (k = 0; k < l1; k++) + { + ah = k*ido; + ac = 2*k*ido; + + for (i = 0; i < ido; i++) + { + complex_t t2; + + RE(ch[ah+i]) = RE(cc[ac+i]) + RE(cc[ac+i+ido]); + RE(t2) = RE(cc[ac+i]) - RE(cc[ac+i+ido]); + + IM(ch[ah+i]) = IM(cc[ac+i]) + IM(cc[ac+i+ido]); + IM(t2) = IM(cc[ac+i]) - IM(cc[ac+i+ido]); + +#if 1 + ComplexMult(&RE(ch[ah+i+l1*ido]), &IM(ch[ah+i+l1*ido]), + RE(t2), IM(t2), RE(wa[i]), IM(wa[i])); +#else + ComplexMult(&IM(ch[ah+i+l1*ido]), &RE(ch[ah+i+l1*ido]), + IM(t2), RE(t2), RE(wa[i]), IM(wa[i])); +#endif + } + } + } +} + + +static void passf3(const uint16_t ido, const uint16_t l1, const complex_t *cc, + complex_t *ch, const complex_t *wa1, const complex_t *wa2, + const int8_t isign) +{ + static real_t taur = FRAC_CONST(-0.5); + static real_t taui = FRAC_CONST(0.866025403784439); + uint16_t i, k, ac, ah; + complex_t c2, c3, d2, d3, t2; + + if (ido == 1) + { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // TODO: remove this code once fuzzer proves it is totally unreahable + // 3 is never the the biggest factor for supported frame lengths; + // consequently `ido` should never be 1. + __builtin_trap(); + /* +#endif + if (isign == 1) + { + for (k = 0; k < l1; k++) + { + ac = 3*k+1; + ah = k; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+1]); + IM(t2) = IM(cc[ac]) + IM(cc[ac+1]); + RE(c2) = RE(cc[ac-1]) + MUL_F(RE(t2),taur); + IM(c2) = IM(cc[ac-1]) + MUL_F(IM(t2),taur); + + RE(ch[ah]) = RE(cc[ac-1]) + RE(t2); + IM(ch[ah]) = IM(cc[ac-1]) + IM(t2); + + RE(c3) = MUL_F((RE(cc[ac]) - RE(cc[ac+1])), taui); + IM(c3) = MUL_F((IM(cc[ac]) - IM(cc[ac+1])), taui); + + RE(ch[ah+l1]) = RE(c2) - IM(c3); + IM(ch[ah+l1]) = IM(c2) + RE(c3); + RE(ch[ah+2*l1]) = RE(c2) + IM(c3); + IM(ch[ah+2*l1]) = IM(c2) - RE(c3); + } + } else { + for (k = 0; k < l1; k++) + { + ac = 3*k+1; + ah = k; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+1]); + IM(t2) = IM(cc[ac]) + IM(cc[ac+1]); + RE(c2) = RE(cc[ac-1]) + MUL_F(RE(t2),taur); + IM(c2) = IM(cc[ac-1]) + MUL_F(IM(t2),taur); + + RE(ch[ah]) = RE(cc[ac-1]) + RE(t2); + IM(ch[ah]) = IM(cc[ac-1]) + IM(t2); + + RE(c3) = MUL_F((RE(cc[ac]) - RE(cc[ac+1])), taui); + IM(c3) = MUL_F((IM(cc[ac]) - IM(cc[ac+1])), taui); + + RE(ch[ah+l1]) = RE(c2) + IM(c3); + IM(ch[ah+l1]) = IM(c2) - RE(c3); + RE(ch[ah+2*l1]) = RE(c2) - IM(c3); + IM(ch[ah+2*l1]) = IM(c2) + RE(c3); + } + } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + */ +#endif + } else { + if (isign == 1) + { + for (k = 0; k < l1; k++) + { + for (i = 0; i < ido; i++) + { + ac = i + (3*k+1)*ido; + ah = i + k * ido; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+ido]); + RE(c2) = RE(cc[ac-ido]) + MUL_F(RE(t2),taur); + IM(t2) = IM(cc[ac]) + IM(cc[ac+ido]); + IM(c2) = IM(cc[ac-ido]) + MUL_F(IM(t2),taur); + + RE(ch[ah]) = RE(cc[ac-ido]) + RE(t2); + IM(ch[ah]) = IM(cc[ac-ido]) + IM(t2); + + RE(c3) = MUL_F((RE(cc[ac]) - RE(cc[ac+ido])), taui); + IM(c3) = MUL_F((IM(cc[ac]) - IM(cc[ac+ido])), taui); + + RE(d2) = RE(c2) - IM(c3); + IM(d3) = IM(c2) - RE(c3); + RE(d3) = RE(c2) + IM(c3); + IM(d2) = IM(c2) + RE(c3); + +#if 1 + ComplexMult(&IM(ch[ah+l1*ido]), &RE(ch[ah+l1*ido]), + IM(d2), RE(d2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&IM(ch[ah+2*l1*ido]), &RE(ch[ah+2*l1*ido]), + IM(d3), RE(d3), RE(wa2[i]), IM(wa2[i])); +#else + ComplexMult(&RE(ch[ah+l1*ido]), &IM(ch[ah+l1*ido]), + RE(d2), IM(d2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&RE(ch[ah+2*l1*ido]), &IM(ch[ah+2*l1*ido]), + RE(d3), IM(d3), RE(wa2[i]), IM(wa2[i])); +#endif + } + } + } else { + for (k = 0; k < l1; k++) + { + for (i = 0; i < ido; i++) + { + ac = i + (3*k+1)*ido; + ah = i + k * ido; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+ido]); + RE(c2) = RE(cc[ac-ido]) + MUL_F(RE(t2),taur); + IM(t2) = IM(cc[ac]) + IM(cc[ac+ido]); + IM(c2) = IM(cc[ac-ido]) + MUL_F(IM(t2),taur); + + RE(ch[ah]) = RE(cc[ac-ido]) + RE(t2); + IM(ch[ah]) = IM(cc[ac-ido]) + IM(t2); + + RE(c3) = MUL_F((RE(cc[ac]) - RE(cc[ac+ido])), taui); + IM(c3) = MUL_F((IM(cc[ac]) - IM(cc[ac+ido])), taui); + + RE(d2) = RE(c2) + IM(c3); + IM(d3) = IM(c2) + RE(c3); + RE(d3) = RE(c2) - IM(c3); + IM(d2) = IM(c2) - RE(c3); + +#if 1 + ComplexMult(&RE(ch[ah+l1*ido]), &IM(ch[ah+l1*ido]), + RE(d2), IM(d2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&RE(ch[ah+2*l1*ido]), &IM(ch[ah+2*l1*ido]), + RE(d3), IM(d3), RE(wa2[i]), IM(wa2[i])); +#else + ComplexMult(&IM(ch[ah+l1*ido]), &RE(ch[ah+l1*ido]), + IM(d2), RE(d2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&IM(ch[ah+2*l1*ido]), &RE(ch[ah+2*l1*ido]), + IM(d3), RE(d3), RE(wa2[i]), IM(wa2[i])); +#endif + } + } + } + } +} + + +static void passf4pos(const uint16_t ido, const uint16_t l1, const complex_t *cc, + complex_t *ch, const complex_t *wa1, const complex_t *wa2, + const complex_t *wa3) +{ + uint16_t i, k, ac, ah; + + if (ido == 1) + { + for (k = 0; k < l1; k++) + { + complex_t t1, t2, t3, t4; + + ac = 4*k; + ah = k; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+2]); + RE(t1) = RE(cc[ac]) - RE(cc[ac+2]); + IM(t2) = IM(cc[ac]) + IM(cc[ac+2]); + IM(t1) = IM(cc[ac]) - IM(cc[ac+2]); + RE(t3) = RE(cc[ac+1]) + RE(cc[ac+3]); + IM(t4) = RE(cc[ac+1]) - RE(cc[ac+3]); + IM(t3) = IM(cc[ac+3]) + IM(cc[ac+1]); + RE(t4) = IM(cc[ac+3]) - IM(cc[ac+1]); + + RE(ch[ah]) = RE(t2) + RE(t3); + RE(ch[ah+2*l1]) = RE(t2) - RE(t3); + + IM(ch[ah]) = IM(t2) + IM(t3); + IM(ch[ah+2*l1]) = IM(t2) - IM(t3); + + RE(ch[ah+l1]) = RE(t1) + RE(t4); + RE(ch[ah+3*l1]) = RE(t1) - RE(t4); + + IM(ch[ah+l1]) = IM(t1) + IM(t4); + IM(ch[ah+3*l1]) = IM(t1) - IM(t4); + } + } else { + for (k = 0; k < l1; k++) + { + ac = 4*k*ido; + ah = k*ido; + + for (i = 0; i < ido; i++) + { + complex_t c2, c3, c4, t1, t2, t3, t4; + + RE(t2) = RE(cc[ac+i]) + RE(cc[ac+i+2*ido]); + RE(t1) = RE(cc[ac+i]) - RE(cc[ac+i+2*ido]); + IM(t2) = IM(cc[ac+i]) + IM(cc[ac+i+2*ido]); + IM(t1) = IM(cc[ac+i]) - IM(cc[ac+i+2*ido]); + RE(t3) = RE(cc[ac+i+ido]) + RE(cc[ac+i+3*ido]); + IM(t4) = RE(cc[ac+i+ido]) - RE(cc[ac+i+3*ido]); + IM(t3) = IM(cc[ac+i+3*ido]) + IM(cc[ac+i+ido]); + RE(t4) = IM(cc[ac+i+3*ido]) - IM(cc[ac+i+ido]); + + RE(c2) = RE(t1) + RE(t4); + RE(c4) = RE(t1) - RE(t4); + + IM(c2) = IM(t1) + IM(t4); + IM(c4) = IM(t1) - IM(t4); + + RE(ch[ah+i]) = RE(t2) + RE(t3); + RE(c3) = RE(t2) - RE(t3); + + IM(ch[ah+i]) = IM(t2) + IM(t3); + IM(c3) = IM(t2) - IM(t3); + +#if 1 + ComplexMult(&IM(ch[ah+i+l1*ido]), &RE(ch[ah+i+l1*ido]), + IM(c2), RE(c2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&IM(ch[ah+i+2*l1*ido]), &RE(ch[ah+i+2*l1*ido]), + IM(c3), RE(c3), RE(wa2[i]), IM(wa2[i])); + ComplexMult(&IM(ch[ah+i+3*l1*ido]), &RE(ch[ah+i+3*l1*ido]), + IM(c4), RE(c4), RE(wa3[i]), IM(wa3[i])); +#else + ComplexMult(&RE(ch[ah+i+l1*ido]), &IM(ch[ah+i+l1*ido]), + RE(c2), IM(c2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&RE(ch[ah+i+2*l1*ido]), &IM(ch[ah+i+2*l1*ido]), + RE(c3), IM(c3), RE(wa2[i]), IM(wa2[i])); + ComplexMult(&RE(ch[ah+i+3*l1*ido]), &IM(ch[ah+i+3*l1*ido]), + RE(c4), IM(c4), RE(wa3[i]), IM(wa3[i])); +#endif + } + } + } +} + +static void passf4neg(const uint16_t ido, const uint16_t l1, const complex_t *cc, + complex_t *ch, const complex_t *wa1, const complex_t *wa2, + const complex_t *wa3) +{ + uint16_t i, k, ac, ah; + + if (ido == 1) + { + for (k = 0; k < l1; k++) + { + complex_t t1, t2, t3, t4; + + ac = 4*k; + ah = k; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+2]); + RE(t1) = RE(cc[ac]) - RE(cc[ac+2]); + IM(t2) = IM(cc[ac]) + IM(cc[ac+2]); + IM(t1) = IM(cc[ac]) - IM(cc[ac+2]); + RE(t3) = RE(cc[ac+1]) + RE(cc[ac+3]); + IM(t4) = RE(cc[ac+1]) - RE(cc[ac+3]); + IM(t3) = IM(cc[ac+3]) + IM(cc[ac+1]); + RE(t4) = IM(cc[ac+3]) - IM(cc[ac+1]); + + RE(ch[ah]) = RE(t2) + RE(t3); + RE(ch[ah+2*l1]) = RE(t2) - RE(t3); + + IM(ch[ah]) = IM(t2) + IM(t3); + IM(ch[ah+2*l1]) = IM(t2) - IM(t3); + + RE(ch[ah+l1]) = RE(t1) - RE(t4); + RE(ch[ah+3*l1]) = RE(t1) + RE(t4); + + IM(ch[ah+l1]) = IM(t1) - IM(t4); + IM(ch[ah+3*l1]) = IM(t1) + IM(t4); + } + } else { + for (k = 0; k < l1; k++) + { + ac = 4*k*ido; + ah = k*ido; + + for (i = 0; i < ido; i++) + { + complex_t c2, c3, c4, t1, t2, t3, t4; + + RE(t2) = RE(cc[ac+i]) + RE(cc[ac+i+2*ido]); + RE(t1) = RE(cc[ac+i]) - RE(cc[ac+i+2*ido]); + IM(t2) = IM(cc[ac+i]) + IM(cc[ac+i+2*ido]); + IM(t1) = IM(cc[ac+i]) - IM(cc[ac+i+2*ido]); + RE(t3) = RE(cc[ac+i+ido]) + RE(cc[ac+i+3*ido]); + IM(t4) = RE(cc[ac+i+ido]) - RE(cc[ac+i+3*ido]); + IM(t3) = IM(cc[ac+i+3*ido]) + IM(cc[ac+i+ido]); + RE(t4) = IM(cc[ac+i+3*ido]) - IM(cc[ac+i+ido]); + + RE(c2) = RE(t1) - RE(t4); + RE(c4) = RE(t1) + RE(t4); + + IM(c2) = IM(t1) - IM(t4); + IM(c4) = IM(t1) + IM(t4); + + RE(ch[ah+i]) = RE(t2) + RE(t3); + RE(c3) = RE(t2) - RE(t3); + + IM(ch[ah+i]) = IM(t2) + IM(t3); + IM(c3) = IM(t2) - IM(t3); + +#if 1 + ComplexMult(&RE(ch[ah+i+l1*ido]), &IM(ch[ah+i+l1*ido]), + RE(c2), IM(c2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&RE(ch[ah+i+2*l1*ido]), &IM(ch[ah+i+2*l1*ido]), + RE(c3), IM(c3), RE(wa2[i]), IM(wa2[i])); + ComplexMult(&RE(ch[ah+i+3*l1*ido]), &IM(ch[ah+i+3*l1*ido]), + RE(c4), IM(c4), RE(wa3[i]), IM(wa3[i])); +#else + ComplexMult(&IM(ch[ah+i+l1*ido]), &RE(ch[ah+i+l1*ido]), + IM(c2), RE(c2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&IM(ch[ah+i+2*l1*ido]), &RE(ch[ah+i+2*l1*ido]), + IM(c3), RE(c3), RE(wa2[i]), IM(wa2[i])); + ComplexMult(&IM(ch[ah+i+3*l1*ido]), &RE(ch[ah+i+3*l1*ido]), + IM(c4), RE(c4), RE(wa3[i]), IM(wa3[i])); +#endif + } + } + } +} + +static void passf5(const uint16_t ido, const uint16_t l1, const complex_t *cc, + complex_t *ch, const complex_t *wa1, const complex_t *wa2, const complex_t *wa3, + const complex_t *wa4, const int8_t isign) +{ + static real_t tr11 = FRAC_CONST(0.309016994374947); + static real_t ti11 = FRAC_CONST(0.951056516295154); + static real_t tr12 = FRAC_CONST(-0.809016994374947); + static real_t ti12 = FRAC_CONST(0.587785252292473); + uint16_t k, ac, ah; + complex_t c2, c3, c4, c5, t2, t3, t4, t5; + (void)wa1; + (void)wa2; + (void)wa3; + (void)wa4; + + if (ido == 1) + { + if (isign == 1) + { + for (k = 0; k < l1; k++) + { + ac = 5*k + 1; + ah = k; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+3]); + IM(t2) = IM(cc[ac]) + IM(cc[ac+3]); + RE(t3) = RE(cc[ac+1]) + RE(cc[ac+2]); + IM(t3) = IM(cc[ac+1]) + IM(cc[ac+2]); + RE(t4) = RE(cc[ac+1]) - RE(cc[ac+2]); + IM(t4) = IM(cc[ac+1]) - IM(cc[ac+2]); + RE(t5) = RE(cc[ac]) - RE(cc[ac+3]); + IM(t5) = IM(cc[ac]) - IM(cc[ac+3]); + + RE(ch[ah]) = RE(cc[ac-1]) + RE(t2) + RE(t3); + IM(ch[ah]) = IM(cc[ac-1]) + IM(t2) + IM(t3); + + RE(c2) = RE(cc[ac-1]) + MUL_F(RE(t2),tr11) + MUL_F(RE(t3),tr12); + IM(c2) = IM(cc[ac-1]) + MUL_F(IM(t2),tr11) + MUL_F(IM(t3),tr12); + RE(c3) = RE(cc[ac-1]) + MUL_F(RE(t2),tr12) + MUL_F(RE(t3),tr11); + IM(c3) = IM(cc[ac-1]) + MUL_F(IM(t2),tr12) + MUL_F(IM(t3),tr11); + + ComplexMult(&RE(c5), &RE(c4), + ti11, ti12, RE(t5), RE(t4)); + ComplexMult(&IM(c5), &IM(c4), + ti11, ti12, IM(t5), IM(t4)); + + RE(ch[ah+l1]) = RE(c2) - IM(c5); + IM(ch[ah+l1]) = IM(c2) + RE(c5); + RE(ch[ah+2*l1]) = RE(c3) - IM(c4); + IM(ch[ah+2*l1]) = IM(c3) + RE(c4); + RE(ch[ah+3*l1]) = RE(c3) + IM(c4); + IM(ch[ah+3*l1]) = IM(c3) - RE(c4); + RE(ch[ah+4*l1]) = RE(c2) + IM(c5); + IM(ch[ah+4*l1]) = IM(c2) - RE(c5); + } + } else { + for (k = 0; k < l1; k++) + { + ac = 5*k + 1; + ah = k; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+3]); + IM(t2) = IM(cc[ac]) + IM(cc[ac+3]); + RE(t3) = RE(cc[ac+1]) + RE(cc[ac+2]); + IM(t3) = IM(cc[ac+1]) + IM(cc[ac+2]); + RE(t4) = RE(cc[ac+1]) - RE(cc[ac+2]); + IM(t4) = IM(cc[ac+1]) - IM(cc[ac+2]); + RE(t5) = RE(cc[ac]) - RE(cc[ac+3]); + IM(t5) = IM(cc[ac]) - IM(cc[ac+3]); + + RE(ch[ah]) = RE(cc[ac-1]) + RE(t2) + RE(t3); + IM(ch[ah]) = IM(cc[ac-1]) + IM(t2) + IM(t3); + + RE(c2) = RE(cc[ac-1]) + MUL_F(RE(t2),tr11) + MUL_F(RE(t3),tr12); + IM(c2) = IM(cc[ac-1]) + MUL_F(IM(t2),tr11) + MUL_F(IM(t3),tr12); + RE(c3) = RE(cc[ac-1]) + MUL_F(RE(t2),tr12) + MUL_F(RE(t3),tr11); + IM(c3) = IM(cc[ac-1]) + MUL_F(IM(t2),tr12) + MUL_F(IM(t3),tr11); + + ComplexMult(&RE(c4), &RE(c5), + ti12, ti11, RE(t5), RE(t4)); + ComplexMult(&IM(c4), &IM(c5), + ti12, ti11, IM(t5), IM(t4)); + + RE(ch[ah+l1]) = RE(c2) + IM(c5); + IM(ch[ah+l1]) = IM(c2) - RE(c5); + RE(ch[ah+2*l1]) = RE(c3) + IM(c4); + IM(ch[ah+2*l1]) = IM(c3) - RE(c4); + RE(ch[ah+3*l1]) = RE(c3) - IM(c4); + IM(ch[ah+3*l1]) = IM(c3) + RE(c4); + RE(ch[ah+4*l1]) = RE(c2) - IM(c5); + IM(ch[ah+4*l1]) = IM(c2) + RE(c5); + } + } + } else { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // TODO: remove this code once fuzzer proves it is totally unreahable + // 5 if the biggest factor and it never repeated for supported frame + // lengths; consequently `ido` should always be 1. + __builtin_trap(); + /* +#else + uint16_t i; + complex_t d3, d4, d5, d2; +#endif + if (isign == 1) + { + for (k = 0; k < l1; k++) + { + for (i = 0; i < ido; i++) + { + ac = i + (k*5 + 1) * ido; + ah = i + k * ido; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+3*ido]); + IM(t2) = IM(cc[ac]) + IM(cc[ac+3*ido]); + RE(t3) = RE(cc[ac+ido]) + RE(cc[ac+2*ido]); + IM(t3) = IM(cc[ac+ido]) + IM(cc[ac+2*ido]); + RE(t4) = RE(cc[ac+ido]) - RE(cc[ac+2*ido]); + IM(t4) = IM(cc[ac+ido]) - IM(cc[ac+2*ido]); + RE(t5) = RE(cc[ac]) - RE(cc[ac+3*ido]); + IM(t5) = IM(cc[ac]) - IM(cc[ac+3*ido]); + + RE(ch[ah]) = RE(cc[ac-ido]) + RE(t2) + RE(t3); + IM(ch[ah]) = IM(cc[ac-ido]) + IM(t2) + IM(t3); + + RE(c2) = RE(cc[ac-ido]) + MUL_F(RE(t2),tr11) + MUL_F(RE(t3),tr12); + IM(c2) = IM(cc[ac-ido]) + MUL_F(IM(t2),tr11) + MUL_F(IM(t3),tr12); + RE(c3) = RE(cc[ac-ido]) + MUL_F(RE(t2),tr12) + MUL_F(RE(t3),tr11); + IM(c3) = IM(cc[ac-ido]) + MUL_F(IM(t2),tr12) + MUL_F(IM(t3),tr11); + + ComplexMult(&RE(c5), &RE(c4), + ti11, ti12, RE(t5), RE(t4)); + ComplexMult(&IM(c5), &IM(c4), + ti11, ti12, IM(t5), IM(t4)); + + IM(d2) = IM(c2) + RE(c5); + IM(d3) = IM(c3) + RE(c4); + RE(d4) = RE(c3) + IM(c4); + RE(d5) = RE(c2) + IM(c5); + RE(d2) = RE(c2) - IM(c5); + IM(d5) = IM(c2) - RE(c5); + RE(d3) = RE(c3) - IM(c4); + IM(d4) = IM(c3) - RE(c4); + +#if 1 + ComplexMult(&IM(ch[ah+l1*ido]), &RE(ch[ah+l1*ido]), + IM(d2), RE(d2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&IM(ch[ah+2*l1*ido]), &RE(ch[ah+2*l1*ido]), + IM(d3), RE(d3), RE(wa2[i]), IM(wa2[i])); + ComplexMult(&IM(ch[ah+3*l1*ido]), &RE(ch[ah+3*l1*ido]), + IM(d4), RE(d4), RE(wa3[i]), IM(wa3[i])); + ComplexMult(&IM(ch[ah+4*l1*ido]), &RE(ch[ah+4*l1*ido]), + IM(d5), RE(d5), RE(wa4[i]), IM(wa4[i])); +#else + ComplexMult(&RE(ch[ah+l1*ido]), &IM(ch[ah+l1*ido]), + RE(d2), IM(d2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&RE(ch[ah+2*l1*ido]), &IM(ch[ah+2*l1*ido]), + RE(d3), IM(d3), RE(wa2[i]), IM(wa2[i])); + ComplexMult(&RE(ch[ah+3*l1*ido]), &IM(ch[ah+3*l1*ido]), + RE(d4), IM(d4), RE(wa3[i]), IM(wa3[i])); + ComplexMult(&RE(ch[ah+4*l1*ido]), &IM(ch[ah+4*l1*ido]), + RE(d5), IM(d5), RE(wa4[i]), IM(wa4[i])); +#endif + } + } + } else { + for (k = 0; k < l1; k++) + { + for (i = 0; i < ido; i++) + { + ac = i + (k*5 + 1) * ido; + ah = i + k * ido; + + RE(t2) = RE(cc[ac]) + RE(cc[ac+3*ido]); + IM(t2) = IM(cc[ac]) + IM(cc[ac+3*ido]); + RE(t3) = RE(cc[ac+ido]) + RE(cc[ac+2*ido]); + IM(t3) = IM(cc[ac+ido]) + IM(cc[ac+2*ido]); + RE(t4) = RE(cc[ac+ido]) - RE(cc[ac+2*ido]); + IM(t4) = IM(cc[ac+ido]) - IM(cc[ac+2*ido]); + RE(t5) = RE(cc[ac]) - RE(cc[ac+3*ido]); + IM(t5) = IM(cc[ac]) - IM(cc[ac+3*ido]); + + RE(ch[ah]) = RE(cc[ac-ido]) + RE(t2) + RE(t3); + IM(ch[ah]) = IM(cc[ac-ido]) + IM(t2) + IM(t3); + + RE(c2) = RE(cc[ac-ido]) + MUL_F(RE(t2),tr11) + MUL_F(RE(t3),tr12); + IM(c2) = IM(cc[ac-ido]) + MUL_F(IM(t2),tr11) + MUL_F(IM(t3),tr12); + RE(c3) = RE(cc[ac-ido]) + MUL_F(RE(t2),tr12) + MUL_F(RE(t3),tr11); + IM(c3) = IM(cc[ac-ido]) + MUL_F(IM(t2),tr12) + MUL_F(IM(t3),tr11); + + ComplexMult(&RE(c4), &RE(c5), + ti12, ti11, RE(t5), RE(t4)); + ComplexMult(&IM(c4), &IM(c5), + ti12, ti11, IM(t5), IM(t4)); + + IM(d2) = IM(c2) - RE(c5); + IM(d3) = IM(c3) - RE(c4); + RE(d4) = RE(c3) - IM(c4); + RE(d5) = RE(c2) - IM(c5); + RE(d2) = RE(c2) + IM(c5); + IM(d5) = IM(c2) + RE(c5); + RE(d3) = RE(c3) + IM(c4); + IM(d4) = IM(c3) + RE(c4); + +#if 1 + ComplexMult(&RE(ch[ah+l1*ido]), &IM(ch[ah+l1*ido]), + RE(d2), IM(d2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&RE(ch[ah+2*l1*ido]), &IM(ch[ah+2*l1*ido]), + RE(d3), IM(d3), RE(wa2[i]), IM(wa2[i])); + ComplexMult(&RE(ch[ah+3*l1*ido]), &IM(ch[ah+3*l1*ido]), + RE(d4), IM(d4), RE(wa3[i]), IM(wa3[i])); + ComplexMult(&RE(ch[ah+4*l1*ido]), &IM(ch[ah+4*l1*ido]), + RE(d5), IM(d5), RE(wa4[i]), IM(wa4[i])); +#else + ComplexMult(&IM(ch[ah+l1*ido]), &RE(ch[ah+l1*ido]), + IM(d2), RE(d2), RE(wa1[i]), IM(wa1[i])); + ComplexMult(&IM(ch[ah+2*l1*ido]), &RE(ch[ah+2*l1*ido]), + IM(d3), RE(d3), RE(wa2[i]), IM(wa2[i])); + ComplexMult(&IM(ch[ah+3*l1*ido]), &RE(ch[ah+3*l1*ido]), + IM(d4), RE(d4), RE(wa3[i]), IM(wa3[i])); + ComplexMult(&IM(ch[ah+4*l1*ido]), &RE(ch[ah+4*l1*ido]), + IM(d5), RE(d5), RE(wa4[i]), IM(wa4[i])); +#endif + } + } + } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + */ +#endif + } +} + + +/*---------------------------------------------------------------------- + cfftf1, cfftf, cfftb, cffti1, cffti. Complex FFTs. + ----------------------------------------------------------------------*/ + +static INLINE void cfftf1pos(uint16_t n, complex_t *c, complex_t *ch, + const uint16_t *ifac, const complex_t *wa, + const int8_t isign) +{ + uint16_t i; + uint16_t k1, l1, l2; + uint16_t na, nf, ip, iw, ix2, ix3, ix4, ido; + + nf = ifac[1]; + na = 0; + l1 = 1; + iw = 0; + + for (k1 = 2; k1 <= nf+1; k1++) + { + ip = ifac[k1]; + l2 = ip*l1; + ido = n / l2; + + switch (ip) + { + case 4: + ix2 = iw + ido; + ix3 = ix2 + ido; + + if (na == 0) + passf4pos((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)c, ch, &wa[iw], &wa[ix2], &wa[ix3]); + else + passf4pos((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)ch, c, &wa[iw], &wa[ix2], &wa[ix3]); + + na = 1 - na; + break; + case 2: + if (na == 0) + passf2pos((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)c, ch, &wa[iw]); + else + passf2pos((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)ch, c, &wa[iw]); + + na = 1 - na; + break; + case 3: + ix2 = iw + ido; + + if (na == 0) + passf3((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)c, ch, &wa[iw], &wa[ix2], isign); + else + passf3((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)ch, c, &wa[iw], &wa[ix2], isign); + + na = 1 - na; + break; + case 5: + ix2 = iw + ido; + ix3 = ix2 + ido; + ix4 = ix3 + ido; + + if (na == 0) + passf5((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)c, ch, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], isign); + else + passf5((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)ch, c, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], isign); + + na = 1 - na; + break; + } + + l1 = l2; + iw += (ip-1) * ido; + } + + if (na == 0) + return; + + for (i = 0; i < n; i++) + { + RE(c[i]) = RE(ch[i]); + IM(c[i]) = IM(ch[i]); + } +} + +static INLINE void cfftf1neg(uint16_t n, complex_t *c, complex_t *ch, + const uint16_t *ifac, const complex_t *wa, + const int8_t isign) +{ + uint16_t i; + uint16_t k1, l1, l2; + uint16_t na, nf, ip, iw, ix2, ix3, ix4, ido; + + nf = ifac[1]; + na = 0; + l1 = 1; + iw = 0; + + for (k1 = 2; k1 <= nf+1; k1++) + { + ip = ifac[k1]; + l2 = ip*l1; + ido = n / l2; + + switch (ip) + { + case 4: + ix2 = iw + ido; + ix3 = ix2 + ido; + + if (na == 0) + passf4neg((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)c, ch, &wa[iw], &wa[ix2], &wa[ix3]); + else + passf4neg((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)ch, c, &wa[iw], &wa[ix2], &wa[ix3]); + + na = 1 - na; + break; + case 2: + if (na == 0) + passf2neg((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)c, ch, &wa[iw]); + else + passf2neg((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)ch, c, &wa[iw]); + + na = 1 - na; + break; + case 3: + ix2 = iw + ido; + + if (na == 0) + passf3((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)c, ch, &wa[iw], &wa[ix2], isign); + else + passf3((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)ch, c, &wa[iw], &wa[ix2], isign); + + na = 1 - na; + break; + case 5: + ix2 = iw + ido; + ix3 = ix2 + ido; + ix4 = ix3 + ido; + + if (na == 0) + passf5((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)c, ch, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], isign); + else + passf5((const uint16_t)ido, (const uint16_t)l1, (const complex_t*)ch, c, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], isign); + + na = 1 - na; + break; + } + + l1 = l2; + iw += (ip-1) * ido; + } + + if (na == 0) + return; + + for (i = 0; i < n; i++) + { + RE(c[i]) = RE(ch[i]); + IM(c[i]) = IM(ch[i]); + } +} + +void cfftf(cfft_info *cfft, complex_t *c) +{ + cfftf1neg(cfft->n, c, cfft->work, (const uint16_t*)cfft->ifac, (const complex_t*)cfft->tab, -1); +} + +void cfftb(cfft_info *cfft, complex_t *c) +{ + cfftf1pos(cfft->n, c, cfft->work, (const uint16_t*)cfft->ifac, (const complex_t*)cfft->tab, +1); +} + +static void cffti1(uint16_t n, complex_t *wa, uint16_t *ifac) +{ + static uint16_t ntryh[4] = {3, 4, 2, 5}; +#ifndef FIXED_POINT + real_t arg, argh, argld, fi; + uint16_t ido, ipm; + uint16_t i1, k1, l1, l2; + uint16_t ld, ii, ip; +#endif + uint16_t ntry = 0, i, j; + uint16_t ib; + uint16_t nf, nl, nq, nr; + + nl = n; + nf = 0; + j = 0; + +startloop: + j++; + + if (j <= 4) + ntry = ntryh[j-1]; + else + ntry += 2; + + do + { + nq = nl / ntry; + nr = nl - ntry*nq; + + if (nr != 0) + goto startloop; + + nf++; + ifac[nf+1] = ntry; + nl = nq; + + if (ntry == 2 && nf != 1) + { + for (i = 2; i <= nf; i++) + { + ib = nf - i + 2; + ifac[ib+1] = ifac[ib]; + } + ifac[2] = 2; + } + } while (nl != 1); + + ifac[0] = n; + ifac[1] = nf; + +#ifndef FIXED_POINT + argh = (real_t)2.0*(real_t)M_PI / (real_t)n; + i = 0; + l1 = 1; + + for (k1 = 1; k1 <= nf; k1++) + { + ip = ifac[k1+1]; + ld = 0; + l2 = l1*ip; + ido = n / l2; + ipm = ip - 1; + + for (j = 0; j < ipm; j++) + { + i1 = i; + RE(wa[i]) = 1.0; + IM(wa[i]) = 0.0; + ld += l1; + fi = 0; + argld = ld*argh; + + for (ii = 0; ii < ido; ii++) + { + i++; + fi++; + arg = fi * argld; + RE(wa[i]) = (real_t)cos(arg); +#if 1 + IM(wa[i]) = (real_t)sin(arg); +#else + IM(wa[i]) = (real_t)-sin(arg); +#endif + } + + if (ip > 5) + { + RE(wa[i1]) = RE(wa[i]); + IM(wa[i1]) = IM(wa[i]); + } + } + l1 = l2; + } +#else // FIXED_POINT + (void)wa; /* whoa! does it work for FIXED_POINT? */ +#endif +} + +cfft_info *cffti(uint16_t n) +{ + cfft_info *cfft = (cfft_info*)faad_malloc(sizeof(cfft_info)); + + cfft->n = n; + cfft->work = (complex_t*)faad_malloc(n*sizeof(complex_t)); + +#ifndef FIXED_POINT + cfft->tab = (complex_t*)faad_malloc(n*sizeof(complex_t)); + + cffti1(n, cfft->tab, cfft->ifac); +#else + cffti1(n, NULL, cfft->ifac); + + switch (n) + { + case 64: cfft->tab = (complex_t*)cfft_tab_64; break; + case 512: cfft->tab = (complex_t*)cfft_tab_512; break; +#ifdef LD_DEC + case 256: cfft->tab = (complex_t*)cfft_tab_256; break; +#endif + +#ifdef ALLOW_SMALL_FRAMELENGTH + case 60: cfft->tab = (complex_t*)cfft_tab_60; break; + case 480: cfft->tab = (complex_t*)cfft_tab_480; break; +#ifdef LD_DEC + case 240: cfft->tab = (complex_t*)cfft_tab_240; break; +#endif +#endif + case 128: cfft->tab = (complex_t*)cfft_tab_128; break; + } +#endif + + return cfft; +} + +void cfftu(cfft_info *cfft) +{ + if (cfft->work) faad_free(cfft->work); +#ifndef FIXED_POINT + if (cfft->tab) faad_free(cfft->tab); +#endif + + if (cfft) faad_free(cfft); +} + diff --git a/third_party/faad_hdc/upstream/libfaad/cfft.h b/third_party/faad_hdc/upstream/libfaad/cfft.h new file mode 100644 index 000000000..312def9f2 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/cfft.h @@ -0,0 +1,56 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: cfft.h,v 1.24 2007/11/01 12:33:29 menno Exp $ +**/ + +#ifndef __CFFT_H__ +#define __CFFT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + uint16_t n; + uint16_t ifac[15]; + complex_t *work; + complex_t *tab; +} cfft_info; + + +void cfftf(cfft_info *cfft, complex_t *c); +void cfftb(cfft_info *cfft, complex_t *c); +cfft_info *cffti(uint16_t n); +void cfftu(cfft_info *cfft); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/cfft_tab.h b/third_party/faad_hdc/upstream/libfaad/cfft_tab.h new file mode 100644 index 000000000..1a0a342cf --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/cfft_tab.h @@ -0,0 +1,1823 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: cfft_tab.h,v 1.21 2007/11/01 12:33:29 menno Exp $ +**/ + +#ifndef __CFFT_TAB_H__ +#define __CFFT_TAB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef FIXED_POINT + +ALIGN static const complex_t cfft_tab_512[] = +{ + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.999924719333649), FRAC_CONST(0.012271538376808) }, + { FRAC_CONST(0.999698817729950), FRAC_CONST(0.024541229009628) }, + { FRAC_CONST(0.999322354793549), FRAC_CONST(0.036807224154472) }, + { FRAC_CONST(0.998795449733734), FRAC_CONST(0.049067676067352) }, + { FRAC_CONST(0.998118102550507), FRAC_CONST(0.061320740729570) }, + { FRAC_CONST(0.997290432453156), FRAC_CONST(0.073564566671848) }, + { FRAC_CONST(0.996312618255615), FRAC_CONST(0.085797317326069) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.993906974792480), FRAC_CONST(0.110222205519676) }, + { FRAC_CONST(0.992479562759399), FRAC_CONST(0.122410677373409) }, + { FRAC_CONST(0.990902662277222), FRAC_CONST(0.134580716490746) }, + { FRAC_CONST(0.989176511764526), FRAC_CONST(0.146730467677116) }, + { FRAC_CONST(0.987301409244537), FRAC_CONST(0.158858150243759) }, + { FRAC_CONST(0.985277652740479), FRAC_CONST(0.170961901545525) }, + { FRAC_CONST(0.983105480670929), FRAC_CONST(0.183039888739586) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.978317379951477), FRAC_CONST(0.207111388444901) }, + { FRAC_CONST(0.975702106952667), FRAC_CONST(0.219101235270500) }, + { FRAC_CONST(0.972939968109131), FRAC_CONST(0.231058120727539) }, + { FRAC_CONST(0.970031261444092), FRAC_CONST(0.242980197072029) }, + { FRAC_CONST(0.966976463794708), FRAC_CONST(0.254865676164627) }, + { FRAC_CONST(0.963776051998138), FRAC_CONST(0.266712784767151) }, + { FRAC_CONST(0.960430502891541), FRAC_CONST(0.278519690036774) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.953306019306183), FRAC_CONST(0.302005946636200) }, + { FRAC_CONST(0.949528157711029), FRAC_CONST(0.313681751489639) }, + { FRAC_CONST(0.945607304573059), FRAC_CONST(0.325310319662094) }, + { FRAC_CONST(0.941544055938721), FRAC_CONST(0.336889863014221) }, + { FRAC_CONST(0.937339007854462), FRAC_CONST(0.348418682813644) }, + { FRAC_CONST(0.932992815971375), FRAC_CONST(0.359895050525665) }, + { FRAC_CONST(0.928506076335907), FRAC_CONST(0.371317207813263) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.919113874435425), FRAC_CONST(0.393992066383362) }, + { FRAC_CONST(0.914209723472595), FRAC_CONST(0.405241340398788) }, + { FRAC_CONST(0.909167945384979), FRAC_CONST(0.416429579257965) }, + { FRAC_CONST(0.903989315032959), FRAC_CONST(0.427555084228516) }, + { FRAC_CONST(0.898674488067627), FRAC_CONST(0.438616245985031) }, + { FRAC_CONST(0.893224298954010), FRAC_CONST(0.449611335992813) }, + { FRAC_CONST(0.887639641761780), FRAC_CONST(0.460538715124130) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.876070082187653), FRAC_CONST(0.482183754444122) }, + { FRAC_CONST(0.870086967945099), FRAC_CONST(0.492898225784302) }, + { FRAC_CONST(0.863972842693329), FRAC_CONST(0.503538370132446) }, + { FRAC_CONST(0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(0.851355195045471), FRAC_CONST(0.524589717388153) }, + { FRAC_CONST(0.844853579998016), FRAC_CONST(0.534997642040253) }, + { FRAC_CONST(0.838224709033966), FRAC_CONST(0.545324981212616) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.824589252471924), FRAC_CONST(0.565731823444366) }, + { FRAC_CONST(0.817584812641144), FRAC_CONST(0.575808227062225) }, + { FRAC_CONST(0.810457170009613), FRAC_CONST(0.585797905921936) }, + { FRAC_CONST(0.803207516670227), FRAC_CONST(0.595699310302734) }, + { FRAC_CONST(0.795836925506592), FRAC_CONST(0.605511009693146) }, + { FRAC_CONST(0.788346409797668), FRAC_CONST(0.615231633186340) }, + { FRAC_CONST(0.780737221240997), FRAC_CONST(0.624859511852264) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.765167236328125), FRAC_CONST(0.643831551074982) }, + { FRAC_CONST(0.757208824157715), FRAC_CONST(0.653172850608826) }, + { FRAC_CONST(0.749136388301849), FRAC_CONST(0.662415802478790) }, + { FRAC_CONST(0.740951120853424), FRAC_CONST(0.671558976173401) }, + { FRAC_CONST(0.732654273509979), FRAC_CONST(0.680601000785828) }, + { FRAC_CONST(0.724247097969055), FRAC_CONST(0.689540565013886) }, + { FRAC_CONST(0.715730786323547), FRAC_CONST(0.698376297950745) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.698376238346100), FRAC_CONST(0.715730845928192) }, + { FRAC_CONST(0.689540505409241), FRAC_CONST(0.724247097969055) }, + { FRAC_CONST(0.680601000785828), FRAC_CONST(0.732654273509979) }, + { FRAC_CONST(0.671558916568756), FRAC_CONST(0.740951180458069) }, + { FRAC_CONST(0.662415742874146), FRAC_CONST(0.749136388301849) }, + { FRAC_CONST(0.653172791004181), FRAC_CONST(0.757208883762360) }, + { FRAC_CONST(0.643831551074982), FRAC_CONST(0.765167295932770) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.624859452247620), FRAC_CONST(0.780737280845642) }, + { FRAC_CONST(0.615231573581696), FRAC_CONST(0.788346409797668) }, + { FRAC_CONST(0.605511009693146), FRAC_CONST(0.795836925506592) }, + { FRAC_CONST(0.595699310302734), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(0.585797846317291), FRAC_CONST(0.810457170009613) }, + { FRAC_CONST(0.575808167457581), FRAC_CONST(0.817584812641144) }, + { FRAC_CONST(0.565731823444366), FRAC_CONST(0.824589312076569) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.545324981212616), FRAC_CONST(0.838224709033966) }, + { FRAC_CONST(0.534997642040253), FRAC_CONST(0.844853579998016) }, + { FRAC_CONST(0.524589657783508), FRAC_CONST(0.851355195045471) }, + { FRAC_CONST(0.514102697372437), FRAC_CONST(0.857728660106659) }, + { FRAC_CONST(0.503538429737091), FRAC_CONST(0.863972842693329) }, + { FRAC_CONST(0.492898195981979), FRAC_CONST(0.870086967945099) }, + { FRAC_CONST(0.482183724641800), FRAC_CONST(0.876070141792297) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.460538715124130), FRAC_CONST(0.887639641761780) }, + { FRAC_CONST(0.449611306190491), FRAC_CONST(0.893224298954010) }, + { FRAC_CONST(0.438616186380386), FRAC_CONST(0.898674488067627) }, + { FRAC_CONST(0.427555114030838), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(0.416429549455643), FRAC_CONST(0.909168004989624) }, + { FRAC_CONST(0.405241280794144), FRAC_CONST(0.914209783077240) }, + { FRAC_CONST(0.393991947174072), FRAC_CONST(0.919113874435425) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.371317148208618), FRAC_CONST(0.928506076335907) }, + { FRAC_CONST(0.359894961118698), FRAC_CONST(0.932992815971375) }, + { FRAC_CONST(0.348418682813644), FRAC_CONST(0.937339007854462) }, + { FRAC_CONST(0.336889833211899), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(0.325310230255127), FRAC_CONST(0.945607364177704) }, + { FRAC_CONST(0.313681662082672), FRAC_CONST(0.949528217315674) }, + { FRAC_CONST(0.302005946636200), FRAC_CONST(0.953306019306183) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.278519600629807), FRAC_CONST(0.960430562496185) }, + { FRAC_CONST(0.266712754964828), FRAC_CONST(0.963776051998138) }, + { FRAC_CONST(0.254865646362305), FRAC_CONST(0.966976463794708) }, + { FRAC_CONST(0.242980122566223), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(0.231058135628700), FRAC_CONST(0.972939968109131) }, + { FRAC_CONST(0.219101220369339), FRAC_CONST(0.975702106952667) }, + { FRAC_CONST(0.207111328840256), FRAC_CONST(0.978317379951477) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.183039888739586), FRAC_CONST(0.983105480670929) }, + { FRAC_CONST(0.170961856842041), FRAC_CONST(0.985277652740479) }, + { FRAC_CONST(0.158858075737953), FRAC_CONST(0.987301409244537) }, + { FRAC_CONST(0.146730497479439), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(0.134580686688423), FRAC_CONST(0.990902662277222) }, + { FRAC_CONST(0.122410625219345), FRAC_CONST(0.992479562759399) }, + { FRAC_CONST(0.110222116112709), FRAC_CONST(0.993906974792480) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(0.085797272622585), FRAC_CONST(0.996312618255615) }, + { FRAC_CONST(0.073564492166042), FRAC_CONST(0.997290432453156) }, + { FRAC_CONST(0.061320748180151), FRAC_CONST(0.998118102550507) }, + { FRAC_CONST(0.049067649990320), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(0.036807164549828), FRAC_CONST(0.999322414398193) }, + { FRAC_CONST(0.024541135877371), FRAC_CONST(0.999698817729950) }, + { FRAC_CONST(0.012271529063582), FRAC_CONST(0.999924719333649) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.012271616607904), FRAC_CONST(0.999924719333649) }, + { FRAC_CONST(-0.024541223421693), FRAC_CONST(0.999698817729950) }, + { FRAC_CONST(-0.036807250231504), FRAC_CONST(0.999322354793549) }, + { FRAC_CONST(-0.049067739397287), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(-0.061320833861828), FRAC_CONST(0.998118102550507) }, + { FRAC_CONST(-0.073564574122429), FRAC_CONST(0.997290432453156) }, + { FRAC_CONST(-0.085797362029552), FRAC_CONST(0.996312618255615) }, + { FRAC_CONST(-0.098017223179340), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(-0.110222205519676), FRAC_CONST(0.993906974792480) }, + { FRAC_CONST(-0.122410707175732), FRAC_CONST(0.992479503154755) }, + { FRAC_CONST(-0.134580776095390), FRAC_CONST(0.990902602672577) }, + { FRAC_CONST(-0.146730571985245), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(-0.158858165144920), FRAC_CONST(0.987301409244537) }, + { FRAC_CONST(-0.170961946249008), FRAC_CONST(0.985277652740479) }, + { FRAC_CONST(-0.183039978146553), FRAC_CONST(0.983105480670929) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.207111418247223), FRAC_CONST(0.978317379951477) }, + { FRAC_CONST(-0.219101309776306), FRAC_CONST(0.975702106952667) }, + { FRAC_CONST(-0.231058210134506), FRAC_CONST(0.972939908504486) }, + { FRAC_CONST(-0.242980197072029), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(-0.254865705966949), FRAC_CONST(0.966976463794708) }, + { FRAC_CONST(-0.266712844371796), FRAC_CONST(0.963776051998138) }, + { FRAC_CONST(-0.278519690036774), FRAC_CONST(0.960430502891541) }, + { FRAC_CONST(-0.290284723043442), FRAC_CONST(0.956940293312073) }, + { FRAC_CONST(-0.302006036043167), FRAC_CONST(0.953306019306183) }, + { FRAC_CONST(-0.313681721687317), FRAC_CONST(0.949528157711029) }, + { FRAC_CONST(-0.325310319662094), FRAC_CONST(0.945607304573059) }, + { FRAC_CONST(-0.336889922618866), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(-0.348418772220612), FRAC_CONST(0.937338948249817) }, + { FRAC_CONST(-0.359895050525665), FRAC_CONST(0.932992815971375) }, + { FRAC_CONST(-0.371317237615585), FRAC_CONST(0.928506076335907) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.393992036581039), FRAC_CONST(0.919113874435425) }, + { FRAC_CONST(-0.405241340398788), FRAC_CONST(0.914209723472595) }, + { FRAC_CONST(-0.416429519653320), FRAC_CONST(0.909168004989624) }, + { FRAC_CONST(-0.427555084228516), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(-0.438616245985031), FRAC_CONST(0.898674428462982) }, + { FRAC_CONST(-0.449611365795136), FRAC_CONST(0.893224298954010) }, + { FRAC_CONST(-0.460538804531097), FRAC_CONST(0.887639582157135) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.482183903455734), FRAC_CONST(0.876070022583008) }, + { FRAC_CONST(-0.492898166179657), FRAC_CONST(0.870087027549744) }, + { FRAC_CONST(-0.503538370132446), FRAC_CONST(0.863972842693329) }, + { FRAC_CONST(-0.514102756977081), FRAC_CONST(0.857728600502014) }, + { FRAC_CONST(-0.524589717388153), FRAC_CONST(0.851355135440826) }, + { FRAC_CONST(-0.534997701644897), FRAC_CONST(0.844853520393372) }, + { FRAC_CONST(-0.545325100421906), FRAC_CONST(0.838224649429321) }, + { FRAC_CONST(-0.555570363998413), FRAC_CONST(0.831469535827637) }, + { FRAC_CONST(-0.565731763839722), FRAC_CONST(0.824589312076569) }, + { FRAC_CONST(-0.575808167457581), FRAC_CONST(0.817584812641144) }, + { FRAC_CONST(-0.585797905921936), FRAC_CONST(0.810457170009613) }, + { FRAC_CONST(-0.595699369907379), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(-0.605511128902435), FRAC_CONST(0.795836865901947) }, + { FRAC_CONST(-0.615231692790985), FRAC_CONST(0.788346350193024) }, + { FRAC_CONST(-0.624859631061554), FRAC_CONST(0.780737102031708) }, + { FRAC_CONST(-0.634393274784088), FRAC_CONST(0.773010492324829) }, + { FRAC_CONST(-0.643831551074982), FRAC_CONST(0.765167236328125) }, + { FRAC_CONST(-0.653172850608826), FRAC_CONST(0.757208824157715) }, + { FRAC_CONST(-0.662415802478790), FRAC_CONST(0.749136328697205) }, + { FRAC_CONST(-0.671559035778046), FRAC_CONST(0.740951061248779) }, + { FRAC_CONST(-0.680601119995117), FRAC_CONST(0.732654154300690) }, + { FRAC_CONST(-0.689540684223175), FRAC_CONST(0.724246978759766) }, + { FRAC_CONST(-0.698376238346100), FRAC_CONST(0.715730845928192) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.715730845928192), FRAC_CONST(0.698376238346100) }, + { FRAC_CONST(-0.724247157573700), FRAC_CONST(0.689540505409241) }, + { FRAC_CONST(-0.732654333114624), FRAC_CONST(0.680600941181183) }, + { FRAC_CONST(-0.740951240062714), FRAC_CONST(0.671558856964111) }, + { FRAC_CONST(-0.749136507511139), FRAC_CONST(0.662415623664856) }, + { FRAC_CONST(-0.757208824157715), FRAC_CONST(0.653172850608826) }, + { FRAC_CONST(-0.765167295932770), FRAC_CONST(0.643831551074982) }, + { FRAC_CONST(-0.773010492324829), FRAC_CONST(0.634393274784088) }, + { FRAC_CONST(-0.780737280845642), FRAC_CONST(0.624859452247620) }, + { FRAC_CONST(-0.788346469402313), FRAC_CONST(0.615231513977051) }, + { FRAC_CONST(-0.795836985111237), FRAC_CONST(0.605510950088501) }, + { FRAC_CONST(-0.803207635879517), FRAC_CONST(0.595699131488800) }, + { FRAC_CONST(-0.810457170009613), FRAC_CONST(0.585797846317291) }, + { FRAC_CONST(-0.817584812641144), FRAC_CONST(0.575808167457581) }, + { FRAC_CONST(-0.824589312076569), FRAC_CONST(0.565731763839722) }, + { FRAC_CONST(-0.831469655036926), FRAC_CONST(0.555570185184479) }, + { FRAC_CONST(-0.838224768638611), FRAC_CONST(0.545324862003326) }, + { FRAC_CONST(-0.844853639602661), FRAC_CONST(0.534997463226318) }, + { FRAC_CONST(-0.851355314254761), FRAC_CONST(0.524589538574219) }, + { FRAC_CONST(-0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(-0.863972842693329), FRAC_CONST(0.503538370132446) }, + { FRAC_CONST(-0.870087027549744), FRAC_CONST(0.492898136377335) }, + { FRAC_CONST(-0.876070141792297), FRAC_CONST(0.482183694839478) }, + { FRAC_CONST(-0.881921350955963), FRAC_CONST(0.471396625041962) }, + { FRAC_CONST(-0.887639701366425), FRAC_CONST(0.460538566112518) }, + { FRAC_CONST(-0.893224298954010), FRAC_CONST(0.449611365795136) }, + { FRAC_CONST(-0.898674488067627), FRAC_CONST(0.438616245985031) }, + { FRAC_CONST(-0.903989315032959), FRAC_CONST(0.427555054426193) }, + { FRAC_CONST(-0.909168004989624), FRAC_CONST(0.416429489850998) }, + { FRAC_CONST(-0.914209783077240), FRAC_CONST(0.405241221189499) }, + { FRAC_CONST(-0.919113874435425), FRAC_CONST(0.393991917371750) }, + { FRAC_CONST(-0.923879623413086), FRAC_CONST(0.382683277130127) }, + { FRAC_CONST(-0.928506076335907), FRAC_CONST(0.371317237615585) }, + { FRAC_CONST(-0.932992815971375), FRAC_CONST(0.359895050525665) }, + { FRAC_CONST(-0.937339007854462), FRAC_CONST(0.348418653011322) }, + { FRAC_CONST(-0.941544115543365), FRAC_CONST(0.336889803409576) }, + { FRAC_CONST(-0.945607364177704), FRAC_CONST(0.325310200452805) }, + { FRAC_CONST(-0.949528217315674), FRAC_CONST(0.313681602478027) }, + { FRAC_CONST(-0.953306078910828), FRAC_CONST(0.302005797624588) }, + { FRAC_CONST(-0.956940352916718), FRAC_CONST(0.290284723043442) }, + { FRAC_CONST(-0.960430502891541), FRAC_CONST(0.278519690036774) }, + { FRAC_CONST(-0.963776051998138), FRAC_CONST(0.266712725162506) }, + { FRAC_CONST(-0.966976463794708), FRAC_CONST(0.254865586757660) }, + { FRAC_CONST(-0.970031261444092), FRAC_CONST(0.242980077862740) }, + { FRAC_CONST(-0.972939968109131), FRAC_CONST(0.231057971715927) }, + { FRAC_CONST(-0.975702166557312), FRAC_CONST(0.219101071357727) }, + { FRAC_CONST(-0.978317379951477), FRAC_CONST(0.207111403346062) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.983105480670929), FRAC_CONST(0.183039844036102) }, + { FRAC_CONST(-0.985277652740479), FRAC_CONST(0.170961812138557) }, + { FRAC_CONST(-0.987301409244537), FRAC_CONST(0.158858031034470) }, + { FRAC_CONST(-0.989176511764526), FRAC_CONST(0.146730333566666) }, + { FRAC_CONST(-0.990902662277222), FRAC_CONST(0.134580522775650) }, + { FRAC_CONST(-0.992479503154755), FRAC_CONST(0.122410699725151) }, + { FRAC_CONST(-0.993906974792480), FRAC_CONST(0.110222198069096) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(0.098017096519470) }, + { FRAC_CONST(-0.996312618255615), FRAC_CONST(0.085797227919102) }, + { FRAC_CONST(-0.997290492057800), FRAC_CONST(0.073564447462559) }, + { FRAC_CONST(-0.998118102550507), FRAC_CONST(0.061320584267378) }, + { FRAC_CONST(-0.998795449733734), FRAC_CONST(0.049067486077547) }, + { FRAC_CONST(-0.999322354793549), FRAC_CONST(0.036807239055634) }, + { FRAC_CONST(-0.999698817729950), FRAC_CONST(0.024541210383177) }, + { FRAC_CONST(-0.999924719333649), FRAC_CONST(0.012271485291421) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.999698817729950), FRAC_CONST(0.024541229009628) }, + { FRAC_CONST(0.998795449733734), FRAC_CONST(0.049067676067352) }, + { FRAC_CONST(0.997290432453156), FRAC_CONST(0.073564566671848) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.992479562759399), FRAC_CONST(0.122410677373409) }, + { FRAC_CONST(0.989176511764526), FRAC_CONST(0.146730467677116) }, + { FRAC_CONST(0.985277652740479), FRAC_CONST(0.170961901545525) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.975702106952667), FRAC_CONST(0.219101235270500) }, + { FRAC_CONST(0.970031261444092), FRAC_CONST(0.242980197072029) }, + { FRAC_CONST(0.963776051998138), FRAC_CONST(0.266712784767151) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.949528157711029), FRAC_CONST(0.313681751489639) }, + { FRAC_CONST(0.941544055938721), FRAC_CONST(0.336889863014221) }, + { FRAC_CONST(0.932992815971375), FRAC_CONST(0.359895050525665) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.914209723472595), FRAC_CONST(0.405241340398788) }, + { FRAC_CONST(0.903989315032959), FRAC_CONST(0.427555084228516) }, + { FRAC_CONST(0.893224298954010), FRAC_CONST(0.449611335992813) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.870086967945099), FRAC_CONST(0.492898225784302) }, + { FRAC_CONST(0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(0.844853579998016), FRAC_CONST(0.534997642040253) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.817584812641144), FRAC_CONST(0.575808227062225) }, + { FRAC_CONST(0.803207516670227), FRAC_CONST(0.595699310302734) }, + { FRAC_CONST(0.788346409797668), FRAC_CONST(0.615231633186340) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.757208824157715), FRAC_CONST(0.653172850608826) }, + { FRAC_CONST(0.740951120853424), FRAC_CONST(0.671558976173401) }, + { FRAC_CONST(0.724247097969055), FRAC_CONST(0.689540565013886) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.689540505409241), FRAC_CONST(0.724247097969055) }, + { FRAC_CONST(0.671558916568756), FRAC_CONST(0.740951180458069) }, + { FRAC_CONST(0.653172791004181), FRAC_CONST(0.757208883762360) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.615231573581696), FRAC_CONST(0.788346409797668) }, + { FRAC_CONST(0.595699310302734), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(0.575808167457581), FRAC_CONST(0.817584812641144) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.534997642040253), FRAC_CONST(0.844853579998016) }, + { FRAC_CONST(0.514102697372437), FRAC_CONST(0.857728660106659) }, + { FRAC_CONST(0.492898195981979), FRAC_CONST(0.870086967945099) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.449611306190491), FRAC_CONST(0.893224298954010) }, + { FRAC_CONST(0.427555114030838), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(0.405241280794144), FRAC_CONST(0.914209783077240) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.359894961118698), FRAC_CONST(0.932992815971375) }, + { FRAC_CONST(0.336889833211899), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(0.313681662082672), FRAC_CONST(0.949528217315674) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.266712754964828), FRAC_CONST(0.963776051998138) }, + { FRAC_CONST(0.242980122566223), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(0.219101220369339), FRAC_CONST(0.975702106952667) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.170961856842041), FRAC_CONST(0.985277652740479) }, + { FRAC_CONST(0.146730497479439), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(0.122410625219345), FRAC_CONST(0.992479562759399) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(0.073564492166042), FRAC_CONST(0.997290432453156) }, + { FRAC_CONST(0.049067649990320), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(0.024541135877371), FRAC_CONST(0.999698817729950) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.998795449733734), FRAC_CONST(0.049067676067352) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.989176511764526), FRAC_CONST(0.146730467677116) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.970031261444092), FRAC_CONST(0.242980197072029) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.941544055938721), FRAC_CONST(0.336889863014221) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.903989315032959), FRAC_CONST(0.427555084228516) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.803207516670227), FRAC_CONST(0.595699310302734) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.740951120853424), FRAC_CONST(0.671558976173401) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.671558916568756), FRAC_CONST(0.740951180458069) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.595699310302734), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.514102697372437), FRAC_CONST(0.857728660106659) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.427555114030838), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.336889833211899), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.242980122566223), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.146730497479439), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(0.049067649990320), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.049067739397287), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(-0.098017223179340), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(-0.146730571985245), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.242980197072029), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(-0.290284723043442), FRAC_CONST(0.956940293312073) }, + { FRAC_CONST(-0.336889922618866), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.427555084228516), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.514102756977081), FRAC_CONST(0.857728600502014) }, + { FRAC_CONST(-0.555570363998413), FRAC_CONST(0.831469535827637) }, + { FRAC_CONST(-0.595699369907379), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(-0.634393274784088), FRAC_CONST(0.773010492324829) }, + { FRAC_CONST(-0.671559035778046), FRAC_CONST(0.740951061248779) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.740951240062714), FRAC_CONST(0.671558856964111) }, + { FRAC_CONST(-0.773010492324829), FRAC_CONST(0.634393274784088) }, + { FRAC_CONST(-0.803207635879517), FRAC_CONST(0.595699131488800) }, + { FRAC_CONST(-0.831469655036926), FRAC_CONST(0.555570185184479) }, + { FRAC_CONST(-0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(-0.881921350955963), FRAC_CONST(0.471396625041962) }, + { FRAC_CONST(-0.903989315032959), FRAC_CONST(0.427555054426193) }, + { FRAC_CONST(-0.923879623413086), FRAC_CONST(0.382683277130127) }, + { FRAC_CONST(-0.941544115543365), FRAC_CONST(0.336889803409576) }, + { FRAC_CONST(-0.956940352916718), FRAC_CONST(0.290284723043442) }, + { FRAC_CONST(-0.970031261444092), FRAC_CONST(0.242980077862740) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.989176511764526), FRAC_CONST(0.146730333566666) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(0.098017096519470) }, + { FRAC_CONST(-0.998795449733734), FRAC_CONST(0.049067486077547) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.997290432453156), FRAC_CONST(0.073564566671848) }, + { FRAC_CONST(0.989176511764526), FRAC_CONST(0.146730467677116) }, + { FRAC_CONST(0.975702106952667), FRAC_CONST(0.219101235270500) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.932992815971375), FRAC_CONST(0.359895050525665) }, + { FRAC_CONST(0.903989315032959), FRAC_CONST(0.427555084228516) }, + { FRAC_CONST(0.870086967945099), FRAC_CONST(0.492898225784302) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.788346469402313), FRAC_CONST(0.615231573581696) }, + { FRAC_CONST(0.740951120853424), FRAC_CONST(0.671558976173401) }, + { FRAC_CONST(0.689540505409241), FRAC_CONST(0.724247097969055) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.575808227062225), FRAC_CONST(0.817584812641144) }, + { FRAC_CONST(0.514102697372437), FRAC_CONST(0.857728660106659) }, + { FRAC_CONST(0.449611306190491), FRAC_CONST(0.893224298954010) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.313681751489639), FRAC_CONST(0.949528157711029) }, + { FRAC_CONST(0.242980241775513), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(0.170961856842041), FRAC_CONST(0.985277652740479) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(0.024541255086660), FRAC_CONST(0.999698817729950) }, + { FRAC_CONST(-0.049067739397287), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(-0.122410707175732), FRAC_CONST(0.992479503154755) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.266712725162506), FRAC_CONST(0.963776051998138) }, + { FRAC_CONST(-0.336889803409576), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(-0.405241340398788), FRAC_CONST(0.914209723472595) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.534997701644897), FRAC_CONST(0.844853520393372) }, + { FRAC_CONST(-0.595699369907379), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(-0.653172850608826), FRAC_CONST(0.757208824157715) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.757208824157715), FRAC_CONST(0.653172850608826) }, + { FRAC_CONST(-0.803207516670227), FRAC_CONST(0.595699369907379) }, + { FRAC_CONST(-0.844853520393372), FRAC_CONST(0.534997701644897) }, + { FRAC_CONST(-0.881921231746674), FRAC_CONST(0.471396833658218) }, + { FRAC_CONST(-0.914209783077240), FRAC_CONST(0.405241221189499) }, + { FRAC_CONST(-0.941544115543365), FRAC_CONST(0.336889803409576) }, + { FRAC_CONST(-0.963776051998138), FRAC_CONST(0.266712725162506) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.992479503154755), FRAC_CONST(0.122410699725151) }, + { FRAC_CONST(-0.998795449733734), FRAC_CONST(0.049067724496126) }, + { FRAC_CONST(-0.999698817729950), FRAC_CONST(-0.024541147053242) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(-0.098017267882824) }, + { FRAC_CONST(-0.985277652740479), FRAC_CONST(-0.170961990952492) }, + { FRAC_CONST(-0.970031261444092), FRAC_CONST(-0.242980241775513) }, + { FRAC_CONST(-0.949528157711029), FRAC_CONST(-0.313681781291962) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(-0.893224298954010), FRAC_CONST(-0.449611306190491) }, + { FRAC_CONST(-0.857728660106659), FRAC_CONST(-0.514102697372437) }, + { FRAC_CONST(-0.817584872245789), FRAC_CONST(-0.575808107852936) }, + { FRAC_CONST(-0.773010551929474), FRAC_CONST(-0.634393215179443) }, + { FRAC_CONST(-0.724247038364410), FRAC_CONST(-0.689540624618530) }, + { FRAC_CONST(-0.671558916568756), FRAC_CONST(-0.740951180458069) }, + { FRAC_CONST(-0.615231573581696), FRAC_CONST(-0.788346469402313) }, + { FRAC_CONST(-0.555570006370544), FRAC_CONST(-0.831469774246216) }, + { FRAC_CONST(-0.492898195981979), FRAC_CONST(-0.870086967945099) }, + { FRAC_CONST(-0.427554935216904), FRAC_CONST(-0.903989374637604) }, + { FRAC_CONST(-0.359895110130310), FRAC_CONST(-0.932992756366730) }, + { FRAC_CONST(-0.290284544229507), FRAC_CONST(-0.956940352916718) }, + { FRAC_CONST(-0.219101369380951), FRAC_CONST(-0.975702106952667) }, + { FRAC_CONST(-0.146730408072472), FRAC_CONST(-0.989176511764526) }, + { FRAC_CONST(-0.073564760386944), FRAC_CONST(-0.997290432453156) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.555570363998413), FRAC_CONST(0.831469535827637) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.831469655036926), FRAC_CONST(0.555570185184479) }, + { FRAC_CONST(-0.923879623413086), FRAC_CONST(0.382683277130127) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.881921231746674), FRAC_CONST(0.471396833658218) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(-0.098017267882824) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(-0.773010551929474), FRAC_CONST(-0.634393215179443) }, + { FRAC_CONST(-0.555570006370544), FRAC_CONST(-0.831469774246216) }, + { FRAC_CONST(-0.290284544229507), FRAC_CONST(-0.956940352916718) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.000000011924881), FRAC_CONST(-1.000000000000000) } +}; + +#ifdef ALLOW_SMALL_FRAMELENGTH +ALIGN static const complex_t cfft_tab_480[] = +{ + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.999914348125458), FRAC_CONST(0.013089596293867) }, + { FRAC_CONST(0.999657332897186), FRAC_CONST(0.026176949962974) }, + { FRAC_CONST(0.999229013919830), FRAC_CONST(0.039259817451239) }, + { FRAC_CONST(0.998629510402679), FRAC_CONST(0.052335958927870) }, + { FRAC_CONST(0.997858941555023), FRAC_CONST(0.065403133630753) }, + { FRAC_CONST(0.996917307376862), FRAC_CONST(0.078459098935127) }, + { FRAC_CONST(0.995804905891418), FRAC_CONST(0.091501623392105) }, + { FRAC_CONST(0.994521915912628), FRAC_CONST(0.104528464376926) }, + { FRAC_CONST(0.993068456649780), FRAC_CONST(0.117537401616573) }, + { FRAC_CONST(0.991444885730743), FRAC_CONST(0.130526199936867) }, + { FRAC_CONST(0.989651381969452), FRAC_CONST(0.143492624163628) }, + { FRAC_CONST(0.987688362598419), FRAC_CONST(0.156434476375580) }, + { FRAC_CONST(0.985556066036224), FRAC_CONST(0.169349506497383) }, + { FRAC_CONST(0.983254909515381), FRAC_CONST(0.182235524058342) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.978147625923157), FRAC_CONST(0.207911700010300) }, + { FRAC_CONST(0.975342333316803), FRAC_CONST(0.220697447657585) }, + { FRAC_CONST(0.972369909286499), FRAC_CONST(0.233445376157761) }, + { FRAC_CONST(0.969230890274048), FRAC_CONST(0.246153295040131) }, + { FRAC_CONST(0.965925812721252), FRAC_CONST(0.258819043636322) }, + { FRAC_CONST(0.962455213069916), FRAC_CONST(0.271440446376801) }, + { FRAC_CONST(0.958819746971130), FRAC_CONST(0.284015357494354) }, + { FRAC_CONST(0.955019950866699), FRAC_CONST(0.296541601419449) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.946930110454559), FRAC_CONST(0.321439445018768) }, + { FRAC_CONST(0.942641496658325), FRAC_CONST(0.333806872367859) }, + { FRAC_CONST(0.938191354274750), FRAC_CONST(0.346117079257965) }, + { FRAC_CONST(0.933580398559570), FRAC_CONST(0.358367949724197) }, + { FRAC_CONST(0.928809583187103), FRAC_CONST(0.370557427406311) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.918791174888611), FRAC_CONST(0.394743889570236) }, + { FRAC_CONST(0.913545429706573), FRAC_CONST(0.406736642122269) }, + { FRAC_CONST(0.908143162727356), FRAC_CONST(0.418659746646881) }, + { FRAC_CONST(0.902585268020630), FRAC_CONST(0.430511116981506) }, + { FRAC_CONST(0.896872758865356), FRAC_CONST(0.442288726568222) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.884987652301788), FRAC_CONST(0.465614527463913) }, + { FRAC_CONST(0.878817081451416), FRAC_CONST(0.477158784866333) }, + { FRAC_CONST(0.872496008872986), FRAC_CONST(0.488621264696121) }, + { FRAC_CONST(0.866025388240814), FRAC_CONST(0.500000000000000) }, + { FRAC_CONST(0.859406411647797), FRAC_CONST(0.511293113231659) }, + { FRAC_CONST(0.852640151977539), FRAC_CONST(0.522498548030853) }, + { FRAC_CONST(0.845727801322937), FRAC_CONST(0.533614516258240) }, + { FRAC_CONST(0.838670551776886), FRAC_CONST(0.544639050960541) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.824126183986664), FRAC_CONST(0.566406250000000) }, + { FRAC_CONST(0.816641509532928), FRAC_CONST(0.577145218849182) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.801253795623779), FRAC_CONST(0.598324596881866) }, + { FRAC_CONST(0.793353319168091), FRAC_CONST(0.608761429786682) }, + { FRAC_CONST(0.785316884517670), FRAC_CONST(0.619093954563141) }, + { FRAC_CONST(0.777145922183990), FRAC_CONST(0.629320383071899) }, + { FRAC_CONST(0.768841803073883), FRAC_CONST(0.639438986778259) }, + { FRAC_CONST(0.760405957698822), FRAC_CONST(0.649448096752167) }, + { FRAC_CONST(0.751839756965637), FRAC_CONST(0.659345865249634) }, + { FRAC_CONST(0.743144810199738), FRAC_CONST(0.669130623340607) }, + { FRAC_CONST(0.734322488307953), FRAC_CONST(0.678800761699677) }, + { FRAC_CONST(0.725374400615692), FRAC_CONST(0.688354551792145) }, + { FRAC_CONST(0.716301918029785), FRAC_CONST(0.697790503501892) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.697790443897247), FRAC_CONST(0.716301977634430) }, + { FRAC_CONST(0.688354551792145), FRAC_CONST(0.725374400615692) }, + { FRAC_CONST(0.678800702095032), FRAC_CONST(0.734322547912598) }, + { FRAC_CONST(0.669130563735962), FRAC_CONST(0.743144869804382) }, + { FRAC_CONST(0.659345805644989), FRAC_CONST(0.751839816570282) }, + { FRAC_CONST(0.649448037147522), FRAC_CONST(0.760405957698822) }, + { FRAC_CONST(0.639438986778259), FRAC_CONST(0.768841862678528) }, + { FRAC_CONST(0.629320383071899), FRAC_CONST(0.777145981788635) }, + { FRAC_CONST(0.619093954563141), FRAC_CONST(0.785316944122314) }, + { FRAC_CONST(0.608761370182037), FRAC_CONST(0.793353378772736) }, + { FRAC_CONST(0.598324596881866), FRAC_CONST(0.801253855228424) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.577145159244537), FRAC_CONST(0.816641569137573) }, + { FRAC_CONST(0.566406250000000), FRAC_CONST(0.824126183986664) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.544638991355896), FRAC_CONST(0.838670611381531) }, + { FRAC_CONST(0.533614516258240), FRAC_CONST(0.845727801322937) }, + { FRAC_CONST(0.522498488426209), FRAC_CONST(0.852640211582184) }, + { FRAC_CONST(0.511293113231659), FRAC_CONST(0.859406411647797) }, + { FRAC_CONST(0.499999970197678), FRAC_CONST(0.866025447845459) }, + { FRAC_CONST(0.488621175289154), FRAC_CONST(0.872496068477631) }, + { FRAC_CONST(0.477158755064011), FRAC_CONST(0.878817141056061) }, + { FRAC_CONST(0.465614467859268), FRAC_CONST(0.884987652301788) }, + { FRAC_CONST(0.453990519046783), FRAC_CONST(0.891006529331207) }, + { FRAC_CONST(0.442288666963577), FRAC_CONST(0.896872758865356) }, + { FRAC_CONST(0.430511027574539), FRAC_CONST(0.902585327625275) }, + { FRAC_CONST(0.418659746646881), FRAC_CONST(0.908143162727356) }, + { FRAC_CONST(0.406736612319946), FRAC_CONST(0.913545489311218) }, + { FRAC_CONST(0.394743800163269), FRAC_CONST(0.918791234493256) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.370557397603989), FRAC_CONST(0.928809583187103) }, + { FRAC_CONST(0.358367860317230), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(0.346117049455643), FRAC_CONST(0.938191354274750) }, + { FRAC_CONST(0.333806812763214), FRAC_CONST(0.942641496658325) }, + { FRAC_CONST(0.321439474821091), FRAC_CONST(0.946930110454559) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.296541512012482), FRAC_CONST(0.955019950866699) }, + { FRAC_CONST(0.284015327692032), FRAC_CONST(0.958819746971130) }, + { FRAC_CONST(0.271440386772156), FRAC_CONST(0.962455272674561) }, + { FRAC_CONST(0.258819073438644), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(0.246153265237808), FRAC_CONST(0.969230890274048) }, + { FRAC_CONST(0.233445301651955), FRAC_CONST(0.972369909286499) }, + { FRAC_CONST(0.220697447657585), FRAC_CONST(0.975342333316803) }, + { FRAC_CONST(0.207911655306816), FRAC_CONST(0.978147625923157) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.182235524058342), FRAC_CONST(0.983254909515381) }, + { FRAC_CONST(0.169349446892738), FRAC_CONST(0.985556066036224) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(0.143492594361305), FRAC_CONST(0.989651381969452) }, + { FRAC_CONST(0.130526125431061), FRAC_CONST(0.991444885730743) }, + { FRAC_CONST(0.117537401616573), FRAC_CONST(0.993068456649780) }, + { FRAC_CONST(0.104528419673443), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(0.091501533985138), FRAC_CONST(0.995804905891418) }, + { FRAC_CONST(0.078459084033966), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(0.065403074026108), FRAC_CONST(0.997858941555023) }, + { FRAC_CONST(0.052335973829031), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(0.039259787648916), FRAC_CONST(0.999229013919830) }, + { FRAC_CONST(0.026176875457168), FRAC_CONST(0.999657332897186) }, + { FRAC_CONST(0.013089597225189), FRAC_CONST(0.999914348125458) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.013089684769511), FRAC_CONST(0.999914348125458) }, + { FRAC_CONST(-0.026176963001490), FRAC_CONST(0.999657332897186) }, + { FRAC_CONST(-0.039259877055883), FRAC_CONST(0.999229013919830) }, + { FRAC_CONST(-0.052336059510708), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(-0.065403163433075), FRAC_CONST(0.997858941555023) }, + { FRAC_CONST(-0.078459173440933), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(-0.091501623392105), FRAC_CONST(0.995804905891418) }, + { FRAC_CONST(-0.104528509080410), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(-0.117537491023541), FRAC_CONST(0.993068456649780) }, + { FRAC_CONST(-0.130526214838028), FRAC_CONST(0.991444885730743) }, + { FRAC_CONST(-0.143492683768272), FRAC_CONST(0.989651381969452) }, + { FRAC_CONST(-0.156434446573257), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.169349536299706), FRAC_CONST(0.985556066036224) }, + { FRAC_CONST(-0.182235598564148), FRAC_CONST(0.983254909515381) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.207911744713783), FRAC_CONST(0.978147566318512) }, + { FRAC_CONST(-0.220697522163391), FRAC_CONST(0.975342273712158) }, + { FRAC_CONST(-0.233445391058922), FRAC_CONST(0.972369909286499) }, + { FRAC_CONST(-0.246153354644775), FRAC_CONST(0.969230890274048) }, + { FRAC_CONST(-0.258819162845612), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(-0.271440476179123), FRAC_CONST(0.962455213069916) }, + { FRAC_CONST(-0.284015417098999), FRAC_CONST(0.958819687366486) }, + { FRAC_CONST(-0.296541571617126), FRAC_CONST(0.955019950866699) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.321439564228058), FRAC_CONST(0.946930110454559) }, + { FRAC_CONST(-0.333806872367859), FRAC_CONST(0.942641496658325) }, + { FRAC_CONST(-0.346117109060287), FRAC_CONST(0.938191294670105) }, + { FRAC_CONST(-0.358367949724197), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(-0.370557487010956), FRAC_CONST(0.928809523582459) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.394743859767914), FRAC_CONST(0.918791234493256) }, + { FRAC_CONST(-0.406736701726913), FRAC_CONST(0.913545429706573) }, + { FRAC_CONST(-0.418659836053848), FRAC_CONST(0.908143103122711) }, + { FRAC_CONST(-0.430511116981506), FRAC_CONST(0.902585268020630) }, + { FRAC_CONST(-0.442288637161255), FRAC_CONST(0.896872758865356) }, + { FRAC_CONST(-0.453990608453751), FRAC_CONST(0.891006469726563) }, + { FRAC_CONST(-0.465614557266235), FRAC_CONST(0.884987592697144) }, + { FRAC_CONST(-0.477158725261688), FRAC_CONST(0.878817141056061) }, + { FRAC_CONST(-0.488621354103088), FRAC_CONST(0.872495949268341) }, + { FRAC_CONST(-0.500000059604645), FRAC_CONST(0.866025388240814) }, + { FRAC_CONST(-0.511293053627014), FRAC_CONST(0.859406411647797) }, + { FRAC_CONST(-0.522498667240143), FRAC_CONST(0.852640092372894) }, + { FRAC_CONST(-0.533614575862885), FRAC_CONST(0.845727801322937) }, + { FRAC_CONST(-0.544639050960541), FRAC_CONST(0.838670551776886) }, + { FRAC_CONST(-0.555570363998413), FRAC_CONST(0.831469535827637) }, + { FRAC_CONST(-0.566406309604645), FRAC_CONST(0.824126124382019) }, + { FRAC_CONST(-0.577145218849182), FRAC_CONST(0.816641569137573) }, + { FRAC_CONST(-0.587785184383392), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(-0.598324656486511), FRAC_CONST(0.801253736019135) }, + { FRAC_CONST(-0.608761429786682), FRAC_CONST(0.793353319168091) }, + { FRAC_CONST(-0.619093894958496), FRAC_CONST(0.785316944122314) }, + { FRAC_CONST(-0.629320502281189), FRAC_CONST(0.777145862579346) }, + { FRAC_CONST(-0.639439046382904), FRAC_CONST(0.768841803073883) }, + { FRAC_CONST(-0.649448037147522), FRAC_CONST(0.760405957698822) }, + { FRAC_CONST(-0.659345924854279), FRAC_CONST(0.751839697360992) }, + { FRAC_CONST(-0.669130682945251), FRAC_CONST(0.743144810199738) }, + { FRAC_CONST(-0.678800761699677), FRAC_CONST(0.734322488307953) }, + { FRAC_CONST(-0.688354671001434), FRAC_CONST(0.725374281406403) }, + { FRAC_CONST(-0.697790503501892), FRAC_CONST(0.716301858425140) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.716302037239075), FRAC_CONST(0.697790324687958) }, + { FRAC_CONST(-0.725374460220337), FRAC_CONST(0.688354492187500) }, + { FRAC_CONST(-0.734322547912598), FRAC_CONST(0.678800702095032) }, + { FRAC_CONST(-0.743144929409027), FRAC_CONST(0.669130444526672) }, + { FRAC_CONST(-0.751839876174927), FRAC_CONST(0.659345746040344) }, + { FRAC_CONST(-0.760406017303467), FRAC_CONST(0.649448037147522) }, + { FRAC_CONST(-0.768841803073883), FRAC_CONST(0.639439046382904) }, + { FRAC_CONST(-0.777146041393280), FRAC_CONST(0.629320263862610) }, + { FRAC_CONST(-0.785316944122314), FRAC_CONST(0.619093894958496) }, + { FRAC_CONST(-0.793353319168091), FRAC_CONST(0.608761429786682) }, + { FRAC_CONST(-0.801253914833069), FRAC_CONST(0.598324477672577) }, + { FRAC_CONST(-0.809017062187195), FRAC_CONST(0.587785184383392) }, + { FRAC_CONST(-0.816641569137573), FRAC_CONST(0.577145218849182) }, + { FRAC_CONST(-0.824126303195953), FRAC_CONST(0.566406130790710) }, + { FRAC_CONST(-0.831469655036926), FRAC_CONST(0.555570185184479) }, + { FRAC_CONST(-0.838670551776886), FRAC_CONST(0.544639050960541) }, + { FRAC_CONST(-0.845727920532227), FRAC_CONST(0.533614337444305) }, + { FRAC_CONST(-0.852640211582184), FRAC_CONST(0.522498488426209) }, + { FRAC_CONST(-0.859406411647797), FRAC_CONST(0.511293053627014) }, + { FRAC_CONST(-0.866025388240814), FRAC_CONST(0.500000059604645) }, + { FRAC_CONST(-0.872496068477631), FRAC_CONST(0.488621145486832) }, + { FRAC_CONST(-0.878817141056061), FRAC_CONST(0.477158725261688) }, + { FRAC_CONST(-0.884987652301788), FRAC_CONST(0.465614557266235) }, + { FRAC_CONST(-0.891006588935852), FRAC_CONST(0.453990370035172) }, + { FRAC_CONST(-0.896872758865356), FRAC_CONST(0.442288637161255) }, + { FRAC_CONST(-0.902585268020630), FRAC_CONST(0.430511116981506) }, + { FRAC_CONST(-0.908143222332001), FRAC_CONST(0.418659597635269) }, + { FRAC_CONST(-0.913545489311218), FRAC_CONST(0.406736582517624) }, + { FRAC_CONST(-0.918791234493256), FRAC_CONST(0.394743859767914) }, + { FRAC_CONST(-0.923879623413086), FRAC_CONST(0.382683277130127) }, + { FRAC_CONST(-0.928809583187103), FRAC_CONST(0.370557337999344) }, + { FRAC_CONST(-0.933580458164215), FRAC_CONST(0.358367919921875) }, + { FRAC_CONST(-0.938191413879395), FRAC_CONST(0.346116900444031) }, + { FRAC_CONST(-0.942641556262970), FRAC_CONST(0.333806753158569) }, + { FRAC_CONST(-0.946930170059204), FRAC_CONST(0.321439445018768) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.955020010471344), FRAC_CONST(0.296541452407837) }, + { FRAC_CONST(-0.958819746971130), FRAC_CONST(0.284015297889709) }, + { FRAC_CONST(-0.962455213069916), FRAC_CONST(0.271440476179123) }, + { FRAC_CONST(-0.965925872325897), FRAC_CONST(0.258818924427032) }, + { FRAC_CONST(-0.969230949878693), FRAC_CONST(0.246153235435486) }, + { FRAC_CONST(-0.972369909286499), FRAC_CONST(0.233445376157761) }, + { FRAC_CONST(-0.975342333316803), FRAC_CONST(0.220697283744812) }, + { FRAC_CONST(-0.978147625923157), FRAC_CONST(0.207911610603333) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.983254909515381), FRAC_CONST(0.182235360145569) }, + { FRAC_CONST(-0.985556066036224), FRAC_CONST(0.169349402189255) }, + { FRAC_CONST(-0.987688362598419), FRAC_CONST(0.156434446573257) }, + { FRAC_CONST(-0.989651441574097), FRAC_CONST(0.143492430448532) }, + { FRAC_CONST(-0.991444885730743), FRAC_CONST(0.130526080727577) }, + { FRAC_CONST(-0.993068456649780), FRAC_CONST(0.117537356913090) }, + { FRAC_CONST(-0.994521915912628), FRAC_CONST(0.104528494179249) }, + { FRAC_CONST(-0.995804965496063), FRAC_CONST(0.091501489281654) }, + { FRAC_CONST(-0.996917366981506), FRAC_CONST(0.078459039330482) }, + { FRAC_CONST(-0.997858941555023), FRAC_CONST(0.065403148531914) }, + { FRAC_CONST(-0.998629570007324), FRAC_CONST(0.052335809916258) }, + { FRAC_CONST(-0.999229013919830), FRAC_CONST(0.039259742945433) }, + { FRAC_CONST(-0.999657332897186), FRAC_CONST(0.026176951825619) }, + { FRAC_CONST(-0.999914348125458), FRAC_CONST(0.013089434243739) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.999657332897186), FRAC_CONST(0.026176949962974) }, + { FRAC_CONST(0.998629510402679), FRAC_CONST(0.052335958927870) }, + { FRAC_CONST(0.996917307376862), FRAC_CONST(0.078459098935127) }, + { FRAC_CONST(0.994521915912628), FRAC_CONST(0.104528464376926) }, + { FRAC_CONST(0.991444885730743), FRAC_CONST(0.130526199936867) }, + { FRAC_CONST(0.987688362598419), FRAC_CONST(0.156434476375580) }, + { FRAC_CONST(0.983254909515381), FRAC_CONST(0.182235524058342) }, + { FRAC_CONST(0.978147625923157), FRAC_CONST(0.207911700010300) }, + { FRAC_CONST(0.972369909286499), FRAC_CONST(0.233445376157761) }, + { FRAC_CONST(0.965925812721252), FRAC_CONST(0.258819043636322) }, + { FRAC_CONST(0.958819746971130), FRAC_CONST(0.284015357494354) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.942641496658325), FRAC_CONST(0.333806872367859) }, + { FRAC_CONST(0.933580398559570), FRAC_CONST(0.358367949724197) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.913545429706573), FRAC_CONST(0.406736642122269) }, + { FRAC_CONST(0.902585268020630), FRAC_CONST(0.430511116981506) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.878817081451416), FRAC_CONST(0.477158784866333) }, + { FRAC_CONST(0.866025388240814), FRAC_CONST(0.500000000000000) }, + { FRAC_CONST(0.852640151977539), FRAC_CONST(0.522498548030853) }, + { FRAC_CONST(0.838670551776886), FRAC_CONST(0.544639050960541) }, + { FRAC_CONST(0.824126183986664), FRAC_CONST(0.566406250000000) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.793353319168091), FRAC_CONST(0.608761429786682) }, + { FRAC_CONST(0.777145922183990), FRAC_CONST(0.629320383071899) }, + { FRAC_CONST(0.760405957698822), FRAC_CONST(0.649448096752167) }, + { FRAC_CONST(0.743144810199738), FRAC_CONST(0.669130623340607) }, + { FRAC_CONST(0.725374400615692), FRAC_CONST(0.688354551792145) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.688354551792145), FRAC_CONST(0.725374400615692) }, + { FRAC_CONST(0.669130563735962), FRAC_CONST(0.743144869804382) }, + { FRAC_CONST(0.649448037147522), FRAC_CONST(0.760405957698822) }, + { FRAC_CONST(0.629320383071899), FRAC_CONST(0.777145981788635) }, + { FRAC_CONST(0.608761370182037), FRAC_CONST(0.793353378772736) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.566406250000000), FRAC_CONST(0.824126183986664) }, + { FRAC_CONST(0.544638991355896), FRAC_CONST(0.838670611381531) }, + { FRAC_CONST(0.522498488426209), FRAC_CONST(0.852640211582184) }, + { FRAC_CONST(0.499999970197678), FRAC_CONST(0.866025447845459) }, + { FRAC_CONST(0.477158755064011), FRAC_CONST(0.878817141056061) }, + { FRAC_CONST(0.453990519046783), FRAC_CONST(0.891006529331207) }, + { FRAC_CONST(0.430511027574539), FRAC_CONST(0.902585327625275) }, + { FRAC_CONST(0.406736612319946), FRAC_CONST(0.913545489311218) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.358367860317230), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(0.333806812763214), FRAC_CONST(0.942641496658325) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.284015327692032), FRAC_CONST(0.958819746971130) }, + { FRAC_CONST(0.258819073438644), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(0.233445301651955), FRAC_CONST(0.972369909286499) }, + { FRAC_CONST(0.207911655306816), FRAC_CONST(0.978147625923157) }, + { FRAC_CONST(0.182235524058342), FRAC_CONST(0.983254909515381) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(0.130526125431061), FRAC_CONST(0.991444885730743) }, + { FRAC_CONST(0.104528419673443), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(0.078459084033966), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(0.052335973829031), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(0.026176875457168), FRAC_CONST(0.999657332897186) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.026176963001490), FRAC_CONST(0.999657332897186) }, + { FRAC_CONST(-0.052336059510708), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(-0.078459173440933), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(-0.104528509080410), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(-0.130526214838028), FRAC_CONST(0.991444885730743) }, + { FRAC_CONST(-0.156434446573257), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.182235598564148), FRAC_CONST(0.983254909515381) }, + { FRAC_CONST(-0.207911744713783), FRAC_CONST(0.978147566318512) }, + { FRAC_CONST(-0.233445391058922), FRAC_CONST(0.972369909286499) }, + { FRAC_CONST(-0.258819162845612), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(-0.284015417098999), FRAC_CONST(0.958819687366486) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.333806872367859), FRAC_CONST(0.942641496658325) }, + { FRAC_CONST(-0.358367949724197), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.406736701726913), FRAC_CONST(0.913545429706573) }, + { FRAC_CONST(-0.430511116981506), FRAC_CONST(0.902585268020630) }, + { FRAC_CONST(-0.453990608453751), FRAC_CONST(0.891006469726563) }, + { FRAC_CONST(-0.477158725261688), FRAC_CONST(0.878817141056061) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.998629510402679), FRAC_CONST(0.052335958927870) }, + { FRAC_CONST(0.994521915912628), FRAC_CONST(0.104528464376926) }, + { FRAC_CONST(0.987688362598419), FRAC_CONST(0.156434476375580) }, + { FRAC_CONST(0.978147625923157), FRAC_CONST(0.207911700010300) }, + { FRAC_CONST(0.965925812721252), FRAC_CONST(0.258819043636322) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.933580398559570), FRAC_CONST(0.358367949724197) }, + { FRAC_CONST(0.913545429706573), FRAC_CONST(0.406736642122269) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.866025388240814), FRAC_CONST(0.500000000000000) }, + { FRAC_CONST(0.838670551776886), FRAC_CONST(0.544639050960541) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.777145922183990), FRAC_CONST(0.629320383071899) }, + { FRAC_CONST(0.743144810199738), FRAC_CONST(0.669130623340607) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.669130563735962), FRAC_CONST(0.743144869804382) }, + { FRAC_CONST(0.629320383071899), FRAC_CONST(0.777145981788635) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.544638991355896), FRAC_CONST(0.838670611381531) }, + { FRAC_CONST(0.499999970197678), FRAC_CONST(0.866025447845459) }, + { FRAC_CONST(0.453990519046783), FRAC_CONST(0.891006529331207) }, + { FRAC_CONST(0.406736612319946), FRAC_CONST(0.913545489311218) }, + { FRAC_CONST(0.358367860317230), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.258819073438644), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(0.207911655306816), FRAC_CONST(0.978147625923157) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(0.104528419673443), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(0.052335973829031), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.052336059510708), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(-0.104528509080410), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(-0.156434446573257), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.207911744713783), FRAC_CONST(0.978147566318512) }, + { FRAC_CONST(-0.258819162845612), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.358367949724197), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(-0.406736701726913), FRAC_CONST(0.913545429706573) }, + { FRAC_CONST(-0.453990608453751), FRAC_CONST(0.891006469726563) }, + { FRAC_CONST(-0.500000059604645), FRAC_CONST(0.866025388240814) }, + { FRAC_CONST(-0.544639050960541), FRAC_CONST(0.838670551776886) }, + { FRAC_CONST(-0.587785184383392), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(-0.629320502281189), FRAC_CONST(0.777145862579346) }, + { FRAC_CONST(-0.669130682945251), FRAC_CONST(0.743144810199738) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.743144929409027), FRAC_CONST(0.669130444526672) }, + { FRAC_CONST(-0.777146041393280), FRAC_CONST(0.629320263862610) }, + { FRAC_CONST(-0.809017062187195), FRAC_CONST(0.587785184383392) }, + { FRAC_CONST(-0.838670551776886), FRAC_CONST(0.544639050960541) }, + { FRAC_CONST(-0.866025388240814), FRAC_CONST(0.500000059604645) }, + { FRAC_CONST(-0.891006588935852), FRAC_CONST(0.453990370035172) }, + { FRAC_CONST(-0.913545489311218), FRAC_CONST(0.406736582517624) }, + { FRAC_CONST(-0.933580458164215), FRAC_CONST(0.358367919921875) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.965925872325897), FRAC_CONST(0.258818924427032) }, + { FRAC_CONST(-0.978147625923157), FRAC_CONST(0.207911610603333) }, + { FRAC_CONST(-0.987688362598419), FRAC_CONST(0.156434446573257) }, + { FRAC_CONST(-0.994521915912628), FRAC_CONST(0.104528494179249) }, + { FRAC_CONST(-0.998629570007324), FRAC_CONST(0.052335809916258) }, + { FRAC_CONST(-1.000000000000000), FRAC_CONST(-0.000000087422777) }, + { FRAC_CONST(-0.998629510402679), FRAC_CONST(-0.052335985004902) }, + { FRAC_CONST(-0.994521856307983), FRAC_CONST(-0.104528672993183) }, + { FRAC_CONST(-0.987688302993774), FRAC_CONST(-0.156434610486031) }, + { FRAC_CONST(-0.978147566318512), FRAC_CONST(-0.207911789417267) }, + { FRAC_CONST(-0.965925812721252), FRAC_CONST(-0.258819073438644) }, + { FRAC_CONST(-0.951056540012360), FRAC_CONST(-0.309016972780228) }, + { FRAC_CONST(-0.933580398559570), FRAC_CONST(-0.358368098735809) }, + { FRAC_CONST(-0.913545429706573), FRAC_CONST(-0.406736731529236) }, + { FRAC_CONST(-0.891006529331207), FRAC_CONST(-0.453990548849106) }, + { FRAC_CONST(-0.866025269031525), FRAC_CONST(-0.500000178813934) }, + { FRAC_CONST(-0.838670492172241), FRAC_CONST(-0.544639170169830) }, + { FRAC_CONST(-0.809016942977905), FRAC_CONST(-0.587785363197327) }, + { FRAC_CONST(-0.777145922183990), FRAC_CONST(-0.629320442676544) }, + { FRAC_CONST(-0.743144810199738), FRAC_CONST(-0.669130623340607) }, + { FRAC_CONST(-0.707106649875641), FRAC_CONST(-0.707106888294220) }, + { FRAC_CONST(-0.669130504131317), FRAC_CONST(-0.743144869804382) }, + { FRAC_CONST(-0.629320323467255), FRAC_CONST(-0.777145981788635) }, + { FRAC_CONST(-0.587785065174103), FRAC_CONST(-0.809017121791840) }, + { FRAC_CONST(-0.544639110565186), FRAC_CONST(-0.838670551776886) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.996917307376862), FRAC_CONST(0.078459098935127) }, + { FRAC_CONST(0.987688362598419), FRAC_CONST(0.156434476375580) }, + { FRAC_CONST(0.972369909286499), FRAC_CONST(0.233445376157761) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.852640151977539), FRAC_CONST(0.522498548030853) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.760405957698822), FRAC_CONST(0.649448096752167) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.649448037147522), FRAC_CONST(0.760405957698822) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.522498488426209), FRAC_CONST(0.852640211582184) }, + { FRAC_CONST(0.453990519046783), FRAC_CONST(0.891006529331207) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.233445301651955), FRAC_CONST(0.972369909286499) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(0.078459084033966), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.987688362598419), FRAC_CONST(0.156434476375580) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.453990519046783), FRAC_CONST(0.891006529331207) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.156434446573257), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.453990608453751), FRAC_CONST(0.891006469726563) }, + { FRAC_CONST(-0.587785184383392), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.809017062187195), FRAC_CONST(0.587785184383392) }, + { FRAC_CONST(-0.891006588935852), FRAC_CONST(0.453990370035172) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.987688362598419), FRAC_CONST(0.156434446573257) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.972369909286499), FRAC_CONST(0.233445376157761) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.760405957698822), FRAC_CONST(0.649448096752167) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.078459173440933), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.522498667240143), FRAC_CONST(0.852640092372894) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.852640211582184), FRAC_CONST(0.522498488426209) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.996917366981506), FRAC_CONST(0.078459039330482) }, + { FRAC_CONST(-0.987688302993774), FRAC_CONST(-0.156434610486031) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(-0.809016942977905), FRAC_CONST(-0.587785363197327) }, + { FRAC_CONST(-0.649447917938232), FRAC_CONST(-0.760406076908112) }, + { FRAC_CONST(-0.453990221023560), FRAC_CONST(-0.891006648540497) }, + { FRAC_CONST(-0.233445450663567), FRAC_CONST(-0.972369909286499) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.809017062187195), FRAC_CONST(0.587785184383392) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.809016942977905), FRAC_CONST(-0.587785363197327) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.309017121791840), FRAC_CONST(-0.951056480407715) } +}; +#endif + +ALIGN static const complex_t cfft_tab_64[] = +{ + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.555570363998413), FRAC_CONST(0.831469535827637) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.831469655036926), FRAC_CONST(0.555570185184479) }, + { FRAC_CONST(-0.923879623413086), FRAC_CONST(0.382683277130127) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.881921231746674), FRAC_CONST(0.471396833658218) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(-0.098017267882824) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(-0.773010551929474), FRAC_CONST(-0.634393215179443) }, + { FRAC_CONST(-0.555570006370544), FRAC_CONST(-0.831469774246216) }, + { FRAC_CONST(-0.290284544229507), FRAC_CONST(-0.956940352916718) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.000000011924881), FRAC_CONST(-1.000000000000000) } +}; + +#ifdef ALLOW_SMALL_FRAMELENGTH +ALIGN static const complex_t cfft_tab_60[] = +{ + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.994521915912628), FRAC_CONST(0.104528464376926) }, + { FRAC_CONST(0.978147625923157), FRAC_CONST(0.207911700010300) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.913545429706573), FRAC_CONST(0.406736642122269) }, + { FRAC_CONST(0.866025388240814), FRAC_CONST(0.500000000000000) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.743144810199738), FRAC_CONST(0.669130623340607) }, + { FRAC_CONST(0.669130563735962), FRAC_CONST(0.743144869804382) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.499999970197678), FRAC_CONST(0.866025447845459) }, + { FRAC_CONST(0.406736612319946), FRAC_CONST(0.913545489311218) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.207911655306816), FRAC_CONST(0.978147625923157) }, + { FRAC_CONST(0.104528419673443), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.104528509080410), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(-0.207911744713783), FRAC_CONST(0.978147566318512) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.406736701726913), FRAC_CONST(0.913545429706573) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.978147625923157), FRAC_CONST(0.207911700010300) }, + { FRAC_CONST(0.913545429706573), FRAC_CONST(0.406736642122269) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.669130563735962), FRAC_CONST(0.743144869804382) }, + { FRAC_CONST(0.499999970197678), FRAC_CONST(0.866025447845459) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.104528419673443), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(-0.104528509080410), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.500000059604645), FRAC_CONST(0.866025388240814) }, + { FRAC_CONST(-0.669130682945251), FRAC_CONST(0.743144810199738) }, + { FRAC_CONST(-0.809017062187195), FRAC_CONST(0.587785184383392) }, + { FRAC_CONST(-0.913545489311218), FRAC_CONST(0.406736582517624) }, + { FRAC_CONST(-0.978147625923157), FRAC_CONST(0.207911610603333) }, + { FRAC_CONST(-1.000000000000000), FRAC_CONST(-0.000000087422777) }, + { FRAC_CONST(-0.978147566318512), FRAC_CONST(-0.207911789417267) }, + { FRAC_CONST(-0.913545429706573), FRAC_CONST(-0.406736731529236) }, + { FRAC_CONST(-0.809016942977905), FRAC_CONST(-0.587785363197327) }, + { FRAC_CONST(-0.669130504131317), FRAC_CONST(-0.743144869804382) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.809017062187195), FRAC_CONST(0.587785184383392) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.809016942977905), FRAC_CONST(-0.587785363197327) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.309017121791840), FRAC_CONST(-0.951056480407715) } +}; +#endif + +#ifdef LD_DEC + +ALIGN static const complex_t cfft_tab_256[] = +{ + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.999698817729950), FRAC_CONST(0.024541229009628) }, + { FRAC_CONST(0.998795449733734), FRAC_CONST(0.049067676067352) }, + { FRAC_CONST(0.997290432453156), FRAC_CONST(0.073564566671848) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.992479562759399), FRAC_CONST(0.122410677373409) }, + { FRAC_CONST(0.989176511764526), FRAC_CONST(0.146730467677116) }, + { FRAC_CONST(0.985277652740479), FRAC_CONST(0.170961901545525) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.975702106952667), FRAC_CONST(0.219101235270500) }, + { FRAC_CONST(0.970031261444092), FRAC_CONST(0.242980197072029) }, + { FRAC_CONST(0.963776051998138), FRAC_CONST(0.266712784767151) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.949528157711029), FRAC_CONST(0.313681751489639) }, + { FRAC_CONST(0.941544055938721), FRAC_CONST(0.336889863014221) }, + { FRAC_CONST(0.932992815971375), FRAC_CONST(0.359895050525665) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.914209723472595), FRAC_CONST(0.405241340398788) }, + { FRAC_CONST(0.903989315032959), FRAC_CONST(0.427555084228516) }, + { FRAC_CONST(0.893224298954010), FRAC_CONST(0.449611335992813) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.870086967945099), FRAC_CONST(0.492898225784302) }, + { FRAC_CONST(0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(0.844853579998016), FRAC_CONST(0.534997642040253) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.817584812641144), FRAC_CONST(0.575808227062225) }, + { FRAC_CONST(0.803207516670227), FRAC_CONST(0.595699310302734) }, + { FRAC_CONST(0.788346409797668), FRAC_CONST(0.615231633186340) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.757208824157715), FRAC_CONST(0.653172850608826) }, + { FRAC_CONST(0.740951120853424), FRAC_CONST(0.671558976173401) }, + { FRAC_CONST(0.724247097969055), FRAC_CONST(0.689540565013886) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.689540505409241), FRAC_CONST(0.724247097969055) }, + { FRAC_CONST(0.671558916568756), FRAC_CONST(0.740951180458069) }, + { FRAC_CONST(0.653172791004181), FRAC_CONST(0.757208883762360) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.615231573581696), FRAC_CONST(0.788346409797668) }, + { FRAC_CONST(0.595699310302734), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(0.575808167457581), FRAC_CONST(0.817584812641144) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.534997642040253), FRAC_CONST(0.844853579998016) }, + { FRAC_CONST(0.514102697372437), FRAC_CONST(0.857728660106659) }, + { FRAC_CONST(0.492898195981979), FRAC_CONST(0.870086967945099) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.449611306190491), FRAC_CONST(0.893224298954010) }, + { FRAC_CONST(0.427555114030838), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(0.405241280794144), FRAC_CONST(0.914209783077240) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.359894961118698), FRAC_CONST(0.932992815971375) }, + { FRAC_CONST(0.336889833211899), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(0.313681662082672), FRAC_CONST(0.949528217315674) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.266712754964828), FRAC_CONST(0.963776051998138) }, + { FRAC_CONST(0.242980122566223), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(0.219101220369339), FRAC_CONST(0.975702106952667) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.170961856842041), FRAC_CONST(0.985277652740479) }, + { FRAC_CONST(0.146730497479439), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(0.122410625219345), FRAC_CONST(0.992479562759399) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(0.073564492166042), FRAC_CONST(0.997290432453156) }, + { FRAC_CONST(0.049067649990320), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(0.024541135877371), FRAC_CONST(0.999698817729950) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.998795449733734), FRAC_CONST(0.049067676067352) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.989176511764526), FRAC_CONST(0.146730467677116) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.970031261444092), FRAC_CONST(0.242980197072029) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.941544055938721), FRAC_CONST(0.336889863014221) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.903989315032959), FRAC_CONST(0.427555084228516) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.803207516670227), FRAC_CONST(0.595699310302734) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.740951120853424), FRAC_CONST(0.671558976173401) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.671558916568756), FRAC_CONST(0.740951180458069) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.595699310302734), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.514102697372437), FRAC_CONST(0.857728660106659) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.427555114030838), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.336889833211899), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.242980122566223), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.146730497479439), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(0.049067649990320), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.049067739397287), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(-0.098017223179340), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(-0.146730571985245), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.242980197072029), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(-0.290284723043442), FRAC_CONST(0.956940293312073) }, + { FRAC_CONST(-0.336889922618866), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.427555084228516), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.514102756977081), FRAC_CONST(0.857728600502014) }, + { FRAC_CONST(-0.555570363998413), FRAC_CONST(0.831469535827637) }, + { FRAC_CONST(-0.595699369907379), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(-0.634393274784088), FRAC_CONST(0.773010492324829) }, + { FRAC_CONST(-0.671559035778046), FRAC_CONST(0.740951061248779) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.740951240062714), FRAC_CONST(0.671558856964111) }, + { FRAC_CONST(-0.773010492324829), FRAC_CONST(0.634393274784088) }, + { FRAC_CONST(-0.803207635879517), FRAC_CONST(0.595699131488800) }, + { FRAC_CONST(-0.831469655036926), FRAC_CONST(0.555570185184479) }, + { FRAC_CONST(-0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(-0.881921350955963), FRAC_CONST(0.471396625041962) }, + { FRAC_CONST(-0.903989315032959), FRAC_CONST(0.427555054426193) }, + { FRAC_CONST(-0.923879623413086), FRAC_CONST(0.382683277130127) }, + { FRAC_CONST(-0.941544115543365), FRAC_CONST(0.336889803409576) }, + { FRAC_CONST(-0.956940352916718), FRAC_CONST(0.290284723043442) }, + { FRAC_CONST(-0.970031261444092), FRAC_CONST(0.242980077862740) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.989176511764526), FRAC_CONST(0.146730333566666) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(0.098017096519470) }, + { FRAC_CONST(-0.998795449733734), FRAC_CONST(0.049067486077547) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.997290432453156), FRAC_CONST(0.073564566671848) }, + { FRAC_CONST(0.989176511764526), FRAC_CONST(0.146730467677116) }, + { FRAC_CONST(0.975702106952667), FRAC_CONST(0.219101235270500) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.932992815971375), FRAC_CONST(0.359895050525665) }, + { FRAC_CONST(0.903989315032959), FRAC_CONST(0.427555084228516) }, + { FRAC_CONST(0.870086967945099), FRAC_CONST(0.492898225784302) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.788346469402313), FRAC_CONST(0.615231573581696) }, + { FRAC_CONST(0.740951120853424), FRAC_CONST(0.671558976173401) }, + { FRAC_CONST(0.689540505409241), FRAC_CONST(0.724247097969055) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.575808227062225), FRAC_CONST(0.817584812641144) }, + { FRAC_CONST(0.514102697372437), FRAC_CONST(0.857728660106659) }, + { FRAC_CONST(0.449611306190491), FRAC_CONST(0.893224298954010) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.313681751489639), FRAC_CONST(0.949528157711029) }, + { FRAC_CONST(0.242980241775513), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(0.170961856842041), FRAC_CONST(0.985277652740479) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(0.024541255086660), FRAC_CONST(0.999698817729950) }, + { FRAC_CONST(-0.049067739397287), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(-0.122410707175732), FRAC_CONST(0.992479503154755) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.266712725162506), FRAC_CONST(0.963776051998138) }, + { FRAC_CONST(-0.336889803409576), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(-0.405241340398788), FRAC_CONST(0.914209723472595) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.534997701644897), FRAC_CONST(0.844853520393372) }, + { FRAC_CONST(-0.595699369907379), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(-0.653172850608826), FRAC_CONST(0.757208824157715) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.757208824157715), FRAC_CONST(0.653172850608826) }, + { FRAC_CONST(-0.803207516670227), FRAC_CONST(0.595699369907379) }, + { FRAC_CONST(-0.844853520393372), FRAC_CONST(0.534997701644897) }, + { FRAC_CONST(-0.881921231746674), FRAC_CONST(0.471396833658218) }, + { FRAC_CONST(-0.914209783077240), FRAC_CONST(0.405241221189499) }, + { FRAC_CONST(-0.941544115543365), FRAC_CONST(0.336889803409576) }, + { FRAC_CONST(-0.963776051998138), FRAC_CONST(0.266712725162506) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.992479503154755), FRAC_CONST(0.122410699725151) }, + { FRAC_CONST(-0.998795449733734), FRAC_CONST(0.049067724496126) }, + { FRAC_CONST(-0.999698817729950), FRAC_CONST(-0.024541147053242) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(-0.098017267882824) }, + { FRAC_CONST(-0.985277652740479), FRAC_CONST(-0.170961990952492) }, + { FRAC_CONST(-0.970031261444092), FRAC_CONST(-0.242980241775513) }, + { FRAC_CONST(-0.949528157711029), FRAC_CONST(-0.313681781291962) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(-0.893224298954010), FRAC_CONST(-0.449611306190491) }, + { FRAC_CONST(-0.857728660106659), FRAC_CONST(-0.514102697372437) }, + { FRAC_CONST(-0.817584872245789), FRAC_CONST(-0.575808107852936) }, + { FRAC_CONST(-0.773010551929474), FRAC_CONST(-0.634393215179443) }, + { FRAC_CONST(-0.724247038364410), FRAC_CONST(-0.689540624618530) }, + { FRAC_CONST(-0.671558916568756), FRAC_CONST(-0.740951180458069) }, + { FRAC_CONST(-0.615231573581696), FRAC_CONST(-0.788346469402313) }, + { FRAC_CONST(-0.555570006370544), FRAC_CONST(-0.831469774246216) }, + { FRAC_CONST(-0.492898195981979), FRAC_CONST(-0.870086967945099) }, + { FRAC_CONST(-0.427554935216904), FRAC_CONST(-0.903989374637604) }, + { FRAC_CONST(-0.359895110130310), FRAC_CONST(-0.932992756366730) }, + { FRAC_CONST(-0.290284544229507), FRAC_CONST(-0.956940352916718) }, + { FRAC_CONST(-0.219101369380951), FRAC_CONST(-0.975702106952667) }, + { FRAC_CONST(-0.146730408072472), FRAC_CONST(-0.989176511764526) }, + { FRAC_CONST(-0.073564760386944), FRAC_CONST(-0.997290432453156) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.555570363998413), FRAC_CONST(0.831469535827637) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.831469655036926), FRAC_CONST(0.555570185184479) }, + { FRAC_CONST(-0.923879623413086), FRAC_CONST(0.382683277130127) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.881921231746674), FRAC_CONST(0.471396833658218) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(-0.098017267882824) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(-0.773010551929474), FRAC_CONST(-0.634393215179443) }, + { FRAC_CONST(-0.555570006370544), FRAC_CONST(-0.831469774246216) }, + { FRAC_CONST(-0.290284544229507), FRAC_CONST(-0.956940352916718) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.000000011924881), FRAC_CONST(-1.000000000000000) } +}; + +#ifdef ALLOW_SMALL_FRAMELENGTH +ALIGN static const complex_t cfft_tab_240[] = +{ + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.999657332897186), FRAC_CONST(0.026176949962974) }, + { FRAC_CONST(0.998629510402679), FRAC_CONST(0.052335958927870) }, + { FRAC_CONST(0.996917307376862), FRAC_CONST(0.078459098935127) }, + { FRAC_CONST(0.994521915912628), FRAC_CONST(0.104528464376926) }, + { FRAC_CONST(0.991444885730743), FRAC_CONST(0.130526199936867) }, + { FRAC_CONST(0.987688362598419), FRAC_CONST(0.156434476375580) }, + { FRAC_CONST(0.983254909515381), FRAC_CONST(0.182235524058342) }, + { FRAC_CONST(0.978147625923157), FRAC_CONST(0.207911700010300) }, + { FRAC_CONST(0.972369909286499), FRAC_CONST(0.233445376157761) }, + { FRAC_CONST(0.965925812721252), FRAC_CONST(0.258819043636322) }, + { FRAC_CONST(0.958819746971130), FRAC_CONST(0.284015357494354) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.942641496658325), FRAC_CONST(0.333806872367859) }, + { FRAC_CONST(0.933580398559570), FRAC_CONST(0.358367949724197) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.913545429706573), FRAC_CONST(0.406736642122269) }, + { FRAC_CONST(0.902585268020630), FRAC_CONST(0.430511116981506) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.878817081451416), FRAC_CONST(0.477158784866333) }, + { FRAC_CONST(0.866025388240814), FRAC_CONST(0.500000000000000) }, + { FRAC_CONST(0.852640151977539), FRAC_CONST(0.522498548030853) }, + { FRAC_CONST(0.838670551776886), FRAC_CONST(0.544639050960541) }, + { FRAC_CONST(0.824126183986664), FRAC_CONST(0.566406250000000) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.793353319168091), FRAC_CONST(0.608761429786682) }, + { FRAC_CONST(0.777145922183990), FRAC_CONST(0.629320383071899) }, + { FRAC_CONST(0.760405957698822), FRAC_CONST(0.649448096752167) }, + { FRAC_CONST(0.743144810199738), FRAC_CONST(0.669130623340607) }, + { FRAC_CONST(0.725374400615692), FRAC_CONST(0.688354551792145) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.688354551792145), FRAC_CONST(0.725374400615692) }, + { FRAC_CONST(0.669130563735962), FRAC_CONST(0.743144869804382) }, + { FRAC_CONST(0.649448037147522), FRAC_CONST(0.760405957698822) }, + { FRAC_CONST(0.629320383071899), FRAC_CONST(0.777145981788635) }, + { FRAC_CONST(0.608761370182037), FRAC_CONST(0.793353378772736) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.566406250000000), FRAC_CONST(0.824126183986664) }, + { FRAC_CONST(0.544638991355896), FRAC_CONST(0.838670611381531) }, + { FRAC_CONST(0.522498488426209), FRAC_CONST(0.852640211582184) }, + { FRAC_CONST(0.499999970197678), FRAC_CONST(0.866025447845459) }, + { FRAC_CONST(0.477158755064011), FRAC_CONST(0.878817141056061) }, + { FRAC_CONST(0.453990519046783), FRAC_CONST(0.891006529331207) }, + { FRAC_CONST(0.430511027574539), FRAC_CONST(0.902585327625275) }, + { FRAC_CONST(0.406736612319946), FRAC_CONST(0.913545489311218) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.358367860317230), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(0.333806812763214), FRAC_CONST(0.942641496658325) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.284015327692032), FRAC_CONST(0.958819746971130) }, + { FRAC_CONST(0.258819073438644), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(0.233445301651955), FRAC_CONST(0.972369909286499) }, + { FRAC_CONST(0.207911655306816), FRAC_CONST(0.978147625923157) }, + { FRAC_CONST(0.182235524058342), FRAC_CONST(0.983254909515381) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(0.130526125431061), FRAC_CONST(0.991444885730743) }, + { FRAC_CONST(0.104528419673443), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(0.078459084033966), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(0.052335973829031), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(0.026176875457168), FRAC_CONST(0.999657332897186) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.026176963001490), FRAC_CONST(0.999657332897186) }, + { FRAC_CONST(-0.052336059510708), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(-0.078459173440933), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(-0.104528509080410), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(-0.130526214838028), FRAC_CONST(0.991444885730743) }, + { FRAC_CONST(-0.156434446573257), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.182235598564148), FRAC_CONST(0.983254909515381) }, + { FRAC_CONST(-0.207911744713783), FRAC_CONST(0.978147566318512) }, + { FRAC_CONST(-0.233445391058922), FRAC_CONST(0.972369909286499) }, + { FRAC_CONST(-0.258819162845612), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(-0.284015417098999), FRAC_CONST(0.958819687366486) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.333806872367859), FRAC_CONST(0.942641496658325) }, + { FRAC_CONST(-0.358367949724197), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.406736701726913), FRAC_CONST(0.913545429706573) }, + { FRAC_CONST(-0.430511116981506), FRAC_CONST(0.902585268020630) }, + { FRAC_CONST(-0.453990608453751), FRAC_CONST(0.891006469726563) }, + { FRAC_CONST(-0.477158725261688), FRAC_CONST(0.878817141056061) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.998629510402679), FRAC_CONST(0.052335958927870) }, + { FRAC_CONST(0.994521915912628), FRAC_CONST(0.104528464376926) }, + { FRAC_CONST(0.987688362598419), FRAC_CONST(0.156434476375580) }, + { FRAC_CONST(0.978147625923157), FRAC_CONST(0.207911700010300) }, + { FRAC_CONST(0.965925812721252), FRAC_CONST(0.258819043636322) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.933580398559570), FRAC_CONST(0.358367949724197) }, + { FRAC_CONST(0.913545429706573), FRAC_CONST(0.406736642122269) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.866025388240814), FRAC_CONST(0.500000000000000) }, + { FRAC_CONST(0.838670551776886), FRAC_CONST(0.544639050960541) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.777145922183990), FRAC_CONST(0.629320383071899) }, + { FRAC_CONST(0.743144810199738), FRAC_CONST(0.669130623340607) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.669130563735962), FRAC_CONST(0.743144869804382) }, + { FRAC_CONST(0.629320383071899), FRAC_CONST(0.777145981788635) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.544638991355896), FRAC_CONST(0.838670611381531) }, + { FRAC_CONST(0.499999970197678), FRAC_CONST(0.866025447845459) }, + { FRAC_CONST(0.453990519046783), FRAC_CONST(0.891006529331207) }, + { FRAC_CONST(0.406736612319946), FRAC_CONST(0.913545489311218) }, + { FRAC_CONST(0.358367860317230), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.258819073438644), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(0.207911655306816), FRAC_CONST(0.978147625923157) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(0.104528419673443), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(0.052335973829031), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.052336059510708), FRAC_CONST(0.998629510402679) }, + { FRAC_CONST(-0.104528509080410), FRAC_CONST(0.994521915912628) }, + { FRAC_CONST(-0.156434446573257), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.207911744713783), FRAC_CONST(0.978147566318512) }, + { FRAC_CONST(-0.258819162845612), FRAC_CONST(0.965925812721252) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.358367949724197), FRAC_CONST(0.933580458164215) }, + { FRAC_CONST(-0.406736701726913), FRAC_CONST(0.913545429706573) }, + { FRAC_CONST(-0.453990608453751), FRAC_CONST(0.891006469726563) }, + { FRAC_CONST(-0.500000059604645), FRAC_CONST(0.866025388240814) }, + { FRAC_CONST(-0.544639050960541), FRAC_CONST(0.838670551776886) }, + { FRAC_CONST(-0.587785184383392), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(-0.629320502281189), FRAC_CONST(0.777145862579346) }, + { FRAC_CONST(-0.669130682945251), FRAC_CONST(0.743144810199738) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.743144929409027), FRAC_CONST(0.669130444526672) }, + { FRAC_CONST(-0.777146041393280), FRAC_CONST(0.629320263862610) }, + { FRAC_CONST(-0.809017062187195), FRAC_CONST(0.587785184383392) }, + { FRAC_CONST(-0.838670551776886), FRAC_CONST(0.544639050960541) }, + { FRAC_CONST(-0.866025388240814), FRAC_CONST(0.500000059604645) }, + { FRAC_CONST(-0.891006588935852), FRAC_CONST(0.453990370035172) }, + { FRAC_CONST(-0.913545489311218), FRAC_CONST(0.406736582517624) }, + { FRAC_CONST(-0.933580458164215), FRAC_CONST(0.358367919921875) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.965925872325897), FRAC_CONST(0.258818924427032) }, + { FRAC_CONST(-0.978147625923157), FRAC_CONST(0.207911610603333) }, + { FRAC_CONST(-0.987688362598419), FRAC_CONST(0.156434446573257) }, + { FRAC_CONST(-0.994521915912628), FRAC_CONST(0.104528494179249) }, + { FRAC_CONST(-0.998629570007324), FRAC_CONST(0.052335809916258) }, + { FRAC_CONST(-1.000000000000000), FRAC_CONST(-0.000000087422777) }, + { FRAC_CONST(-0.998629510402679), FRAC_CONST(-0.052335985004902) }, + { FRAC_CONST(-0.994521856307983), FRAC_CONST(-0.104528672993183) }, + { FRAC_CONST(-0.987688302993774), FRAC_CONST(-0.156434610486031) }, + { FRAC_CONST(-0.978147566318512), FRAC_CONST(-0.207911789417267) }, + { FRAC_CONST(-0.965925812721252), FRAC_CONST(-0.258819073438644) }, + { FRAC_CONST(-0.951056540012360), FRAC_CONST(-0.309016972780228) }, + { FRAC_CONST(-0.933580398559570), FRAC_CONST(-0.358368098735809) }, + { FRAC_CONST(-0.913545429706573), FRAC_CONST(-0.406736731529236) }, + { FRAC_CONST(-0.891006529331207), FRAC_CONST(-0.453990548849106) }, + { FRAC_CONST(-0.866025269031525), FRAC_CONST(-0.500000178813934) }, + { FRAC_CONST(-0.838670492172241), FRAC_CONST(-0.544639170169830) }, + { FRAC_CONST(-0.809016942977905), FRAC_CONST(-0.587785363197327) }, + { FRAC_CONST(-0.777145922183990), FRAC_CONST(-0.629320442676544) }, + { FRAC_CONST(-0.743144810199738), FRAC_CONST(-0.669130623340607) }, + { FRAC_CONST(-0.707106649875641), FRAC_CONST(-0.707106888294220) }, + { FRAC_CONST(-0.669130504131317), FRAC_CONST(-0.743144869804382) }, + { FRAC_CONST(-0.629320323467255), FRAC_CONST(-0.777145981788635) }, + { FRAC_CONST(-0.587785065174103), FRAC_CONST(-0.809017121791840) }, + { FRAC_CONST(-0.544639110565186), FRAC_CONST(-0.838670551776886) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.996917307376862), FRAC_CONST(0.078459098935127) }, + { FRAC_CONST(0.987688362598419), FRAC_CONST(0.156434476375580) }, + { FRAC_CONST(0.972369909286499), FRAC_CONST(0.233445376157761) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.852640151977539), FRAC_CONST(0.522498548030853) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.760405957698822), FRAC_CONST(0.649448096752167) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.649448037147522), FRAC_CONST(0.760405957698822) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.522498488426209), FRAC_CONST(0.852640211582184) }, + { FRAC_CONST(0.453990519046783), FRAC_CONST(0.891006529331207) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.233445301651955), FRAC_CONST(0.972369909286499) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(0.078459084033966), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.987688362598419), FRAC_CONST(0.156434476375580) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.453990519046783), FRAC_CONST(0.891006529331207) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.156434446573257), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.453990608453751), FRAC_CONST(0.891006469726563) }, + { FRAC_CONST(-0.587785184383392), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.809017062187195), FRAC_CONST(0.587785184383392) }, + { FRAC_CONST(-0.891006588935852), FRAC_CONST(0.453990370035172) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.987688362598419), FRAC_CONST(0.156434446573257) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.972369909286499), FRAC_CONST(0.233445376157761) }, + { FRAC_CONST(0.891006529331207), FRAC_CONST(0.453990519046783) }, + { FRAC_CONST(0.760405957698822), FRAC_CONST(0.649448096752167) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.156434372067451), FRAC_CONST(0.987688362598419) }, + { FRAC_CONST(-0.078459173440933), FRAC_CONST(0.996917307376862) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.522498667240143), FRAC_CONST(0.852640092372894) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.852640211582184), FRAC_CONST(0.522498488426209) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.996917366981506), FRAC_CONST(0.078459039330482) }, + { FRAC_CONST(-0.987688302993774), FRAC_CONST(-0.156434610486031) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(-0.809016942977905), FRAC_CONST(-0.587785363197327) }, + { FRAC_CONST(-0.649447917938232), FRAC_CONST(-0.760406076908112) }, + { FRAC_CONST(-0.453990221023560), FRAC_CONST(-0.891006648540497) }, + { FRAC_CONST(-0.233445450663567), FRAC_CONST(-0.972369909286499) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.951056540012360), FRAC_CONST(0.309017002582550) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.809017002582550), FRAC_CONST(0.587785243988037) }, + { FRAC_CONST(0.309016972780228), FRAC_CONST(0.951056540012360) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.809017062187195), FRAC_CONST(0.587785184383392) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.587785243988037), FRAC_CONST(0.809017002582550) }, + { FRAC_CONST(-0.309017032384872), FRAC_CONST(0.951056480407715) }, + { FRAC_CONST(-0.951056599617004), FRAC_CONST(0.309016793966293) }, + { FRAC_CONST(-0.809016942977905), FRAC_CONST(-0.587785363197327) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.309017121791840), FRAC_CONST(-0.951056480407715) } +}; +#endif + +#endif + +ALIGN static const complex_t cfft_tab_128[] = +{ + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.998795449733734), FRAC_CONST(0.049067676067352) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.989176511764526), FRAC_CONST(0.146730467677116) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.970031261444092), FRAC_CONST(0.242980197072029) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.941544055938721), FRAC_CONST(0.336889863014221) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.903989315032959), FRAC_CONST(0.427555084228516) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.803207516670227), FRAC_CONST(0.595699310302734) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.740951120853424), FRAC_CONST(0.671558976173401) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.671558916568756), FRAC_CONST(0.740951180458069) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.595699310302734), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.514102697372437), FRAC_CONST(0.857728660106659) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.427555114030838), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.336889833211899), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.242980122566223), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.146730497479439), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(0.049067649990320), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.049067739397287), FRAC_CONST(0.998795449733734) }, + { FRAC_CONST(-0.098017223179340), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(-0.146730571985245), FRAC_CONST(0.989176511764526) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.242980197072029), FRAC_CONST(0.970031261444092) }, + { FRAC_CONST(-0.290284723043442), FRAC_CONST(0.956940293312073) }, + { FRAC_CONST(-0.336889922618866), FRAC_CONST(0.941544055938721) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.427555084228516), FRAC_CONST(0.903989315032959) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.514102756977081), FRAC_CONST(0.857728600502014) }, + { FRAC_CONST(-0.555570363998413), FRAC_CONST(0.831469535827637) }, + { FRAC_CONST(-0.595699369907379), FRAC_CONST(0.803207516670227) }, + { FRAC_CONST(-0.634393274784088), FRAC_CONST(0.773010492324829) }, + { FRAC_CONST(-0.671559035778046), FRAC_CONST(0.740951061248779) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.740951240062714), FRAC_CONST(0.671558856964111) }, + { FRAC_CONST(-0.773010492324829), FRAC_CONST(0.634393274784088) }, + { FRAC_CONST(-0.803207635879517), FRAC_CONST(0.595699131488800) }, + { FRAC_CONST(-0.831469655036926), FRAC_CONST(0.555570185184479) }, + { FRAC_CONST(-0.857728600502014), FRAC_CONST(0.514102756977081) }, + { FRAC_CONST(-0.881921350955963), FRAC_CONST(0.471396625041962) }, + { FRAC_CONST(-0.903989315032959), FRAC_CONST(0.427555054426193) }, + { FRAC_CONST(-0.923879623413086), FRAC_CONST(0.382683277130127) }, + { FRAC_CONST(-0.941544115543365), FRAC_CONST(0.336889803409576) }, + { FRAC_CONST(-0.956940352916718), FRAC_CONST(0.290284723043442) }, + { FRAC_CONST(-0.970031261444092), FRAC_CONST(0.242980077862740) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.989176511764526), FRAC_CONST(0.146730333566666) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(0.098017096519470) }, + { FRAC_CONST(-0.998795449733734), FRAC_CONST(0.049067486077547) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.995184719562531), FRAC_CONST(0.098017141222954) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.881921231746674), FRAC_CONST(0.471396744251251) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.773010432720184), FRAC_CONST(0.634393334388733) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.471396654844284), FRAC_CONST(0.881921291351318) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.290284633636475), FRAC_CONST(0.956940352916718) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.980785250663757), FRAC_CONST(0.195090323686600) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.555570185184479), FRAC_CONST(0.831469655036926) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.195090234279633), FRAC_CONST(0.980785310268402) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.382683515548706), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.555570363998413), FRAC_CONST(0.831469535827637) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.831469655036926), FRAC_CONST(0.555570185184479) }, + { FRAC_CONST(-0.923879623413086), FRAC_CONST(0.382683277130127) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.956940352916718), FRAC_CONST(0.290284663438797) }, + { FRAC_CONST(0.831469595432281), FRAC_CONST(0.555570244789124) }, + { FRAC_CONST(0.634393274784088), FRAC_CONST(0.773010432720184) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(0.098017133772373), FRAC_CONST(0.995184719562531) }, + { FRAC_CONST(-0.195090323686600), FRAC_CONST(0.980785250663757) }, + { FRAC_CONST(-0.471396833658218), FRAC_CONST(0.881921231746674) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.881921231746674), FRAC_CONST(0.471396833658218) }, + { FRAC_CONST(-0.980785310268402), FRAC_CONST(0.195090308785439) }, + { FRAC_CONST(-0.995184719562531), FRAC_CONST(-0.098017267882824) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(-0.773010551929474), FRAC_CONST(-0.634393215179443) }, + { FRAC_CONST(-0.555570006370544), FRAC_CONST(-0.831469774246216) }, + { FRAC_CONST(-0.290284544229507), FRAC_CONST(-0.956940352916718) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.923879504203796), FRAC_CONST(0.382683455944061) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.000000043711388), FRAC_CONST(1.000000000000000) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.382683426141739), FRAC_CONST(0.923879504203796) }, + { FRAC_CONST(-0.707106769084930), FRAC_CONST(0.707106769084930) }, + { FRAC_CONST(-0.923879504203796), FRAC_CONST(-0.382683426141739) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(1.000000000000000), FRAC_CONST(0.000000000000000) }, + { FRAC_CONST(0.000000011924881), FRAC_CONST(-1.000000000000000) } +}; + +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb.h new file mode 100644 index 000000000..e4245eef5 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb.h @@ -0,0 +1,137 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb.h,v 1.8 2007/11/01 12:34:10 menno Exp $ +**/ + +#ifndef __HCB_H__ +#define __HCB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Optimal huffman decoding for AAC taken from: + * "SELECTING AN OPTIMAL HUFFMAN DECODER FOR AAC" by + * VLADIMIR Z. MESAROVIC , RAGHUNATH RAO, MIROSLAV V. DOKIC, and SACHIN DEO + * AES paper 5436 + * + * 2 methods are used for huffman decoding: + * - binary search + * - 2-step table lookup + * + * The choice of the "optimal" method is based on the fact that if the + * memory size for the Two-step is exorbitantly high then the decision + * is Binary search for that codebook. However, for marginally more memory + * size, if Twostep outperforms even the best case of Binary then the + * decision is Two-step for that codebook. + * + * The following methods are used for the different tables. + * codebook "optimal" method + * HCB_1 2-Step + * HCB_2 2-Step + * HCB_3 Binary + * HCB_4 2-Step + * HCB_5 Binary + * HCB_6 2-Step + * HCB_7 Binary + * HCB_8 2-Step + * HCB_9 Binary + * HCB_10 2-Step + * HCB_11 2-Step + * HCB_SF Binary + * + */ + + +#define ZERO_HCB 0 +#define FIRST_PAIR_HCB 5 +#define ESC_HCB 11 +#define QUAD_LEN 4 +#define PAIR_LEN 2 +#define NOISE_HCB 13 +#define INTENSITY_HCB2 14 +#define INTENSITY_HCB 15 + +/* Maximal accessed codebook index. */ +#define LAST_CB_IDX 11 + +/* 1st step table */ +typedef struct +{ + uint8_t offset; + uint8_t extra_bits; +} hcb; + +/* 2nd step table with quadruple data */ +typedef struct +{ + uint8_t bits; + int8_t x; + int8_t y; +} hcb_2_pair; + +typedef struct +{ + uint8_t bits; + int8_t x; + int8_t y; + int8_t v; + int8_t w; +} hcb_2_quad; + +/* binary search table */ +typedef struct +{ + uint8_t is_leaf; + int8_t data[4]; +} hcb_bin_quad; + +typedef struct +{ + uint8_t is_leaf; + int8_t data[2]; +} hcb_bin_pair; + +#include "codebook/hcb_1.h" +#include "codebook/hcb_2.h" +#include "codebook/hcb_3.h" +#include "codebook/hcb_4.h" +#include "codebook/hcb_5.h" +#include "codebook/hcb_6.h" +#include "codebook/hcb_7.h" +#include "codebook/hcb_8.h" +#include "codebook/hcb_9.h" +#include "codebook/hcb_10.h" +#include "codebook/hcb_11.h" +#include "codebook/hcb_sf.h" + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_1.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_1.h new file mode 100644 index 000000000..b3939fe85 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_1.h @@ -0,0 +1,191 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_1.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* 2-step huffman table HCB_1 */ + + +/* 1st step: 5 bits + * 2^5 = 32 entries + * + * Used to find offset into 2nd step table and number of extra bits to get + */ +static const hcb hcb1_1[32] = { + /* 1 bit codeword */ + { /* 00000 */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + + /* 5 bit codewords */ + { /* 10000 */ 1, 0 }, + { /* 10001 */ 2, 0 }, + { /* 10010 */ 3, 0 }, + { /* 10011 */ 4, 0 }, + { /* 10100 */ 5, 0 }, + { /* 10101 */ 6, 0 }, + { /* 10110 */ 7, 0 }, + { /* 10111 */ 8, 0 }, + + /* 7 bit codewords */ + { /* 11000 */ 9, 2 }, + { /* 11001 */ 13, 2 }, + { /* 11010 */ 17, 2 }, + { /* 11011 */ 21, 2 }, + { /* 11100 */ 25, 2 }, + { /* 11101 */ 29, 2 }, + + /* 9 bit codewords */ + { /* 11110 */ 33, 4 }, + + /* 9/10/11 bit codewords */ + { /* 11111 */ 49, 6 } + + /* Size of second level table is 49 + 64 = 113 */ +}; + +/* 2nd step table + * + * Gives size of codeword and actual data (x,y,v,w) + */ +static const hcb_2_quad hcb1_2[113] = { + /* 1 bit codeword */ + { 1, 0, 0, 0, 0 }, + + /* 5 bit codewords */ + { 5, 1, 0, 0, 0 }, + { 5, -1, 0, 0, 0 }, + { 5, 0, 0, 0, -1 }, + { 5, 0, 1, 0, 0 }, + { 5, 0, 0, 0, 1 }, + { 5, 0, 0, -1, 0 }, + { 5, 0, 0, 1, 0 }, + { 5, 0, -1, 0, 0 }, + + /* 7 bit codewords */ + /* first 5 bits: 11000 */ + { 7, 1, -1, 0, 0 }, + { 7, -1, 1, 0, 0 }, + { 7, 0, 0, -1, 1 }, + { 7, 0, 1, -1, 0 }, + /* first 5 bits: 11001 */ + { 7, 0, -1, 1, 0 }, + { 7, 0, 0, 1, -1 }, + { 7, 1, 1, 0, 0 }, + { 7, 0, 0, -1, -1 }, + /* first 5 bits: 11010 */ + { 7, -1, -1, 0, 0 }, + { 7, 0, -1, -1, 0 }, + { 7, 1, 0, -1, 0 }, + { 7, 0, 1, 0, -1 }, + /* first 5 bits: 11011 */ + { 7, -1, 0, 1, 0 }, + { 7, 0, 0, 1, 1 }, + { 7, 1, 0, 1, 0 }, + { 7, 0, -1, 0, 1 }, + /* first 5 bits: 11100 */ + { 7, 0, 1, 1, 0 }, + { 7, 0, 1, 0, 1 }, + { 7, -1, 0, -1, 0 }, + { 7, 1, 0, 0, 1 }, + /* first 5 bits: 11101 */ + { 7, -1, 0, 0, -1 }, + { 7, 1, 0, 0, -1 }, + { 7, -1, 0, 0, 1 }, + { 7, 0, -1, 0, -1 }, + + /* 9 bit codeword */ + /* first 5 bits: 11110 */ + { 9, 1, 1, -1, 0 }, + { 9, -1, 1, -1, 0 }, + { 9, 1, -1, 1, 0 }, + { 9, 0, 1, 1, -1 }, + { 9, 0, 1, -1, 1 }, + { 9, 0, -1, 1, 1 }, + { 9, 0, -1, 1, -1 }, + { 9, 1, -1, -1, 0 }, + { 9, 1, 0, -1, 1 }, + { 9, 0, 1, -1, -1 }, + { 9, -1, 1, 1, 0 }, + { 9, -1, 0, 1, -1 }, + { 9, -1, -1, 1, 0 }, + { 9, 0, -1, -1, 1 }, + { 9, 1, -1, 0, 1 }, + { 9, 1, -1, 0, -1 }, + + /* 9/10/11 bit codewords */ + /* first 5 bits: 11111 */ + /* 9 bit: reading 11 bits -> 2 too much so 4 entries for each codeword */ + { 9, -1, 1, 0, -1 }, { 9, -1, 1, 0, -1 }, { 9, -1, 1, 0, -1 }, { 9, -1, 1, 0, -1 }, + { 9, -1, -1, -1, 0 }, { 9, -1, -1, -1, 0 }, { 9, -1, -1, -1, 0 }, { 9, -1, -1, -1, 0 }, + { 9, 0, -1, -1, -1 }, { 9, 0, -1, -1, -1 }, { 9, 0, -1, -1, -1 }, { 9, 0, -1, -1, -1 }, + { 9, 0, 1, 1, 1 }, { 9, 0, 1, 1, 1 }, { 9, 0, 1, 1, 1 }, { 9, 0, 1, 1, 1 }, + { 9, 1, 0, 1, -1 }, { 9, 1, 0, 1, -1 }, { 9, 1, 0, 1, -1 }, { 9, 1, 0, 1, -1 }, + { 9, 1, 1, 0, 1 }, { 9, 1, 1, 0, 1 }, { 9, 1, 1, 0, 1 }, { 9, 1, 1, 0, 1 }, + { 9, -1, 1, 0, 1 }, { 9, -1, 1, 0, 1 }, { 9, -1, 1, 0, 1 }, { 9, -1, 1, 0, 1 }, + { 9, 1, 1, 1, 0 }, { 9, 1, 1, 1, 0 }, { 9, 1, 1, 1, 0 }, { 9, 1, 1, 1, 0 }, + /* 10 bit: reading 11 bits -> 1 too much so 2 entries for each codeword */ + { 10, -1, -1, 0, 1 }, { 10, -1, -1, 0, 1 }, + { 10, -1, 0, -1, -1 }, { 10, -1, 0, -1, -1 }, + { 10, 1, 1, 0, -1 }, { 10, 1, 1, 0, -1 }, + { 10, 1, 0, -1, -1 }, { 10, 1, 0, -1, -1 }, + { 10, -1, 0, -1, 1 }, { 10, -1, 0, -1, 1 }, + { 10, -1, -1, 0, -1 }, { 10, -1, -1, 0, -1 }, + { 10, -1, 0, 1, 1 }, { 10, -1, 0, 1, 1 }, + { 10, 1, 0, 1, 1 }, { 10, 1, 0, 1, 1 }, + /* 11 bit */ + { 11, 1, -1, 1, -1 }, + { 11, -1, 1, -1, 1 }, + { 11, -1, 1, 1, -1 }, + { 11, 1, -1, -1, 1 }, + { 11, 1, 1, 1, 1 }, + { 11, -1, -1, 1, 1 }, + { 11, 1, 1, -1, -1 }, + { 11, -1, -1, 1, -1 }, + { 11, -1, -1, -1, -1 }, + { 11, 1, 1, -1, 1 }, + { 11, 1, -1, 1, 1 }, + { 11, -1, 1, 1, 1 }, + { 11, -1, 1, -1, -1 }, + { 11, -1, -1, -1, 1 }, + { 11, 1, -1, -1, -1 }, + { 11, 1, 1, 1, -1 } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_10.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_10.h new file mode 100644 index 000000000..254333247 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_10.h @@ -0,0 +1,314 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_10.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* 2-step huffman table HCB_10 */ + + +/* 1st step: 6 bits + * 2^6 = 64 entries + * + * Used to find offset into 2nd step table and number of extra bits to get + */ +static const hcb hcb10_1[64] = { + /* 4 bit codewords */ + { /* 000000 */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* 000100 */ 1, 0 }, + { /* */ 1, 0 }, + { /* */ 1, 0 }, + { /* */ 1, 0 }, + { /* 001000 */ 2, 0 }, + { /* */ 2, 0 }, + { /* */ 2, 0 }, + { /* */ 2, 0 }, + /* 5 bit codewords */ + { /* 001100 */ 3, 0 }, + { /* */ 3, 0 }, + { /* 001110 */ 4, 0 }, + { /* */ 4, 0 }, + { /* 010000 */ 5, 0 }, + { /* */ 5, 0 }, + { /* 010010 */ 6, 0 }, + { /* */ 6, 0 }, + { /* 010100 */ 7, 0 }, + { /* */ 7, 0 }, + { /* 010110 */ 8, 0 }, + { /* */ 8, 0 }, + { /* 011000 */ 9, 0 }, + { /* */ 9, 0 }, + { /* 011010 */ 10, 0 }, + { /* */ 10, 0 }, + /* 6 bit codewords */ + { /* 011100 */ 11, 0 }, + { /* 011101 */ 12, 0 }, + { /* 011110 */ 13, 0 }, + { /* 011111 */ 14, 0 }, + { /* 100000 */ 15, 0 }, + { /* 100001 */ 16, 0 }, + { /* 100010 */ 17, 0 }, + { /* 100011 */ 18, 0 }, + { /* 100100 */ 19, 0 }, + { /* 100101 */ 20, 0 }, + { /* 100110 */ 21, 0 }, + { /* 100111 */ 22, 0 }, + { /* 101000 */ 23, 0 }, + { /* 101001 */ 24, 0 }, + /* 7 bit codewords */ + { /* 101010 */ 25, 1 }, + { /* 101011 */ 27, 1 }, + { /* 101100 */ 29, 1 }, + { /* 101101 */ 31, 1 }, + { /* 101110 */ 33, 1 }, + { /* 101111 */ 35, 1 }, + { /* 110000 */ 37, 1 }, + { /* 110001 */ 39, 1 }, + /* 7/8 bit codewords */ + { /* 110010 */ 41, 2 }, + /* 8 bit codewords */ + { /* 110011 */ 45, 2 }, + { /* 110100 */ 49, 2 }, + { /* 110101 */ 53, 2 }, + { /* 110110 */ 57, 2 }, + { /* 110111 */ 61, 2 }, + /* 8/9 bit codewords */ + { /* 111000 */ 65, 3 }, + /* 9 bit codewords */ + { /* 111001 */ 73, 3 }, + { /* 111010 */ 81, 3 }, + { /* 111011 */ 89, 3 }, + /* 9/10 bit codewords */ + { /* 111100 */ 97, 4 }, + /* 10 bit codewords */ + { /* 111101 */ 113, 4 }, + { /* 111110 */ 129, 4 }, + /* 10/11/12 bit codewords */ + { /* 111111 */ 145, 6 } + + /* Size of second level table is 145 + 64 = 209 */ +}; + +/* 2nd step table + * + * Gives size of codeword and actual data (x,y,v,w) + */ +static const hcb_2_pair hcb10_2[209] = { + /* 4 bit codewords */ + { 4, 1, 1 }, + { 4, 1, 2 }, + { 4, 2, 1 }, + + /* 5 bit codewords */ + { 5, 2, 2 }, + { 5, 1, 0 }, + { 5, 0, 1 }, + { 5, 1, 3 }, + { 5, 3, 2 }, + { 5, 3, 1 }, + { 5, 2, 3 }, + { 5, 3, 3 }, + + /* 6 bit codewords */ + { 6, 2, 0 }, + { 6, 0, 2 }, + { 6, 2, 4 }, + { 6, 4, 2 }, + { 6, 1, 4 }, + { 6, 4, 1 }, + { 6, 0, 0 }, + { 6, 4, 3 }, + { 6, 3, 4 }, + { 6, 3, 0 }, + { 6, 0, 3 }, + { 6, 4, 4 }, + { 6, 2, 5 }, + { 6, 5, 2 }, + + /* 7 bit codewords */ + { 7, 1, 5 }, + { 7, 5, 1 }, + { 7, 5, 3 }, + { 7, 3, 5 }, + { 7, 5, 4 }, + { 7, 4, 5 }, + { 7, 6, 2 }, + { 7, 2, 6 }, + { 7, 6, 3 }, + { 7, 4, 0 }, + { 7, 6, 1 }, + { 7, 0, 4 }, + { 7, 1, 6 }, + { 7, 3, 6 }, + { 7, 5, 5 }, + { 7, 6, 4 }, + + /* 7/8 bit codewords */ + { 7, 4, 6 }, { 7, 4, 6 }, + { 8, 6, 5 }, + { 8, 7, 2 }, + + /* 8 bit codewords */ + { 8, 3, 7 }, + { 8, 2, 7 }, + { 8, 5, 6 }, + { 8, 8, 2 }, + { 8, 7, 3 }, + { 8, 5, 0 }, + { 8, 7, 1 }, + { 8, 0, 5 }, + { 8, 8, 1 }, + { 8, 1, 7 }, + { 8, 8, 3 }, + { 8, 7, 4 }, + { 8, 4, 7 }, + { 8, 2, 8 }, + { 8, 6, 6 }, + { 8, 7, 5 }, + { 8, 1, 8 }, + { 8, 3, 8 }, + { 8, 8, 4 }, + { 8, 4, 8 }, + + /* 8/9 bit codewords */ + { 8, 5, 7 }, { 8, 5, 7 }, + { 8, 8, 5 }, { 8, 8, 5 }, + { 8, 5, 8 }, { 8, 5, 8 }, + { 9, 7, 6 }, + { 9, 6, 7 }, + + /* 9 bit codewords */ + { 9, 9, 2 }, + { 9, 6, 0 }, + { 9, 6, 8 }, + { 9, 9, 3 }, + { 9, 3, 9 }, + { 9, 9, 1 }, + { 9, 2, 9 }, + { 9, 0, 6 }, + { 9, 8, 6 }, + { 9, 9, 4 }, + { 9, 4, 9 }, + { 9, 10, 2 }, + { 9, 1, 9 }, + { 9, 7, 7 }, + { 9, 8, 7 }, + { 9, 9, 5 }, + { 9, 7, 8 }, + { 9, 10, 3 }, + { 9, 5, 9 }, + { 9, 10, 4 }, + { 9, 2, 10 }, + { 9, 10, 1 }, + { 9, 3, 10 }, + { 9, 9, 6 }, + + /* 9/10 bit codewords */ + { 9, 6, 9 }, { 9, 6, 9 }, + { 9, 8, 0 }, { 9, 8, 0 }, + { 9, 4, 10 }, { 9, 4, 10 }, + { 9, 7, 0 }, { 9, 7, 0 }, + { 9, 11, 2 }, { 9, 11, 2 }, + { 10, 7, 9 }, + { 10, 11, 3 }, + { 10, 10, 6 }, + { 10, 1, 10 }, + { 10, 11, 1 }, + { 10, 9, 7 }, + + /* 10 bit codewords */ + { 10, 0, 7 }, + { 10, 8, 8 }, + { 10, 10, 5 }, + { 10, 3, 11 }, + { 10, 5, 10 }, + { 10, 8, 9 }, + { 10, 11, 5 }, + { 10, 0, 8 }, + { 10, 11, 4 }, + { 10, 2, 11 }, + { 10, 7, 10 }, + { 10, 6, 10 }, + { 10, 10, 7 }, + { 10, 4, 11 }, + { 10, 1, 11 }, + { 10, 12, 2 }, + { 10, 9, 8 }, + { 10, 12, 3 }, + { 10, 11, 6 }, + { 10, 5, 11 }, + { 10, 12, 4 }, + { 10, 11, 7 }, + { 10, 12, 5 }, + { 10, 3, 12 }, + { 10, 6, 11 }, + { 10, 9, 0 }, + { 10, 10, 8 }, + { 10, 10, 0 }, + { 10, 12, 1 }, + { 10, 0, 9 }, + { 10, 4, 12 }, + { 10, 9, 9 }, + + /* 10/11/12 bit codewords */ + { 10, 12, 6 }, { 10, 12, 6 }, { 10, 12, 6 }, { 10, 12, 6 }, + { 10, 2, 12 }, { 10, 2, 12 }, { 10, 2, 12 }, { 10, 2, 12 }, + { 10, 8, 10 }, { 10, 8, 10 }, { 10, 8, 10 }, { 10, 8, 10 }, + { 11, 9, 10 }, { 11, 9, 10 }, + { 11, 1, 12 }, { 11, 1, 12 }, + { 11, 11, 8 }, { 11, 11, 8 }, + { 11, 12, 7 }, { 11, 12, 7 }, + { 11, 7, 11 }, { 11, 7, 11 }, + { 11, 5, 12 }, { 11, 5, 12 }, + { 11, 6, 12 }, { 11, 6, 12 }, + { 11, 10, 9 }, { 11, 10, 9 }, + { 11, 8, 11 }, { 11, 8, 11 }, + { 11, 12, 8 }, { 11, 12, 8 }, + { 11, 0, 10 }, { 11, 0, 10 }, + { 11, 7, 12 }, { 11, 7, 12 }, + { 11, 11, 0 }, { 11, 11, 0 }, + { 11, 10, 10 }, { 11, 10, 10 }, + { 11, 11, 9 }, { 11, 11, 9 }, + { 11, 11, 10 }, { 11, 11, 10 }, + { 11, 0, 11 }, { 11, 0, 11 }, + { 11, 11, 11 }, { 11, 11, 11 }, + { 11, 9, 11 }, { 11, 9, 11 }, + { 11, 10, 11 }, { 11, 10, 11 }, + { 11, 12, 0 }, { 11, 12, 0 }, + { 11, 8, 12 }, { 11, 8, 12 }, + { 12, 12, 9 }, + { 12, 10, 12 }, + { 12, 9, 12 }, + { 12, 11, 12 }, + { 12, 12, 11 }, + { 12, 0, 12 }, + { 12, 12, 10 }, + { 12, 12, 12 } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_11.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_11.h new file mode 100644 index 000000000..be191de5f --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_11.h @@ -0,0 +1,417 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_11.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* 2-step huffman table HCB_11 */ + + +/* 1st step: 5 bits + * 2^5 = 32 entries + * + * Used to find offset into 2nd step table and number of extra bits to get + */ +static const hcb hcb11_1[32] = { + /* 4 bits */ + { /* 00000 */ 0, 0 }, + { /* */ 0, 0 }, + { /* 00010 */ 1, 0 }, + { /* */ 1, 0 }, + + /* 5 bits */ + { /* 00100 */ 2, 0 }, + { /* 00101 */ 3, 0 }, + { /* 00110 */ 4, 0 }, + { /* 00111 */ 5, 0 }, + { /* 01000 */ 6, 0 }, + { /* 01001 */ 7, 0 }, + + /* 6 bits */ + { /* 01010 */ 8, 1 }, + { /* 01011 */ 10, 1 }, + { /* 01100 */ 12, 1 }, + + /* 6/7 bits */ + { /* 01101 */ 14, 2 }, + + /* 7 bits */ + { /* 01110 */ 18, 2 }, + { /* 01111 */ 22, 2 }, + { /* 10000 */ 26, 2 }, + + /* 7/8 bits */ + { /* 10001 */ 30, 3 }, + + /* 8 bits */ + { /* 10010 */ 38, 3 }, + { /* 10011 */ 46, 3 }, + { /* 10100 */ 54, 3 }, + { /* 10101 */ 62, 3 }, + { /* 10110 */ 70, 3 }, + { /* 10111 */ 78, 3 }, + + /* 8/9 bits */ + { /* 11000 */ 86, 4 }, + + /* 9 bits */ + { /* 11001 */ 102, 4 }, + { /* 11010 */ 118, 4 }, + { /* 11011 */ 134, 4 }, + + /* 9/10 bits */ + { /* 11100 */ 150, 5 }, + + /* 10 bits */ + { /* 11101 */ 182, 5 }, + { /* 11110 */ 214, 5 }, + + /* 10/11/12 bits */ + { /* 11111 */ 246, 7 } + + /* Size of second level table is 246 + 128 = 374 */ +}; + +/* 2nd step table + * + * Gives size of codeword and actual data (x,y,v,w) + */ +static hcb_2_pair hcb11_2[374] = { + /* 4 */ + { 4, 0, 0 }, + { 4, 1, 1 }, + + /* 5 */ + { 5, 16, 16 }, + { 5, 1, 0 }, + { 5, 0, 1 }, + { 5, 2, 1 }, + { 5, 1, 2 }, + { 5, 2, 2 }, + + /* 6 */ + { 6, 1, 3 }, + { 6, 3, 1 }, + { 6, 3, 2 }, + { 6, 2, 0 }, + { 6, 2, 3 }, + { 6, 0, 2 }, + + /* 6/7 */ + { 6, 3, 3 }, { 6, 3, 3 }, + { 7, 4, 1 }, + { 7, 1, 4 }, + + /* 7 */ + { 7, 4, 2 }, + { 7, 2, 4 }, + { 7, 4, 3 }, + { 7, 3, 4 }, + { 7, 3, 0 }, + { 7, 0, 3 }, + { 7, 5, 1 }, + { 7, 5, 2 }, + { 7, 2, 5 }, + { 7, 4, 4 }, + { 7, 1, 5 }, + { 7, 5, 3 }, + + /* 7/8 */ + { 7, 3, 5 }, { 7, 3, 5 }, + { 7, 5, 4 }, { 7, 5, 4 }, + { 8, 4, 5 }, + { 8, 6, 2 }, + { 8, 2, 6 }, + { 8, 6, 1 }, + + /* 8 */ + { 8, 6, 3 }, + { 8, 3, 6 }, + { 8, 1, 6 }, + { 8, 4, 16 }, + { 8, 3, 16 }, + { 8, 16, 5 }, + { 8, 16, 3 }, + { 8, 16, 4 }, + { 8, 6, 4 }, + { 8, 16, 6 }, + { 8, 4, 0 }, + { 8, 4, 6 }, + { 8, 0, 4 }, + { 8, 2, 16 }, + { 8, 5, 5 }, + { 8, 5, 16 }, + { 8, 16, 7 }, + { 8, 16, 2 }, + { 8, 16, 8 }, + { 8, 2, 7 }, + { 8, 7, 2 }, + { 8, 3, 7 }, + { 8, 6, 5 }, + { 8, 5, 6 }, + { 8, 6, 16 }, + { 8, 16, 10 }, + { 8, 7, 3 }, + { 8, 7, 1 }, + { 8, 16, 9 }, + { 8, 7, 16 }, + { 8, 1, 16 }, + { 8, 1, 7 }, + { 8, 4, 7 }, + { 8, 16, 11 }, + { 8, 7, 4 }, + { 8, 16, 12 }, + { 8, 8, 16 }, + { 8, 16, 1 }, + { 8, 6, 6 }, + { 8, 9, 16 }, + { 8, 2, 8 }, + { 8, 5, 7 }, + { 8, 10, 16 }, + { 8, 16, 13 }, + { 8, 8, 3 }, + { 8, 8, 2 }, + { 8, 3, 8 }, + { 8, 5, 0 }, + + /* 8/9 */ + { 8, 16, 14 }, { 8, 16, 14 }, + { 8, 11, 16 }, { 8, 11, 16 }, + { 8, 7, 5 }, { 8, 7, 5 }, + { 8, 4, 8 }, { 8, 4, 8 }, + { 8, 6, 7 }, { 8, 6, 7 }, + { 8, 7, 6 }, { 8, 7, 6 }, + { 8, 0, 5 }, { 8, 0, 5 }, + { 9, 8, 4 }, + { 9, 16, 15 }, + + /* 9 */ + { 9, 12, 16 }, + { 9, 1, 8 }, + { 9, 8, 1 }, + { 9, 14, 16 }, + { 9, 5, 8 }, + { 9, 13, 16 }, + { 9, 3, 9 }, + { 9, 8, 5 }, + { 9, 7, 7 }, + { 9, 2, 9 }, + { 9, 8, 6 }, + { 9, 9, 2 }, + { 9, 9, 3 }, + { 9, 15, 16 }, + { 9, 4, 9 }, + { 9, 6, 8 }, + { 9, 6, 0 }, + { 9, 9, 4 }, + { 9, 5, 9 }, + { 9, 8, 7 }, + { 9, 7, 8 }, + { 9, 1, 9 }, + { 9, 10, 3 }, + { 9, 0, 6 }, + { 9, 10, 2 }, + { 9, 9, 1 }, + { 9, 9, 5 }, + { 9, 4, 10 }, + { 9, 2, 10 }, + { 9, 9, 6 }, + { 9, 3, 10 }, + { 9, 6, 9 }, + { 9, 10, 4 }, + { 9, 8, 8 }, + { 9, 10, 5 }, + { 9, 9, 7 }, + { 9, 11, 3 }, + { 9, 1, 10 }, + { 9, 7, 0 }, + { 9, 10, 6 }, + { 9, 7, 9 }, + { 9, 3, 11 }, + { 9, 5, 10 }, + { 9, 10, 1 }, + { 9, 4, 11 }, + { 9, 11, 2 }, + { 9, 13, 2 }, + { 9, 6, 10 }, + + /* 9/10 */ + { 9, 13, 3 }, { 9, 13, 3 }, + { 9, 2, 11 }, { 9, 2, 11 }, + { 9, 16, 0 }, { 9, 16, 0 }, + { 9, 5, 11 }, { 9, 5, 11 }, + { 9, 11, 5 }, { 9, 11, 5 }, + { 10, 11, 4 }, + { 10, 9, 8 }, + { 10, 7, 10 }, + { 10, 8, 9 }, + { 10, 0, 16 }, + { 10, 4, 13 }, + { 10, 0, 7 }, + { 10, 3, 13 }, + { 10, 11, 6 }, + { 10, 13, 1 }, + { 10, 13, 4 }, + { 10, 12, 3 }, + { 10, 2, 13 }, + { 10, 13, 5 }, + { 10, 8, 10 }, + { 10, 6, 11 }, + { 10, 10, 8 }, + { 10, 10, 7 }, + { 10, 14, 2 }, + { 10, 12, 4 }, + { 10, 1, 11 }, + { 10, 4, 12 }, + + /* 10 */ + { 10, 11, 1 }, + { 10, 3, 12 }, + { 10, 1, 13 }, + { 10, 12, 2 }, + { 10, 7, 11 }, + { 10, 3, 14 }, + { 10, 5, 12 }, + { 10, 5, 13 }, + { 10, 14, 4 }, + { 10, 4, 14 }, + { 10, 11, 7 }, + { 10, 14, 3 }, + { 10, 12, 5 }, + { 10, 13, 6 }, + { 10, 12, 6 }, + { 10, 8, 0 }, + { 10, 11, 8 }, + { 10, 2, 12 }, + { 10, 9, 9 }, + { 10, 14, 5 }, + { 10, 6, 13 }, + { 10, 10, 10 }, + { 10, 15, 2 }, + { 10, 8, 11 }, + { 10, 9, 10 }, + { 10, 14, 6 }, + { 10, 10, 9 }, + { 10, 5, 14 }, + { 10, 11, 9 }, + { 10, 14, 1 }, + { 10, 2, 14 }, + { 10, 6, 12 }, + { 10, 1, 12 }, + { 10, 13, 8 }, + { 10, 0, 8 }, + { 10, 13, 7 }, + { 10, 7, 12 }, + { 10, 12, 7 }, + { 10, 7, 13 }, + { 10, 15, 3 }, + { 10, 12, 1 }, + { 10, 6, 14 }, + { 10, 2, 15 }, + { 10, 15, 5 }, + { 10, 15, 4 }, + { 10, 1, 14 }, + { 10, 9, 11 }, + { 10, 4, 15 }, + { 10, 14, 7 }, + { 10, 8, 13 }, + { 10, 13, 9 }, + { 10, 8, 12 }, + { 10, 5, 15 }, + { 10, 3, 15 }, + { 10, 10, 11 }, + { 10, 11, 10 }, + { 10, 12, 8 }, + { 10, 15, 6 }, + { 10, 15, 7 }, + { 10, 8, 14 }, + { 10, 15, 1 }, + { 10, 7, 14 }, + { 10, 9, 0 }, + { 10, 0, 9 }, + + /* 10/11/12 */ + { 10, 9, 13 }, { 10, 9, 13 }, { 10, 9, 13 }, { 10, 9, 13 }, + { 10, 9, 12 }, { 10, 9, 12 }, { 10, 9, 12 }, { 10, 9, 12 }, + { 10, 12, 9 }, { 10, 12, 9 }, { 10, 12, 9 }, { 10, 12, 9 }, + { 10, 14, 8 }, { 10, 14, 8 }, { 10, 14, 8 }, { 10, 14, 8 }, + { 10, 10, 13 }, { 10, 10, 13 }, { 10, 10, 13 }, { 10, 10, 13 }, + { 10, 14, 9 }, { 10, 14, 9 }, { 10, 14, 9 }, { 10, 14, 9 }, + { 10, 12, 10 }, { 10, 12, 10 }, { 10, 12, 10 }, { 10, 12, 10 }, + { 10, 6, 15 }, { 10, 6, 15 }, { 10, 6, 15 }, { 10, 6, 15 }, + { 10, 7, 15 }, { 10, 7, 15 }, { 10, 7, 15 }, { 10, 7, 15 }, + + { 11, 9, 14 }, { 11, 9, 14 }, + { 11, 15, 8 }, { 11, 15, 8 }, + { 11, 11, 11 }, { 11, 11, 11 }, + { 11, 11, 14 }, { 11, 11, 14 }, + { 11, 1, 15 }, { 11, 1, 15 }, + { 11, 10, 12 }, { 11, 10, 12 }, + { 11, 10, 14 }, { 11, 10, 14 }, + { 11, 13, 11 }, { 11, 13, 11 }, + { 11, 13, 10 }, { 11, 13, 10 }, + { 11, 11, 13 }, { 11, 11, 13 }, + { 11, 11, 12 }, { 11, 11, 12 }, + { 11, 8, 15 }, { 11, 8, 15 }, + { 11, 14, 11 }, { 11, 14, 11 }, + { 11, 13, 12 }, { 11, 13, 12 }, + { 11, 12, 13 }, { 11, 12, 13 }, + { 11, 15, 9 }, { 11, 15, 9 }, + { 11, 14, 10 }, { 11, 14, 10 }, + { 11, 10, 0 }, { 11, 10, 0 }, + { 11, 12, 11 }, { 11, 12, 11 }, + { 11, 9, 15 }, { 11, 9, 15 }, + { 11, 0, 10 }, { 11, 0, 10 }, + { 11, 12, 12 }, { 11, 12, 12 }, + { 11, 11, 0 }, { 11, 11, 0 }, + { 11, 12, 14 }, { 11, 12, 14 }, + { 11, 10, 15 }, { 11, 10, 15 }, + { 11, 13, 13 }, { 11, 13, 13 }, + { 11, 0, 13 }, { 11, 0, 13 }, + { 11, 14, 12 }, { 11, 14, 12 }, + { 11, 15, 10 }, { 11, 15, 10 }, + { 11, 15, 11 }, { 11, 15, 11 }, + { 11, 11, 15 }, { 11, 11, 15 }, + { 11, 14, 13 }, { 11, 14, 13 }, + { 11, 13, 0 }, { 11, 13, 0 }, + { 11, 0, 11 }, { 11, 0, 11 }, + { 11, 13, 14 }, { 11, 13, 14 }, + { 11, 15, 12 }, { 11, 15, 12 }, + { 11, 15, 13 }, { 11, 15, 13 }, + { 11, 12, 15 }, { 11, 12, 15 }, + { 11, 14, 0 }, { 11, 14, 0 }, + { 11, 14, 14 }, { 11, 14, 14 }, + { 11, 13, 15 }, { 11, 13, 15 }, + { 11, 12, 0 }, { 11, 12, 0 }, + { 11, 14, 15 }, { 11, 14, 15 }, + { 12, 0, 14 }, + { 12, 0, 12 }, + { 12, 15, 14 }, + { 12, 15, 0 }, + { 12, 0, 15 }, + { 12, 15, 15 } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_2.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_2.h new file mode 100644 index 000000000..00a7c09ed --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_2.h @@ -0,0 +1,192 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_2.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* 2-step huffman table HCB_2 */ + + +/* 1st step: 5 bits + * 2^5 = 32 entries + * + * Used to find offset into 2nd step table and number of extra bits to get + */ +static const hcb hcb2_1[32] = { + /* 3 bit codeword */ + { /* 00000 */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* 00011 */ 0, 0 }, + + /* 4 bit codeword */ + { /* 00100 */ 1, 0 }, + { /* 00101 */ 1, 0 }, + + /* 5 bit codewords */ + { /* 00110 */ 2, 0 }, + { /* 00111 */ 3, 0 }, + { /* 01000 */ 4, 0 }, + { /* 01001 */ 5, 0 }, + { /* 01010 */ 6, 0 }, + { /* 01011 */ 7, 0 }, + { /* 01100 */ 8, 0 }, + + /* 6 bit codewords */ + { /* 01101 */ 9, 1 }, + { /* 01110 */ 11, 1 }, + { /* 01111 */ 13, 1 }, + { /* 10000 */ 15, 1 }, + { /* 10001 */ 17, 1 }, + { /* 10010 */ 19, 1 }, + { /* 10011 */ 21, 1 }, + { /* 10100 */ 23, 1 }, + { /* 10101 */ 25, 1 }, + { /* 10110 */ 27, 1 }, + { /* 10111 */ 29, 1 }, + { /* 11000 */ 31, 1 }, + + /* 7 bit codewords */ + { /* 11001 */ 33, 2 }, + { /* 11010 */ 37, 2 }, + { /* 11011 */ 41, 2 }, + + /* 7/8 bit codewords */ + { /* 11100 */ 45, 3 }, + + /* 8 bit codewords */ + { /* 11101 */ 53, 3 }, + { /* 11110 */ 61, 3 }, + + /* 8/9 bit codewords */ + { /* 11111 */ 69, 4 } + + /* Size of second level table is 69 + 16 = 85 */ +}; + +/* 2nd step table + * + * Gives size of codeword and actual data (x,y,v,w) + */ +static const hcb_2_quad hcb2_2[85] = { + /* 3 bit codeword */ + { 3, 0, 0, 0, 0 }, + + /* 4 bit codeword */ + { 4, 1, 0, 0, 0 }, + + /* 5 bit codewords */ + { 5, -1, 0, 0, 0 }, + { 5, 0, 0, 0, 1 }, + { 5, 0, 0, -1, 0 }, + { 5, 0, 0, 0, -1 }, + { 5, 0, -1, 0, 0 }, + { 5, 0, 0, 1, 0 }, + { 5, 0, 1, 0, 0 }, + + /* 6 bit codewords */ + { 6, 0, -1, 1, 0 }, + { 6, -1, 1, 0, 0 }, + { 6, 0, 1, -1, 0 }, + { 6, 0, 0, 1, -1 }, + { 6, 0, 1, 0, -1 }, + { 6, 0, 0, -1, 1 }, + { 6, -1, 0, 0, -1 }, + { 6, 1, -1, 0, 0 }, + { 6, 1, 0, -1, 0 }, + { 6, -1, -1, 0, 0 }, + { 6, 0, 0, -1, -1 }, + { 6, 1, 0, 1, 0 }, + { 6, 1, 0, 0, 1 }, + { 6, 0, -1, 0, 1 }, + { 6, -1, 0, 1, 0 }, + { 6, 0, 1, 0, 1 }, + { 6, 0, -1, -1, 0 }, + { 6, -1, 0, 0, 1 }, + { 6, 0, -1, 0, -1 }, + { 6, -1, 0, -1, 0 }, + { 6, 1, 1, 0, 0 }, + { 6, 0, 1, 1, 0 }, + { 6, 0, 0, 1, 1 }, + { 6, 1, 0, 0, -1 }, + + /* 7 bit codewords */ + { 7, 0, 1, -1, 1 }, + { 7, 1, 0, -1, 1 }, + { 7, -1, 1, -1, 0 }, + { 7, 0, -1, 1, -1 }, + { 7, 1, -1, 1, 0 }, + { 7, 1, 1, 0, -1 }, + { 7, 1, 0, 1, 1 }, + { 7, -1, 1, 1, 0 }, + { 7, 0, -1, -1, 1 }, + { 7, 1, 1, 1, 0 }, + { 7, -1, 0, 1, -1 }, + { 7, -1, -1, -1, 0 }, + + /* 7/8 bit codewords */ + { 7, -1, 0, -1, 1 }, { 7, -1, 0, -1, 1 }, + { 7, 1, -1, -1, 0 }, { 7, 1, -1, -1, 0 }, + { 7, 1, 1, -1, 0 }, { 7, 1, 1, -1, 0 }, + { 8, 1, -1, 0, 1 }, + { 8, -1, 1, 0, -1 }, + + /* 8 bit codewords */ + { 8, -1, -1, 1, 0 }, + { 8, -1, 0, 1, 1 }, + { 8, -1, -1, 0, 1 }, + { 8, -1, -1, 0, -1 }, + { 8, 0, -1, -1, -1 }, + { 8, 1, 0, 1, -1 }, + { 8, 1, 0, -1, -1 }, + { 8, 0, 1, -1, -1 }, + { 8, 0, 1, 1, 1 }, + { 8, -1, 1, 0, 1 }, + { 8, -1, 0, -1, -1 }, + { 8, 0, 1, 1, -1 }, + { 8, 1, -1, 0, -1 }, + { 8, 0, -1, 1, 1 }, + { 8, 1, 1, 0, 1 }, + { 8, 1, -1, 1, -1 }, + + /* 8/9 bit codewords */ + { 8, -1, 1, -1, 1 }, { 8, -1, 1, -1, 1 }, + { 9, 1, -1, -1, 1 }, + { 9, -1, -1, -1, -1 }, + { 9, -1, 1, 1, -1 }, + { 9, -1, 1, 1, 1 }, + { 9, 1, 1, 1, 1 }, + { 9, -1, -1, 1, -1 }, + { 9, 1, -1, 1, 1 }, + { 9, -1, 1, -1, -1 }, + { 9, -1, -1, 1, 1 }, + { 9, 1, 1, -1, -1 }, + { 9, 1, -1, -1, -1 }, + { 9, -1, -1, -1, 1 }, + { 9, 1, 1, -1, 1 }, + { 9, 1, 1, 1, -1 } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_3.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_3.h new file mode 100644 index 000000000..34358d6d1 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_3.h @@ -0,0 +1,196 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_3.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* Binary search huffman table HCB_3 */ + + +static const hcb_bin_quad hcb3[161] = { + { /* 0 */ 0, { 1, 2, 0, 0 } }, + { /* 1 */ 1, { 0, 0, 0, 0 } }, /* 0 */ + { /* 2 */ 0, { 1, 2, 0, 0 } }, + { /* 3 */ 0, { 2, 3, 0, 0 } }, + { /* 4 */ 0, { 3, 4, 0, 0 } }, + { /* 5 */ 0, { 4, 5, 0, 0 } }, + { /* 6 */ 0, { 5, 6, 0, 0 } }, + { /* 7 */ 0, { 6, 7, 0, 0 } }, + { /* 8 */ 0, { 7, 8, 0, 0 } }, + { /* 9 */ 1, { 1, 0, 0, 0 } }, /* 1000 */ + { /* 10 */ 1, { 0, 0, 0, 1 } }, /* 1001 */ + { /* 11 */ 1, { 0, 1, 0, 0 } }, /* 1010 */ + { /* 12 */ 1, { 0, 0, 1, 0 } }, /* 1011 */ + { /* 13 */ 0, { 4, 5, 0, 0 } }, + { /* 14 */ 0, { 5, 6, 0, 0 } }, + { /* 15 */ 0, { 6, 7, 0, 0 } }, + { /* 16 */ 0, { 7, 8, 0, 0 } }, + { /* 17 */ 1, { 1, 1, 0, 0 } }, + { /* 18 */ 1, { 0, 0, 1, 1 } }, + { /* 19 */ 0, { 6, 7, 0, 0 } }, + { /* 20 */ 0, { 7, 8, 0, 0 } }, + { /* 21 */ 0, { 8, 9, 0, 0 } }, + { /* 22 */ 0, { 9, 10, 0, 0 } }, + { /* 23 */ 0, { 10, 11, 0, 0 } }, + { /* 24 */ 0, { 11, 12, 0, 0 } }, + { /* 25 */ 1, { 0, 1, 1, 0 } }, /* 110100 */ + { /* 26 */ 1, { 0, 1, 0, 1 } }, /* 110101 */ + { /* 27 */ 1, { 1, 0, 1, 0 } }, /* 110110 */ + { /* 28 */ 1, { 0, 1, 1, 1 } }, /* 110111 */ + { /* 29 */ 1, { 1, 0, 0, 1 } }, /* 111000 */ + { /* 30 */ 1, { 1, 1, 1, 0 } }, /* 111001 */ + { /* 31 */ 0, { 6, 7, 0, 0 } }, + { /* 32 */ 0, { 7, 8, 0, 0 } }, + { /* 33 */ 0, { 8, 9, 0, 0 } }, + { /* 34 */ 0, { 9, 10, 0, 0 } }, + { /* 35 */ 0, { 10, 11, 0, 0 } }, + { /* 36 */ 0, { 11, 12, 0, 0 } }, + { /* 37 */ 1, { 1, 1, 1, 1 } }, /* 1110100 */ + { /* 38 */ 1, { 1, 0, 1, 1 } }, /* 1110101 */ + { /* 39 */ 1, { 1, 1, 0, 1 } }, /* 1110110 */ + { /* 40 */ 0, { 9, 10, 0, 0 } }, + { /* 41 */ 0, { 10, 11, 0, 0 } }, + { /* 42 */ 0, { 11, 12, 0, 0 } }, + { /* 43 */ 0, { 12, 13, 0, 0 } }, + { /* 44 */ 0, { 13, 14, 0, 0 } }, + { /* 45 */ 0, { 14, 15, 0, 0 } }, + { /* 46 */ 0, { 15, 16, 0, 0 } }, + { /* 47 */ 0, { 16, 17, 0, 0 } }, + { /* 48 */ 0, { 17, 18, 0, 0 } }, + { /* 49 */ 1, { 2, 0, 0, 0 } }, /* 11101110 */ + { /* 50 */ 1, { 0, 0, 0, 2 } }, /* 11101111 */ + { /* 51 */ 1, { 0, 0, 1, 2 } }, /* 11110000 */ + { /* 52 */ 1, { 2, 1, 0, 0 } }, /* 11110001 */ + { /* 53 */ 1, { 1, 2, 1, 0 } }, /* 11110010 */ + { /* 54 */ 0, { 13, 14, 0, 0 } }, + { /* 55 */ 0, { 14, 15, 0, 0 } }, + { /* 56 */ 0, { 15, 16, 0, 0 } }, + { /* 57 */ 0, { 16, 17, 0, 0 } }, + { /* 58 */ 0, { 17, 18, 0, 0 } }, + { /* 59 */ 0, { 18, 19, 0, 0 } }, + { /* 60 */ 0, { 19, 20, 0, 0 } }, + { /* 61 */ 0, { 20, 21, 0, 0 } }, + { /* 62 */ 0, { 21, 22, 0, 0 } }, + { /* 63 */ 0, { 22, 23, 0, 0 } }, + { /* 64 */ 0, { 23, 24, 0, 0 } }, + { /* 65 */ 0, { 24, 25, 0, 0 } }, + { /* 66 */ 0, { 25, 26, 0, 0 } }, + { /* 67 */ 1, { 0, 0, 2, 1 } }, + { /* 68 */ 1, { 0, 1, 2, 1 } }, + { /* 69 */ 1, { 1, 2, 0, 0 } }, + { /* 70 */ 1, { 0, 1, 1, 2 } }, + { /* 71 */ 1, { 2, 1, 1, 0 } }, + { /* 72 */ 1, { 0, 0, 2, 0 } }, + { /* 73 */ 1, { 0, 2, 1, 0 } }, + { /* 74 */ 1, { 0, 1, 2, 0 } }, + { /* 75 */ 1, { 0, 2, 0, 0 } }, + { /* 76 */ 1, { 0, 1, 0, 2 } }, + { /* 77 */ 1, { 2, 0, 1, 0 } }, + { /* 78 */ 1, { 1, 2, 1, 1 } }, + { /* 79 */ 1, { 0, 2, 1, 1 } }, + { /* 80 */ 1, { 1, 1, 2, 0 } }, + { /* 81 */ 1, { 1, 1, 2, 1 } }, + { /* 82 */ 0, { 11, 12, 0, 0 } }, + { /* 83 */ 0, { 12, 13, 0, 0 } }, + { /* 84 */ 0, { 13, 14, 0, 0 } }, + { /* 85 */ 0, { 14, 15, 0, 0 } }, + { /* 86 */ 0, { 15, 16, 0, 0 } }, + { /* 87 */ 0, { 16, 17, 0, 0 } }, + { /* 88 */ 0, { 17, 18, 0, 0 } }, + { /* 89 */ 0, { 18, 19, 0, 0 } }, + { /* 90 */ 0, { 19, 20, 0, 0 } }, + { /* 91 */ 0, { 20, 21, 0, 0 } }, + { /* 92 */ 0, { 21, 22, 0, 0 } }, + { /* 93 */ 1, { 1, 2, 0, 1 } }, /* 1111101010 */ + { /* 94 */ 1, { 1, 0, 2, 0 } }, /* 1111101011 */ + { /* 95 */ 1, { 1, 0, 2, 1 } }, /* 1111101100 */ + { /* 96 */ 1, { 0, 2, 0, 1 } }, /* 1111101101 */ + { /* 97 */ 1, { 2, 1, 1, 1 } }, /* 1111101110 */ + { /* 98 */ 1, { 1, 1, 1, 2 } }, /* 1111101111 */ + { /* 99 */ 1, { 2, 1, 0, 1 } }, /* 1111110000 */ + { /* 100 */ 1, { 1, 0, 1, 2 } }, /* 1111110001 */ + { /* 101 */ 1, { 0, 0, 2, 2 } }, /* 1111110010 */ + { /* 102 */ 1, { 0, 1, 2, 2 } }, /* 1111110011 */ + { /* 103 */ 1, { 2, 2, 1, 0 } }, /* 1111110100 */ + { /* 104 */ 1, { 1, 2, 2, 0 } }, /* 1111110101 */ + { /* 105 */ 1, { 1, 0, 0, 2 } }, /* 1111110110 */ + { /* 106 */ 1, { 2, 0, 0, 1 } }, /* 1111110111 */ + { /* 107 */ 1, { 0, 2, 2, 1 } }, /* 1111111000 */ + { /* 108 */ 0, { 7, 8, 0, 0 } }, + { /* 109 */ 0, { 8, 9, 0, 0 } }, + { /* 110 */ 0, { 9, 10, 0, 0 } }, + { /* 111 */ 0, { 10, 11, 0, 0 } }, + { /* 112 */ 0, { 11, 12, 0, 0 } }, + { /* 113 */ 0, { 12, 13, 0, 0 } }, + { /* 114 */ 0, { 13, 14, 0, 0 } }, + { /* 115 */ 1, { 2, 2, 0, 0 } }, /* 11111110010 */ + { /* 116 */ 1, { 1, 2, 2, 1 } }, /* 11111110011 */ + { /* 117 */ 1, { 1, 1, 0, 2 } }, /* 11111110100 */ + { /* 118 */ 1, { 2, 0, 1, 1 } }, /* 11111110101 */ + { /* 119 */ 1, { 1, 1, 2, 2 } }, /* 11111110110 */ + { /* 120 */ 1, { 2, 2, 1, 1 } }, /* 11111110111 */ + { /* 121 */ 1, { 0, 2, 2, 0 } }, /* 11111111000 */ + { /* 122 */ 1, { 0, 2, 1, 2 } }, /* 11111111001 */ + { /* 123 */ 0, { 6, 7, 0, 0 } }, + { /* 124 */ 0, { 7, 8, 0, 0 } }, + { /* 125 */ 0, { 8, 9, 0, 0 } }, + { /* 126 */ 0, { 9, 10, 0, 0 } }, + { /* 127 */ 0, { 10, 11, 0, 0 } }, + { /* 128 */ 0, { 11, 12, 0, 0 } }, + { /* 129 */ 1, { 1, 0, 2, 2 } }, /* 111111110100 */ + { /* 130 */ 1, { 2, 2, 0, 1 } }, /* 111111110101 */ + { /* 131 */ 1, { 2, 1, 2, 0 } }, /* 111111110110 */ + { /* 132 */ 1, { 2, 2, 2, 0 } }, /* 111111110111 */ + { /* 133 */ 1, { 0, 2, 2, 2 } }, /* 111111111000 */ + { /* 134 */ 1, { 2, 2, 2, 1 } }, /* 111111111001 */ + { /* 135 */ 1, { 2, 1, 2, 1 } }, /* 111111111010 */ + { /* 136 */ 1, { 1, 2, 1, 2 } }, /* 111111111011 */ + { /* 137 */ 1, { 1, 2, 2, 2 } }, /* 111111111100 */ + { /* 138 */ 0, { 3, 4, 0, 0 } }, + { /* 139 */ 0, { 4, 5, 0, 0 } }, + { /* 140 */ 0, { 5, 6, 0, 0 } }, + { /* 141 */ 1, { 0, 2, 0, 2 } }, /* 1111111111010 */ + { /* 142 */ 1, { 2, 0, 2, 0 } }, /* 1111111111011 */ + { /* 143 */ 1, { 1, 2, 0, 2 } }, /* 1111111111100 */ + { /* 144 */ 0, { 3, 4, 0, 0 } }, + { /* 145 */ 0, { 4, 5, 0, 0 } }, + { /* 146 */ 0, { 5, 6, 0, 0 } }, + { /* 147 */ 1, { 2, 0, 2, 1 } }, /* 11111111111010 */ + { /* 148 */ 1, { 2, 1, 1, 2 } }, /* 11111111111011 */ + { /* 149 */ 1, { 2, 1, 0, 2 } }, /* 11111111111100 */ + { /* 150 */ 0, { 3, 4, 0, 0 } }, + { /* 151 */ 0, { 4, 5, 0, 0 } }, + { /* 152 */ 0, { 5, 6, 0, 0 } }, + { /* 153 */ 1, { 2, 2, 2, 2 } }, /* 111111111111010 */ + { /* 154 */ 1, { 2, 2, 1, 2 } }, /* 111111111111011 */ + { /* 155 */ 1, { 2, 1, 2, 2 } }, /* 111111111111100 */ + { /* 156 */ 1, { 2, 0, 1, 2 } }, /* 111111111111101 */ + { /* 157 */ 1, { 2, 0, 0, 2 } }, /* 111111111111110 */ + { /* 158 */ 0, { 1, 2, 0, 0 } }, + { /* 159 */ 1, { 2, 2, 0, 2 } }, /* 1111111111111110 */ + { /* 160 */ 1, { 2, 0, 2, 2 } } /* 1111111111111111 */ +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_4.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_4.h new file mode 100644 index 000000000..0b85b37b6 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_4.h @@ -0,0 +1,201 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_4.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* 2-step huffman table HCB_4 */ + + +/* 1st step: 5 bits + * 2^5 = 32 entries + * + * Used to find offset into 2nd step table and number of extra bits to get + */ +static const hcb hcb4_1[32] = { + /* 4 bit codewords */ + { /* 00000 */ 0, 0 }, + { /* */ 0, 0 }, + { /* 00010 */ 1, 0 }, + { /* */ 1, 0 }, + { /* 00100 */ 2, 0 }, + { /* */ 2, 0 }, + { /* 00110 */ 3, 0 }, + { /* */ 3, 0 }, + { /* 01000 */ 4, 0 }, + { /* */ 4, 0 }, + { /* 01010 */ 5, 0 }, + { /* */ 5, 0 }, + { /* 01100 */ 6, 0 }, + { /* */ 6, 0 }, + { /* 01110 */ 7, 0 }, + { /* */ 7, 0 }, + { /* 10000 */ 8, 0 }, + { /* */ 8, 0 }, + { /* 10010 */ 9, 0 }, + { /* */ 9, 0 }, + + /* 5 bit codewords */ + { /* 10100 */ 10, 0 }, + { /* 10101 */ 11, 0 }, + { /* 10110 */ 12, 0 }, + { /* 10111 */ 13, 0 }, + { /* 11000 */ 14, 0 }, + { /* 11001 */ 15, 0 }, + + /* 7 bit codewords */ + { /* 11010 */ 16, 2 }, + { /* 11011 */ 20, 2 }, + + /* 7/8 bit codewords */ + { /* 11100 */ 24, 3 }, + + /* 8 bit codewords */ + { /* 11101 */ 32, 3 }, + + /* 8/9 bit codewords */ + { /* 11110 */ 40, 4 }, + + /* 9/10/11/12 bit codewords */ + { /* 11111 */ 56, 7 } + + /* Size of second level table is 56 + 128 = 184 */ +}; + +/* 2nd step table + * + * Gives size of codeword and actual data (x,y,v,w) + */ +static const hcb_2_quad hcb4_2[184] = { + /* 4 bit codewords */ + { 4, 1, 1, 1, 1 }, + { 4, 0, 1, 1, 1 }, + { 4, 1, 1, 0, 1 }, + { 4, 1, 1, 1, 0 }, + { 4, 1, 0, 1, 1 }, + { 4, 1, 0, 0, 0 }, + { 4, 1, 1, 0, 0 }, + { 4, 0, 0, 0, 0 }, + { 4, 0, 0, 1, 1 }, + { 4, 1, 0, 1, 0 }, + + /* 5 bit codewords */ + { 5, 1, 0, 0, 1 }, + { 5, 0, 1, 1, 0 }, + { 5, 0, 0, 0, 1 }, + { 5, 0, 1, 0, 1 }, + { 5, 0, 0, 1, 0 }, + { 5, 0, 1, 0, 0 }, + + /* 7 bit codewords */ + /* first 5 bits: 11010 */ + { 7, 2, 1, 1, 1 }, + { 7, 1, 1, 2, 1 }, + { 7, 1, 2, 1, 1 }, + { 7, 1, 1, 1, 2 }, + /* first 5 bits: 11011 */ + { 7, 2, 1, 1, 0 }, + { 7, 2, 1, 0, 1 }, + { 7, 1, 2, 1, 0 }, + { 7, 2, 0, 1, 1 }, + + /* 7/8 bit codewords */ + /* first 5 bits: 11100 */ + { 7, 0, 1, 2, 1 }, { 7, 0, 1, 2, 1 }, + { 8, 0, 1, 1, 2 }, + { 8, 1, 1, 2, 0 }, + { 8, 0, 2, 1, 1 }, + { 8, 1, 0, 1, 2 }, + { 8, 1, 2, 0, 1 }, + { 8, 1, 1, 0, 2 }, + + /* 8 bit codewords */ + { 8, 1, 0, 2, 1 }, + { 8, 2, 1, 0, 0 }, + { 8, 2, 0, 1, 0 }, + { 8, 1, 2, 0, 0 }, + { 8, 2, 0, 0, 1 }, + { 8, 0, 1, 0, 2 }, + { 8, 0, 2, 1, 0 }, + { 8, 0, 0, 1, 2 }, + + /* 8/9 bit codewords */ + { 8, 0, 1, 2, 0 }, { 8, 0, 1, 2, 0 }, + { 8, 0, 2, 0, 1 }, { 8, 0, 2, 0, 1 }, + { 8, 1, 0, 0, 2 }, { 8, 1, 0, 0, 2 }, + { 8, 0, 0, 2, 1 }, { 8, 0, 0, 2, 1 }, + { 8, 1, 0, 2, 0 }, { 8, 1, 0, 2, 0 }, + { 8, 2, 0, 0, 0 }, { 8, 2, 0, 0, 0 }, + { 8, 0, 0, 0, 2 }, { 8, 0, 0, 0, 2 }, + { 9, 0, 2, 0, 0 }, + { 9, 0, 0, 2, 0 }, + + /* 9/10/11 bit codewords */ + /* 9 bit codewords repeated 2^3 = 8 times */ + { 9, 1, 2, 2, 1 }, { 9, 1, 2, 2, 1 }, { 9, 1, 2, 2, 1 }, { 9, 1, 2, 2, 1 }, + { 9, 1, 2, 2, 1 }, { 9, 1, 2, 2, 1 }, { 9, 1, 2, 2, 1 }, { 9, 1, 2, 2, 1 }, + { 9, 2, 2, 1, 1 }, { 9, 2, 2, 1, 1 }, { 9, 2, 2, 1, 1 }, { 9, 2, 2, 1, 1 }, + { 9, 2, 2, 1, 1 }, { 9, 2, 2, 1, 1 }, { 9, 2, 2, 1, 1 }, { 9, 2, 2, 1, 1 }, + { 9, 2, 1, 2, 1 }, { 9, 2, 1, 2, 1 }, { 9, 2, 1, 2, 1 }, { 9, 2, 1, 2, 1 }, + { 9, 2, 1, 2, 1 }, { 9, 2, 1, 2, 1 }, { 9, 2, 1, 2, 1 }, { 9, 2, 1, 2, 1 }, + { 9, 1, 1, 2, 2 }, { 9, 1, 1, 2, 2 }, { 9, 1, 1, 2, 2 }, { 9, 1, 1, 2, 2 }, + { 9, 1, 1, 2, 2 }, { 9, 1, 1, 2, 2 }, { 9, 1, 1, 2, 2 }, { 9, 1, 1, 2, 2 }, + { 9, 1, 2, 1, 2 }, { 9, 1, 2, 1, 2 }, { 9, 1, 2, 1, 2 }, { 9, 1, 2, 1, 2 }, + { 9, 1, 2, 1, 2 }, { 9, 1, 2, 1, 2 }, { 9, 1, 2, 1, 2 }, { 9, 1, 2, 1, 2 }, + { 9, 2, 1, 1, 2 }, { 9, 2, 1, 1, 2 }, { 9, 2, 1, 1, 2 }, { 9, 2, 1, 1, 2 }, + { 9, 2, 1, 1, 2 }, { 9, 2, 1, 1, 2 }, { 9, 2, 1, 1, 2 }, { 9, 2, 1, 1, 2 }, + /* 10 bit codewords repeated 2^2 = 4 times */ + { 10, 1, 2, 2, 0 }, { 10, 1, 2, 2, 0 }, { 10, 1, 2, 2, 0 }, { 10, 1, 2, 2, 0 }, + { 10, 2, 2, 1, 0 }, { 10, 2, 2, 1, 0 }, { 10, 2, 2, 1, 0 }, { 10, 2, 2, 1, 0 }, + { 10, 2, 1, 2, 0 }, { 10, 2, 1, 2, 0 }, { 10, 2, 1, 2, 0 }, { 10, 2, 1, 2, 0 }, + { 10, 0, 2, 2, 1 }, { 10, 0, 2, 2, 1 }, { 10, 0, 2, 2, 1 }, { 10, 0, 2, 2, 1 }, + { 10, 0, 1, 2, 2 }, { 10, 0, 1, 2, 2 }, { 10, 0, 1, 2, 2 }, { 10, 0, 1, 2, 2 }, + { 10, 2, 2, 0, 1 }, { 10, 2, 2, 0, 1 }, { 10, 2, 2, 0, 1 }, { 10, 2, 2, 0, 1 }, + { 10, 0, 2, 1, 2 }, { 10, 0, 2, 1, 2 }, { 10, 0, 2, 1, 2 }, { 10, 0, 2, 1, 2 }, + { 10, 2, 0, 2, 1 }, { 10, 2, 0, 2, 1 }, { 10, 2, 0, 2, 1 }, { 10, 2, 0, 2, 1 }, + { 10, 1, 0, 2, 2 }, { 10, 1, 0, 2, 2 }, { 10, 1, 0, 2, 2 }, { 10, 1, 0, 2, 2 }, + { 10, 2, 2, 2, 1 }, { 10, 2, 2, 2, 1 }, { 10, 2, 2, 2, 1 }, { 10, 2, 2, 2, 1 }, + { 10, 1, 2, 0, 2 }, { 10, 1, 2, 0, 2 }, { 10, 1, 2, 0, 2 }, { 10, 1, 2, 0, 2 }, + { 10, 2, 0, 1, 2 }, { 10, 2, 0, 1, 2 }, { 10, 2, 0, 1, 2 }, { 10, 2, 0, 1, 2 }, + { 10, 2, 1, 0, 2 }, { 10, 2, 1, 0, 2 }, { 10, 2, 1, 0, 2 }, { 10, 2, 1, 0, 2 }, + { 10, 1, 2, 2, 2 }, { 10, 1, 2, 2, 2 }, { 10, 1, 2, 2, 2 }, { 10, 1, 2, 2, 2 }, + /* 11 bit codewords repeated 2^1 = 2 times */ + { 11, 2, 1, 2, 2 }, { 11, 2, 1, 2, 2 }, + { 11, 2, 2, 1, 2 }, { 11, 2, 2, 1, 2 }, + { 11, 0, 2, 2, 0 }, { 11, 0, 2, 2, 0 }, + { 11, 2, 2, 0, 0 }, { 11, 2, 2, 0, 0 }, + { 11, 0, 0, 2, 2 }, { 11, 0, 0, 2, 2 }, + { 11, 2, 0, 2, 0 }, { 11, 2, 0, 2, 0 }, + { 11, 0, 2, 0, 2 }, { 11, 0, 2, 0, 2 }, + { 11, 2, 0, 0, 2 }, { 11, 2, 0, 0, 2 }, + { 11, 2, 2, 2, 2 }, { 11, 2, 2, 2, 2 }, + { 11, 0, 2, 2, 2 }, { 11, 0, 2, 2, 2 }, + { 11, 2, 2, 2, 0 }, { 11, 2, 2, 2, 0 }, + /* 12 bit codewords */ + { 12, 2, 2, 0, 2 }, + { 12, 2, 0, 2, 2 }, +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_5.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_5.h new file mode 100644 index 000000000..16c7c5a55 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_5.h @@ -0,0 +1,196 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_5.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* Binary search huffman table HCB_5 */ + + +static const hcb_bin_pair hcb5[161] = { + { /* 0 */ 0, { 1, 2 } }, + { /* 1 */ 1, { 0, 0 } }, /* 0 */ + { /* 2 */ 0, { 1, 2 } }, + { /* 3 */ 0, { 2, 3 } }, + { /* 4 */ 0, { 3, 4 } }, + { /* 5 */ 0, { 4, 5 } }, + { /* 6 */ 0, { 5, 6 } }, + { /* 7 */ 0, { 6, 7 } }, + { /* 8 */ 0, { 7, 8 } }, + { /* 9 */ 1, { -1, 0 } }, /* 1000 */ + { /* 10 */ 1, { 1, 0 } }, /* 1001 */ + { /* 11 */ 1, { 0, 1 } }, /* 1010 */ + { /* 12 */ 1, { 0, -1 } }, /* 1011 */ + { /* 13 */ 0, { 4, 5 } }, + { /* 14 */ 0, { 5, 6 } }, + { /* 15 */ 0, { 6, 7 } }, + { /* 16 */ 0, { 7, 8 } }, + { /* 17 */ 1, { 1, -1 } }, + { /* 18 */ 1, { -1, 1 } }, + { /* 19 */ 1, { -1, -1 } }, + { /* 20 */ 1, { 1, 1 } }, + { /* 21 */ 0, { 4, 5 } }, + { /* 22 */ 0, { 5, 6 } }, + { /* 23 */ 0, { 6, 7 } }, + { /* 24 */ 0, { 7, 8 } }, + { /* 25 */ 0, { 8, 9 } }, + { /* 26 */ 0, { 9, 10 } }, + { /* 27 */ 0, { 10, 11 } }, + { /* 28 */ 0, { 11, 12 } }, + { /* 29 */ 0, { 12, 13 } }, + { /* 30 */ 0, { 13, 14 } }, + { /* 31 */ 0, { 14, 15 } }, + { /* 32 */ 0, { 15, 16 } }, + { /* 33 */ 1, { -2, 0 } }, + { /* 34 */ 1, { 0, 2 } }, + { /* 35 */ 1, { 2, 0 } }, + { /* 36 */ 1, { 0, -2 } }, + { /* 37 */ 0, { 12, 13 } }, + { /* 38 */ 0, { 13, 14 } }, + { /* 39 */ 0, { 14, 15 } }, + { /* 40 */ 0, { 15, 16 } }, + { /* 41 */ 0, { 16, 17 } }, + { /* 42 */ 0, { 17, 18 } }, + { /* 43 */ 0, { 18, 19 } }, + { /* 44 */ 0, { 19, 20 } }, + { /* 45 */ 0, { 20, 21 } }, + { /* 46 */ 0, { 21, 22 } }, + { /* 47 */ 0, { 22, 23 } }, + { /* 48 */ 0, { 23, 24 } }, + { /* 49 */ 1, { -2, -1 } }, + { /* 50 */ 1, { 2, 1 } }, + { /* 51 */ 1, { -1, -2 } }, + { /* 52 */ 1, { 1, 2 } }, + { /* 53 */ 1, { -2, 1 } }, + { /* 54 */ 1, { 2, -1 } }, + { /* 55 */ 1, { -1, 2 } }, + { /* 56 */ 1, { 1, -2 } }, + { /* 57 */ 1, { -3, 0 } }, + { /* 58 */ 1, { 3, 0 } }, + { /* 59 */ 1, { 0, -3 } }, + { /* 60 */ 1, { 0, 3 } }, + { /* 61 */ 0, { 12, 13 } }, + { /* 62 */ 0, { 13, 14 } }, + { /* 63 */ 0, { 14, 15 } }, + { /* 64 */ 0, { 15, 16 } }, + { /* 65 */ 0, { 16, 17 } }, + { /* 66 */ 0, { 17, 18 } }, + { /* 67 */ 0, { 18, 19 } }, + { /* 68 */ 0, { 19, 20 } }, + { /* 69 */ 0, { 20, 21 } }, + { /* 70 */ 0, { 21, 22 } }, + { /* 71 */ 0, { 22, 23 } }, + { /* 72 */ 0, { 23, 24 } }, + { /* 73 */ 1, { -3, -1 } }, + { /* 74 */ 1, { 1, 3 } }, + { /* 75 */ 1, { 3, 1 } }, + { /* 76 */ 1, { -1, -3 } }, + { /* 77 */ 1, { -3, 1 } }, + { /* 78 */ 1, { 3, -1 } }, + { /* 79 */ 1, { 1, -3 } }, + { /* 80 */ 1, { -1, 3 } }, + { /* 81 */ 1, { -2, 2 } }, + { /* 82 */ 1, { 2, 2 } }, + { /* 83 */ 1, { -2, -2 } }, + { /* 84 */ 1, { 2, -2 } }, + { /* 85 */ 0, { 12, 13 } }, + { /* 86 */ 0, { 13, 14 } }, + { /* 87 */ 0, { 14, 15 } }, + { /* 88 */ 0, { 15, 16 } }, + { /* 89 */ 0, { 16, 17 } }, + { /* 90 */ 0, { 17, 18 } }, + { /* 91 */ 0, { 18, 19 } }, + { /* 92 */ 0, { 19, 20 } }, + { /* 93 */ 0, { 20, 21 } }, + { /* 94 */ 0, { 21, 22 } }, + { /* 95 */ 0, { 22, 23 } }, + { /* 96 */ 0, { 23, 24 } }, + { /* 97 */ 1, { -3, -2 } }, + { /* 98 */ 1, { 3, -2 } }, + { /* 99 */ 1, { -2, 3 } }, + { /* 100 */ 1, { 2, -3 } }, + { /* 101 */ 1, { 3, 2 } }, + { /* 102 */ 1, { 2, 3 } }, + { /* 103 */ 1, { -3, 2 } }, + { /* 104 */ 1, { -2, -3 } }, + { /* 105 */ 1, { 0, -4 } }, + { /* 106 */ 1, { -4, 0 } }, + { /* 107 */ 1, { 4, 1 } }, + { /* 108 */ 1, { 4, 0 } }, + { /* 109 */ 0, { 12, 13 } }, + { /* 110 */ 0, { 13, 14 } }, + { /* 111 */ 0, { 14, 15 } }, + { /* 112 */ 0, { 15, 16 } }, + { /* 113 */ 0, { 16, 17 } }, + { /* 114 */ 0, { 17, 18 } }, + { /* 115 */ 0, { 18, 19 } }, + { /* 116 */ 0, { 19, 20 } }, + { /* 117 */ 0, { 20, 21 } }, + { /* 118 */ 0, { 21, 22 } }, + { /* 119 */ 0, { 22, 23 } }, + { /* 120 */ 0, { 23, 24 } }, + { /* 121 */ 1, { -4, -1 } }, + { /* 122 */ 1, { 0, 4 } }, + { /* 123 */ 1, { 4, -1 } }, + { /* 124 */ 1, { -1, -4 } }, + { /* 125 */ 1, { 1, 4 } }, + { /* 126 */ 1, { -1, 4 } }, + { /* 127 */ 1, { -4, 1 } }, + { /* 128 */ 1, { 1, -4 } }, + { /* 129 */ 1, { 3, -3 } }, + { /* 130 */ 1, { -3, -3 } }, + { /* 131 */ 1, { -3, 3 } }, + { /* 132 */ 1, { -2, 4 } }, + { /* 133 */ 1, { -4, -2 } }, + { /* 134 */ 1, { 4, 2 } }, + { /* 135 */ 1, { 2, -4 } }, + { /* 136 */ 1, { 2, 4 } }, + { /* 137 */ 1, { 3, 3 } }, + { /* 138 */ 1, { -4, 2 } }, + { /* 139 */ 0, { 6, 7 } }, + { /* 140 */ 0, { 7, 8 } }, + { /* 141 */ 0, { 8, 9 } }, + { /* 142 */ 0, { 9, 10 } }, + { /* 143 */ 0, { 10, 11 } }, + { /* 144 */ 0, { 11, 12 } }, + { /* 145 */ 1, { -2, -4 } }, + { /* 146 */ 1, { 4, -2 } }, + { /* 147 */ 1, { 3, -4 } }, + { /* 148 */ 1, { -4, -3 } }, + { /* 149 */ 1, { -4, 3 } }, + { /* 150 */ 1, { 3, 4 } }, + { /* 151 */ 1, { -3, 4 } }, + { /* 152 */ 1, { 4, 3 } }, + { /* 153 */ 1, { 4, -3 } }, + { /* 154 */ 1, { -3, -4 } }, + { /* 155 */ 0, { 2, 3 } }, + { /* 156 */ 0, { 3, 4 } }, + { /* 157 */ 1, { 4, -4 } }, + { /* 158 */ 1, { -4, 4 } }, + { /* 159 */ 1, { 4, 4 } }, + { /* 160 */ 1, { -4, -4 } } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_6.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_6.h new file mode 100644 index 000000000..c401eafc4 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_6.h @@ -0,0 +1,184 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_6.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* 2-step huffman table HCB_6 */ + + +/* 1st step: 5 bits + * 2^5 = 32 entries + * + * Used to find offset into 2nd step table and number of extra bits to get + */ +static const hcb hcb6_1[32] = { + /* 4 bit codewords */ + { /* 00000 */ 0, 0 }, + { /* */ 0, 0 }, + { /* 00010 */ 1, 0 }, + { /* */ 1, 0 }, + { /* 00100 */ 2, 0 }, + { /* */ 2, 0 }, + { /* 00110 */ 3, 0 }, + { /* */ 3, 0 }, + { /* 01000 */ 4, 0 }, + { /* */ 4, 0 }, + { /* 01010 */ 5, 0 }, + { /* */ 5, 0 }, + { /* 01100 */ 6, 0 }, + { /* */ 6, 0 }, + { /* 01110 */ 7, 0 }, + { /* */ 7, 0 }, + { /* 10000 */ 8, 0 }, + { /* */ 8, 0 }, + + /* 6 bit codewords */ + { /* 10010 */ 9, 1 }, + { /* 10011 */ 11, 1 }, + { /* 10100 */ 13, 1 }, + { /* 10101 */ 15, 1 }, + { /* 10110 */ 17, 1 }, + { /* 10111 */ 19, 1 }, + { /* 11000 */ 21, 1 }, + { /* 11001 */ 23, 1 }, + + /* 7 bit codewords */ + { /* 11010 */ 25, 2 }, + { /* 11011 */ 29, 2 }, + { /* 11100 */ 33, 2 }, + + /* 7/8 bit codewords */ + { /* 11101 */ 37, 3 }, + + /* 8/9 bit codewords */ + { /* 11110 */ 45, 4 }, + + /* 9/10/11 bit codewords */ + { /* 11111 */ 61, 6 } + + /* Size of second level table is 61 + 64 = 125 */ +}; + +/* 2nd step table + * + * Gives size of codeword and actual data (x,y,v,w) + */ +static const hcb_2_pair hcb6_2[125] = { + /* 4 bit codewords */ + { 4, 0, 0 }, + { 4, 1, 0 }, + { 4, 0, -1 }, + { 4, 0, 1 }, + { 4, -1, 0 }, + { 4, 1, 1 }, + { 4, -1, 1 }, + { 4, 1, -1 }, + { 4, -1, -1 }, + + /* 6 bit codewords */ + { 6, 2, -1 }, + { 6, 2, 1 }, + { 6, -2, 1 }, + { 6, -2, -1 }, + { 6, -2, 0 }, + { 6, -1, 2 }, + { 6, 2, 0 }, + { 6, 1, -2 }, + { 6, 1, 2 }, + { 6, 0, -2 }, + { 6, -1, -2 }, + { 6, 0, 2 }, + { 6, 2, -2 }, + { 6, -2, 2 }, + { 6, -2, -2 }, + { 6, 2, 2 }, + + /* 7 bit codewords */ + { 7, -3, 1 }, + { 7, 3, 1 }, + { 7, 3, -1 }, + { 7, -1, 3 }, + { 7, -3, -1 }, + { 7, 1, 3 }, + { 7, 1, -3 }, + { 7, -1, -3 }, + { 7, 3, 0 }, + { 7, -3, 0 }, + { 7, 0, -3 }, + { 7, 0, 3 }, + + /* 7/8 bit codewords */ + { 7, 3, 2 }, { 7, 3, 2 }, + { 8, -3, -2 }, + { 8, -2, 3 }, + { 8, 2, 3 }, + { 8, 3, -2 }, + { 8, 2, -3 }, + { 8, -2, -3 }, + + /* 8 bit codewords */ + { 8, -3, 2 }, { 8, -3, 2 }, + { 8, 3, 3 }, { 8, 3, 3 }, + { 9, 3, -3 }, + { 9, -3, -3 }, + { 9, -3, 3 }, + { 9, 1, -4 }, + { 9, -1, -4 }, + { 9, 4, 1 }, + { 9, -4, 1 }, + { 9, -4, -1 }, + { 9, 1, 4 }, + { 9, 4, -1 }, + { 9, -1, 4 }, + { 9, 0, -4 }, + + /* 9/10/11 bit codewords */ + { 9, -4, 2 }, { 9, -4, 2 }, { 9, -4, 2 }, { 9, -4, 2 }, + { 9, -4, -2 }, { 9, -4, -2 }, { 9, -4, -2 }, { 9, -4, -2 }, + { 9, 2, 4 }, { 9, 2, 4 }, { 9, 2, 4 }, { 9, 2, 4 }, + { 9, -2, -4 }, { 9, -2, -4 }, { 9, -2, -4 }, { 9, -2, -4 }, + { 9, -4, 0 }, { 9, -4, 0 }, { 9, -4, 0 }, { 9, -4, 0 }, + { 9, 4, 2 }, { 9, 4, 2 }, { 9, 4, 2 }, { 9, 4, 2 }, + { 9, 4, -2 }, { 9, 4, -2 }, { 9, 4, -2 }, { 9, 4, -2 }, + { 9, -2, 4 }, { 9, -2, 4 }, { 9, -2, 4 }, { 9, -2, 4 }, + { 9, 4, 0 }, { 9, 4, 0 }, { 9, 4, 0 }, { 9, 4, 0 }, + { 9, 2, -4 }, { 9, 2, -4 }, { 9, 2, -4 }, { 9, 2, -4 }, + { 9, 0, 4 }, { 9, 0, 4 }, { 9, 0, 4 }, { 9, 0, 4 }, + { 10, -3, -4 }, { 10, -3, -4 }, + { 10, -3, 4 }, { 10, -3, 4 }, + { 10, 3, -4 }, { 10, 3, -4 }, + { 10, 4, -3 }, { 10, 4, -3 }, + { 10, 3, 4 }, { 10, 3, 4 }, + { 10, 4, 3 }, { 10, 4, 3 }, + { 10, -4, 3 }, { 10, -4, 3 }, + { 10, -4, -3 }, { 10, -4, -3 }, + { 11, 4, 4 }, + { 11, -4, 4 }, + { 11, -4, -4 }, + { 11, 4, -4 } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_7.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_7.h new file mode 100644 index 000000000..c365b1410 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_7.h @@ -0,0 +1,162 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_7.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* Binary search huffman table HCB_7 */ + + +static const hcb_bin_pair hcb7[127] = { + { /* 0 */ 0, { 1, 2 } }, + { /* 1 */ 1, { 0, 0 } }, + { /* 2 */ 0, { 1, 2 } }, + { /* 3 */ 0, { 2, 3 } }, + { /* 4 */ 0, { 3, 4 } }, + { /* 5 */ 1, { 1, 0 } }, + { /* 6 */ 1, { 0, 1 } }, + { /* 7 */ 0, { 2, 3 } }, + { /* 8 */ 0, { 3, 4 } }, + { /* 9 */ 1, { 1, 1 } }, + { /* 10 */ 0, { 3, 4 } }, + { /* 11 */ 0, { 4, 5 } }, + { /* 12 */ 0, { 5, 6 } }, + { /* 13 */ 0, { 6, 7 } }, + { /* 14 */ 0, { 7, 8 } }, + { /* 15 */ 0, { 8, 9 } }, + { /* 16 */ 0, { 9, 10 } }, + { /* 17 */ 0, { 10, 11 } }, + { /* 18 */ 0, { 11, 12 } }, + { /* 19 */ 1, { 2, 1 } }, + { /* 20 */ 1, { 1, 2 } }, + { /* 21 */ 1, { 2, 0 } }, + { /* 22 */ 1, { 0, 2 } }, + { /* 23 */ 0, { 8, 9 } }, + { /* 24 */ 0, { 9, 10 } }, + { /* 25 */ 0, { 10, 11 } }, + { /* 26 */ 0, { 11, 12 } }, + { /* 27 */ 0, { 12, 13 } }, + { /* 28 */ 0, { 13, 14 } }, + { /* 29 */ 0, { 14, 15 } }, + { /* 30 */ 0, { 15, 16 } }, + { /* 31 */ 1, { 3, 1 } }, + { /* 32 */ 1, { 1, 3 } }, + { /* 33 */ 1, { 2, 2 } }, + { /* 34 */ 1, { 3, 0 } }, + { /* 35 */ 1, { 0, 3 } }, + { /* 36 */ 0, { 11, 12 } }, + { /* 37 */ 0, { 12, 13 } }, + { /* 38 */ 0, { 13, 14 } }, + { /* 39 */ 0, { 14, 15 } }, + { /* 40 */ 0, { 15, 16 } }, + { /* 41 */ 0, { 16, 17 } }, + { /* 42 */ 0, { 17, 18 } }, + { /* 43 */ 0, { 18, 19 } }, + { /* 44 */ 0, { 19, 20 } }, + { /* 45 */ 0, { 20, 21 } }, + { /* 46 */ 0, { 21, 22 } }, + { /* 47 */ 1, { 2, 3 } }, + { /* 48 */ 1, { 3, 2 } }, + { /* 49 */ 1, { 1, 4 } }, + { /* 50 */ 1, { 4, 1 } }, + { /* 51 */ 1, { 1, 5 } }, + { /* 52 */ 1, { 5, 1 } }, + { /* 53 */ 1, { 3, 3 } }, + { /* 54 */ 1, { 2, 4 } }, + { /* 55 */ 1, { 0, 4 } }, + { /* 56 */ 1, { 4, 0 } }, + { /* 57 */ 0, { 12, 13 } }, + { /* 58 */ 0, { 13, 14 } }, + { /* 59 */ 0, { 14, 15 } }, + { /* 60 */ 0, { 15, 16 } }, + { /* 61 */ 0, { 16, 17 } }, + { /* 62 */ 0, { 17, 18 } }, + { /* 63 */ 0, { 18, 19 } }, + { /* 64 */ 0, { 19, 20 } }, + { /* 65 */ 0, { 20, 21 } }, + { /* 66 */ 0, { 21, 22 } }, + { /* 67 */ 0, { 22, 23 } }, + { /* 68 */ 0, { 23, 24 } }, + { /* 69 */ 1, { 4, 2 } }, + { /* 70 */ 1, { 2, 5 } }, + { /* 71 */ 1, { 5, 2 } }, + { /* 72 */ 1, { 0, 5 } }, + { /* 73 */ 1, { 6, 1 } }, + { /* 74 */ 1, { 5, 0 } }, + { /* 75 */ 1, { 1, 6 } }, + { /* 76 */ 1, { 4, 3 } }, + { /* 77 */ 1, { 3, 5 } }, + { /* 78 */ 1, { 3, 4 } }, + { /* 79 */ 1, { 5, 3 } }, + { /* 80 */ 1, { 2, 6 } }, + { /* 81 */ 1, { 6, 2 } }, + { /* 82 */ 1, { 1, 7 } }, + { /* 83 */ 0, { 10, 11 } }, + { /* 84 */ 0, { 11, 12 } }, + { /* 85 */ 0, { 12, 13 } }, + { /* 86 */ 0, { 13, 14 } }, + { /* 87 */ 0, { 14, 15 } }, + { /* 88 */ 0, { 15, 16 } }, + { /* 89 */ 0, { 16, 17 } }, + { /* 90 */ 0, { 17, 18 } }, + { /* 91 */ 0, { 18, 19 } }, + { /* 92 */ 0, { 19, 20 } }, + { /* 93 */ 1, { 3, 6 } }, + { /* 94 */ 1, { 0, 6 } }, + { /* 95 */ 1, { 6, 0 } }, + { /* 96 */ 1, { 4, 4 } }, + { /* 97 */ 1, { 7, 1 } }, + { /* 98 */ 1, { 4, 5 } }, + { /* 99 */ 1, { 7, 2 } }, + { /* 100 */ 1, { 5, 4 } }, + { /* 101 */ 1, { 6, 3 } }, + { /* 102 */ 1, { 2, 7 } }, + { /* 103 */ 1, { 7, 3 } }, + { /* 104 */ 1, { 6, 4 } }, + { /* 105 */ 1, { 5, 5 } }, + { /* 106 */ 1, { 4, 6 } }, + { /* 107 */ 1, { 3, 7 } }, + { /* 108 */ 0, { 5, 6 } }, + { /* 109 */ 0, { 6, 7 } }, + { /* 110 */ 0, { 7, 8 } }, + { /* 111 */ 0, { 8, 9 } }, + { /* 112 */ 0, { 9, 10 } }, + { /* 113 */ 1, { 7, 0 } }, + { /* 114 */ 1, { 0, 7 } }, + { /* 115 */ 1, { 6, 5 } }, + { /* 116 */ 1, { 5, 6 } }, + { /* 117 */ 1, { 7, 4 } }, + { /* 118 */ 1, { 4, 7 } }, + { /* 119 */ 1, { 5, 7 } }, + { /* 120 */ 1, { 7, 5 } }, + { /* 121 */ 0, { 2, 3 } }, + { /* 122 */ 0, { 3, 4 } }, + { /* 123 */ 1, { 7, 6 } }, + { /* 124 */ 1, { 6, 6 } }, + { /* 125 */ 1, { 6, 7 } }, + { /* 126 */ 1, { 7, 7 } } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_8.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_8.h new file mode 100644 index 000000000..0344eb0e0 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_8.h @@ -0,0 +1,175 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_8.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* 2-step huffman table HCB_8 */ + + +/* 1st step: 5 bits + * 2^5 = 32 entries + * + * Used to find offset into 2nd step table and number of extra bits to get + */ +static const hcb hcb8_1[32] = { + /* 3 bit codeword */ + { /* 00000 */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + { /* */ 0, 0 }, + + /* 4 bit codewords */ + { /* 00100 */ 1, 0 }, + { /* */ 1, 0 }, + { /* 00110 */ 2, 0 }, + { /* */ 2, 0 }, + { /* 01000 */ 3, 0 }, + { /* */ 3, 0 }, + { /* 01010 */ 4, 0 }, + { /* */ 4, 0 }, + { /* 01100 */ 5, 0 }, + { /* */ 5, 0 }, + + /* 5 bit codewords */ + { /* 01110 */ 6, 0 }, + { /* 01111 */ 7, 0 }, + { /* 10000 */ 8, 0 }, + { /* 10001 */ 9, 0 }, + { /* 10010 */ 10, 0 }, + { /* 10011 */ 11, 0 }, + { /* 10100 */ 12, 0 }, + + /* 6 bit codewords */ + { /* 10101 */ 13, 1 }, + { /* 10110 */ 15, 1 }, + { /* 10111 */ 17, 1 }, + { /* 11000 */ 19, 1 }, + { /* 11001 */ 21, 1 }, + + /* 7 bit codewords */ + { /* 11010 */ 23, 2 }, + { /* 11011 */ 27, 2 }, + { /* 11100 */ 31, 2 }, + + /* 7/8 bit codewords */ + { /* 11101 */ 35, 3 }, + + /* 8 bit codewords */ + { /* 11110 */ 43, 3 }, + + /* 8/9/10 bit codewords */ + { /* 11111 */ 51, 5 } + + /* Size of second level table is 51 + 32 = 83 */ +}; + +/* 2nd step table + * + * Gives size of codeword and actual data (x,y,v,w) + */ +static const hcb_2_pair hcb8_2[83] = { + /* 3 bit codeword */ + { 3, 1, 1 }, + + /* 4 bit codewords */ + { 4, 2, 1 }, + { 4, 1, 0 }, + { 4, 1, 2 }, + { 4, 0, 1 }, + { 4, 2, 2 }, + + /* 5 bit codewords */ + { 5, 0, 0 }, + { 5, 2, 0 }, + { 5, 0, 2 }, + { 5, 3, 1 }, + { 5, 1, 3 }, + { 5, 3, 2 }, + { 5, 2, 3 }, + + /* 6 bit codewords */ + { 6, 3, 3 }, + { 6, 4, 1 }, + { 6, 1, 4 }, + { 6, 4, 2 }, + { 6, 2, 4 }, + { 6, 3, 0 }, + { 6, 0, 3 }, + { 6, 4, 3 }, + { 6, 3, 4 }, + { 6, 5, 2 }, + + /* 7 bit codewords */ + { 7, 5, 1 }, + { 7, 2, 5 }, + { 7, 1, 5 }, + { 7, 5, 3 }, + { 7, 3, 5 }, + { 7, 4, 4 }, + { 7, 5, 4 }, + { 7, 0, 4 }, + { 7, 4, 5 }, + { 7, 4, 0 }, + { 7, 2, 6 }, + { 7, 6, 2 }, + + /* 7/8 bit codewords */ + { 7, 6, 1 }, { 7, 6, 1 }, + { 7, 1, 6 }, { 7, 1, 6 }, + { 8, 3, 6 }, + { 8, 6, 3 }, + { 8, 5, 5 }, + { 8, 5, 0 }, + + /* 8 bit codewords */ + { 8, 6, 4 }, + { 8, 0, 5 }, + { 8, 4, 6 }, + { 8, 7, 1 }, + { 8, 7, 2 }, + { 8, 2, 7 }, + { 8, 6, 5 }, + { 8, 7, 3 }, + + /* 8/9/10 bit codewords */ + { 8, 1, 7 }, { 8, 1, 7 }, { 8, 1, 7 }, { 8, 1, 7 }, + { 8, 5, 6 }, { 8, 5, 6 }, { 8, 5, 6 }, { 8, 5, 6 }, + { 8, 3, 7 }, { 8, 3, 7 }, { 8, 3, 7 }, { 8, 3, 7 }, + { 9, 6, 6 }, { 9, 6, 6 }, + { 9, 7, 4 }, { 9, 7, 4 }, + { 9, 6, 0 }, { 9, 6, 0 }, + { 9, 4, 7 }, { 9, 4, 7 }, + { 9, 0, 6 }, { 9, 0, 6 }, + { 9, 7, 5 }, { 9, 7, 5 }, + { 9, 7, 6 }, { 9, 7, 6 }, + { 9, 6, 7 }, { 9, 6, 7 }, + { 10, 5, 7 }, + { 10, 7, 0 }, + { 10, 0, 7 }, + { 10, 7, 7 } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_9.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_9.h new file mode 100644 index 000000000..8f91ae273 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_9.h @@ -0,0 +1,372 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_9.h,v 1.5 2007/11/01 12:34:11 menno Exp $ +**/ + +/* Binary search huffman table HCB_9 */ + + +static const hcb_bin_pair hcb9[337] = { + { /* 0 */ 0, { 1, 2 } }, + { /* 1 */ 1, { 0, 0 } }, + { /* 2 */ 0, { 1, 2 } }, + { /* 3 */ 0, { 2, 3 } }, + { /* 4 */ 0, { 3, 4 } }, + { /* 5 */ 1, { 1, 0 } }, + { /* 6 */ 1, { 0, 1 } }, + { /* 7 */ 0, { 2, 3 } }, + { /* 8 */ 0, { 3, 4 } }, + { /* 9 */ 1, { 1, 1 } }, + { /* 10 */ 0, { 3, 4 } }, + { /* 11 */ 0, { 4, 5 } }, + { /* 12 */ 0, { 5, 6 } }, + { /* 13 */ 0, { 6, 7 } }, + { /* 14 */ 0, { 7, 8 } }, + { /* 15 */ 0, { 8, 9 } }, + { /* 16 */ 0, { 9, 10 } }, + { /* 17 */ 0, { 10, 11 } }, + { /* 18 */ 0, { 11, 12 } }, + { /* 19 */ 1, { 2, 1 } }, + { /* 20 */ 1, { 1, 2 } }, + { /* 21 */ 1, { 2, 0 } }, + { /* 22 */ 1, { 0, 2 } }, + { /* 23 */ 0, { 8, 9 } }, + { /* 24 */ 0, { 9, 10 } }, + { /* 25 */ 0, { 10, 11 } }, + { /* 26 */ 0, { 11, 12 } }, + { /* 27 */ 0, { 12, 13 } }, + { /* 28 */ 0, { 13, 14 } }, + { /* 29 */ 0, { 14, 15 } }, + { /* 30 */ 0, { 15, 16 } }, + { /* 31 */ 1, { 3, 1 } }, + { /* 32 */ 1, { 2, 2 } }, + { /* 33 */ 1, { 1, 3 } }, + { /* 34 */ 0, { 13, 14 } }, + { /* 35 */ 0, { 14, 15 } }, + { /* 36 */ 0, { 15, 16 } }, + { /* 37 */ 0, { 16, 17 } }, + { /* 38 */ 0, { 17, 18 } }, + { /* 39 */ 0, { 18, 19 } }, + { /* 40 */ 0, { 19, 20 } }, + { /* 41 */ 0, { 20, 21 } }, + { /* 42 */ 0, { 21, 22 } }, + { /* 43 */ 0, { 22, 23 } }, + { /* 44 */ 0, { 23, 24 } }, + { /* 45 */ 0, { 24, 25 } }, + { /* 46 */ 0, { 25, 26 } }, + { /* 47 */ 1, { 3, 0 } }, + { /* 48 */ 1, { 0, 3 } }, + { /* 49 */ 1, { 2, 3 } }, + { /* 50 */ 1, { 3, 2 } }, + { /* 51 */ 1, { 1, 4 } }, + { /* 52 */ 1, { 4, 1 } }, + { /* 53 */ 1, { 2, 4 } }, + { /* 54 */ 1, { 1, 5 } }, + { /* 55 */ 0, { 18, 19 } }, + { /* 56 */ 0, { 19, 20 } }, + { /* 57 */ 0, { 20, 21 } }, + { /* 58 */ 0, { 21, 22 } }, + { /* 59 */ 0, { 22, 23 } }, + { /* 60 */ 0, { 23, 24 } }, + { /* 61 */ 0, { 24, 25 } }, + { /* 62 */ 0, { 25, 26 } }, + { /* 63 */ 0, { 26, 27 } }, + { /* 64 */ 0, { 27, 28 } }, + { /* 65 */ 0, { 28, 29 } }, + { /* 66 */ 0, { 29, 30 } }, + { /* 67 */ 0, { 30, 31 } }, + { /* 68 */ 0, { 31, 32 } }, + { /* 69 */ 0, { 32, 33 } }, + { /* 70 */ 0, { 33, 34 } }, + { /* 71 */ 0, { 34, 35 } }, + { /* 72 */ 0, { 35, 36 } }, + { /* 73 */ 1, { 4, 2 } }, + { /* 74 */ 1, { 3, 3 } }, + { /* 75 */ 1, { 0, 4 } }, + { /* 76 */ 1, { 4, 0 } }, + { /* 77 */ 1, { 5, 1 } }, + { /* 78 */ 1, { 2, 5 } }, + { /* 79 */ 1, { 1, 6 } }, + { /* 80 */ 1, { 3, 4 } }, + { /* 81 */ 1, { 5, 2 } }, + { /* 82 */ 1, { 6, 1 } }, + { /* 83 */ 1, { 4, 3 } }, + { /* 84 */ 0, { 25, 26 } }, + { /* 85 */ 0, { 26, 27 } }, + { /* 86 */ 0, { 27, 28 } }, + { /* 87 */ 0, { 28, 29 } }, + { /* 88 */ 0, { 29, 30 } }, + { /* 89 */ 0, { 30, 31 } }, + { /* 90 */ 0, { 31, 32 } }, + { /* 91 */ 0, { 32, 33 } }, + { /* 92 */ 0, { 33, 34 } }, + { /* 93 */ 0, { 34, 35 } }, + { /* 94 */ 0, { 35, 36 } }, + { /* 95 */ 0, { 36, 37 } }, + { /* 96 */ 0, { 37, 38 } }, + { /* 97 */ 0, { 38, 39 } }, + { /* 98 */ 0, { 39, 40 } }, + { /* 99 */ 0, { 40, 41 } }, + { /* 100 */ 0, { 41, 42 } }, + { /* 101 */ 0, { 42, 43 } }, + { /* 102 */ 0, { 43, 44 } }, + { /* 103 */ 0, { 44, 45 } }, + { /* 104 */ 0, { 45, 46 } }, + { /* 105 */ 0, { 46, 47 } }, + { /* 106 */ 0, { 47, 48 } }, + { /* 107 */ 0, { 48, 49 } }, + { /* 108 */ 0, { 49, 50 } }, + { /* 109 */ 1, { 0, 5 } }, + { /* 110 */ 1, { 2, 6 } }, + { /* 111 */ 1, { 5, 0 } }, + { /* 112 */ 1, { 1, 7 } }, + { /* 113 */ 1, { 3, 5 } }, + { /* 114 */ 1, { 1, 8 } }, + { /* 115 */ 1, { 8, 1 } }, + { /* 116 */ 1, { 4, 4 } }, + { /* 117 */ 1, { 5, 3 } }, + { /* 118 */ 1, { 6, 2 } }, + { /* 119 */ 1, { 7, 1 } }, + { /* 120 */ 1, { 0, 6 } }, + { /* 121 */ 1, { 8, 2 } }, + { /* 122 */ 1, { 2, 8 } }, + { /* 123 */ 1, { 3, 6 } }, + { /* 124 */ 1, { 2, 7 } }, + { /* 125 */ 1, { 4, 5 } }, + { /* 126 */ 1, { 9, 1 } }, + { /* 127 */ 1, { 1, 9 } }, + { /* 128 */ 1, { 7, 2 } }, + { /* 129 */ 0, { 30, 31 } }, + { /* 130 */ 0, { 31, 32 } }, + { /* 131 */ 0, { 32, 33 } }, + { /* 132 */ 0, { 33, 34 } }, + { /* 133 */ 0, { 34, 35 } }, + { /* 134 */ 0, { 35, 36 } }, + { /* 135 */ 0, { 36, 37 } }, + { /* 136 */ 0, { 37, 38 } }, + { /* 137 */ 0, { 38, 39 } }, + { /* 138 */ 0, { 39, 40 } }, + { /* 139 */ 0, { 40, 41 } }, + { /* 140 */ 0, { 41, 42 } }, + { /* 141 */ 0, { 42, 43 } }, + { /* 142 */ 0, { 43, 44 } }, + { /* 143 */ 0, { 44, 45 } }, + { /* 144 */ 0, { 45, 46 } }, + { /* 145 */ 0, { 46, 47 } }, + { /* 146 */ 0, { 47, 48 } }, + { /* 147 */ 0, { 48, 49 } }, + { /* 148 */ 0, { 49, 50 } }, + { /* 149 */ 0, { 50, 51 } }, + { /* 150 */ 0, { 51, 52 } }, + { /* 151 */ 0, { 52, 53 } }, + { /* 152 */ 0, { 53, 54 } }, + { /* 153 */ 0, { 54, 55 } }, + { /* 154 */ 0, { 55, 56 } }, + { /* 155 */ 0, { 56, 57 } }, + { /* 156 */ 0, { 57, 58 } }, + { /* 157 */ 0, { 58, 59 } }, + { /* 158 */ 0, { 59, 60 } }, + { /* 159 */ 1, { 6, 0 } }, + { /* 160 */ 1, { 5, 4 } }, + { /* 161 */ 1, { 6, 3 } }, + { /* 162 */ 1, { 8, 3 } }, + { /* 163 */ 1, { 0, 7 } }, + { /* 164 */ 1, { 9, 2 } }, + { /* 165 */ 1, { 3, 8 } }, + { /* 166 */ 1, { 4, 6 } }, + { /* 167 */ 1, { 3, 7 } }, + { /* 168 */ 1, { 0, 8 } }, + { /* 169 */ 1, { 10, 1 } }, + { /* 170 */ 1, { 6, 4 } }, + { /* 171 */ 1, { 2, 9 } }, + { /* 172 */ 1, { 5, 5 } }, + { /* 173 */ 1, { 8, 0 } }, + { /* 174 */ 1, { 7, 0 } }, + { /* 175 */ 1, { 7, 3 } }, + { /* 176 */ 1, { 10, 2 } }, + { /* 177 */ 1, { 9, 3 } }, + { /* 178 */ 1, { 8, 4 } }, + { /* 179 */ 1, { 1, 10 } }, + { /* 180 */ 1, { 7, 4 } }, + { /* 181 */ 1, { 6, 5 } }, + { /* 182 */ 1, { 5, 6 } }, + { /* 183 */ 1, { 4, 8 } }, + { /* 184 */ 1, { 4, 7 } }, + { /* 185 */ 1, { 3, 9 } }, + { /* 186 */ 1, { 11, 1 } }, + { /* 187 */ 1, { 5, 8 } }, + { /* 188 */ 1, { 9, 0 } }, + { /* 189 */ 1, { 8, 5 } }, + { /* 190 */ 0, { 29, 30 } }, + { /* 191 */ 0, { 30, 31 } }, + { /* 192 */ 0, { 31, 32 } }, + { /* 193 */ 0, { 32, 33 } }, + { /* 194 */ 0, { 33, 34 } }, + { /* 195 */ 0, { 34, 35 } }, + { /* 196 */ 0, { 35, 36 } }, + { /* 197 */ 0, { 36, 37 } }, + { /* 198 */ 0, { 37, 38 } }, + { /* 199 */ 0, { 38, 39 } }, + { /* 200 */ 0, { 39, 40 } }, + { /* 201 */ 0, { 40, 41 } }, + { /* 202 */ 0, { 41, 42 } }, + { /* 203 */ 0, { 42, 43 } }, + { /* 204 */ 0, { 43, 44 } }, + { /* 205 */ 0, { 44, 45 } }, + { /* 206 */ 0, { 45, 46 } }, + { /* 207 */ 0, { 46, 47 } }, + { /* 208 */ 0, { 47, 48 } }, + { /* 209 */ 0, { 48, 49 } }, + { /* 210 */ 0, { 49, 50 } }, + { /* 211 */ 0, { 50, 51 } }, + { /* 212 */ 0, { 51, 52 } }, + { /* 213 */ 0, { 52, 53 } }, + { /* 214 */ 0, { 53, 54 } }, + { /* 215 */ 0, { 54, 55 } }, + { /* 216 */ 0, { 55, 56 } }, + { /* 217 */ 0, { 56, 57 } }, + { /* 218 */ 0, { 57, 58 } }, + { /* 219 */ 1, { 10, 3 } }, + { /* 220 */ 1, { 2, 10 } }, + { /* 221 */ 1, { 0, 9 } }, + { /* 222 */ 1, { 11, 2 } }, + { /* 223 */ 1, { 9, 4 } }, + { /* 224 */ 1, { 6, 6 } }, + { /* 225 */ 1, { 12, 1 } }, + { /* 226 */ 1, { 4, 9 } }, + { /* 227 */ 1, { 8, 6 } }, + { /* 228 */ 1, { 1, 11 } }, + { /* 229 */ 1, { 9, 5 } }, + { /* 230 */ 1, { 10, 4 } }, + { /* 231 */ 1, { 5, 7 } }, + { /* 232 */ 1, { 7, 5 } }, + { /* 233 */ 1, { 2, 11 } }, + { /* 234 */ 1, { 1, 12 } }, + { /* 235 */ 1, { 12, 2 } }, + { /* 236 */ 1, { 11, 3 } }, + { /* 237 */ 1, { 3, 10 } }, + { /* 238 */ 1, { 5, 9 } }, + { /* 239 */ 1, { 6, 7 } }, + { /* 240 */ 1, { 8, 7 } }, + { /* 241 */ 1, { 11, 4 } }, + { /* 242 */ 1, { 0, 10 } }, + { /* 243 */ 1, { 7, 6 } }, + { /* 244 */ 1, { 12, 3 } }, + { /* 245 */ 1, { 10, 0 } }, + { /* 246 */ 1, { 10, 5 } }, + { /* 247 */ 1, { 4, 10 } }, + { /* 248 */ 1, { 6, 8 } }, + { /* 249 */ 1, { 2, 12 } }, + { /* 250 */ 1, { 9, 6 } }, + { /* 251 */ 1, { 9, 7 } }, + { /* 252 */ 1, { 4, 11 } }, + { /* 253 */ 1, { 11, 0 } }, + { /* 254 */ 1, { 6, 9 } }, + { /* 255 */ 1, { 3, 11 } }, + { /* 256 */ 1, { 5, 10 } }, + { /* 257 */ 0, { 20, 21 } }, + { /* 258 */ 0, { 21, 22 } }, + { /* 259 */ 0, { 22, 23 } }, + { /* 260 */ 0, { 23, 24 } }, + { /* 261 */ 0, { 24, 25 } }, + { /* 262 */ 0, { 25, 26 } }, + { /* 263 */ 0, { 26, 27 } }, + { /* 264 */ 0, { 27, 28 } }, + { /* 265 */ 0, { 28, 29 } }, + { /* 266 */ 0, { 29, 30 } }, + { /* 267 */ 0, { 30, 31 } }, + { /* 268 */ 0, { 31, 32 } }, + { /* 269 */ 0, { 32, 33 } }, + { /* 270 */ 0, { 33, 34 } }, + { /* 271 */ 0, { 34, 35 } }, + { /* 272 */ 0, { 35, 36 } }, + { /* 273 */ 0, { 36, 37 } }, + { /* 274 */ 0, { 37, 38 } }, + { /* 275 */ 0, { 38, 39 } }, + { /* 276 */ 0, { 39, 40 } }, + { /* 277 */ 1, { 8, 8 } }, + { /* 278 */ 1, { 7, 8 } }, + { /* 279 */ 1, { 12, 5 } }, + { /* 280 */ 1, { 3, 12 } }, + { /* 281 */ 1, { 11, 5 } }, + { /* 282 */ 1, { 7, 7 } }, + { /* 283 */ 1, { 12, 4 } }, + { /* 284 */ 1, { 11, 6 } }, + { /* 285 */ 1, { 10, 6 } }, + { /* 286 */ 1, { 4, 12 } }, + { /* 287 */ 1, { 7, 9 } }, + { /* 288 */ 1, { 5, 11 } }, + { /* 289 */ 1, { 0, 11 } }, + { /* 290 */ 1, { 12, 6 } }, + { /* 291 */ 1, { 6, 10 } }, + { /* 292 */ 1, { 12, 0 } }, + { /* 293 */ 1, { 10, 7 } }, + { /* 294 */ 1, { 5, 12 } }, + { /* 295 */ 1, { 7, 10 } }, + { /* 296 */ 1, { 9, 8 } }, + { /* 297 */ 1, { 0, 12 } }, + { /* 298 */ 1, { 11, 7 } }, + { /* 299 */ 1, { 8, 9 } }, + { /* 300 */ 1, { 9, 9 } }, + { /* 301 */ 1, { 10, 8 } }, + { /* 302 */ 1, { 7, 11 } }, + { /* 303 */ 1, { 12, 7 } }, + { /* 304 */ 1, { 6, 11 } }, + { /* 305 */ 1, { 8, 11 } }, + { /* 306 */ 1, { 11, 8 } }, + { /* 307 */ 1, { 7, 12 } }, + { /* 308 */ 1, { 6, 12 } }, + { /* 309 */ 0, { 8, 9 } }, + { /* 310 */ 0, { 9, 10 } }, + { /* 311 */ 0, { 10, 11 } }, + { /* 312 */ 0, { 11, 12 } }, + { /* 313 */ 0, { 12, 13 } }, + { /* 314 */ 0, { 13, 14 } }, + { /* 315 */ 0, { 14, 15 } }, + { /* 316 */ 0, { 15, 16 } }, + { /* 317 */ 1, { 8, 10 } }, + { /* 318 */ 1, { 10, 9 } }, + { /* 319 */ 1, { 8, 12 } }, + { /* 320 */ 1, { 9, 10 } }, + { /* 321 */ 1, { 9, 11 } }, + { /* 322 */ 1, { 9, 12 } }, + { /* 323 */ 1, { 10, 11 } }, + { /* 324 */ 1, { 12, 9 } }, + { /* 325 */ 1, { 10, 10 } }, + { /* 326 */ 1, { 11, 9 } }, + { /* 327 */ 1, { 12, 8 } }, + { /* 328 */ 1, { 11, 10 } }, + { /* 329 */ 1, { 12, 10 } }, + { /* 330 */ 1, { 12, 11 } }, + { /* 331 */ 0, { 2, 3 } }, + { /* 332 */ 0, { 3, 4 } }, + { /* 333 */ 1, { 10, 12 } }, + { /* 334 */ 1, { 11, 11 } }, + { /* 335 */ 1, { 11, 12 } }, + { /* 336 */ 1, { 12, 12 } } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/codebook/hcb_sf.h b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_sf.h new file mode 100644 index 000000000..3590c079d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/codebook/hcb_sf.h @@ -0,0 +1,276 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcb_sf.h,v 1.7 2007/11/01 12:34:11 menno Exp $ +**/ + +/* Binary search huffman table HCB_SF */ + + +static uint8_t hcb_sf[241][2] = { + { /* 0 */ 1, 2 }, + { /* 1 */ 60, 0 }, + { /* 2 */ 1, 2 }, + { /* 3 */ 2, 3 }, + { /* 4 */ 3, 4 }, + { /* 5 */ 59, 0 }, + { /* 6 */ 3, 4 }, + { /* 7 */ 4, 5 }, + { /* 8 */ 5, 6 }, + { /* 9 */ 61, 0 }, + { /* 10 */ 58, 0 }, + { /* 11 */ 62, 0 }, + { /* 12 */ 3, 4 }, + { /* 13 */ 4, 5 }, + { /* 14 */ 5, 6 }, + { /* 15 */ 57, 0 }, + { /* 16 */ 63, 0 }, + { /* 17 */ 4, 5 }, + { /* 18 */ 5, 6 }, + { /* 19 */ 6, 7 }, + { /* 20 */ 7, 8 }, + { /* 21 */ 56, 0 }, + { /* 22 */ 64, 0 }, + { /* 23 */ 55, 0 }, + { /* 24 */ 65, 0 }, + { /* 25 */ 4, 5 }, + { /* 26 */ 5, 6 }, + { /* 27 */ 6, 7 }, + { /* 28 */ 7, 8 }, + { /* 29 */ 66, 0 }, + { /* 30 */ 54, 0 }, + { /* 31 */ 67, 0 }, + { /* 32 */ 5, 6 }, + { /* 33 */ 6, 7 }, + { /* 34 */ 7, 8 }, + { /* 35 */ 8, 9 }, + { /* 36 */ 9, 10 }, + { /* 37 */ 53, 0 }, + { /* 38 */ 68, 0 }, + { /* 39 */ 52, 0 }, + { /* 40 */ 69, 0 }, + { /* 41 */ 51, 0 }, + { /* 42 */ 5, 6 }, + { /* 43 */ 6, 7 }, + { /* 44 */ 7, 8 }, + { /* 45 */ 8, 9 }, + { /* 46 */ 9, 10 }, + { /* 47 */ 70, 0 }, + { /* 48 */ 50, 0 }, + { /* 49 */ 49, 0 }, + { /* 50 */ 71, 0 }, + { /* 51 */ 6, 7 }, + { /* 52 */ 7, 8 }, + { /* 53 */ 8, 9 }, + { /* 54 */ 9, 10 }, + { /* 55 */ 10, 11 }, + { /* 56 */ 11, 12 }, + { /* 57 */ 72, 0 }, + { /* 58 */ 48, 0 }, + { /* 59 */ 73, 0 }, + { /* 60 */ 47, 0 }, + { /* 61 */ 74, 0 }, + { /* 62 */ 46, 0 }, + { /* 63 */ 6, 7 }, + { /* 64 */ 7, 8 }, + { /* 65 */ 8, 9 }, + { /* 66 */ 9, 10 }, + { /* 67 */ 10, 11 }, + { /* 68 */ 11, 12 }, + { /* 69 */ 76, 0 }, + { /* 70 */ 75, 0 }, + { /* 71 */ 77, 0 }, + { /* 72 */ 78, 0 }, + { /* 73 */ 45, 0 }, + { /* 74 */ 43, 0 }, + { /* 75 */ 6, 7 }, + { /* 76 */ 7, 8 }, + { /* 77 */ 8, 9 }, + { /* 78 */ 9, 10 }, + { /* 79 */ 10, 11 }, + { /* 80 */ 11, 12 }, + { /* 81 */ 44, 0 }, + { /* 82 */ 79, 0 }, + { /* 83 */ 42, 0 }, + { /* 84 */ 41, 0 }, + { /* 85 */ 80, 0 }, + { /* 86 */ 40, 0 }, + { /* 87 */ 6, 7 }, + { /* 88 */ 7, 8 }, + { /* 89 */ 8, 9 }, + { /* 90 */ 9, 10 }, + { /* 91 */ 10, 11 }, + { /* 92 */ 11, 12 }, + { /* 93 */ 81, 0 }, + { /* 94 */ 39, 0 }, + { /* 95 */ 82, 0 }, + { /* 96 */ 38, 0 }, + { /* 97 */ 83, 0 }, + { /* 98 */ 7, 8 }, + { /* 99 */ 8, 9 }, + { /* 100 */ 9, 10 }, + { /* 101 */ 10, 11 }, + { /* 102 */ 11, 12 }, + { /* 103 */ 12, 13 }, + { /* 104 */ 13, 14 }, + { /* 105 */ 37, 0 }, + { /* 106 */ 35, 0 }, + { /* 107 */ 85, 0 }, + { /* 108 */ 33, 0 }, + { /* 109 */ 36, 0 }, + { /* 110 */ 34, 0 }, + { /* 111 */ 84, 0 }, + { /* 112 */ 32, 0 }, + { /* 113 */ 6, 7 }, + { /* 114 */ 7, 8 }, + { /* 115 */ 8, 9 }, + { /* 116 */ 9, 10 }, + { /* 117 */ 10, 11 }, + { /* 118 */ 11, 12 }, + { /* 119 */ 87, 0 }, + { /* 120 */ 89, 0 }, + { /* 121 */ 30, 0 }, + { /* 122 */ 31, 0 }, + { /* 123 */ 8, 9 }, + { /* 124 */ 9, 10 }, + { /* 125 */ 10, 11 }, + { /* 126 */ 11, 12 }, + { /* 127 */ 12, 13 }, + { /* 128 */ 13, 14 }, + { /* 129 */ 14, 15 }, + { /* 130 */ 15, 16 }, + { /* 131 */ 86, 0 }, + { /* 132 */ 29, 0 }, + { /* 133 */ 26, 0 }, + { /* 134 */ 27, 0 }, + { /* 135 */ 28, 0 }, + { /* 136 */ 24, 0 }, + { /* 137 */ 88, 0 }, + { /* 138 */ 9, 10 }, + { /* 139 */ 10, 11 }, + { /* 140 */ 11, 12 }, + { /* 141 */ 12, 13 }, + { /* 142 */ 13, 14 }, + { /* 143 */ 14, 15 }, + { /* 144 */ 15, 16 }, + { /* 145 */ 16, 17 }, + { /* 146 */ 17, 18 }, + { /* 147 */ 25, 0 }, + { /* 148 */ 22, 0 }, + { /* 149 */ 23, 0 }, + { /* 150 */ 15, 16 }, + { /* 151 */ 16, 17 }, + { /* 152 */ 17, 18 }, + { /* 153 */ 18, 19 }, + { /* 154 */ 19, 20 }, + { /* 155 */ 20, 21 }, + { /* 156 */ 21, 22 }, + { /* 157 */ 22, 23 }, + { /* 158 */ 23, 24 }, + { /* 159 */ 24, 25 }, + { /* 160 */ 25, 26 }, + { /* 161 */ 26, 27 }, + { /* 162 */ 27, 28 }, + { /* 163 */ 28, 29 }, + { /* 164 */ 29, 30 }, + { /* 165 */ 90, 0 }, + { /* 166 */ 21, 0 }, + { /* 167 */ 19, 0 }, + { /* 168 */ 3, 0 }, + { /* 169 */ 1, 0 }, + { /* 170 */ 2, 0 }, + { /* 171 */ 0, 0 }, + { /* 172 */ 23, 24 }, + { /* 173 */ 24, 25 }, + { /* 174 */ 25, 26 }, + { /* 175 */ 26, 27 }, + { /* 176 */ 27, 28 }, + { /* 177 */ 28, 29 }, + { /* 178 */ 29, 30 }, + { /* 179 */ 30, 31 }, + { /* 180 */ 31, 32 }, + { /* 181 */ 32, 33 }, + { /* 182 */ 33, 34 }, + { /* 183 */ 34, 35 }, + { /* 184 */ 35, 36 }, + { /* 185 */ 36, 37 }, + { /* 186 */ 37, 38 }, + { /* 187 */ 38, 39 }, + { /* 188 */ 39, 40 }, + { /* 189 */ 40, 41 }, + { /* 190 */ 41, 42 }, + { /* 191 */ 42, 43 }, + { /* 192 */ 43, 44 }, + { /* 193 */ 44, 45 }, + { /* 194 */ 45, 46 }, + { /* 195 */ 98, 0 }, + { /* 196 */ 99, 0 }, + { /* 197 */ 100, 0 }, + { /* 198 */ 101, 0 }, + { /* 199 */ 102, 0 }, + { /* 200 */ 117, 0 }, + { /* 201 */ 97, 0 }, + { /* 202 */ 91, 0 }, + { /* 203 */ 92, 0 }, + { /* 204 */ 93, 0 }, + { /* 205 */ 94, 0 }, + { /* 206 */ 95, 0 }, + { /* 207 */ 96, 0 }, + { /* 208 */ 104, 0 }, + { /* 209 */ 111, 0 }, + { /* 210 */ 112, 0 }, + { /* 211 */ 113, 0 }, + { /* 212 */ 114, 0 }, + { /* 213 */ 115, 0 }, + { /* 214 */ 116, 0 }, + { /* 215 */ 110, 0 }, + { /* 216 */ 105, 0 }, + { /* 217 */ 106, 0 }, + { /* 218 */ 107, 0 }, + { /* 219 */ 108, 0 }, + { /* 220 */ 109, 0 }, + { /* 221 */ 118, 0 }, + { /* 222 */ 6, 0 }, + { /* 223 */ 8, 0 }, + { /* 224 */ 9, 0 }, + { /* 225 */ 10, 0 }, + { /* 226 */ 5, 0 }, + { /* 227 */ 103, 0 }, + { /* 228 */ 120, 0 }, + { /* 229 */ 119, 0 }, + { /* 230 */ 4, 0 }, + { /* 231 */ 7, 0 }, + { /* 232 */ 15, 0 }, + { /* 233 */ 16, 0 }, + { /* 234 */ 18, 0 }, + { /* 235 */ 20, 0 }, + { /* 236 */ 17, 0 }, + { /* 237 */ 11, 0 }, + { /* 238 */ 12, 0 }, + { /* 239 */ 14, 0 }, + { /* 240 */ 13, 0 } +}; diff --git a/third_party/faad_hdc/upstream/libfaad/common.c b/third_party/faad_hdc/upstream/libfaad/common.c new file mode 100644 index 000000000..70d83a46a --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/common.c @@ -0,0 +1,497 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: common.c,v 1.27 2008/03/23 23:03:28 menno Exp $ +**/ + +/* just some common functions that could be used anywhere */ + +#include "common.h" +#include "structs.h" + +#include +#include "syntax.h" + + +/* Returns the sample rate index based on the samplerate */ +uint8_t get_sr_index(const uint32_t samplerate) +{ + if (92017 <= samplerate) return 0; + if (75132 <= samplerate) return 1; + if (55426 <= samplerate) return 2; + if (46009 <= samplerate) return 3; + if (37566 <= samplerate) return 4; + if (27713 <= samplerate) return 5; + if (23004 <= samplerate) return 6; + if (18783 <= samplerate) return 7; + if (13856 <= samplerate) return 8; + if (11502 <= samplerate) return 9; + if (9391 <= samplerate) return 10; + + return 11; +} + +/* Returns the sample rate based on the sample rate index */ +uint32_t get_sample_rate(const uint8_t sr_index) +{ + static const uint32_t sample_rates[] = + { + 96000, 88200, 64000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, 11025, 8000 + }; + + if (sr_index < 12) + return sample_rates[sr_index]; + + return 0; +} + +uint8_t max_pred_sfb(const uint8_t sr_index) +{ + static const uint8_t pred_sfb_max[] = + { + 33, 33, 38, 40, 40, 40, 41, 41, 37, 37, 37, 34 + }; + + + if (sr_index < 12) + return pred_sfb_max[sr_index]; + + return 0; +} + +uint8_t max_tns_sfb(const uint8_t sr_index, const uint8_t object_type, + const uint8_t is_short) +{ + /* entry for each sampling rate + * 1 Main/LC long window + * 2 Main/LC short window + * 3 SSR long window + * 4 SSR short window + */ + static const uint8_t tns_sbf_max[][4] = + { + {31, 9, 28, 7}, /* 96000 */ + {31, 9, 28, 7}, /* 88200 */ + {34, 10, 27, 7}, /* 64000 */ + {40, 14, 26, 6}, /* 48000 */ + {42, 14, 26, 6}, /* 44100 */ + {51, 14, 26, 6}, /* 32000 */ + {46, 14, 29, 7}, /* 24000 */ + {46, 14, 29, 7}, /* 22050 */ + {42, 14, 23, 8}, /* 16000 */ + {42, 14, 23, 8}, /* 12000 */ + {42, 14, 23, 8}, /* 11025 */ + {39, 14, 19, 7}, /* 8000 */ + {39, 14, 19, 7}, /* 7350 */ + {0,0,0,0}, + {0,0,0,0}, + {0,0,0,0} + }; + uint8_t i = 0; + + if (is_short) i++; + if (object_type == SSR) i += 2; + + return tns_sbf_max[sr_index][i]; +} + +/* Returns 0 if an object type is decodable, otherwise returns -1 */ +int8_t can_decode_ot(const uint8_t object_type) +{ + switch (object_type) + { + case LC: + return 0; + case MAIN: +#ifdef MAIN_DEC + return 0; +#else + return -1; +#endif + case SSR: +#ifdef SSR_DEC + return 0; +#else + return -1; +#endif + case LTP: +#ifdef LTP_DEC + return 0; +#else + return -1; +#endif + + /* ER object types */ +#ifdef ERROR_RESILIENCE + case ER_LC: +#ifdef DRM + case DRM_ER_LC: +#endif + return 0; + case ER_LTP: +#ifdef LTP_DEC + return 0; +#else + return -1; +#endif + case LD: +#ifdef LD_DEC + return 0; +#else + return -1; +#endif +#endif + +#ifdef HDC + case HDC_LC: + return 0; +#endif + } + + return -1; +} + +void *faad_malloc(size_t size) +{ +#if 0 // defined(_WIN32) && !defined(_WIN32_WCE) + return _aligned_malloc(size, 16); +#else // #ifdef 0 + return malloc(size); +#endif // #ifdef 0 +} + +/* common free function */ +void faad_free(void *b) +{ +#if 0 // defined(_WIN32) && !defined(_WIN32_WCE) + _aligned_free(b); +#else + free(b); +} +#endif + +static const uint8_t Parity [256] = { // parity + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0 +}; + + +/* + * This is a simple random number generator with good quality for audio purposes. + * It consists of two polycounters with opposite rotation direction and different + * periods. The periods are coprime, so the total period is the product of both. + * + * ------------------------------------------------------------------------------------------------- + * +-> |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| + * | ------------------------------------------------------------------------------------------------- + * | | | | | | | + * | +--+--+--+-XOR-+--------+ + * | | + * +--------------------------------------------------------------------------------------+ + * + * ------------------------------------------------------------------------------------------------- + * |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| <-+ + * ------------------------------------------------------------------------------------------------- | + * | | | | | + * +--+----XOR----+--+ | + * | | + * +----------------------------------------------------------------------------------------+ + * + * + * The first has an period of 3*5*17*257*65537, the second of 7*47*73*178481, + * which gives a period of 18.410.713.077.675.721.215. The result is the + * XORed values of both generators. + */ +uint32_t ne_rng(uint32_t *__r1, uint32_t *__r2) +{ + uint32_t t1, t2, t3, t4; + + t3 = t1 = *__r1; t4 = t2 = *__r2; // Parity calculation is done via table lookup, this is also available + t1 &= 0xF5; t2 >>= 25; // on CPUs without parity, can be implemented in C and avoid unpredictable + t1 = Parity [t1]; t2 &= 0x63; // jumps and slow rotate through the carry flag operations. + t1 <<= 31; t2 = Parity [t2]; + + return (*__r1 = (t3 >> 1) | t1 ) ^ (*__r2 = (t4 + t4) | t2 ); +} + +#ifdef FIXED_POINT + +static uint32_t ones32(uint32_t x) +{ + x -= ((x >> 1) & 0x55555555); + x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); + x = (((x >> 4) + x) & 0x0f0f0f0f); + x += (x >> 8); + x += (x >> 16); + + return (x & 0x0000003f); +} + +static uint32_t ones64(uint64_t x) +{ + return ones32((uint32_t)x) + ones32(x >> 32); +} + +static uint32_t floor_log2(uint64_t x) +{ +#if 1 + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + x |= (x >> 32); + + return (ones64(x) - 1); +#else + uint32_t count = 0; + + while (x >>= 1) + count++; + + return count; +#endif +} + +/* returns position of first bit that is not 0 from msb, + * starting count at lsb */ +uint32_t wl_min_lzc(uint32_t x) +{ +#if 1 + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + + return (ones32(x)); +#else + uint32_t count = 0; + + while (x >>= 1) + count++; + + return (count + 1); +#endif +} + +#define TABLE_BITS 6 +/* just take the maximum number of bits for interpolation */ +#define INTERP_BITS (REAL_BITS-TABLE_BITS) + +static const real_t pow2_tab[] = { + REAL_CONST(1.000000000000000), REAL_CONST(1.010889286051701), REAL_CONST(1.021897148654117), + REAL_CONST(1.033024879021228), REAL_CONST(1.044273782427414), REAL_CONST(1.055645178360557), + REAL_CONST(1.067140400676824), REAL_CONST(1.078760797757120), REAL_CONST(1.090507732665258), + REAL_CONST(1.102382583307841), REAL_CONST(1.114386742595892), REAL_CONST(1.126521618608242), + REAL_CONST(1.138788634756692), REAL_CONST(1.151189229952983), REAL_CONST(1.163724858777578), + REAL_CONST(1.176396991650281), REAL_CONST(1.189207115002721), REAL_CONST(1.202156731452703), + REAL_CONST(1.215247359980469), REAL_CONST(1.228480536106870), REAL_CONST(1.241857812073484), + REAL_CONST(1.255380757024691), REAL_CONST(1.269050957191733), REAL_CONST(1.282870016078778), + REAL_CONST(1.296839554651010), REAL_CONST(1.310961211524764), REAL_CONST(1.325236643159741), + REAL_CONST(1.339667524053303), REAL_CONST(1.354255546936893), REAL_CONST(1.369002422974591), + REAL_CONST(1.383909881963832), REAL_CONST(1.398979672538311), REAL_CONST(1.414213562373095), + REAL_CONST(1.429613338391970), REAL_CONST(1.445180806977047), REAL_CONST(1.460917794180647), + REAL_CONST(1.476826145939499), REAL_CONST(1.492907728291265), REAL_CONST(1.509164427593423), + REAL_CONST(1.525598150744538), REAL_CONST(1.542210825407941), REAL_CONST(1.559004400237837), + REAL_CONST(1.575980845107887), REAL_CONST(1.593142151342267), REAL_CONST(1.610490331949254), + REAL_CONST(1.628027421857348), REAL_CONST(1.645755478153965), REAL_CONST(1.663676580326736), + REAL_CONST(1.681792830507429), REAL_CONST(1.700106353718524), REAL_CONST(1.718619298122478), + REAL_CONST(1.737333835273706), REAL_CONST(1.756252160373300), REAL_CONST(1.775376492526521), + REAL_CONST(1.794709075003107), REAL_CONST(1.814252175500399), REAL_CONST(1.834008086409342), + REAL_CONST(1.853979125083386), REAL_CONST(1.874167634110300), REAL_CONST(1.894575981586966), + REAL_CONST(1.915206561397147), REAL_CONST(1.936061793492294), REAL_CONST(1.957144124175400), + REAL_CONST(1.978456026387951), REAL_CONST(2.000000000000000) +}; + +static const real_t log2_tab[] = { + REAL_CONST(0.000000000000000), REAL_CONST(0.022367813028455), REAL_CONST(0.044394119358453), + REAL_CONST(0.066089190457772), REAL_CONST(0.087462841250339), REAL_CONST(0.108524456778169), + REAL_CONST(0.129283016944966), REAL_CONST(0.149747119504682), REAL_CONST(0.169925001442312), + REAL_CONST(0.189824558880017), REAL_CONST(0.209453365628950), REAL_CONST(0.228818690495881), + REAL_CONST(0.247927513443585), REAL_CONST(0.266786540694901), REAL_CONST(0.285402218862248), + REAL_CONST(0.303780748177103), REAL_CONST(0.321928094887362), REAL_CONST(0.339850002884625), + REAL_CONST(0.357552004618084), REAL_CONST(0.375039431346925), REAL_CONST(0.392317422778760), + REAL_CONST(0.409390936137702), REAL_CONST(0.426264754702098), REAL_CONST(0.442943495848728), + REAL_CONST(0.459431618637297), REAL_CONST(0.475733430966398), REAL_CONST(0.491853096329675), + REAL_CONST(0.507794640198696), REAL_CONST(0.523561956057013), REAL_CONST(0.539158811108031), + REAL_CONST(0.554588851677637), REAL_CONST(0.569855608330948), REAL_CONST(0.584962500721156), + REAL_CONST(0.599912842187128), REAL_CONST(0.614709844115208), REAL_CONST(0.629356620079610), + REAL_CONST(0.643856189774725), REAL_CONST(0.658211482751795), REAL_CONST(0.672425341971496), + REAL_CONST(0.686500527183218), REAL_CONST(0.700439718141092), REAL_CONST(0.714245517666123), + REAL_CONST(0.727920454563199), REAL_CONST(0.741466986401147), REAL_CONST(0.754887502163469), + REAL_CONST(0.768184324776926), REAL_CONST(0.781359713524660), REAL_CONST(0.794415866350106), + REAL_CONST(0.807354922057604), REAL_CONST(0.820178962415188), REAL_CONST(0.832890014164742), + REAL_CONST(0.845490050944375), REAL_CONST(0.857980995127572), REAL_CONST(0.870364719583405), + REAL_CONST(0.882643049361841), REAL_CONST(0.894817763307943), REAL_CONST(0.906890595608519), + REAL_CONST(0.918863237274595), REAL_CONST(0.930737337562886), REAL_CONST(0.942514505339240), + REAL_CONST(0.954196310386875), REAL_CONST(0.965784284662087), REAL_CONST(0.977279923499917), + REAL_CONST(0.988684686772166), REAL_CONST(1.000000000000000) +}; + +real_t pow2_fix(real_t val) +{ + uint32_t x1, x2; + uint32_t errcorr; + uint32_t index_frac; + real_t retval; + int32_t whole = (val >> REAL_BITS); + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (whole >= 17) __builtin_trap(); +#endif + + /* rest = [0..1] */ + int32_t rest = val & ((1 << REAL_BITS) - 1); + + /* index into pow2_tab */ + int32_t index = rest >> (REAL_BITS-TABLE_BITS); + + + if (val == 0) + return (1<> (REAL_BITS-TABLE_BITS-INTERP_BITS); + index_frac = index_frac & ((1< 0) + { + retval = 1 << whole; + } else { + retval = REAL_CONST(1) >> -whole; + } + + x1 = pow2_tab[index & ((1<> INTERP_BITS; + + if (whole > 0) + { + retval = retval * (errcorr + x1); + } else { + retval = MUL_R(retval, (errcorr + x1)); + } + + return retval; +} + +uint64_t pow2_int(real_t val) +{ + uint32_t x1, x2; + uint32_t errcorr; + uint32_t index_frac; + uint64_t retval; + int32_t whole = (val >> REAL_BITS); + int32_t exp = 0; + + /* rest = [0..1] */ + int32_t rest = val & ((1 << REAL_BITS) - 1); + + /* index into pow2_tab */ + int32_t index = rest >> (REAL_BITS-TABLE_BITS); + + if (val < 0) + return 0; + if (val == 0) + return 1; + if (whole > COEF_BITS) { + exp = whole - COEF_BITS; + whole = COEF_BITS; + } + + /* leave INTERP_BITS bits */ + index_frac = rest >> (REAL_BITS-TABLE_BITS-INTERP_BITS); + index_frac = index_frac & ((1<= 0) + retval = (uint32_t)(1 << whole); + else + retval = 0; + + x1 = pow2_tab[index & ((1<> INTERP_BITS; + + retval = MUL_R(retval, (errcorr + x1)); + + return retval << exp; +} + +/* ld(x) = ld(x*y/y) = ld(x/y) + ld(y), with y=2^N and [1 <= (x/y) < 2] */ +int32_t log2_int(uint64_t val) +{ + uint32_t frac; + int32_t exp = 0; + uint32_t index; + uint32_t index_frac; + uint32_t x1, x2; + uint32_t errcorr; + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (val == 0) __builtin_trap(); +#endif + + exp = floor_log2(val); + exp -= REAL_BITS; + + /* frac = [1..2] */ + if (exp >= 0) + frac = (uint32_t)(val >> exp); + else + frac = (uint32_t)(val << -exp); + + /* index in the log2 table */ + index = frac >> (REAL_BITS-TABLE_BITS); + + /* leftover part for linear interpolation */ + index_frac = frac & ((1<<(REAL_BITS-TABLE_BITS))-1); + + /* leave INTERP_BITS bits */ + index_frac = index_frac >> (REAL_BITS-TABLE_BITS-INTERP_BITS); + + x1 = log2_tab[index & ((1<> INTERP_BITS; + + return ((exp+REAL_BITS) << REAL_BITS) + errcorr + x1; +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/common.h b/third_party/faad_hdc/upstream/libfaad/common.h new file mode 100644 index 000000000..d6a5c5913 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/common.h @@ -0,0 +1,443 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: common.h,v 1.79 2015/01/26 17:48:53 knik Exp $ +**/ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_CONFIG_H +# include "../config.h" +#endif + +#include "neaacdec.h" + +#if 1 +#define INLINE __inline +#else +#define INLINE inline +#endif + +#if 0 //defined(_WIN32) && !defined(_WIN32_WCE) +#define ALIGN __declspec(align(16)) +#else +#define ALIGN +#endif + +#ifndef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +/* COMPILE TIME DEFINITIONS */ + +/* use double precision */ +/* #define USE_DOUBLE_PRECISION */ +/* use fixed point reals */ +//#define FIXED_POINT +//#define BIG_IQ_TABLE + +/* Use if target platform has address generators with autoincrement */ +//#define PREFER_POINTERS + +#ifdef _WIN32_WCE +#define FIXED_POINT +#endif + +#ifdef __BFIN__ +#define FIXED_POINT +#endif + +#define ERROR_RESILIENCE + + +/* Allow decoding of MAIN profile AAC */ +#define MAIN_DEC +/* Allow decoding of SSR profile AAC */ +//#define SSR_DEC +/* Allow decoding of LTP profile AAC */ +#define LTP_DEC +/* Allow decoding of LD profile AAC */ +#define LD_DEC +/* Allow decoding of Digital Radio Mondiale (DRM) */ +#ifdef DRM_SUPPORT +#define DRM +#define DRM_PS +#endif +/* Allow decoding of HDC */ +#ifdef HDC_SUPPORT +#define DRM +#define DRM_PS +#define HDC +#endif + +/* LD can't do without LTP */ +#ifdef LD_DEC +#ifndef ERROR_RESILIENCE +#define ERROR_RESILIENCE +#endif +#ifndef LTP_DEC +#define LTP_DEC +#endif +#endif + +#define ALLOW_SMALL_FRAMELENGTH + + +// Define LC_ONLY_DECODER if you want a pure AAC LC decoder (independant of SBR_DEC and PS_DEC) +//#define LC_ONLY_DECODER +#ifdef LC_ONLY_DECODER + #undef LD_DEC + #undef LTP_DEC + #undef MAIN_DEC + #undef SSR_DEC + #undef DRM + #undef DRM_PS + #undef ALLOW_SMALL_FRAMELENGTH + #undef ERROR_RESILIENCE +#endif + +// Define DISABLE_SBR if you want to disable SBR decoding. +//#define DISABLE_SBR + +// Define SBR_LOW_POWER if you want only low power SBR decoding without PS. +//#define SBR_LOW_POWER + +#ifndef DISABLE_SBR +# define SBR_DEC +# ifndef SBR_LOW_POWER +# define PS_DEC +# endif // SBR_LOW_POWER +#endif // DISABLE_SBR + +/* FIXED POINT: No MAIN decoding */ +#ifdef FIXED_POINT +# ifdef MAIN_DEC +# undef MAIN_DEC +# endif +#endif // FIXED_POINT + +#ifdef DRM +# ifndef ALLOW_SMALL_FRAMELENGTH +# define ALLOW_SMALL_FRAMELENGTH +# endif +# undef LD_DEC +# undef LTP_DEC +# undef MAIN_DEC +# undef SSR_DEC +#endif + + +#ifdef FIXED_POINT +#define DIV_R(A, B) (((int64_t)A * REAL_PRECISION)/B) +#define DIV_C(A, B) (((int64_t)A * COEF_PRECISION)/B) +#define DIV_F(A, B) (((int64_t)A * FRAC_PRECISION)/B) +#else +#define DIV_R(A, B) ((A)/(B)) +#define DIV_C(A, B) ((A)/(B)) +#define DIV_F(A, B) ((A)/(B)) +#endif + +#ifndef SBR_LOW_POWER +#define qmf_t complex_t +#define QMF_RE(A) RE(A) +#define QMF_IM(A) IM(A) +#else +#define qmf_t real_t +#define QMF_RE(A) (A) +#define QMF_IM(A) +#endif + + +/* END COMPILE TIME DEFINITIONS */ + +#if defined(_WIN32) && !defined(__MINGW32__) + +#include +#include + +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +typedef signed __int64 int64_t; +typedef signed __int32 int32_t; +typedef signed __int16 int16_t; +typedef signed __int8 int8_t; +typedef float float32_t; + + +#else /* WIN */ + +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#include +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# else +/* we need these... */ +#ifndef __TCS__ +typedef unsigned long long uint64_t; +typedef signed long long int64_t; +#else +typedef unsigned long uint64_t; +typedef signed long int64_t; +#endif +typedef unsigned long uint32_t; +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; +typedef signed long int32_t; +typedef signed short int16_t; +typedef signed char int8_t; +# endif +#endif +#if HAVE_UNISTD_H +//# include +#endif + +#ifndef HAVE_FLOAT32_T +typedef float float32_t; +#endif + +#include + +#endif /* WIN */ + +/* FIXED_POINT doesn't work with MAIN and SSR yet */ +#ifdef FIXED_POINT + #undef MAIN_DEC + #undef SSR_DEC +#endif + + +#if defined(FIXED_POINT) + + #include "fixed.h" + +#elif defined(USE_DOUBLE_PRECISION) + + typedef double real_t; + + #include + + #define MUL_R(A,B) ((A)*(B)) + #define MUL_C(A,B) ((A)*(B)) + #define MUL_F(A,B) ((A)*(B)) + + /* Complex multiplication */ + static INLINE void ComplexMult(real_t *y1, real_t *y2, + real_t x1, real_t x2, real_t c1, real_t c2) + { + *y1 = MUL_F(x1, c1) + MUL_F(x2, c2); + *y2 = MUL_F(x2, c1) - MUL_F(x1, c2); + } + + #define REAL_CONST(A) ((real_t)(A)) + #define COEF_CONST(A) ((real_t)(A)) + #define Q2_CONST(A) ((real_t)(A)) + #define FRAC_CONST(A) ((real_t)(A)) /* pure fractional part */ + +#else /* Normal floating point operation */ + + typedef float real_t; + + #define MUL_R(A,B) ((A)*(B)) + #define MUL_C(A,B) ((A)*(B)) + #define MUL_F(A,B) ((A)*(B)) + + #define REAL_CONST(A) ((real_t)(A)) + #define COEF_CONST(A) ((real_t)(A)) + #define Q2_CONST(A) ((real_t)(A)) + #define FRAC_CONST(A) ((real_t)(A)) /* pure fractional part */ + + /* Complex multiplication */ + static INLINE void ComplexMult(real_t *y1, real_t *y2, + real_t x1, real_t x2, real_t c1, real_t c2) + { + *y1 = MUL_F(x1, c1) + MUL_F(x2, c2); + *y2 = MUL_F(x2, c1) - MUL_F(x1, c2); + } + + + #if defined(_WIN32) && defined(_M_IX86) && !defined(__MINGW32__) + #ifndef HAVE_LRINTF + #define HAS_LRINTF + static INLINE long lrintf(float f) + { + int i; + __asm + { + fld f + fistp i + } + return i; + } + #endif /* HAVE_LRINTF */ + #elif (defined(__i386__) && defined(__GNUC__) && \ + !defined(__CYGWIN__) && !defined(__MINGW32__)) + #ifndef HAVE_LRINTF + #define HAS_LRINTF + // from http://www.stereopsis.com/FPU.html + static INLINE long lrintf(float f) + { + int i; + __asm__ __volatile__ ( + "flds %1 \n\t" + "fistpl %0 \n\t" + : "=m" (i) + : "m" (f)); + return i; + } + #endif /* HAVE_LRINTF */ + #endif + + + #ifdef __ICL /* only Intel C compiler has fmath ??? */ + + #include + + #define sin sinf + #define cos cosf + #define log logf + #define floor floorf + #define ceil ceilf + #define sqrt sqrtf + + #else + +#ifdef HAVE_LRINTF +# define HAS_LRINTF +# define _ISOC9X_SOURCE 1 +# define _ISOC99_SOURCE 1 +# define __USE_ISOC9X 1 +# define __USE_ISOC99 1 +#endif + + #include + +#ifdef HAVE_SINF +# define sin sinf +#error +#endif +#ifdef HAVE_COSF +# define cos cosf +#endif +#ifdef HAVE_LOGF +# define log logf +#endif +#ifdef HAVE_EXPF +# define exp expf +#endif +#ifdef HAVE_FLOORF +# define floor floorf +#endif +#ifdef HAVE_CEILF +# define ceil ceilf +#endif +#ifdef HAVE_SQRTF +# define sqrt sqrtf +#endif + + #endif + +#endif + +#ifndef HAS_LRINTF +/* standard cast */ +#define lrintf(f) ((long)(f)) +#endif + +typedef real_t complex_t[2]; +#define RE(A) (A)[0] +#define IM(A) (A)[1] + + +/* common functions */ +uint8_t cpu_has_sse(void); +uint32_t ne_rng(uint32_t *__r1, uint32_t *__r2); +#ifdef FIXED_POINT +uint32_t wl_min_lzc(uint32_t x); +#define LOG2_MIN_INF REAL_CONST(-10000) +int32_t log2_int(uint64_t val); +uint64_t pow2_int(real_t val); +real_t pow2_fix(real_t val); +#endif +uint8_t get_sr_index(const uint32_t samplerate); +uint8_t max_pred_sfb(const uint8_t sr_index); +uint8_t max_tns_sfb(const uint8_t sr_index, const uint8_t object_type, + const uint8_t is_short); +uint32_t get_sample_rate(const uint8_t sr_index); +int8_t can_decode_ot(const uint8_t object_type); + +void *faad_malloc(size_t size); +void faad_free(void *b); + +//#define PROFILE +#ifdef PROFILE +static int64_t faad_get_ts() +{ + __asm + { + rdtsc + } +} +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 /* PI/2 */ +#define M_PI_2 1.57079632679489661923 +#endif + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/decoder.c b/third_party/faad_hdc/upstream/libfaad/decoder.c new file mode 100644 index 000000000..731ebe863 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/decoder.c @@ -0,0 +1,1307 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: decoder.c,v 1.119 2015/01/24 14:21:05 knik Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include +#include +#include + +#include "mp4.h" +#include "syntax.h" +#include "error.h" +#include "output.h" +#include "filtbank.h" +#include "drc.h" +#ifdef SBR_DEC +#include "sbr_dec.h" +#include "sbr_syntax.h" +#endif +#ifdef SSR_DEC +#include "ssr.h" +#endif + +#ifdef ANALYSIS +uint16_t dbg_count; +#endif + +#if !defined(PACKAGE_VERSION) +#error "PACKAGE_VERSION must be defined" +#endif + +/* static function declarations */ +static void* aac_frame_decode(NeAACDecStruct *hDecoder, + NeAACDecFrameInfo *hInfo, + unsigned char *buffer, + unsigned long buffer_size, + void **sample_buffer2, + unsigned long sample_buffer_size); +static void create_channel_config(NeAACDecStruct *hDecoder, + NeAACDecFrameInfo *hInfo); + + +int NeAACDecGetVersion(char **faad_id_string, + char **faad_copyright_string) +{ + static char *libfaadName = PACKAGE_VERSION; + static char *libCopyright = + " Copyright 2002-2004: Ahead Software AG\n" + " http://www.audiocoding.com\n" + " bug tracking: https://sourceforge.net/p/faac/bugs/\n"; + + if (faad_id_string) + *faad_id_string = libfaadName; + + if (faad_copyright_string) + *faad_copyright_string = libCopyright; + + return 0; +} + +char* NeAACDecGetErrorMessage(unsigned char errcode) +{ + if (errcode >= NUM_ERROR_MESSAGES) + return NULL; + return err_msg[errcode]; +} + +unsigned long NeAACDecGetCapabilities(void) +{ + uint32_t cap = 0; + + /* can't do without it */ + cap += LC_DEC_CAP; + +#ifdef MAIN_DEC + cap += MAIN_DEC_CAP; +#endif +#ifdef LTP_DEC + cap += LTP_DEC_CAP; +#endif +#ifdef LD_DEC + cap += LD_DEC_CAP; +#endif +#ifdef ERROR_RESILIENCE + cap += ERROR_RESILIENCE_CAP; +#endif +#ifdef FIXED_POINT + cap += FIXED_POINT_CAP; +#endif + + return cap; +} + +const unsigned char mes[] = { 0x67,0x20,0x61,0x20,0x20,0x20,0x6f,0x20,0x72,0x20,0x65,0x20,0x6e,0x20,0x20,0x20,0x74,0x20,0x68,0x20,0x67,0x20,0x69,0x20,0x72,0x20,0x79,0x20,0x70,0x20,0x6f,0x20,0x63 }; +NeAACDecHandle NeAACDecOpen(void) +{ + uint8_t i; + NeAACDecStruct *hDecoder = NULL; + + if ((hDecoder = (NeAACDecStruct*)faad_malloc(sizeof(NeAACDecStruct))) == NULL) + return NULL; + + memset(hDecoder, 0, sizeof(NeAACDecStruct)); + + hDecoder->cmes = mes; + hDecoder->config.outputFormat = FAAD_FMT_16BIT; + hDecoder->config.defObjectType = MAIN; + hDecoder->config.defSampleRate = 44100; /* Default: 44.1kHz */ + hDecoder->config.downMatrix = 0; + hDecoder->adts_header_present = 0; + hDecoder->adif_header_present = 0; + hDecoder->latm_header_present = 0; +#ifdef ERROR_RESILIENCE + hDecoder->aacSectionDataResilienceFlag = 0; + hDecoder->aacScalefactorDataResilienceFlag = 0; + hDecoder->aacSpectralDataResilienceFlag = 0; +#endif + hDecoder->frameLength = 1024; + + hDecoder->frame = 0; + hDecoder->sample_buffer = NULL; + + // Same as (1, 1) after 1024 iterations; otherwise first values does not look random at all. + hDecoder->__r1 = 0x2bb431ea; + hDecoder->__r2 = 0x206155b7; + + for (i = 0; i < MAX_CHANNELS; i++) + { + hDecoder->element_id[i] = INVALID_ELEMENT_ID; + hDecoder->window_shape_prev[i] = 0; + hDecoder->time_out[i] = NULL; + hDecoder->fb_intermed[i] = NULL; +#ifdef SSR_DEC + hDecoder->ssr_overlap[i] = NULL; + hDecoder->prev_fmd[i] = NULL; +#endif +#ifdef MAIN_DEC + hDecoder->pred_stat[i] = NULL; +#endif +#ifdef LTP_DEC + hDecoder->ltp_lag[i] = 0; + hDecoder->lt_pred_stat[i] = NULL; +#endif + } + +#ifdef SBR_DEC + for (i = 0; i < MAX_SYNTAX_ELEMENTS; i++) + { + hDecoder->sbr[i] = NULL; + } +#endif + + hDecoder->drc = drc_init(REAL_CONST(1.0), REAL_CONST(1.0)); + + return hDecoder; +} + +NeAACDecConfigurationPtr NeAACDecGetCurrentConfiguration(NeAACDecHandle hpDecoder) +{ + NeAACDecStruct* hDecoder = (NeAACDecStruct*)hpDecoder; + if (hDecoder) + { + NeAACDecConfigurationPtr config = &(hDecoder->config); + + return config; + } + + return NULL; +} + +unsigned char NeAACDecSetConfiguration(NeAACDecHandle hpDecoder, + NeAACDecConfigurationPtr config) +{ + NeAACDecStruct* hDecoder = (NeAACDecStruct*)hpDecoder; + if (hDecoder && config) + { + /* check if we can decode this object type */ + if (can_decode_ot(config->defObjectType) < 0) + return 0; + hDecoder->config.defObjectType = config->defObjectType; + + /* samplerate: anything but 0 should be possible */ + if (config->defSampleRate == 0) + return 0; + hDecoder->config.defSampleRate = config->defSampleRate; + + /* check output format */ +#ifdef FIXED_POINT + if ((config->outputFormat < 1) || (config->outputFormat > 4)) + return 0; +#else + if ((config->outputFormat < 1) || (config->outputFormat > 5)) + return 0; +#endif + hDecoder->config.outputFormat = config->outputFormat; + + if (config->downMatrix > 1) + return 0; + hDecoder->config.downMatrix = config->downMatrix; + + /* OK */ + return 1; + } + + return 0; +} + + +#if 0 +static int latmCheck(latm_header *latm, bitfile *ld) +{ + uint32_t good=0, bad=0, bits, m; + + while (ld->bytes_left) + { + bits = faad_latm_frame(latm, ld); + if(bits==0xFFFFFFFF) + bad++; + else + { + good++; + while(bits>0) + { + m = min(bits, 8); + faad_getbits(ld, m); + bits -= m; + } + } + } + + return (good>0); +} +#endif + +long NeAACDecInit(NeAACDecHandle hpDecoder, + unsigned char *buffer, + unsigned long buffer_size, + unsigned long *samplerate, + unsigned char *channels) +{ + uint32_t bits = 0; + bitfile ld; + adif_header adif; + adts_header adts; + NeAACDecStruct* hDecoder = (NeAACDecStruct*)hpDecoder; + + + if ((hDecoder == NULL) || (samplerate == NULL) || (channels == NULL) || (buffer_size == 0)) + return -1; + + adts.old_format = hDecoder->config.useOldADTSFormat; + hDecoder->sf_index = get_sr_index(hDecoder->config.defSampleRate); + hDecoder->object_type = hDecoder->config.defObjectType; + *samplerate = get_sample_rate(hDecoder->sf_index); + *channels = 1; + + if (buffer != NULL) + { +#if 0 + int is_latm; + latm_header *l = &hDecoder->latm_config; +#endif + + faad_initbits(&ld, buffer, buffer_size); + +#if 0 + memset(l, 0, sizeof(latm_header)); + is_latm = latmCheck(l, &ld); + l->inited = 0; + l->frameLength = 0; + faad_rewindbits(&ld); + if(is_latm && l->ASCbits>0) + { + int32_t x; + hDecoder->latm_header_present = 1; + x = NeAACDecInit2(hDecoder, l->ASC, (l->ASCbits+7)/8, samplerate, channels); + if(x!=0) + hDecoder->latm_header_present = 0; + return x; + } else +#endif + /* Check if an ADIF header is present */ + if ((buffer_size >= 8) && (buffer[0] == 'A') && (buffer[1] == 'D') && + (buffer[2] == 'I') && (buffer[3] == 'F')) + { + hDecoder->adif_header_present = 1; + + get_adif_header(&adif, &ld); + faad_byte_align(&ld); + + hDecoder->sf_index = adif.pce[0].sf_index; + hDecoder->object_type = adif.pce[0].object_type + 1; + + *samplerate = get_sample_rate(hDecoder->sf_index); + *channels = adif.pce[0].channels; + + memcpy(&(hDecoder->pce), &(adif.pce[0]), sizeof(program_config)); + hDecoder->pce_set = 1; + + bits = bit2byte(faad_get_processed_bits(&ld)); + + /* Check if an ADTS header is present */ + } else if (adts_frame(&adts, &ld) == 0) { + hDecoder->adts_header_present = 1; + + hDecoder->sf_index = adts.sf_index; + hDecoder->object_type = adts.profile + 1; + + *samplerate = get_sample_rate(hDecoder->sf_index); + *channels = (adts.channel_configuration > 6) ? + 2 : adts.channel_configuration; + } + + if (ld.error) + { + faad_endbits(&ld); + return -1; + } + faad_endbits(&ld); + } + + if (!*samplerate) + return -1; + +#ifdef HDC + if (hDecoder->config.defObjectType == HDC_LC) + { + hDecoder->object_type = hDecoder->config.defObjectType; + } +#endif + +#if (defined(PS_DEC) || defined(DRM_PS)) + /* check if we have a mono file */ + if (*channels == 1) + { + /* upMatrix to 2 channels for implicit signalling of PS */ + *channels = 2; + } +#endif + + hDecoder->channelConfiguration = *channels; + +#ifdef SBR_DEC + /* implicit signalling */ + if (*samplerate <= 24000 && (hDecoder->config.dontUpSampleImplicitSBR == 0)) + { + *samplerate *= 2; + hDecoder->forceUpSampling = 1; + } else if (*samplerate > 24000 && (hDecoder->config.dontUpSampleImplicitSBR == 0)) { + hDecoder->downSampledSBR = 1; + } +#endif + + /* must be done before frameLength is divided by 2 for LD */ +#ifdef SSR_DEC + if (hDecoder->object_type == SSR) + hDecoder->fb = ssr_filter_bank_init(hDecoder->frameLength/SSR_BANDS); + else +#endif + hDecoder->fb = filter_bank_init(hDecoder->frameLength); + +#ifdef LD_DEC + if (hDecoder->object_type == LD) + hDecoder->frameLength >>= 1; +#endif + + if (can_decode_ot(hDecoder->object_type) < 0) + return -1; + + return bits; +} + +/* Init the library using a DecoderSpecificInfo */ +char NeAACDecInit2(NeAACDecHandle hpDecoder, + unsigned char *pBuffer, + unsigned long SizeOfDecoderSpecificInfo, + unsigned long *samplerate, + unsigned char *channels) +{ + NeAACDecStruct* hDecoder = (NeAACDecStruct*)hpDecoder; + int8_t rc; + mp4AudioSpecificConfig mp4ASC; + + if((hDecoder == NULL) + || (pBuffer == NULL) + || (SizeOfDecoderSpecificInfo < 2) + || (samplerate == NULL) + || (channels == NULL)) + { + return -1; + } + + hDecoder->adif_header_present = 0; + hDecoder->adts_header_present = 0; + + /* decode the audio specific config */ + rc = AudioSpecificConfig2(pBuffer, SizeOfDecoderSpecificInfo, &mp4ASC, + &(hDecoder->pce), hDecoder->latm_header_present); + + /* copy the relevant info to the decoder handle */ + *samplerate = mp4ASC.samplingFrequency; + if (mp4ASC.channelsConfiguration) + { + *channels = mp4ASC.channelsConfiguration; + } else { + *channels = hDecoder->pce.channels; + hDecoder->pce_set = 1; + } +#if (defined(PS_DEC) || defined(DRM_PS)) + /* check if we have a mono file */ + if (*channels == 1) + { + /* upMatrix to 2 channels for implicit signalling of PS */ + *channels = 2; + } +#endif + hDecoder->sf_index = mp4ASC.samplingFrequencyIndex; + hDecoder->object_type = mp4ASC.objectTypeIndex; +#ifdef ERROR_RESILIENCE + hDecoder->aacSectionDataResilienceFlag = mp4ASC.aacSectionDataResilienceFlag; + hDecoder->aacScalefactorDataResilienceFlag = mp4ASC.aacScalefactorDataResilienceFlag; + hDecoder->aacSpectralDataResilienceFlag = mp4ASC.aacSpectralDataResilienceFlag; +#endif +#ifdef SBR_DEC + hDecoder->sbr_present_flag = mp4ASC.sbr_present_flag; + hDecoder->downSampledSBR = mp4ASC.downSampledSBR; + if (hDecoder->config.dontUpSampleImplicitSBR == 0) + hDecoder->forceUpSampling = mp4ASC.forceUpSampling; + else + hDecoder->forceUpSampling = 0; + + /* AAC core decoder samplerate is 2 times as low */ + if (((hDecoder->sbr_present_flag == 1)&&(!hDecoder->downSampledSBR)) || hDecoder->forceUpSampling == 1) + { + hDecoder->sf_index = get_sr_index(mp4ASC.samplingFrequency / 2); + } +#endif + + if (rc != 0) + { + return rc; + } + hDecoder->channelConfiguration = mp4ASC.channelsConfiguration; + if (mp4ASC.frameLengthFlag) +#ifdef ALLOW_SMALL_FRAMELENGTH + hDecoder->frameLength = 960; +#else + return -1; +#endif + + /* must be done before frameLength is divided by 2 for LD */ +#ifdef SSR_DEC + if (hDecoder->object_type == SSR) + hDecoder->fb = ssr_filter_bank_init(hDecoder->frameLength/SSR_BANDS); + else +#endif + hDecoder->fb = filter_bank_init(hDecoder->frameLength); + +#ifdef LD_DEC + if (hDecoder->object_type == LD) + hDecoder->frameLength >>= 1; +#endif + + return 0; +} + +#ifdef DRM +char NeAACDecInitDRM(NeAACDecHandle *hpDecoder, + unsigned long samplerate, + unsigned char channels) +{ + NeAACDecStruct** hDecoder = (NeAACDecStruct**)hpDecoder; + if (hDecoder == NULL) + return 1; /* error */ + + NeAACDecClose(*hDecoder); + + *hDecoder = NeAACDecOpen(); + + /* Special object type defined for DRM */ + (*hDecoder)->config.defObjectType = DRM_ER_LC; + + (*hDecoder)->config.defSampleRate = samplerate; +#ifdef ERROR_RESILIENCE // This shoudl always be defined for DRM + (*hDecoder)->aacSectionDataResilienceFlag = 1; /* VCB11 */ + (*hDecoder)->aacScalefactorDataResilienceFlag = 0; /* no RVLC */ + (*hDecoder)->aacSpectralDataResilienceFlag = 1; /* HCR */ +#endif + (*hDecoder)->frameLength = 960; + (*hDecoder)->sf_index = get_sr_index((*hDecoder)->config.defSampleRate); + (*hDecoder)->object_type = (*hDecoder)->config.defObjectType; + + if ((channels == DRMCH_STEREO) || (channels == DRMCH_SBR_STEREO)) + (*hDecoder)->channelConfiguration = 2; + else + (*hDecoder)->channelConfiguration = 1; + +#ifdef SBR_DEC + if ((channels == DRMCH_MONO) || (channels == DRMCH_STEREO)) + (*hDecoder)->sbr_present_flag = 0; + else + (*hDecoder)->sbr_present_flag = 1; +#endif + + (*hDecoder)->fb = filter_bank_init((*hDecoder)->frameLength); + + return 0; +} +#endif + +#ifdef HDC +char NeAACDecInitHDC(NeAACDecHandle *hpDecoder) +{ + NeAACDecStruct** hDecoder = (NeAACDecStruct**)hpDecoder; + if (hDecoder == NULL) + return 1; /* error */ + + NeAACDecClose(*hDecoder); + + *hDecoder = NeAACDecOpen(); + + /* Special object type defined for HDC */ + (*hDecoder)->config.defObjectType = HDC_LC; + + (*hDecoder)->config.defSampleRate = 22050; +#ifdef ERROR_RESILIENCE + (*hDecoder)->aacSectionDataResilienceFlag = 0; + (*hDecoder)->aacScalefactorDataResilienceFlag = 0; + (*hDecoder)->aacSpectralDataResilienceFlag = 0; +#endif + (*hDecoder)->frameLength = 1024; + (*hDecoder)->sf_index = get_sr_index((*hDecoder)->config.defSampleRate); + (*hDecoder)->object_type = (*hDecoder)->config.defObjectType; + + (*hDecoder)->channelConfiguration = 2; + (*hDecoder)->forceUpSampling = 1; + + (*hDecoder)->fb = filter_bank_init((*hDecoder)->frameLength); + return 0; +} +#endif + +void NeAACDecClose(NeAACDecHandle hpDecoder) +{ + uint8_t i; + NeAACDecStruct* hDecoder = (NeAACDecStruct*)hpDecoder; + + if (hDecoder == NULL) + return; + +#ifdef PROFILE + printf("AAC decoder total: %I64d cycles\n", hDecoder->cycles); + printf("requant: %I64d cycles\n", hDecoder->requant_cycles); + printf("spectral_data: %I64d cycles\n", hDecoder->spectral_cycles); + printf("scalefactors: %I64d cycles\n", hDecoder->scalefac_cycles); + printf("output: %I64d cycles\n", hDecoder->output_cycles); +#endif + + for (i = 0; i < MAX_CHANNELS; i++) + { + if (hDecoder->time_out[i]) faad_free(hDecoder->time_out[i]); + if (hDecoder->fb_intermed[i]) faad_free(hDecoder->fb_intermed[i]); +#ifdef SSR_DEC + if (hDecoder->ssr_overlap[i]) faad_free(hDecoder->ssr_overlap[i]); + if (hDecoder->prev_fmd[i]) faad_free(hDecoder->prev_fmd[i]); +#endif +#ifdef MAIN_DEC + if (hDecoder->pred_stat[i]) faad_free(hDecoder->pred_stat[i]); +#endif +#ifdef LTP_DEC + if (hDecoder->lt_pred_stat[i]) faad_free(hDecoder->lt_pred_stat[i]); +#endif + } + +#ifdef SSR_DEC + if (hDecoder->object_type == SSR) + ssr_filter_bank_end(hDecoder->fb); + else +#endif + filter_bank_end(hDecoder->fb); + + drc_end(hDecoder->drc); + + if (hDecoder->sample_buffer) faad_free(hDecoder->sample_buffer); + +#ifdef SBR_DEC + for (i = 0; i < MAX_SYNTAX_ELEMENTS; i++) + { + if (hDecoder->sbr[i]) + sbrDecodeEnd(hDecoder->sbr[i]); + } +#endif + + if (hDecoder) faad_free(hDecoder); +} + +void NeAACDecPostSeekReset(NeAACDecHandle hpDecoder, long frame) +{ + NeAACDecStruct* hDecoder = (NeAACDecStruct*)hpDecoder; + if (hDecoder) + { + hDecoder->postSeekResetFlag = 1; + + if (frame != -1) + hDecoder->frame = frame; + } +} + +static void create_channel_config(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo) +{ + hInfo->num_front_channels = 0; + hInfo->num_side_channels = 0; + hInfo->num_back_channels = 0; + hInfo->num_lfe_channels = 0; + memset(hInfo->channel_position, 0, MAX_CHANNELS*sizeof(uint8_t)); + + if (hDecoder->downMatrix) + { + hInfo->num_front_channels = 2; + hInfo->channel_position[0] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[1] = FRONT_CHANNEL_RIGHT; + return; + } + + /* check if there is a PCE */ + /* TODO: why CPE flag is ignored? */ + if (hDecoder->pce_set) + { + uint8_t i, chpos = 0; + uint8_t chdir, back_center = 0; + + hInfo->num_front_channels = hDecoder->pce.num_front_channels; + hInfo->num_side_channels = hDecoder->pce.num_side_channels; + hInfo->num_back_channels = hDecoder->pce.num_back_channels; + hInfo->num_lfe_channels = hDecoder->pce.num_lfe_channels; + chdir = hInfo->num_front_channels; + if (chdir & 1) + { +#if (defined(PS_DEC) || defined(DRM_PS)) + if (hInfo->num_front_channels == 1 && + hInfo->num_side_channels == 0 && + hInfo->num_back_channels == 0 && + hInfo->num_lfe_channels == 0) + { + /* When PS is enabled output is always stereo */ + hInfo->channel_position[chpos++] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[chpos++] = FRONT_CHANNEL_RIGHT; + } else +#endif + hInfo->channel_position[chpos++] = FRONT_CHANNEL_CENTER; + chdir--; + } + for (i = 0; i < chdir; i++) + { + hInfo->channel_position[chpos++] = + (i & 1) ? FRONT_CHANNEL_RIGHT : FRONT_CHANNEL_LEFT; + } + + for (i = 0; i < hInfo->num_side_channels; i++) + { + hInfo->channel_position[chpos++] = + (i & 1) ? SIDE_CHANNEL_RIGHT : SIDE_CHANNEL_LEFT; + } + + chdir = hInfo->num_back_channels; + if (chdir & 1) + { + back_center = 1; + chdir--; + } + for (i = 0; i < chdir; i++) + { + hInfo->channel_position[chpos++] = + (i & 1) ? BACK_CHANNEL_RIGHT : BACK_CHANNEL_LEFT; + } + if (back_center) + { + hInfo->channel_position[chpos++] = BACK_CHANNEL_CENTER; + } + + for (i = 0; i < hInfo->num_lfe_channels; i++) + { + hInfo->channel_position[chpos++] = LFE_CHANNEL; + } + + } else { + switch (hDecoder->channelConfiguration) + { + case 1: +#if (defined(PS_DEC) || defined(DRM_PS)) + /* When PS is enabled output is always stereo */ + hInfo->num_front_channels = 2; + hInfo->channel_position[0] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[1] = FRONT_CHANNEL_RIGHT; +#else + hInfo->num_front_channels = 1; + hInfo->channel_position[0] = FRONT_CHANNEL_CENTER; +#endif + break; + case 2: + hInfo->num_front_channels = 2; + hInfo->channel_position[0] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[1] = FRONT_CHANNEL_RIGHT; + break; + case 3: + hInfo->num_front_channels = 3; + hInfo->channel_position[0] = FRONT_CHANNEL_CENTER; + hInfo->channel_position[1] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[2] = FRONT_CHANNEL_RIGHT; + break; + case 4: + hInfo->num_front_channels = 3; + hInfo->num_back_channels = 1; + hInfo->channel_position[0] = FRONT_CHANNEL_CENTER; + hInfo->channel_position[1] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[2] = FRONT_CHANNEL_RIGHT; + hInfo->channel_position[3] = BACK_CHANNEL_CENTER; + break; + case 5: + hInfo->num_front_channels = 3; + hInfo->num_back_channels = 2; + hInfo->channel_position[0] = FRONT_CHANNEL_CENTER; + hInfo->channel_position[1] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[2] = FRONT_CHANNEL_RIGHT; + hInfo->channel_position[3] = BACK_CHANNEL_LEFT; + hInfo->channel_position[4] = BACK_CHANNEL_RIGHT; + break; + case 6: + hInfo->num_front_channels = 3; + hInfo->num_back_channels = 2; + hInfo->num_lfe_channels = 1; + hInfo->channel_position[0] = FRONT_CHANNEL_CENTER; + hInfo->channel_position[1] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[2] = FRONT_CHANNEL_RIGHT; + hInfo->channel_position[3] = BACK_CHANNEL_LEFT; + hInfo->channel_position[4] = BACK_CHANNEL_RIGHT; + hInfo->channel_position[5] = LFE_CHANNEL; + break; + case 7: + hInfo->num_front_channels = 3; + hInfo->num_side_channels = 2; + hInfo->num_back_channels = 2; + hInfo->num_lfe_channels = 1; + hInfo->channel_position[0] = FRONT_CHANNEL_CENTER; + hInfo->channel_position[1] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[2] = FRONT_CHANNEL_RIGHT; + hInfo->channel_position[3] = SIDE_CHANNEL_LEFT; + hInfo->channel_position[4] = SIDE_CHANNEL_RIGHT; + hInfo->channel_position[5] = BACK_CHANNEL_LEFT; + hInfo->channel_position[6] = BACK_CHANNEL_RIGHT; + hInfo->channel_position[7] = LFE_CHANNEL; + break; + default: /* channelConfiguration == 0 || channelConfiguration > 7 */ + { + uint8_t i; + uint8_t ch = hDecoder->fr_channels - hDecoder->has_lfe; + if (ch & 1) /* there's either a center front or a center back channel */ + { + uint8_t ch1 = (ch-1)/2; + if (hDecoder->first_syn_ele == ID_SCE) + { + hInfo->num_front_channels = ch1 + 1; + hInfo->num_back_channels = ch1; + hInfo->channel_position[0] = FRONT_CHANNEL_CENTER; + for (i = 1; i <= ch1; i+=2) + { + hInfo->channel_position[i] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[i+1] = FRONT_CHANNEL_RIGHT; + } + for (i = ch1+1; i < ch; i+=2) + { + hInfo->channel_position[i] = BACK_CHANNEL_LEFT; + hInfo->channel_position[i+1] = BACK_CHANNEL_RIGHT; + } + } else { + hInfo->num_front_channels = ch1; + hInfo->num_back_channels = ch1 + 1; + for (i = 0; i < ch1; i+=2) + { + hInfo->channel_position[i] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[i+1] = FRONT_CHANNEL_RIGHT; + } + for (i = ch1; i < ch-1; i+=2) + { + hInfo->channel_position[i] = BACK_CHANNEL_LEFT; + hInfo->channel_position[i+1] = BACK_CHANNEL_RIGHT; + } + hInfo->channel_position[ch-1] = BACK_CHANNEL_CENTER; + } + } else { + uint8_t ch1 = (ch)/2; + hInfo->num_front_channels = ch1; + hInfo->num_back_channels = ch1; + if (ch1 & 1) + { + hInfo->channel_position[0] = FRONT_CHANNEL_CENTER; + for (i = 1; i <= ch1; i+=2) + { + hInfo->channel_position[i] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[i+1] = FRONT_CHANNEL_RIGHT; + } + for (i = ch1+1; i < ch-1; i+=2) + { + hInfo->channel_position[i] = BACK_CHANNEL_LEFT; + hInfo->channel_position[i+1] = BACK_CHANNEL_RIGHT; + } + hInfo->channel_position[ch-1] = BACK_CHANNEL_CENTER; + } else { + for (i = 0; i < ch1; i+=2) + { + hInfo->channel_position[i] = FRONT_CHANNEL_LEFT; + hInfo->channel_position[i+1] = FRONT_CHANNEL_RIGHT; + } + for (i = ch1; i < ch; i+=2) + { + hInfo->channel_position[i] = BACK_CHANNEL_LEFT; + hInfo->channel_position[i+1] = BACK_CHANNEL_RIGHT; + } + } + } + hInfo->num_lfe_channels = hDecoder->has_lfe; + for (i = ch; i < hDecoder->fr_channels; i++) + { + hInfo->channel_position[i] = LFE_CHANNEL; + } + } + break; + } + } +} + +void* NeAACDecDecode(NeAACDecHandle hpDecoder, + NeAACDecFrameInfo *hInfo, + unsigned char *buffer, + unsigned long buffer_size) +{ + NeAACDecStruct* hDecoder = (NeAACDecStruct*)hpDecoder; + return aac_frame_decode(hDecoder, hInfo, buffer, buffer_size, NULL, 0); +} + +void* NeAACDecDecode2(NeAACDecHandle hpDecoder, + NeAACDecFrameInfo *hInfo, + unsigned char *buffer, + unsigned long buffer_size, + void **sample_buffer, + unsigned long sample_buffer_size) +{ + NeAACDecStruct* hDecoder = (NeAACDecStruct*)hpDecoder; + if ((sample_buffer_size == 0) || (sample_buffer == NULL) || (*sample_buffer == NULL)) + { + hInfo->error = 27; + return NULL; + } + + return aac_frame_decode(hDecoder, hInfo, buffer, buffer_size, + sample_buffer, sample_buffer_size); +} + +static void* aac_frame_decode(NeAACDecStruct *hDecoder, + NeAACDecFrameInfo *hInfo, + unsigned char *buffer, + unsigned long buffer_size, + void **sample_buffer2, + unsigned long sample_buffer_size) +{ + uint16_t i; + uint8_t channels = 0; + uint8_t output_channels = 0; + bitfile ld; + uint32_t bitsconsumed; + uint16_t frame_len; + void *sample_buffer; +#if 0 + uint32_t startbit=0, endbit=0, payload_bits=0; +#endif + uint32_t required_buffer_size=0; + +#ifdef PROFILE + int64_t count = faad_get_ts(); +#endif + + /* safety checks */ + if ((hDecoder == NULL) || (hInfo == NULL) || (buffer == NULL)) + { + return NULL; + } + +#if 0 + printf("%d\n", buffer_size*8); +#endif + + frame_len = hDecoder->frameLength; + + + memset(hInfo, 0, sizeof(NeAACDecFrameInfo)); + memset(hDecoder->internal_channel, 0, MAX_CHANNELS*sizeof(hDecoder->internal_channel[0])); + +#ifdef USE_TIME_LIMIT + if ((TIME_LIMIT * get_sample_rate(hDecoder->sf_index)) > hDecoder->TL_count) + { + hDecoder->TL_count += 1024; + } else { + hInfo->error = (NUM_ERROR_MESSAGES-1); + goto error; + } +#endif + + + /* check for some common metadata tag types in the bitstream + * No need to return an error + */ + /* ID3 */ + if (buffer_size >= 128) + { + if (memcmp(buffer, "TAG", 3) == 0) + { + /* found it */ + hInfo->bytesconsumed = 128; /* 128 bytes fixed size */ + /* no error, but no output either */ + return NULL; + } + } + + + /* initialize the bitstream */ + faad_initbits(&ld, buffer, buffer_size); + if (ld.error != 0) + return NULL; + +#if 0 + { + int i; + for (i = 0; i < ((buffer_size+3)>>2); i++) + { + uint8_t *buf; + uint32_t temp = 0; + buf = faad_getbitbuffer(&ld, 32); + //temp = getdword((void*)buf); + temp = *((uint32_t*)buf); + printf("0x%.8X\n", temp); + free(buf); + } + faad_endbits(&ld); + faad_initbits(&ld, buffer, buffer_size); + } +#endif + +#if 0 + if(hDecoder->latm_header_present) + { + payload_bits = faad_latm_frame(&hDecoder->latm_config, &ld); + startbit = faad_get_processed_bits(&ld); + if(payload_bits == -1U) + { + hInfo->error = 1; + goto error; + } + } +#endif + +#ifdef DRM + if (hDecoder->object_type == DRM_ER_LC) + { + /* We do not support stereo right now */ + if (0) //(hDecoder->channelConfiguration == 2) + { + hInfo->error = 28; // Throw CRC error + goto error; + } + + faad_getbits(&ld, 8 + DEBUGVAR(1,1,"NeAACDecDecode(): skip CRC")); + } +#endif + + if (hDecoder->adts_header_present) + { + adts_header adts; + + adts.old_format = hDecoder->config.useOldADTSFormat; + if ((hInfo->error = adts_frame(&adts, &ld)) > 0) + goto error; + + /* MPEG2 does byte_alignment() here, + * but ADTS header is always multiple of 8 bits in MPEG2 + * so not needed to actually do it. + */ + } + +#ifdef ANALYSIS + dbg_count = 0; +#endif + + /* decode the complete bitstream */ +#ifdef DRM + if (/*(hDecoder->object_type == 6) ||*/ hDecoder->object_type == DRM_ER_LC) + { + DRM_aac_scalable_main_element(hDecoder, hInfo, &ld, &hDecoder->pce, hDecoder->drc); + } else { +#endif + +#ifdef HDC + if (hDecoder->object_type == HDC_LC) + { + hdc_data_block(hDecoder, hInfo, &ld, &hDecoder->pce, hDecoder->drc); + } else { +#endif + + raw_data_block(hDecoder, hInfo, &ld, &hDecoder->pce, hDecoder->drc); + +#ifdef HDC + } +#endif + +#ifdef DRM + } +#endif + +#if 0 + if(hDecoder->latm_header_present) + { + endbit = faad_get_processed_bits(&ld); + if(endbit-startbit > payload_bits) + fprintf(stderr, "\r\nERROR, too many payload bits read: %u > %d. Please. report with a link to a sample\n", + endbit-startbit, payload_bits); + if(hDecoder->latm_config.otherDataLenBits > 0) + faad_getbits(&ld, hDecoder->latm_config.otherDataLenBits); + faad_byte_align(&ld); + } +#endif + + channels = hDecoder->fr_channels; + + if (hInfo->error > 0) + goto error; + + /* safety check */ + if (channels == 0 || channels > MAX_CHANNELS) + { + /* invalid number of channels */ + hInfo->error = 12; + goto error; + } + + /* no more bit reading after this */ + bitsconsumed = faad_get_processed_bits(&ld); + hInfo->bytesconsumed = bit2byte(bitsconsumed); + if (ld.error) + { + hInfo->error = 14; + goto error; + } + faad_endbits(&ld); + + + if (!hDecoder->adts_header_present && !hDecoder->adif_header_present +#if 0 + && !hDecoder->latm_header_present +#endif + ) + { + if (hDecoder->channelConfiguration == 0) + hDecoder->channelConfiguration = channels; + + if (channels == 8) /* 7.1 */ + hDecoder->channelConfiguration = 7; + if (channels == 7) /* not a standard channelConfiguration */ + hDecoder->channelConfiguration = 0; + } + + if ((channels == 5 || channels == 6) && hDecoder->config.downMatrix) + { + hDecoder->downMatrix = 1; + output_channels = 2; + } else { + output_channels = channels; + } + +#if (defined(PS_DEC) || defined(DRM_PS)) + hDecoder->upMatrix = 0; + /* check if we have a mono file */ + if (output_channels == 1) + { + /* upMatrix to 2 channels for implicit signalling of PS */ + hDecoder->upMatrix = 1; + output_channels = 2; + } +#endif + + /* Make a channel configuration based on either a PCE or a channelConfiguration */ + if (!hDecoder->downMatrix && hDecoder->pce_set) + { + /* In some codepath program_config_element result is ignored. */ + if (hDecoder->pce.channels > MAX_CHANNELS) + { + hInfo->error = 22; + return NULL; + } + } + create_channel_config(hDecoder, hInfo); + + /* number of samples in this frame */ + hInfo->samples = frame_len*output_channels; + /* number of channels in this frame */ + hInfo->channels = output_channels; + /* samplerate */ + hInfo->samplerate = get_sample_rate(hDecoder->sf_index); + /* object type */ + hInfo->object_type = hDecoder->object_type; + /* sbr */ + hInfo->sbr = NO_SBR; + /* header type */ + hInfo->header_type = RAW; + if (hDecoder->adif_header_present) + hInfo->header_type = ADIF; + if (hDecoder->adts_header_present) + hInfo->header_type = ADTS; +#if 0 + if (hDecoder->latm_header_present) + hInfo->header_type = LATM; +#endif +#if (defined(PS_DEC) || defined(DRM_PS)) + hInfo->ps = hDecoder->ps_used_global; +#endif + + /* check if frame has channel elements */ + if (channels == 0) + { + hDecoder->frame++; + return NULL; + } + + { + static const uint8_t str[] = { sizeof(int16_t), sizeof(int32_t), sizeof(int32_t), + sizeof(float32_t), sizeof(double), sizeof(int16_t), sizeof(int16_t), + sizeof(int16_t), sizeof(int16_t), 0, 0, 0 + }; + uint8_t stride = str[hDecoder->config.outputFormat-1]; +#ifdef SBR_DEC + if (((hDecoder->sbr_present_flag == 1)&&(!hDecoder->downSampledSBR)) || (hDecoder->forceUpSampling == 1)) + { + stride = 2 * stride; + } +#endif + required_buffer_size = frame_len*output_channels*stride; + } + + /* check if we want to use internal sample_buffer */ + if (sample_buffer_size == 0) + { + /* allocate the buffer for the final samples */ + if (hDecoder->sample_buffer_size != required_buffer_size) + { + if (hDecoder->sample_buffer) + faad_free(hDecoder->sample_buffer); + hDecoder->sample_buffer = NULL; + hDecoder->sample_buffer = faad_malloc(required_buffer_size); + hDecoder->sample_buffer_size = required_buffer_size; + } + } else if (sample_buffer_size < required_buffer_size) { + /* provided sample buffer is not big enough */ + hInfo->error = 27; + return NULL; + } + + if (sample_buffer_size == 0) + { + sample_buffer = hDecoder->sample_buffer; + } else { + sample_buffer = *sample_buffer2; + } + +#ifdef SBR_DEC + if ((hDecoder->sbr_present_flag == 1) || (hDecoder->forceUpSampling == 1)) + { + uint8_t ele; + + /* this data is different when SBR is used or when the data is upsampled */ + if (!hDecoder->downSampledSBR) + { + frame_len *= 2; + hInfo->samples *= 2; + hInfo->samplerate *= 2; + } + + /* check if every element was provided with SBR data */ + for (ele = 0; ele < hDecoder->fr_ch_ele; ele++) + { + if (hDecoder->sbr[ele] == NULL) + { + hInfo->error = 25; + goto error; + } + } + + /* sbr */ + if (hDecoder->sbr_present_flag == 1) + { + hInfo->object_type = HE_AAC; + hInfo->sbr = SBR_UPSAMPLED; + } else { + hInfo->sbr = NO_SBR_UPSAMPLED; + } + if (hDecoder->downSampledSBR) + { + hInfo->sbr = SBR_DOWNSAMPLED; + } + } +#endif + + + sample_buffer = output_to_PCM(hDecoder, hDecoder->time_out, sample_buffer, + output_channels, frame_len, hDecoder->config.outputFormat); + + +#ifdef DRM + //conceal_output(hDecoder, frame_len, output_channels, sample_buffer); +#endif + + + hDecoder->postSeekResetFlag = 0; + + hDecoder->frame++; +#ifdef LD_DEC + if (hDecoder->object_type != LD) + { +#endif + if (hDecoder->frame <= 1) + hInfo->samples = 0; +#ifdef LD_DEC + } else { + /* LD encoders will give lower delay */ + if (hDecoder->frame <= 0) + hInfo->samples = 0; + } +#endif + + /* cleanup */ +#ifdef ANALYSIS + fflush(stdout); +#endif + +#ifdef PROFILE + count = faad_get_ts() - count; + hDecoder->cycles += count; +#endif + + return sample_buffer; + +error: + + /* reset filterbank state */ + for (i = 0; i < MAX_CHANNELS; i++) + { + if (hDecoder->fb_intermed[i] != NULL) + { + memset(hDecoder->fb_intermed[i], 0, hDecoder->frameLength*sizeof(real_t)); + } + } +#ifdef SBR_DEC + for (i = 0; i < MAX_SYNTAX_ELEMENTS; i++) + { + if (hDecoder->sbr[i] != NULL) + { + sbrReset(hDecoder->sbr[i]); + } + } +#endif + + + faad_endbits(&ld); + + /* cleanup */ +#ifdef ANALYSIS + fflush(stdout); +#endif + + return NULL; +} diff --git a/third_party/faad_hdc/upstream/libfaad/drc.c b/third_party/faad_hdc/upstream/libfaad/drc.c new file mode 100644 index 000000000..e3c840b59 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/drc.c @@ -0,0 +1,172 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: drc.c,v 1.29 2015/02/22 10:09:29 knik Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include +#include "syntax.h" +#include "drc.h" + +drc_info *drc_init(real_t cut, real_t boost) +{ + drc_info *drc = (drc_info*)faad_malloc(sizeof(drc_info)); + memset(drc, 0, sizeof(drc_info)); + + drc->ctrl1 = cut; + drc->ctrl2 = boost; + + drc->num_bands = 1; + drc->band_top[0] = 1024/4 - 1; + drc->dyn_rng_sgn[0] = 1; + drc->dyn_rng_ctl[0] = 0; + + return drc; +} + +void drc_end(drc_info *drc) +{ + if (drc) faad_free(drc); +} + +#ifdef FIXED_POINT +static real_t drc_pow2_table[] = +{ + COEF_CONST(0.5146511183), + COEF_CONST(0.5297315472), + COEF_CONST(0.5452538663), + COEF_CONST(0.5612310242), + COEF_CONST(0.5776763484), + COEF_CONST(0.5946035575), + COEF_CONST(0.6120267717), + COEF_CONST(0.6299605249), + COEF_CONST(0.6484197773), + COEF_CONST(0.6674199271), + COEF_CONST(0.6869768237), + COEF_CONST(0.7071067812), + COEF_CONST(0.7278265914), + COEF_CONST(0.7491535384), + COEF_CONST(0.7711054127), + COEF_CONST(0.7937005260), + COEF_CONST(0.8169577266), + COEF_CONST(0.8408964153), + COEF_CONST(0.8655365610), + COEF_CONST(0.8908987181), + COEF_CONST(0.9170040432), + COEF_CONST(0.9438743127), + COEF_CONST(0.9715319412), + COEF_CONST(1.0000000000), + COEF_CONST(1.0293022366), + COEF_CONST(1.0594630944), + COEF_CONST(1.0905077327), + COEF_CONST(1.1224620483), + COEF_CONST(1.1553526969), + COEF_CONST(1.1892071150), + COEF_CONST(1.2240535433), + COEF_CONST(1.2599210499), + COEF_CONST(1.2968395547), + COEF_CONST(1.3348398542), + COEF_CONST(1.3739536475), + COEF_CONST(1.4142135624), + COEF_CONST(1.4556531828), + COEF_CONST(1.4983070769), + COEF_CONST(1.5422108254), + COEF_CONST(1.5874010520), + COEF_CONST(1.6339154532), + COEF_CONST(1.6817928305), + COEF_CONST(1.7310731220), + COEF_CONST(1.7817974363), + COEF_CONST(1.8340080864), + COEF_CONST(1.8877486254), + COEF_CONST(1.9430638823) +}; +#endif + +void drc_decode(drc_info *drc, real_t *spec) +{ + uint16_t i, bd, top; +#ifdef FIXED_POINT + int32_t exp, frac; +#else + real_t factor, exp; +#endif + uint16_t bottom = 0; + + if (drc->num_bands == 1) + drc->band_top[0] = 1024/4 - 1; + + for (bd = 0; bd < drc->num_bands; bd++) + { + top = 4 * (drc->band_top[bd] + 1); + +#ifndef FIXED_POINT + /* Decode DRC gain factor */ + if (drc->dyn_rng_sgn[bd]) /* compress */ + exp = ((-drc->ctrl1 * drc->dyn_rng_ctl[bd]) - (DRC_REF_LEVEL - drc->prog_ref_level))/REAL_CONST(24.0); + else /* boost */ + exp = ((drc->ctrl2 * drc->dyn_rng_ctl[bd]) - (DRC_REF_LEVEL - drc->prog_ref_level))/REAL_CONST(24.0); + factor = (real_t)pow(2.0, exp); + + /* Apply gain factor */ + for (i = bottom; i < top; i++) + spec[i] *= factor; +#else + /* Decode DRC gain factor */ + if (drc->dyn_rng_sgn[bd]) /* compress */ + { + exp = -1 * (drc->dyn_rng_ctl[bd] - (DRC_REF_LEVEL - drc->prog_ref_level))/ 24; + frac = -1 * (drc->dyn_rng_ctl[bd] - (DRC_REF_LEVEL - drc->prog_ref_level)) % 24; + } else { /* boost */ + exp = (drc->dyn_rng_ctl[bd] - (DRC_REF_LEVEL - drc->prog_ref_level))/ 24; + frac = (drc->dyn_rng_ctl[bd] - (DRC_REF_LEVEL - drc->prog_ref_level)) % 24; + } + + /* Apply gain factor */ + if (exp < 0) + { + for (i = bottom; i < top; i++) + { + spec[i] >>= -exp; + if (frac) + spec[i] = MUL_R(spec[i],drc_pow2_table[frac+23]); + } + } else { + for (i = bottom; i < top; i++) + { + spec[i] = (int32_t)(((uint32_t)spec[i]) << exp); + if (frac) + spec[i] = MUL_R(spec[i],drc_pow2_table[frac+23]); + } + } +#endif + + bottom = top; + } +} diff --git a/third_party/faad_hdc/upstream/libfaad/drc.h b/third_party/faad_hdc/upstream/libfaad/drc.h new file mode 100644 index 000000000..3e3d82310 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/drc.h @@ -0,0 +1,49 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: drc.h,v 1.22 2007/11/01 12:33:30 menno Exp $ +**/ + +#ifndef __DRC_H__ +#define __DRC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRC_REF_LEVEL 20*4 /* -20 dB */ + + +drc_info *drc_init(real_t cut, real_t boost); +void drc_end(drc_info *drc); +void drc_decode(drc_info *drc, real_t *spec); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/drm_dec.c b/third_party/faad_hdc/upstream/libfaad/drm_dec.c new file mode 100644 index 000000000..5cea4923f --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/drm_dec.c @@ -0,0 +1,1023 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: drm_dec.c,v 1.9 2007/11/01 12:33:30 menno Exp $ +**/ + +#include +#include +#include +#include +#include "common.h" + +#ifdef DRM + +#include "sbr_dec.h" +#include "drm_dec.h" +#include "bits.h" + +/* constants */ +#define DECAY_CUTOFF 3 +#define DECAY_SLOPE 0.05f + +/* macros */ +#define IS_HDC(num_subsamples) ((num_subsamples) == 32) + +/* type definitaions */ +typedef const int8_t (*drm_ps_huff_tab)[2]; + + +/* binary search huffman tables */ +static const int8_t f_huffman_sa[][2] = +{ + { /*0*/ -15, 1 }, /* index 0: 1 bits: x */ + { 2, 3 }, /* index 1: 2 bits: 1x */ + { /*7*/ -8, 4 }, /* index 2: 3 bits: 10x */ + { 5, 6 }, /* index 3: 3 bits: 11x */ + { /*1*/ -14, /*-1*/ -16 }, /* index 4: 4 bits: 101x */ + { /*-2*/ -17, 7 }, /* index 5: 4 bits: 110x */ + { 8, 9 }, /* index 6: 4 bits: 111x */ + { /*2*/ -13, /*-3*/ -18 }, /* index 7: 5 bits: 1101x */ + { /*3*/ -12, 10 }, /* index 8: 5 bits: 1110x */ + { 11, 12 }, /* index 9: 5 bits: 1111x */ + { /*4*/ -11, /*5*/ -10 }, /* index 10: 6 bits: 11101x */ + { /*-4*/ -19, /*-5*/ -20 }, /* index 11: 6 bits: 11110x */ + { /*6*/ -9, 13 }, /* index 12: 6 bits: 11111x */ + { /*-7*/ -22, /*-6*/ -21 } /* index 13: 7 bits: 111111x */ +}; + +static const int8_t t_huffman_sa[][2] = +{ + { /*0*/ -15, 1 }, /* index 0: 1 bits: x */ + { 2, 3 }, /* index 1: 2 bits: 1x */ + { /*-1*/ -16, /*1*/ -14 }, /* index 2: 3 bits: 10x */ + { 4, 5 }, /* index 3: 3 bits: 11x */ + { /*-2*/ -17, /*2*/ -13 }, /* index 4: 4 bits: 110x */ + { 6, 7 }, /* index 5: 4 bits: 111x */ + { /*-3*/ -18, /*3*/ -12 }, /* index 6: 5 bits: 1110x */ + { 8, 9 }, /* index 7: 5 bits: 1111x */ + { /*-4*/ -19, /*4*/ -11 }, /* index 8: 6 bits: 11110x */ + { 10, 11 }, /* index 9: 6 bits: 11111x */ + { /*-5*/ -20, /*5*/ -10 }, /* index 10: 7 bits: 111110x */ + { /*-6*/ -21, 12 }, /* index 11: 7 bits: 111111x */ + { /*-7*/ -22, 13 }, /* index 12: 8 bits: 1111111x */ + { /*6*/ -9, /*7*/ -8 } /* index 13: 9 bits: 11111111x */ +}; + +static const int8_t f_huffman_pan[][2] = +{ + { /*0*/ -15, 1 }, /* index 0: 1 bits: x */ + { /*-1*/ -16, 2 }, /* index 1: 2 bits: 1x */ + { /*1*/ -14, 3 }, /* index 2: 3 bits: 11x */ + { 4, 5 }, /* index 3: 4 bits: 111x */ + { /*-2*/ -17, /*2*/ -13 }, /* index 4: 5 bits: 1110x */ + { 6, 7 }, /* index 5: 5 bits: 1111x */ + { /*-3*/ -18, /*3*/ -12 }, /* index 6: 6 bits: 11110x */ + { 8, 9 }, /* index 7: 6 bits: 11111x */ + { /*-4*/ -19, /*4*/ -11 }, /* index 8: 7 bits: 111110x */ + { 10, 11 }, /* index 9: 7 bits: 111111x */ + { /*-5*/ -20, /*5*/ -10 }, /* index 10: 8 bits: 1111110x */ + { 12, 13 }, /* index 11: 8 bits: 1111111x */ + { /*-6*/ -21, /*6*/ -9 }, /* index 12: 9 bits: 11111110x */ + { /*-7*/ -22, 14 }, /* index 13: 9 bits: 11111111x */ + { /*7*/ -8, 15 }, /* index 14: 10 bits: 111111111x */ + { 16, 17 }, /* index 15: 11 bits: 1111111111x */ + { /*-8*/ -23, /*8*/ -7 }, /* index 16: 12 bits: 11111111110x */ + { 18, 19 }, /* index 17: 12 bits: 11111111111x */ + { /*-10*/ -25, 20 }, /* index 18: 13 bits: 111111111110x */ + { 21, 22 }, /* index 19: 13 bits: 111111111111x */ + { /*-9*/ -24, /*9*/ -6 }, /* index 20: 14 bits: 1111111111101x */ + { /*10*/ -5, 23 }, /* index 21: 14 bits: 1111111111110x */ + { 24, 25 }, /* index 22: 14 bits: 1111111111111x */ + { /*-13*/ -28, /*-11*/ -26 }, /* index 23: 15 bits: 11111111111101x */ + { /*11*/ -4, /*13*/ -2 }, /* index 24: 15 bits: 11111111111110x */ + { 26, 27 }, /* index 25: 15 bits: 11111111111111x */ + { /*-14*/ -29, /*-12*/ -27 }, /* index 26: 16 bits: 111111111111110x */ + { /*12*/ -3, /*14*/ -1 } /* index 27: 16 bits: 111111111111111x */ +}; + +static const int8_t t_huffman_pan[][2] = +{ + { /*0*/ -15, 1 }, /* index 0: 1 bits: x */ + { /*-1*/ -16, 2 }, /* index 1: 2 bits: 1x */ + { /*1*/ -14, 3 }, /* index 2: 3 bits: 11x */ + { /*-2*/ -17, 4 }, /* index 3: 4 bits: 111x */ + { /*2*/ -13, 5 }, /* index 4: 5 bits: 1111x */ + { /*-3*/ -18, 6 }, /* index 5: 6 bits: 11111x */ + { /*3*/ -12, 7 }, /* index 6: 7 bits: 111111x */ + { /*-4*/ -19, 8 }, /* index 7: 8 bits: 1111111x */ + { /*4*/ -11, 9 }, /* index 8: 9 bits: 11111111x */ + { 10, 11 }, /* index 9: 10 bits: 111111111x */ + { /*-5*/ -20, /*5*/ -10 }, /* index 10: 11 bits: 1111111110x */ + { 12, 13 }, /* index 11: 11 bits: 1111111111x */ + { /*-6*/ -21, /*6*/ -9 }, /* index 12: 12 bits: 11111111110x */ + { 14, 15 }, /* index 13: 12 bits: 11111111111x */ + { /*-7*/ -22, /*7*/ -8 }, /* index 14: 13 bits: 111111111110x */ + { 16, 17 }, /* index 15: 13 bits: 111111111111x */ + { /*-8*/ -23, /*8*/ -7 }, /* index 16: 14 bits: 1111111111110x */ + { 18, 19 }, /* index 17: 14 bits: 1111111111111x */ + { /*-10*/ -25, /*10*/ -5 }, /* index 18: 15 bits: 11111111111110x */ + { 20, 21 }, /* index 19: 15 bits: 11111111111111x */ + { /*-9*/ -24, /*9*/ -6 }, /* index 20: 16 bits: 111111111111110x */ + { 22, 23 }, /* index 21: 16 bits: 111111111111111x */ + { 24, 25 }, /* index 22: 17 bits: 1111111111111110x */ + { 26, 27 }, /* index 23: 17 bits: 1111111111111111x */ + { /*-14*/ -29, /*-13*/ -28 }, /* index 24: 18 bits: 11111111111111100x */ + { /*-12*/ -27, /*-11*/ -26 }, /* index 25: 18 bits: 11111111111111101x */ + { /*11*/ -4, /*12*/ -3 }, /* index 26: 18 bits: 11111111111111110x */ + { /*13*/ -2, /*14*/ -1 } /* index 27: 18 bits: 11111111111111111x */ +}; + +/* There are 3 classes in the standard but the last 2 are identical */ +static const real_t sa_quant[8][2] = +{ + { FRAC_CONST(0.0000), FRAC_CONST(0.0000) }, + { FRAC_CONST(0.0501), FRAC_CONST(0.1778) }, + { FRAC_CONST(0.0706), FRAC_CONST(0.2818) }, + { FRAC_CONST(0.0995), FRAC_CONST(0.4467) }, + { FRAC_CONST(0.1399), FRAC_CONST(0.5623) }, + { FRAC_CONST(0.1957), FRAC_CONST(0.7079) }, + { FRAC_CONST(0.2713), FRAC_CONST(0.8913) }, + { FRAC_CONST(0.3699), FRAC_CONST(1.0000) }, +}; + +/* We don't need the actual quantizer values */ +#if 0 +static const real_t pan_quant[8][5] = +{ + { COEF_CONST(0.0000), COEF_CONST(0.0000), COEF_CONST(0.0000), COEF_CONST(0.0000), COEF_CONST(0.0000) }, + { COEF_CONST(0.1661), COEF_CONST(0.1661), COEF_CONST(0.3322), COEF_CONST(0.3322), COEF_CONST(0.3322) }, + { COEF_CONST(0.3322), COEF_CONST(0.3322), COEF_CONST(0.6644), COEF_CONST(0.8305), COEF_CONST(0.8305) }, + { COEF_CONST(0.4983), COEF_CONST(0.6644), COEF_CONST(0.9966), COEF_CONST(1.4949), COEF_CONST(1.6610) }, + { COEF_CONST(0.6644), COEF_CONST(0.9966), COEF_CONST(1.4949), COEF_CONST(2.1593), COEF_CONST(2.4914) }, + { COEF_CONST(0.8305), COEF_CONST(1.3288), COEF_CONST(2.1593), COEF_CONST(2.9897), COEF_CONST(3.4880) }, + { COEF_CONST(0.9966), COEF_CONST(1.8271), COEF_CONST(2.8236), COEF_CONST(3.8202), COEF_CONST(4.6507) }, + { COEF_CONST(1.3288), COEF_CONST(2.3253), COEF_CONST(3.4880), COEF_CONST(4.6507), COEF_CONST(5.8134) }, +}; +#endif + +/* 2^(pan_quant[x][y] */ +static const real_t pan_pow_2_pos[8][5] = { + { REAL_CONST(1.0000000), REAL_CONST(1.0000000), REAL_CONST(1.0000000), REAL_CONST(1.0000000), REAL_CONST(1.0000000) }, + { REAL_CONST(1.1220021), REAL_CONST(1.1220021), REAL_CONST(1.2589312), REAL_CONST(1.2589312), REAL_CONST(1.2589312) }, + { REAL_CONST(1.2589312), REAL_CONST(1.2589312), REAL_CONST(1.5849090), REAL_CONST(1.7783016), REAL_CONST(1.7783016) }, + { REAL_CONST(1.4125481), REAL_CONST(1.5849090), REAL_CONST(1.9952921), REAL_CONST(2.8184461), REAL_CONST(3.1623565) }, + { REAL_CONST(1.5849090), REAL_CONST(1.9952922), REAL_CONST(2.8184461), REAL_CONST(4.4669806), REAL_CONST(5.6232337) }, + { REAL_CONST(1.7783016), REAL_CONST(2.5119365), REAL_CONST(4.4669806), REAL_CONST(7.9430881), REAL_CONST(11.219994) }, + { REAL_CONST(1.9952921), REAL_CONST(3.5482312), REAL_CONST(7.0792671), REAL_CONST(14.125206), REAL_CONST(25.118876) }, + { REAL_CONST(2.5119365), REAL_CONST(5.0116998), REAL_CONST(11.219994), REAL_CONST(25.118876), REAL_CONST(56.235140) } +}; + +/* 2^(-pan_quant[x][y] */ +static const real_t pan_pow_2_neg[8][5] = { + { REAL_CONST(1), REAL_CONST(1), REAL_CONST(1), REAL_CONST(1), REAL_CONST(1) }, + { REAL_CONST(0.8912487), REAL_CONST(0.8912487), REAL_CONST(0.7943242), REAL_CONST(0.7943242), REAL_CONST(0.7943242) }, + { REAL_CONST(0.7943242), REAL_CONST(0.7943242), REAL_CONST(0.6309511), REAL_CONST(0.5623344), REAL_CONST(0.5623344) }, + { REAL_CONST(0.7079405), REAL_CONST(0.6309511), REAL_CONST(0.5011797), REAL_CONST(0.3548054), REAL_CONST(0.3162199) }, + { REAL_CONST(0.6309511), REAL_CONST(0.5011797), REAL_CONST(0.3548054), REAL_CONST(0.2238649), REAL_CONST(0.1778336) }, + { REAL_CONST(0.5623343), REAL_CONST(0.3980992), REAL_CONST(0.2238649), REAL_CONST(0.1258956), REAL_CONST(0.0891266) }, + { REAL_CONST(0.5011797), REAL_CONST(0.2818306), REAL_CONST(0.1412576), REAL_CONST(0.0707954), REAL_CONST(0.0398107) }, + { REAL_CONST(0.3980992), REAL_CONST(0.1995331), REAL_CONST(0.0891267), REAL_CONST(0.0398107), REAL_CONST(0.0177825) } +}; + +/* 2^(pan_quant[x][y]/30) */ +static const real_t pan_pow_2_30_pos[8][5] = { + { COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1) }, + { COEF_CONST(1.003845098), COEF_CONST(1.003845098), COEF_CONST(1.007704982), COEF_CONST(1.007704982), COEF_CONST(1.007704982) }, + { COEF_CONST(1.007704982), COEF_CONST(1.007704982), COEF_CONST(1.01546933), COEF_CONST(1.019373909), COEF_CONST(1.019373909) }, + { COEF_CONST(1.011579706), COEF_CONST(1.01546933), COEF_CONST(1.023293502), COEF_CONST(1.035142941), COEF_CONST(1.039123167) }, + { COEF_CONST(1.01546933), COEF_CONST(1.023293502), COEF_CONST(1.035142941), COEF_CONST(1.051155908), COEF_CONST(1.059252598) }, + { COEF_CONST(1.019373909), COEF_CONST(1.03117796), COEF_CONST(1.051155908), COEF_CONST(1.071518432), COEF_CONST(1.0839263) }, + { COEF_CONST(1.023293502), COEF_CONST(1.043118698), COEF_CONST(1.067414119), COEF_CONST(1.092277933), COEF_CONST(1.113439626) }, + { COEF_CONST(1.03117796), COEF_CONST(1.055195268), COEF_CONST(1.0839263), COEF_CONST(1.113439626), COEF_CONST(1.143756546) } +}; + +/* 2^(-pan_quant[x][y]/30) */ +static const real_t pan_pow_2_30_neg[8][5] = { + { COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1) }, + { COEF_CONST(0.99616963), COEF_CONST(0.99616963), COEF_CONST(0.992353931), COEF_CONST(0.992353931), COEF_CONST(0.99235393) }, + { COEF_CONST(0.992353931), COEF_CONST(0.992353931), COEF_CONST(0.984766325), COEF_CONST(0.980994305), COEF_CONST(0.980994305) }, + { COEF_CONST(0.988552848), COEF_CONST(0.984766325), COEF_CONST(0.977236734), COEF_CONST(0.966050157), COEF_CONST(0.962349827) }, + { COEF_CONST(0.984766325), COEF_CONST(0.977236734), COEF_CONST(0.966050157), COEF_CONST(0.951333663), COEF_CONST(0.944061881) }, + { COEF_CONST(0.980994305), COEF_CONST(0.969764715), COEF_CONST(0.951333663), COEF_CONST(0.933255062), COEF_CONST(0.922571949) }, + { COEF_CONST(0.977236734), COEF_CONST(0.958663671), COEF_CONST(0.936843519), COEF_CONST(0.915517901), COEF_CONST(0.898117847) }, + { COEF_CONST(0.969764715), COEF_CONST(0.947691892), COEF_CONST(0.922571949), COEF_CONST(0.898117847), COEF_CONST(0.874311936) } +}; + +/* 2^(pan_quant[x][y]/32) */ +static const real_t pan_pow_2_32_pos[8][5] = { + { COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1) }, + { COEF_CONST(1.003604347), COEF_CONST(1.003604347), COEF_CONST(1.007221686), COEF_CONST(1.007221686), COEF_CONST(1.007221686) }, + { COEF_CONST(1.007221686), COEF_CONST(1.007221686), COEF_CONST(1.014495524), COEF_CONST(1.018152118), COEF_CONST(1.018152118) }, + { COEF_CONST(1.010852062), COEF_CONST(1.014495524), COEF_CONST(1.021821892), COEF_CONST(1.032910767), COEF_CONST(1.036633736) }, + { COEF_CONST(1.014495524), COEF_CONST(1.021821892), COEF_CONST(1.032910767), COEF_CONST(1.04788335), COEF_CONST(1.055448548) }, + { COEF_CONST(1.018152118), COEF_CONST(1.029201168), COEF_CONST(1.04788335), COEF_CONST(1.066902341), COEF_CONST(1.078480432) }, + { COEF_CONST(1.021821892), COEF_CONST(1.040370124), COEF_CONST(1.063070665), COEF_CONST(1.086268878), COEF_CONST(1.105986959) }, + { COEF_CONST(1.029201168), COEF_CONST(1.051658007), COEF_CONST(1.078480432), COEF_CONST(1.105986959), COEF_CONST(1.134195038) } +}; + +/* 2^(-pan_quant[x][y]/32) */ +static const real_t pan_pow_2_32_neg[8][5] = { + { COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1) }, + { COEF_CONST(0.996408597), COEF_CONST(0.996408597), COEF_CONST(0.992830093), COEF_CONST(0.992830093), COEF_CONST(0.992830093) }, + { COEF_CONST(0.992830093), COEF_CONST(0.992830093), COEF_CONST(0.985711594), COEF_CONST(0.982171507), COEF_CONST(0.982171507) }, + { COEF_CONST(0.989264441), COEF_CONST(0.985711594), COEF_CONST(0.978644134), COEF_CONST(0.968137841), COEF_CONST(0.964660869) }, + { COEF_CONST(0.985711594), COEF_CONST(0.978644134), COEF_CONST(0.968137841), COEF_CONST(0.954304695), COEF_CONST(0.947464471) }, + { COEF_CONST(0.982171507), COEF_CONST(0.971627346), COEF_CONST(0.954304695), COEF_CONST(0.93729291), COEF_CONST(0.927230546) }, + { COEF_CONST(0.978644134), COEF_CONST(0.961196383), COEF_CONST(0.940671239), COEF_CONST(0.92058239), COEF_CONST(0.904169793) }, + { COEF_CONST(0.971627346), COEF_CONST(0.950879462), COEF_CONST(0.927230546), COEF_CONST(0.904169793), COEF_CONST(0.881682573) } +}; + +static const real_t g_decayslope[MAX_SA_BAND] = { + FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.95),FRAC_CONST(0.9), FRAC_CONST(0.85), FRAC_CONST(0.8), + FRAC_CONST(0.75),FRAC_CONST(0.7), FRAC_CONST(0.65),FRAC_CONST(0.6), FRAC_CONST(0.55),FRAC_CONST(0.5), FRAC_CONST(0.45), + FRAC_CONST(0.4), FRAC_CONST(0.35),FRAC_CONST(0.3), FRAC_CONST(0.25),FRAC_CONST(0.2), FRAC_CONST(0.15), FRAC_CONST(0.1), + FRAC_CONST(0.05),FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), + FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), + FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0), + FRAC_CONST(0), FRAC_CONST(0), FRAC_CONST(0) +}; + +static const real_t sa_sqrt_1_minus[8][2] = { + { FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.998744206), FRAC_CONST(0.984066644) }, + { FRAC_CONST(0.997504707), FRAC_CONST(0.959473168) }, + { FRAC_CONST(0.995037562), FRAC_CONST(0.894683804) }, + { FRAC_CONST(0.990165638), FRAC_CONST(0.826933317) }, + { FRAC_CONST(0.980663811), FRAC_CONST(0.706312672) }, + { FRAC_CONST(0.962494836), FRAC_CONST(0.45341406) }, + { FRAC_CONST(0.929071574), FRAC_CONST(0) } +}; + +static const uint8_t sa_freq_scale[9] = +{ + 0, 1, 2, 3, 5, 7, 10, 13, 23 +}; + +//static const uint8_t pan_freq_scale[21] = +//{ +// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, +// 11, 12, 13, 14, 15, 18, 22, 26, 32, 64 +//}; + +static const uint8_t pan_quant_class[20] = +{ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 3, 3, 3, 4, 4, 4 +}; + +/* Inverse mapping lookup */ +static const uint8_t pan_inv_freq[64] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19 +}; + +static const uint8_t sa_inv_freq[MAX_SA_BAND] = { + 0, 1, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7 +}; + +static const real_t filter_coeff[] = +{ + FRAC_CONST(0.65143905754106), + FRAC_CONST(0.56471812200776), + FRAC_CONST(0.48954165955695) +}; + +static const uint8_t delay_length[3] = +{ + 3, 4, 5 +}; + +//static const real_t delay_fraction[] = +//{ +// FRAC_CONST(0.43), FRAC_CONST(0.75), FRAC_CONST(0.347) +//}; + +static const real_t peak_decay = FRAC_CONST(0.76592833836465); + +static const real_t smooth_coeff = FRAC_CONST(0.25); + +/* Please note that these are the same tables as in plain PS */ +static const complex_t Q_Fract_allpass_Qmf[][3] = { + { { FRAC_CONST(0.7804303765), FRAC_CONST(0.6252426505) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.8550928831), FRAC_CONST(0.5184748173) } }, + { { FRAC_CONST(-0.4399392009), FRAC_CONST(0.8980275393) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.0643581524), FRAC_CONST(0.9979268909) } }, + { { FRAC_CONST(-0.9723699093), FRAC_CONST(-0.2334454209) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.9146071672), FRAC_CONST(0.4043435752) } }, + { { FRAC_CONST(0.0157073960), FRAC_CONST(-0.9998766184) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.7814115286), FRAC_CONST(-0.6240159869) } }, + { { FRAC_CONST(0.9792228341), FRAC_CONST(-0.2027871907) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.1920081824), FRAC_CONST(-0.9813933372) } }, + { { FRAC_CONST(0.4115142524), FRAC_CONST(0.9114032984) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.9589683414), FRAC_CONST(-0.2835132182) } }, + { { FRAC_CONST(-0.7996847630), FRAC_CONST(0.6004201174) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.6947838664), FRAC_CONST(0.7192186117) } }, + { { FRAC_CONST(-0.7604058385), FRAC_CONST(-0.6494481564) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.3164770305), FRAC_CONST(0.9486001730) } }, + { { FRAC_CONST(0.4679299891), FRAC_CONST(-0.8837655187) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.9874414206), FRAC_CONST(0.1579856575) } }, + { { FRAC_CONST(0.9645573497), FRAC_CONST(0.2638732493) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.5966450572), FRAC_CONST(-0.8025052547) } }, + { { FRAC_CONST(-0.0471066870), FRAC_CONST(0.9988898635) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.4357025325), FRAC_CONST(-0.9000906944) } }, + { { FRAC_CONST(-0.9851093888), FRAC_CONST(0.1719288528) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.9995546937), FRAC_CONST(-0.0298405960) } }, + { { FRAC_CONST(-0.3826831877), FRAC_CONST(-0.9238796234) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.4886211455), FRAC_CONST(0.8724960685) } }, + { { FRAC_CONST(0.8181498647), FRAC_CONST(-0.5750049949) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.5477093458), FRAC_CONST(0.8366686702) } }, + { { FRAC_CONST(0.7396308780), FRAC_CONST(0.6730127335) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.9951074123), FRAC_CONST(-0.0987988561) } }, + { { FRAC_CONST(-0.4954589605), FRAC_CONST(0.8686313629) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.3725017905), FRAC_CONST(-0.9280315042) } }, + { { FRAC_CONST(-0.9557929039), FRAC_CONST(-0.2940406799) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.6506417990), FRAC_CONST(-0.7593847513) } }, + { { FRAC_CONST(0.0784594864), FRAC_CONST(-0.9969173074) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.9741733670), FRAC_CONST(0.2258014232) } }, + { { FRAC_CONST(0.9900237322), FRAC_CONST(-0.1409008205) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.2502108514), FRAC_CONST(0.9681913853) } }, + { { FRAC_CONST(0.3534744382), FRAC_CONST(0.9354441762) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.7427945137), FRAC_CONST(0.6695194840) } }, + { { FRAC_CONST(-0.8358076215), FRAC_CONST(0.5490224361) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.9370992780), FRAC_CONST(-0.3490629196) } }, + { { FRAC_CONST(-0.7181259394), FRAC_CONST(-0.6959131360) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.1237744763), FRAC_CONST(-0.9923103452) } }, + { { FRAC_CONST(0.5224990249), FRAC_CONST(-0.8526399136) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.8226406574), FRAC_CONST(-0.5685616732) } }, + { { FRAC_CONST(0.9460852146), FRAC_CONST(0.3239179254) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.8844994903), FRAC_CONST(0.4665412009) } }, + { { FRAC_CONST(-0.1097348556), FRAC_CONST(0.9939609170) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.0047125919), FRAC_CONST(0.9999889135) } }, + { { FRAC_CONST(-0.9939610362), FRAC_CONST(0.1097337380) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.8888573647), FRAC_CONST(0.4581840038) } }, + { { FRAC_CONST(-0.3239168525), FRAC_CONST(-0.9460855722) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.8172453642), FRAC_CONST(-0.5762898922) } }, + { { FRAC_CONST(0.8526405096), FRAC_CONST(-0.5224980116) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.1331215799), FRAC_CONST(-0.9910997152) } }, + { { FRAC_CONST(0.6959123611), FRAC_CONST(0.7181267142) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.9403476119), FRAC_CONST(-0.3402152061) } }, + { { FRAC_CONST(-0.5490233898), FRAC_CONST(0.8358070254) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.7364512086), FRAC_CONST(0.6764906645) } }, + { { FRAC_CONST(-0.9354437590), FRAC_CONST(-0.3534754813) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.2593250275), FRAC_CONST(0.9657900929) } }, + { { FRAC_CONST(0.1409019381), FRAC_CONST(-0.9900235534) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.9762582779), FRAC_CONST(0.2166097313) } }, + { { FRAC_CONST(0.9969173670), FRAC_CONST(-0.0784583688) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.6434556246), FRAC_CONST(-0.7654833794) } }, + { { FRAC_CONST(0.2940396070), FRAC_CONST(0.9557932615) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.3812320232), FRAC_CONST(-0.9244794250) } }, + { { FRAC_CONST(-0.8686318994), FRAC_CONST(0.4954580069) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.9959943891), FRAC_CONST(-0.0894154981) } }, + { { FRAC_CONST(-0.6730118990), FRAC_CONST(-0.7396316528) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.5397993922), FRAC_CONST(0.8417937160) } }, + { { FRAC_CONST(0.5750059485), FRAC_CONST(-0.8181492686) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.4968227744), FRAC_CONST(0.8678520322) } }, + { { FRAC_CONST(0.9238792062), FRAC_CONST(0.3826842010) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.9992290139), FRAC_CONST(-0.0392601527) } }, + { { FRAC_CONST(-0.1719299555), FRAC_CONST(0.9851091504) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.4271997511), FRAC_CONST(-0.9041572809) } }, + { { FRAC_CONST(-0.9988899231), FRAC_CONST(0.0471055657) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.6041822433), FRAC_CONST(-0.7968461514) } }, + { { FRAC_CONST(-0.2638721764), FRAC_CONST(-0.9645576477) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.9859085083), FRAC_CONST(0.1672853529) } }, + { { FRAC_CONST(0.8837660551), FRAC_CONST(-0.4679289758) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.3075223565), FRAC_CONST(0.9515408874) } }, + { { FRAC_CONST(0.6494473219), FRAC_CONST(0.7604066133) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.7015317082), FRAC_CONST(0.7126382589) } }, + { { FRAC_CONST(-0.6004210114), FRAC_CONST(0.7996840477) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.9562535882), FRAC_CONST(-0.2925389707) } }, + { { FRAC_CONST(-0.9114028811), FRAC_CONST(-0.4115152657) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.1827499419), FRAC_CONST(-0.9831594229) } }, + { { FRAC_CONST(0.2027882934), FRAC_CONST(-0.9792225957) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.7872582674), FRAC_CONST(-0.6166234016) } }, + { { FRAC_CONST(0.9998766780), FRAC_CONST(-0.0157062728) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.9107555747), FRAC_CONST(0.4129458666) } }, + { { FRAC_CONST(0.2334443331), FRAC_CONST(0.9723701477) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.0549497530), FRAC_CONST(0.9984891415) } }, + { { FRAC_CONST(-0.8980280757), FRAC_CONST(0.4399381876) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.8599416018), FRAC_CONST(0.5103924870) } }, + { { FRAC_CONST(-0.6252418160), FRAC_CONST(-0.7804310918) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.8501682281), FRAC_CONST(-0.5265110731) } }, + { { FRAC_CONST(0.6252435446), FRAC_CONST(-0.7804297209) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.0737608299), FRAC_CONST(-0.9972759485) } }, + { { FRAC_CONST(0.8980270624), FRAC_CONST(0.4399402142) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.9183775187), FRAC_CONST(-0.3957053721) } }, + { { FRAC_CONST(-0.2334465086), FRAC_CONST(0.9723696709) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.7754954696), FRAC_CONST(0.6313531399) } }, + { { FRAC_CONST(-0.9998766184), FRAC_CONST(-0.0157085191) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.2012493610), FRAC_CONST(0.9795400500) } }, + { { FRAC_CONST(-0.2027861029), FRAC_CONST(-0.9792230725) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.9615978599), FRAC_CONST(0.2744622827) } }, + { { FRAC_CONST(0.9114037752), FRAC_CONST(-0.4115132093) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.6879743338), FRAC_CONST(-0.7257350087) } }, + { { FRAC_CONST(0.6004192233), FRAC_CONST(0.7996854186) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.3254036009), FRAC_CONST(-0.9455752373) } }, + { { FRAC_CONST(-0.6494490504), FRAC_CONST(0.7604051232) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.9888865948), FRAC_CONST(-0.1486719251) } }, + { { FRAC_CONST(-0.8837650418), FRAC_CONST(-0.4679309726) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.5890548825), FRAC_CONST(0.8080930114) } }, + { { FRAC_CONST(0.2638743520), FRAC_CONST(-0.9645570517) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.4441666007), FRAC_CONST(0.8959442377) } }, + { { FRAC_CONST(0.9988898039), FRAC_CONST(0.0471078083) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.9997915030), FRAC_CONST(0.0204183888) } }, + { { FRAC_CONST(0.1719277352), FRAC_CONST(0.9851095676) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.4803760946), FRAC_CONST(-0.8770626187) } }, + { { FRAC_CONST(-0.9238800406), FRAC_CONST(0.3826821446) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.5555707216), FRAC_CONST(-0.8314692974) } }, + { { FRAC_CONST(-0.5750041008), FRAC_CONST(-0.8181505203) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.9941320419), FRAC_CONST(0.1081734300) } } +}; + +static const complex_t Phi_Fract_Qmf[] = { + { FRAC_CONST(0.8181497455), FRAC_CONST(0.5750052333) }, + { FRAC_CONST(-0.2638730407), FRAC_CONST(0.9645574093) }, + { FRAC_CONST(-0.9969173074), FRAC_CONST(0.0784590989) }, + { FRAC_CONST(-0.4115143716), FRAC_CONST(-0.9114032984) }, + { FRAC_CONST(0.7181262970), FRAC_CONST(-0.6959127784) }, + { FRAC_CONST(0.8980275989), FRAC_CONST(0.4399391711) }, + { FRAC_CONST(-0.1097343117), FRAC_CONST(0.9939609766) }, + { FRAC_CONST(-0.9723699093), FRAC_CONST(0.2334453613) }, + { FRAC_CONST(-0.5490227938), FRAC_CONST(-0.8358073831) }, + { FRAC_CONST(0.6004202366), FRAC_CONST(-0.7996846437) }, + { FRAC_CONST(0.9557930231), FRAC_CONST(0.2940403223) }, + { FRAC_CONST(0.0471064523), FRAC_CONST(0.9988898635) }, + { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, + { FRAC_CONST(-0.6730124950), FRAC_CONST(-0.7396311164) }, + { FRAC_CONST(0.4679298103), FRAC_CONST(-0.8837656379) }, + { FRAC_CONST(0.9900236726), FRAC_CONST(0.1409012377) }, + { FRAC_CONST(0.2027872950), FRAC_CONST(0.9792228341) }, + { FRAC_CONST(-0.8526401520), FRAC_CONST(0.5224985480) }, + { FRAC_CONST(-0.7804304361), FRAC_CONST(-0.6252426505) }, + { FRAC_CONST(0.3239174187), FRAC_CONST(-0.9460853338) }, + { FRAC_CONST(0.9998766184), FRAC_CONST(-0.0157073177) }, + { FRAC_CONST(0.3534748554), FRAC_CONST(0.9354440570) }, + { FRAC_CONST(-0.7604059577), FRAC_CONST(0.6494480371) }, + { FRAC_CONST(-0.8686315417), FRAC_CONST(-0.4954586625) }, + { FRAC_CONST(0.1719291061), FRAC_CONST(-0.9851093292) }, + { FRAC_CONST(0.9851093292), FRAC_CONST(-0.1719291061) }, + { FRAC_CONST(0.4954586625), FRAC_CONST(0.8686315417) }, + { FRAC_CONST(-0.6494480371), FRAC_CONST(0.7604059577) }, + { FRAC_CONST(-0.9354440570), FRAC_CONST(-0.3534748554) }, + { FRAC_CONST(0.0157073177), FRAC_CONST(-0.9998766184) }, + { FRAC_CONST(0.9460853338), FRAC_CONST(-0.3239174187) }, + { FRAC_CONST(0.6252426505), FRAC_CONST(0.7804304361) }, + { FRAC_CONST(-0.5224985480), FRAC_CONST(0.8526401520) }, + { FRAC_CONST(-0.9792228341), FRAC_CONST(-0.2027872950) }, + { FRAC_CONST(-0.1409012377), FRAC_CONST(-0.9900236726) }, + { FRAC_CONST(0.8837656379), FRAC_CONST(-0.4679298103) }, + { FRAC_CONST(0.7396311164), FRAC_CONST(0.6730124950) }, + { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, + { FRAC_CONST(-0.9988898635), FRAC_CONST(-0.0471064523) }, + { FRAC_CONST(-0.2940403223), FRAC_CONST(-0.9557930231) }, + { FRAC_CONST(0.7996846437), FRAC_CONST(-0.6004202366) }, + { FRAC_CONST(0.8358073831), FRAC_CONST(0.5490227938) }, + { FRAC_CONST(-0.2334453613), FRAC_CONST(0.9723699093) }, + { FRAC_CONST(-0.9939609766), FRAC_CONST(0.1097343117) }, + { FRAC_CONST(-0.4399391711), FRAC_CONST(-0.8980275989) }, + { FRAC_CONST(0.6959127784), FRAC_CONST(-0.7181262970) }, + { FRAC_CONST(0.9114032984), FRAC_CONST(0.4115143716) }, + { FRAC_CONST(-0.0784590989), FRAC_CONST(0.9969173074) }, + { FRAC_CONST(-0.9645574093), FRAC_CONST(0.2638730407) }, + { FRAC_CONST(-0.5750052333), FRAC_CONST(-0.8181497455) }, + { FRAC_CONST(0.5750052333), FRAC_CONST(-0.8181497455) }, + { FRAC_CONST(0.9645574093), FRAC_CONST(0.2638730407) }, + { FRAC_CONST(0.0784590989), FRAC_CONST(0.9969173074) }, + { FRAC_CONST(-0.9114032984), FRAC_CONST(0.4115143716) }, + { FRAC_CONST(-0.6959127784), FRAC_CONST(-0.7181262970) }, + { FRAC_CONST(0.4399391711), FRAC_CONST(-0.8980275989) }, + { FRAC_CONST(0.9939609766), FRAC_CONST(0.1097343117) }, + { FRAC_CONST(0.2334453613), FRAC_CONST(0.9723699093) }, + { FRAC_CONST(-0.8358073831), FRAC_CONST(0.5490227938) }, + { FRAC_CONST(-0.7996846437), FRAC_CONST(-0.6004202366) }, + { FRAC_CONST(0.2940403223), FRAC_CONST(-0.9557930231) }, + { FRAC_CONST(0.9988898635), FRAC_CONST(-0.0471064523) }, + { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, + { FRAC_CONST(-0.7396311164), FRAC_CONST(0.6730124950) } +}; + + +/* static function declarations */ +static void drm_ps_sa_element(drm_ps_info *ps, bitfile *ld); +static void drm_ps_pan_element(drm_ps_info *ps, bitfile *ld); +static int8_t huff_dec(bitfile *ld, drm_ps_huff_tab huff); + + +uint16_t drm_ps_data(drm_ps_info *ps, bitfile *ld) +{ + uint16_t bits = (uint16_t)faad_get_processed_bits(ld); + + ps->drm_ps_data_available = 1; + + ps->bs_enable_sa = faad_get1bit(ld); + ps->bs_enable_pan = faad_get1bit(ld); + + if (ps->bs_enable_sa) + { + drm_ps_sa_element(ps, ld); + } + + if (ps->bs_enable_pan) + { + drm_ps_pan_element(ps, ld); + } + + bits = (uint16_t)faad_get_processed_bits(ld) - bits; + + return bits; +} + +static void drm_ps_sa_element(drm_ps_info *ps, bitfile *ld) +{ + drm_ps_huff_tab huff; + uint8_t band; + + ps->bs_sa_dt_flag = faad_get1bit(ld); + if (ps->bs_sa_dt_flag) + { + huff = t_huffman_sa; + } else { + huff = f_huffman_sa; + } + + for (band = 0; band < DRM_NUM_SA_BANDS; band++) + { + ps->bs_sa_data[band] = huff_dec(ld, huff); + } +} + +static void drm_ps_pan_element(drm_ps_info *ps, bitfile *ld) +{ + drm_ps_huff_tab huff; + uint8_t band; + + ps->bs_pan_dt_flag = faad_get1bit(ld); + if (ps->bs_pan_dt_flag) + { + huff = t_huffman_pan; + } else { + huff = f_huffman_pan; + } + + for (band = 0; band < DRM_NUM_PAN_BANDS; band++) + { + ps->bs_pan_data[band] = huff_dec(ld, huff); + } +} + +/* binary search huffman decoding */ +static int8_t huff_dec(bitfile *ld, drm_ps_huff_tab huff) +{ + uint8_t bit; + int8_t index = 0; + + while (index >= 0) + { + bit = (uint8_t)faad_get1bit(ld); + index = huff[index][bit]; + } + + return index + 15; +} + + +static int8_t sa_delta_clip(drm_ps_info *ps, int8_t i) +{ + if (i < 0) { + /* printf(" SAminclip %d", i); */ + ps->sa_decode_error = 1; + return 0; + } else if (i > 7) { + /* printf(" SAmaxclip %d", i); */ + ps->sa_decode_error = 1; + return 7; + } else + return i; +} + +static int8_t pan_delta_clip(drm_ps_info *ps, int8_t i) +{ + if (i < -7) { + /* printf(" PANminclip %d", i); */ + ps->pan_decode_error = 1; + return -7; + } else if (i > 7) { + /* printf(" PANmaxclip %d", i); */ + ps->pan_decode_error = 1; + return 7; + } else + return i; +} + +static void drm_ps_delta_decode(drm_ps_info *ps) +{ + uint8_t band; + + if (ps->bs_enable_sa) + { + if (ps->bs_sa_dt_flag && !ps->g_last_had_sa) + { + /* wait until we get a DT frame */ + ps->bs_enable_sa = 0; + } else if (ps->bs_sa_dt_flag) { + /* DT frame, we have a last frame, so we can decode */ + ps->g_sa_index[0] = sa_delta_clip(ps, ps->g_prev_sa_index[0]+ps->bs_sa_data[0]); + } else { + /* DF always decodable */ + ps->g_sa_index[0] = sa_delta_clip(ps,ps->bs_sa_data[0]); + } + + for (band = 1; band < DRM_NUM_SA_BANDS; band++) + { + if (ps->bs_sa_dt_flag && ps->g_last_had_sa) + { + ps->g_sa_index[band] = sa_delta_clip(ps, ps->g_prev_sa_index[band] + ps->bs_sa_data[band]); + } else if (!ps->bs_sa_dt_flag) { + ps->g_sa_index[band] = sa_delta_clip(ps, ps->g_sa_index[band-1] + ps->bs_sa_data[band]); + } + } + } + + /* An error during SA decoding implies PAN data will be undecodable, too */ + /* Also, we don't like on/off switching in PS, so we force to last settings */ + if (ps->sa_decode_error) { + ps->pan_decode_error = 1; + ps->bs_enable_pan = ps->g_last_had_pan; + ps->bs_enable_sa = ps->g_last_had_sa; + } + + + if (ps->bs_enable_sa) + { + if (ps->sa_decode_error) { + for (band = 0; band < DRM_NUM_SA_BANDS; band++) + { + ps->g_sa_index[band] = ps->g_last_good_sa_index[band]; + } + } else { + for (band = 0; band < DRM_NUM_SA_BANDS; band++) + { + ps->g_last_good_sa_index[band] = ps->g_sa_index[band]; + } + } + } + + if (ps->bs_enable_pan) + { + if (ps->bs_pan_dt_flag && !ps->g_last_had_pan) + { + ps->bs_enable_pan = 0; + } else if (ps->bs_pan_dt_flag) { + ps->g_pan_index[0] = pan_delta_clip(ps, ps->g_prev_pan_index[0]+ps->bs_pan_data[0]); + } else { + ps->g_pan_index[0] = pan_delta_clip(ps, ps->bs_pan_data[0]); + } + + for (band = 1; band < DRM_NUM_PAN_BANDS; band++) + { + if (ps->bs_pan_dt_flag && ps->g_last_had_pan) + { + ps->g_pan_index[band] = pan_delta_clip(ps, ps->g_prev_pan_index[band] + ps->bs_pan_data[band]); + } else if (!ps->bs_pan_dt_flag) { + ps->g_pan_index[band] = pan_delta_clip(ps, ps->g_pan_index[band-1] + ps->bs_pan_data[band]); + } + } + + if (ps->pan_decode_error) { + for (band = 0; band < DRM_NUM_PAN_BANDS; band++) + { + ps->g_pan_index[band] = ps->g_last_good_pan_index[band]; + } + } else { + for (band = 0; band < DRM_NUM_PAN_BANDS; band++) + { + ps->g_last_good_pan_index[band] = ps->g_pan_index[band]; + } + } + } +} + +static void drm_calc_sa_side_signal(drm_ps_info *ps, qmf_t X[38][64], int num_subsamples) +{ + uint8_t s, b, k; + complex_t qfrac, tmp0, tmp, in, R0; + real_t peakdiff; + real_t nrg; + real_t power; + real_t transratio; + real_t new_delay_slopes[NUM_OF_LINKS]; + uint8_t temp_delay_ser[NUM_OF_LINKS]; + complex_t Phi_Fract; +#ifdef FIXED_POINT + uint32_t in_re, in_im; +#endif + + for (b = 0; b < sa_freq_scale[DRM_NUM_SA_BANDS]; b++) + { + /* set delay indices */ + for (k = 0; k < NUM_OF_LINKS; k++) + temp_delay_ser[k] = ps->delay_buf_index_ser[k]; + + RE(Phi_Fract) = RE(Phi_Fract_Qmf[b]); + IM(Phi_Fract) = IM(Phi_Fract_Qmf[b]); + + for (s = 0; s < num_subsamples; s++) + { + const real_t gamma = REAL_CONST(1.5); + const real_t sigma = REAL_CONST(1.5625); + + RE(in) = QMF_RE(X[s][b]); + IM(in) = QMF_IM(X[s][b]); + +#ifdef FIXED_POINT + /* NOTE: all input is scaled by 2^(-5) because of fixed point QMF + * meaning that P will be scaled by 2^(-10) compared to floating point version + */ + in_re = ((abs(RE(in))+(1<<(REAL_BITS-1)))>>REAL_BITS); + in_im = ((abs(IM(in))+(1<<(REAL_BITS-1)))>>REAL_BITS); + power = in_re*in_re + in_im*in_im; +#else + power = MUL_R(RE(in),RE(in)) + MUL_R(IM(in),IM(in)); +#endif + + ps->peakdecay_fast[b] = MUL_F(ps->peakdecay_fast[b], peak_decay); + if (ps->peakdecay_fast[b] < power) + ps->peakdecay_fast[b] = power; + + peakdiff = ps->prev_peakdiff[b]; + peakdiff += MUL_F((ps->peakdecay_fast[b] - power - ps->prev_peakdiff[b]), smooth_coeff); + ps->prev_peakdiff[b] = peakdiff; + + nrg = ps->prev_nrg[b]; + nrg += MUL_F((power - ps->prev_nrg[b]), smooth_coeff); + ps->prev_nrg[b] = nrg; + + if (MUL_R(peakdiff, gamma) <= nrg) { + transratio = sigma; + } else { + transratio = MUL_R(DIV_R(nrg, MUL_R(peakdiff, gamma)), sigma); + } + + for (k = 0; k < NUM_OF_LINKS; k++) + { + new_delay_slopes[k] = MUL_F(g_decayslope[b], filter_coeff[k]); + } + + RE(tmp0) = RE(ps->d_buff[0][b]); + IM(tmp0) = IM(ps->d_buff[0][b]); + + RE(ps->d_buff[0][b]) = RE(ps->d_buff[1][b]); + IM(ps->d_buff[0][b]) = IM(ps->d_buff[1][b]); + + RE(ps->d_buff[1][b]) = RE(in); + IM(ps->d_buff[1][b]) = IM(in); + + ComplexMult(&RE(tmp), &IM(tmp), RE(tmp0), IM(tmp0), RE(Phi_Fract), IM(Phi_Fract)); + + RE(R0) = RE(tmp); + IM(R0) = IM(tmp); + + for (k = 0; k < NUM_OF_LINKS; k++) + { + RE(qfrac) = RE(Q_Fract_allpass_Qmf[b][k]); + IM(qfrac) = IM(Q_Fract_allpass_Qmf[b][k]); + + RE(tmp0) = RE(ps->d2_buff[k][temp_delay_ser[k]][b]); + IM(tmp0) = IM(ps->d2_buff[k][temp_delay_ser[k]][b]); + + ComplexMult(&RE(tmp), &IM(tmp), RE(tmp0), IM(tmp0), RE(qfrac), IM(qfrac)); + + RE(tmp) += -MUL_F(new_delay_slopes[k], RE(R0)); + IM(tmp) += -MUL_F(new_delay_slopes[k], IM(R0)); + + RE(ps->d2_buff[k][temp_delay_ser[k]][b]) = RE(R0) + MUL_F(new_delay_slopes[k], RE(tmp)); + IM(ps->d2_buff[k][temp_delay_ser[k]][b]) = IM(R0) + MUL_F(new_delay_slopes[k], IM(tmp)); + + RE(R0) = RE(tmp); + IM(R0) = IM(tmp); + } + + QMF_RE(ps->SA[s][b]) = MUL_R(RE(R0), transratio); + QMF_IM(ps->SA[s][b]) = MUL_R(IM(R0), transratio); + + for (k = 0; k < NUM_OF_LINKS; k++) + { + if (++temp_delay_ser[k] >= delay_length[k]) + temp_delay_ser[k] = 0; + } + } + } + + for (k = 0; k < NUM_OF_LINKS; k++) + ps->delay_buf_index_ser[k] = temp_delay_ser[k]; +} + +static void drm_add_ambiance(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64], int num_subsamples) +{ + uint8_t s, b, ifreq, qclass, is_hdc; + real_t sa_map[MAX_SA_BAND], sa_dir_map[MAX_SA_BAND], k_sa_map[MAX_SA_BAND], k_sa_dir_map[MAX_SA_BAND]; + real_t new_dir_map, new_sa_map; + + is_hdc = IS_HDC(num_subsamples); + + if (ps->bs_enable_sa) + { + /* Instead of dequantization and mapping, we use an inverse mapping + to look up all the values we need */ + for (b = 0; b < sa_freq_scale[DRM_NUM_SA_BANDS]; b++) + { + const real_t inv_f_num_of_subsamples = is_hdc ? FRAC_CONST(0.03125) : FRAC_CONST(0.03333333333); + + ifreq = sa_inv_freq[b]; + qclass = (b != 0); + + sa_map[b] = sa_quant[ps->g_prev_sa_index[ifreq]][qclass]; + new_sa_map = sa_quant[ps->g_sa_index[ifreq]][qclass]; + + k_sa_map[b] = MUL_F(inv_f_num_of_subsamples, (new_sa_map - sa_map[b])); + + sa_dir_map[b] = sa_sqrt_1_minus[ps->g_prev_sa_index[ifreq]][qclass]; + new_dir_map = sa_sqrt_1_minus[ps->g_sa_index[ifreq]][qclass]; + + k_sa_dir_map[b] = MUL_F(inv_f_num_of_subsamples, (new_dir_map - sa_dir_map[b])); + + } + + for (s = 0; s < num_subsamples; s++) + { + for (b = 0; b < sa_freq_scale[DRM_NUM_SA_BANDS]; b++) + { + QMF_RE(X_right[s][b]) = MUL_F(QMF_RE(X_left[s][b]), sa_dir_map[b]) - MUL_F(QMF_RE(ps->SA[s][b]), sa_map[b]); + QMF_IM(X_right[s][b]) = MUL_F(QMF_IM(X_left[s][b]), sa_dir_map[b]) - MUL_F(QMF_IM(ps->SA[s][b]), sa_map[b]); + QMF_RE(X_left[s][b]) = MUL_F(QMF_RE(X_left[s][b]), sa_dir_map[b]) + MUL_F(QMF_RE(ps->SA[s][b]), sa_map[b]); + QMF_IM(X_left[s][b]) = MUL_F(QMF_IM(X_left[s][b]), sa_dir_map[b]) + MUL_F(QMF_IM(ps->SA[s][b]), sa_map[b]); + + sa_map[b] += k_sa_map[b]; + sa_dir_map[b] += k_sa_dir_map[b]; + } + for (b = sa_freq_scale[DRM_NUM_SA_BANDS]; b < NUM_OF_QMF_CHANNELS; b++) + { + QMF_RE(X_right[s][b]) = QMF_RE(X_left[s][b]); + QMF_IM(X_right[s][b]) = QMF_IM(X_left[s][b]); + } + } + } + else { + for (s = 0; s < num_subsamples; s++) + { + for (b = 0; b < NUM_OF_QMF_CHANNELS; b++) + { + QMF_RE(X_right[s][b]) = QMF_RE(X_left[s][b]); + QMF_IM(X_right[s][b]) = QMF_IM(X_left[s][b]); + } + } + } +} + +static void drm_add_pan(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64], int num_subsamples) +{ + uint8_t s, b, qclass, ifreq, is_hdc; + real_t tmp, coeff1, coeff2; + real_t pan_base[MAX_PAN_BAND]; + real_t pan_delta[MAX_PAN_BAND]; + qmf_t temp_l, temp_r; + + is_hdc = IS_HDC(num_subsamples); + + if (ps->bs_enable_pan) + { + for (b = 0; b < NUM_OF_QMF_CHANNELS; b++) + { + /* Instead of dequantization, 20->64 mapping and 2^G(x,y) we do an + inverse mapping 64->20 and look up the 2^G(x,y) values directly */ + ifreq = pan_inv_freq[b]; + qclass = pan_quant_class[ifreq]; + + if (ps->g_prev_pan_index[ifreq] >= 0) + { + pan_base[b] = pan_pow_2_pos[ps->g_prev_pan_index[ifreq]][qclass]; + } else { + pan_base[b] = pan_pow_2_neg[-ps->g_prev_pan_index[ifreq]][qclass]; + } + + /* 2^((a-b)/32) = 2^(a/32) * 1/(2^(b/32)) if HDC */ + /* 2^((a-b)/30) = 2^(a/30) * 1/(2^(b/30)) if regular DRM */ + /* a en b can be negative so we may need to inverse parts */ + if (ps->g_pan_index[ifreq] >= 0) + { + if (ps->g_prev_pan_index[ifreq] >= 0) + { + if (is_hdc) + { + pan_delta[b] = MUL_C(pan_pow_2_32_pos[ps->g_pan_index[ifreq]][qclass], + pan_pow_2_32_neg[ps->g_prev_pan_index[ifreq]][qclass]); + } else { + pan_delta[b] = MUL_C(pan_pow_2_30_pos[ps->g_pan_index[ifreq]][qclass], + pan_pow_2_30_neg[ps->g_prev_pan_index[ifreq]][qclass]); + } + } else { + if (is_hdc) + { + pan_delta[b] = MUL_C(pan_pow_2_32_pos[ps->g_pan_index[ifreq]][qclass], + pan_pow_2_32_pos[-ps->g_prev_pan_index[ifreq]][qclass]); + } else { + pan_delta[b] = MUL_C(pan_pow_2_30_pos[ps->g_pan_index[ifreq]][qclass], + pan_pow_2_30_pos[-ps->g_prev_pan_index[ifreq]][qclass]); + } + } + } else { + if (ps->g_prev_pan_index[ifreq] >= 0) + { + if (is_hdc) + { + pan_delta[b] = MUL_C(pan_pow_2_32_neg[-ps->g_pan_index[ifreq]][qclass], + pan_pow_2_32_neg[ps->g_prev_pan_index[ifreq]][qclass]); + } else { + pan_delta[b] = MUL_C(pan_pow_2_30_neg[-ps->g_pan_index[ifreq]][qclass], + pan_pow_2_30_neg[ps->g_prev_pan_index[ifreq]][qclass]); + } + } else { + if (is_hdc) + { + pan_delta[b] = MUL_C(pan_pow_2_32_neg[-ps->g_pan_index[ifreq]][qclass], + pan_pow_2_32_pos[-ps->g_prev_pan_index[ifreq]][qclass]); + } else { + pan_delta[b] = MUL_C(pan_pow_2_30_neg[-ps->g_pan_index[ifreq]][qclass], + pan_pow_2_30_pos[-ps->g_prev_pan_index[ifreq]][qclass]); + } + } + } + } + + for (s = 0; s < num_subsamples; s++) + { + /* PAN always uses all 64 channels */ + for (b = 0; b < NUM_OF_QMF_CHANNELS; b++) + { + tmp = pan_base[b]; + + coeff2 = DIV_R(REAL_CONST(2.0), (REAL_CONST(1.0) + tmp)); + coeff1 = MUL_R(coeff2, tmp); + + QMF_RE(temp_l) = QMF_RE(X_left[s][b]); + QMF_IM(temp_l) = QMF_IM(X_left[s][b]); + QMF_RE(temp_r) = QMF_RE(X_right[s][b]); + QMF_IM(temp_r) = QMF_IM(X_right[s][b]); + + QMF_RE(X_left[s][b]) = MUL_R(QMF_RE(temp_l), coeff1); + QMF_IM(X_left[s][b]) = MUL_R(QMF_IM(temp_l), coeff1); + QMF_RE(X_right[s][b]) = MUL_R(QMF_RE(temp_r), coeff2); + QMF_IM(X_right[s][b]) = MUL_R(QMF_IM(temp_r), coeff2); + + /* 2^(a+k*b) = 2^a * 2^b * ... * 2^b */ + /* ^^^^^^^^^^^^^^^ k times */ + pan_base[b] = MUL_C(pan_base[b], pan_delta[b]); + } + } + } +} + +drm_ps_info *drm_ps_init(void) +{ + drm_ps_info *ps = (drm_ps_info*)faad_malloc(sizeof(drm_ps_info)); + + memset(ps, 0, sizeof(drm_ps_info)); + + return ps; +} + +void drm_ps_free(drm_ps_info *ps) +{ + faad_free(ps); +} + +/* main DRM PS decoding function */ +uint8_t drm_ps_decode(drm_ps_info *ps, uint8_t guess, qmf_t X_left[38][64], qmf_t X_right[38][64], uint8_t hdc_sbr) +{ + int num_subsamples = hdc_sbr ? NUM_OF_HDC_SUBSAMPLES : NUM_OF_SUBSAMPLES; + + if (ps == NULL) + { + memcpy(X_right, X_left, sizeof(qmf_t)*num_subsamples*64); + return 0; + } + + if (!ps->drm_ps_data_available && !guess) + { + memcpy(X_right, X_left, sizeof(qmf_t)*num_subsamples*64); + memset(ps->g_prev_sa_index, 0, sizeof(ps->g_prev_sa_index)); + memset(ps->g_prev_pan_index, 0, sizeof(ps->g_prev_pan_index)); + return 0; + } + + /* if SBR CRC doesn't match out, we can assume decode errors to start with, + and we'll guess what the parameters should be */ + if (!guess) + { + ps->sa_decode_error = 0; + ps->pan_decode_error = 0; + drm_ps_delta_decode(ps); + } else + { + ps->sa_decode_error = 1; + ps->pan_decode_error = 1; + /* don't even bother decoding */ + } + + ps->drm_ps_data_available = 0; + + drm_calc_sa_side_signal(ps, X_left, num_subsamples); + drm_add_ambiance(ps, X_left, X_right, num_subsamples); + + if (ps->bs_enable_sa) + { + ps->g_last_had_sa = 1; + + memcpy(ps->g_prev_sa_index, ps->g_sa_index, sizeof(int8_t) * DRM_NUM_SA_BANDS); + + } else { + ps->g_last_had_sa = 0; + } + + if (ps->bs_enable_pan) + { + drm_add_pan(ps, X_left, X_right, num_subsamples); + + ps->g_last_had_pan = 1; + + memcpy(ps->g_prev_pan_index, ps->g_pan_index, sizeof(int8_t) * DRM_NUM_PAN_BANDS); + + } else { + ps->g_last_had_pan = 0; + } + + + return 0; +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/drm_dec.h b/third_party/faad_hdc/upstream/libfaad/drm_dec.h new file mode 100644 index 000000000..56a4b3df2 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/drm_dec.h @@ -0,0 +1,101 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: drm_dec.h,v 1.8 2007/11/01 12:33:30 menno Exp $ +**/ + +#ifndef __DRM_DEC_H__ +#define __DRM_DEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" + +#define DRM_PARAMETRIC_STEREO 0 +#define DRM_NUM_SA_BANDS 8 +#define DRM_NUM_PAN_BANDS 20 +#define NUM_OF_LINKS 3 +#define NUM_OF_QMF_CHANNELS 64 +#define NUM_OF_SUBSAMPLES 30 +#define NUM_OF_HDC_SUBSAMPLES 32 +#define MAX_SA_BAND 46 +#define MAX_PAN_BAND 64 +#define MAX_DELAY 5 + +typedef struct +{ + uint8_t drm_ps_data_available; + uint8_t bs_enable_sa; + uint8_t bs_enable_pan; + + uint8_t bs_sa_dt_flag; + uint8_t bs_pan_dt_flag; + + uint8_t g_last_had_sa; + uint8_t g_last_had_pan; + + int8_t bs_sa_data[DRM_NUM_SA_BANDS]; + int8_t bs_pan_data[DRM_NUM_PAN_BANDS]; + + int8_t g_sa_index[DRM_NUM_SA_BANDS]; + int8_t g_pan_index[DRM_NUM_PAN_BANDS]; + int8_t g_prev_sa_index[DRM_NUM_SA_BANDS]; + int8_t g_prev_pan_index[DRM_NUM_PAN_BANDS]; + + int8_t sa_decode_error; + int8_t pan_decode_error; + + int8_t g_last_good_sa_index[DRM_NUM_SA_BANDS]; + int8_t g_last_good_pan_index[DRM_NUM_PAN_BANDS]; + + uint8_t delay_buf_index_ser[NUM_OF_LINKS]; + + qmf_t SA[NUM_OF_HDC_SUBSAMPLES][MAX_SA_BAND]; + + complex_t d_buff[2][MAX_SA_BAND]; + complex_t d2_buff[NUM_OF_LINKS][MAX_DELAY][MAX_SA_BAND]; + + real_t prev_nrg[MAX_SA_BAND]; + real_t prev_peakdiff[MAX_SA_BAND]; + real_t peakdecay_fast[MAX_SA_BAND]; +} drm_ps_info; + + +uint16_t drm_ps_data(drm_ps_info *ps, bitfile *ld); + +drm_ps_info *drm_ps_init(void); +void drm_ps_free(drm_ps_info *ps); + +uint8_t drm_ps_decode(drm_ps_info *ps, uint8_t guess, qmf_t X_left[38][64], qmf_t X_right[38][64], uint8_t hdc_sdr); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/error.c b/third_party/faad_hdc/upstream/libfaad/error.c new file mode 100644 index 000000000..4f844e66a --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/error.c @@ -0,0 +1,70 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: error.c,v 1.33 2008/09/19 23:31:39 menno Exp $ +**/ + +#include "common.h" +#include "error.h" + +char *err_msg[] = { + "No error", + "Gain control not yet implemented", + "Pulse coding not allowed in short blocks", + "Invalid huffman codebook", + "Scalefactor out of range", + "Unable to find ADTS syncword", + "Channel coupling not yet implemented", + "Channel configuration not allowed in error resilient frame", + "Bit error in error resilient scalefactor decoding", + "Error decoding huffman scalefactor (bitstream error)", + "Error decoding huffman codeword (bitstream error)", + "Non existent huffman codebook number found", + "Invalid number of channels", + "Maximum number of bitstream elements exceeded", + "Input data buffer too small", + "Array index out of range", + "Maximum number of scalefactor bands exceeded", + "Quantised value out of range", + "LTP lag out of range", + "Invalid SBR parameter decoded", + "SBR called without being initialised", + "Unexpected channel configuration change", + "Error in program_config_element", + "First SBR frame is not the same as first AAC frame", + "Unexpected fill element with SBR data", + "Not all elements were provided with SBR data", + "LTP decoding not available", + "Output data buffer too small", + "CRC error in DRM data", + "PNS not allowed in DRM data stream", + "No standard extension payload allowed in DRM", + "PCE shall be the first element in a frame", + "Bitstream value not allowed by specification", + "MAIN prediction not initialised" +}; + diff --git a/third_party/faad_hdc/upstream/libfaad/error.h b/third_party/faad_hdc/upstream/libfaad/error.h new file mode 100644 index 000000000..b559da49d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/error.h @@ -0,0 +1,44 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: error.h,v 1.27 2008/09/19 23:31:40 menno Exp $ +**/ + +#ifndef __ERROR_H__ +#define __ERROR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NUM_ERROR_MESSAGES 34 +extern char *err_msg[]; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/faad2.pc.in b/third_party/faad_hdc/upstream/libfaad/faad2.pc.in new file mode 100644 index 000000000..e16eaa49e --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/faad2.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: FAAD2 +Description: Freeware Advanced Audio (AAC) Decoder +Version: @VERSION@ +Libs: -L${libdir} -lfaad +Libs.private: -lm +Cflags: -I${includedir} diff --git a/third_party/faad_hdc/upstream/libfaad/filtbank.c b/third_party/faad_hdc/upstream/libfaad/filtbank.c new file mode 100644 index 000000000..a5a47f65a --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/filtbank.c @@ -0,0 +1,408 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: filtbank.c,v 1.46 2009/01/26 23:51:15 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include +#ifdef _WIN32_WCE +#define assert(x) +#else +#include +#endif + +#include "filtbank.h" +#include "syntax.h" +#include "kbd_win.h" +#include "sine_win.h" +#include "mdct.h" + + +fb_info *filter_bank_init(uint16_t frame_len) +{ + uint16_t nshort = frame_len/8; +#ifdef LD_DEC + uint16_t frame_len_ld = frame_len/2; +#endif + + fb_info *fb = (fb_info*)faad_malloc(sizeof(fb_info)); + memset(fb, 0, sizeof(fb_info)); + + /* normal */ + fb->mdct256 = faad_mdct_init(2*nshort); + fb->mdct2048 = faad_mdct_init(2*frame_len); +#ifdef LD_DEC + /* LD */ + fb->mdct1024 = faad_mdct_init(2*frame_len_ld); +#endif + +#ifdef ALLOW_SMALL_FRAMELENGTH + if (frame_len == 1024) + { +#endif + fb->long_window[0] = sine_long_1024; + fb->short_window[0] = sine_short_128; + fb->long_window[1] = kbd_long_1024; + fb->short_window[1] = kbd_short_128; +#ifdef LD_DEC + fb->ld_window[0] = sine_mid_512; + fb->ld_window[1] = ld_mid_512; +#endif +#ifdef ALLOW_SMALL_FRAMELENGTH + } else /* (frame_len == 960) */ { + fb->long_window[0] = sine_long_960; + fb->short_window[0] = sine_short_120; + fb->long_window[1] = kbd_long_960; + fb->short_window[1] = kbd_short_120; +#ifdef LD_DEC + fb->ld_window[0] = sine_mid_480; + fb->ld_window[1] = ld_mid_480; +#endif + } +#endif + + return fb; +} + +void filter_bank_end(fb_info *fb) +{ + if (fb != NULL) + { +#ifdef PROFILE + printf("FB: %I64d cycles\n", fb->cycles); +#endif + + faad_mdct_end(fb->mdct256); + faad_mdct_end(fb->mdct2048); +#ifdef LD_DEC + faad_mdct_end(fb->mdct1024); +#endif + + faad_free(fb); + } +} + +static INLINE void imdct_long(fb_info *fb, real_t *in_data, real_t *out_data, uint16_t len) +{ +#ifdef LD_DEC + mdct_info *mdct = NULL; + + switch (len) + { + case 2048: + case 1920: + mdct = fb->mdct2048; + break; + case 1024: + case 960: + mdct = fb->mdct1024; + break; + } + + faad_imdct(mdct, in_data, out_data); +#else + (void)len; + faad_imdct(fb->mdct2048, in_data, out_data); +#endif +} + + +#ifdef LTP_DEC +static INLINE void mdct(fb_info *fb, real_t *in_data, real_t *out_data, uint16_t len) +{ + mdct_info *mdct = NULL; + + switch (len) + { + case 2048: + case 1920: + mdct = fb->mdct2048; + break; + case 256: + case 240: + mdct = fb->mdct256; + break; +#ifdef LD_DEC + case 1024: + case 960: + mdct = fb->mdct1024; + break; +#endif + } + + faad_mdct(mdct, in_data, out_data); +} +#endif + +void ifilter_bank(fb_info *fb, uint8_t window_sequence, uint8_t window_shape, + uint8_t window_shape_prev, real_t *freq_in, + real_t *time_out, real_t *overlap, + uint8_t object_type, uint16_t frame_len) +{ + int16_t i; + ALIGN real_t transf_buf[2*1024] = {0}; + + const real_t *window_long = NULL; + const real_t *window_long_prev = NULL; + const real_t *window_short = NULL; + const real_t *window_short_prev = NULL; + + uint16_t nlong = frame_len; + uint16_t nshort = frame_len/8; + uint16_t trans = nshort/2; + + uint16_t nflat_ls = (nlong-nshort)/2; + +#ifdef PROFILE + int64_t count = faad_get_ts(); +#endif + + /* select windows of current frame and previous frame (Sine or KBD) */ +#ifdef LD_DEC + if (object_type == LD) + { + window_long = fb->ld_window[window_shape]; + window_long_prev = fb->ld_window[window_shape_prev]; + } else { +#else + (void)object_type; +#endif + window_long = fb->long_window[window_shape]; + window_long_prev = fb->long_window[window_shape_prev]; + window_short = fb->short_window[window_shape]; + window_short_prev = fb->short_window[window_shape_prev]; +#ifdef LD_DEC + } +#endif + +#if 0 + for (i = 0; i < 1024; i++) + { + printf("%d\n", freq_in[i]); + } +#endif + +#if 0 + printf("%d %d\n", window_sequence, window_shape); +#endif + + switch (window_sequence) + { + case ONLY_LONG_SEQUENCE: + /* perform iMDCT */ + imdct_long(fb, freq_in, transf_buf, 2*nlong); + + /* add second half output of previous frame to windowed output of current frame */ + for (i = 0; i < nlong; i+=4) + { + time_out[i] = overlap[i] + MUL_F(transf_buf[i],window_long_prev[i]); + time_out[i+1] = overlap[i+1] + MUL_F(transf_buf[i+1],window_long_prev[i+1]); + time_out[i+2] = overlap[i+2] + MUL_F(transf_buf[i+2],window_long_prev[i+2]); + time_out[i+3] = overlap[i+3] + MUL_F(transf_buf[i+3],window_long_prev[i+3]); + } + + /* window the second half and save as overlap for next frame */ + for (i = 0; i < nlong; i+=4) + { + overlap[i] = MUL_F(transf_buf[nlong+i],window_long[nlong-1-i]); + overlap[i+1] = MUL_F(transf_buf[nlong+i+1],window_long[nlong-2-i]); + overlap[i+2] = MUL_F(transf_buf[nlong+i+2],window_long[nlong-3-i]); + overlap[i+3] = MUL_F(transf_buf[nlong+i+3],window_long[nlong-4-i]); + } + break; + + case LONG_START_SEQUENCE: + /* perform iMDCT */ + imdct_long(fb, freq_in, transf_buf, 2*nlong); + + /* add second half output of previous frame to windowed output of current frame */ + for (i = 0; i < nlong; i+=4) + { + time_out[i] = overlap[i] + MUL_F(transf_buf[i],window_long_prev[i]); + time_out[i+1] = overlap[i+1] + MUL_F(transf_buf[i+1],window_long_prev[i+1]); + time_out[i+2] = overlap[i+2] + MUL_F(transf_buf[i+2],window_long_prev[i+2]); + time_out[i+3] = overlap[i+3] + MUL_F(transf_buf[i+3],window_long_prev[i+3]); + } + + /* window the second half and save as overlap for next frame */ + /* construct second half window using padding with 1's and 0's */ + for (i = 0; i < nflat_ls; i++) + overlap[i] = transf_buf[nlong+i]; + for (i = 0; i < nshort; i++) + overlap[nflat_ls+i] = MUL_F(transf_buf[nlong+nflat_ls+i],window_short[nshort-i-1]); + for (i = 0; i < nflat_ls; i++) + overlap[nflat_ls+nshort+i] = 0; + break; + + case EIGHT_SHORT_SEQUENCE: + /* perform iMDCT for each short block */ + faad_imdct(fb->mdct256, freq_in+0*nshort, transf_buf+2*nshort*0); + faad_imdct(fb->mdct256, freq_in+1*nshort, transf_buf+2*nshort*1); + faad_imdct(fb->mdct256, freq_in+2*nshort, transf_buf+2*nshort*2); + faad_imdct(fb->mdct256, freq_in+3*nshort, transf_buf+2*nshort*3); + faad_imdct(fb->mdct256, freq_in+4*nshort, transf_buf+2*nshort*4); + faad_imdct(fb->mdct256, freq_in+5*nshort, transf_buf+2*nshort*5); + faad_imdct(fb->mdct256, freq_in+6*nshort, transf_buf+2*nshort*6); + faad_imdct(fb->mdct256, freq_in+7*nshort, transf_buf+2*nshort*7); + + /* add second half output of previous frame to windowed output of current frame */ + for (i = 0; i < nflat_ls; i++) + time_out[i] = overlap[i]; + for(i = 0; i < nshort; i++) + { + time_out[nflat_ls+ i] = overlap[nflat_ls+ i] + MUL_F(transf_buf[nshort*0+i],window_short_prev[i]); + time_out[nflat_ls+1*nshort+i] = overlap[nflat_ls+nshort*1+i] + MUL_F(transf_buf[nshort*1+i],window_short[nshort-1-i]) + MUL_F(transf_buf[nshort*2+i],window_short[i]); + time_out[nflat_ls+2*nshort+i] = overlap[nflat_ls+nshort*2+i] + MUL_F(transf_buf[nshort*3+i],window_short[nshort-1-i]) + MUL_F(transf_buf[nshort*4+i],window_short[i]); + time_out[nflat_ls+3*nshort+i] = overlap[nflat_ls+nshort*3+i] + MUL_F(transf_buf[nshort*5+i],window_short[nshort-1-i]) + MUL_F(transf_buf[nshort*6+i],window_short[i]); + if (i < trans) + time_out[nflat_ls+4*nshort+i] = overlap[nflat_ls+nshort*4+i] + MUL_F(transf_buf[nshort*7+i],window_short[nshort-1-i]) + MUL_F(transf_buf[nshort*8+i],window_short[i]); + } + + /* window the second half and save as overlap for next frame */ + for(i = 0; i < nshort; i++) + { + if (i >= trans) + overlap[nflat_ls+4*nshort+i-nlong] = MUL_F(transf_buf[nshort*7+i],window_short[nshort-1-i]) + MUL_F(transf_buf[nshort*8+i],window_short[i]); + overlap[nflat_ls+5*nshort+i-nlong] = MUL_F(transf_buf[nshort*9+i],window_short[nshort-1-i]) + MUL_F(transf_buf[nshort*10+i],window_short[i]); + overlap[nflat_ls+6*nshort+i-nlong] = MUL_F(transf_buf[nshort*11+i],window_short[nshort-1-i]) + MUL_F(transf_buf[nshort*12+i],window_short[i]); + overlap[nflat_ls+7*nshort+i-nlong] = MUL_F(transf_buf[nshort*13+i],window_short[nshort-1-i]) + MUL_F(transf_buf[nshort*14+i],window_short[i]); + overlap[nflat_ls+8*nshort+i-nlong] = MUL_F(transf_buf[nshort*15+i],window_short[nshort-1-i]); + } + for (i = 0; i < nflat_ls; i++) + overlap[nflat_ls+nshort+i] = 0; + break; + + case LONG_STOP_SEQUENCE: + /* perform iMDCT */ + imdct_long(fb, freq_in, transf_buf, 2*nlong); + + /* add second half output of previous frame to windowed output of current frame */ + /* construct first half window using padding with 1's and 0's */ + for (i = 0; i < nflat_ls; i++) + time_out[i] = overlap[i]; + for (i = 0; i < nshort; i++) + time_out[nflat_ls+i] = overlap[nflat_ls+i] + MUL_F(transf_buf[nflat_ls+i],window_short_prev[i]); + for (i = 0; i < nflat_ls; i++) + time_out[nflat_ls+nshort+i] = overlap[nflat_ls+nshort+i] + transf_buf[nflat_ls+nshort+i]; + + /* window the second half and save as overlap for next frame */ + for (i = 0; i < nlong; i++) + overlap[i] = MUL_F(transf_buf[nlong+i],window_long[nlong-1-i]); + break; + } + +#if 0 + for (i = 0; i < 1024; i++) + { + printf("%d\n", time_out[i]); + //printf("0x%.8X\n", time_out[i]); + } +#endif + + +#ifdef PROFILE + count = faad_get_ts() - count; + fb->cycles += count; +#endif +} + + +#ifdef LTP_DEC +/* only works for LTP -> no overlapping, no short blocks */ +void filter_bank_ltp(fb_info *fb, uint8_t window_sequence, uint8_t window_shape, + uint8_t window_shape_prev, real_t *in_data, real_t *out_mdct, + uint8_t object_type, uint16_t frame_len) +{ + int16_t i; + ALIGN real_t windowed_buf[2*1024] = {0}; + + const real_t *window_long = NULL; + const real_t *window_long_prev = NULL; + const real_t *window_short = NULL; + const real_t *window_short_prev = NULL; + + uint16_t nlong = frame_len; + uint16_t nshort = frame_len/8; + uint16_t nflat_ls = (nlong-nshort)/2; + + assert(window_sequence != EIGHT_SHORT_SEQUENCE); + +#ifdef LD_DEC + if (object_type == LD) + { + window_long = fb->ld_window[window_shape]; + window_long_prev = fb->ld_window[window_shape_prev]; + } else { +#endif + window_long = fb->long_window[window_shape]; + window_long_prev = fb->long_window[window_shape_prev]; + window_short = fb->short_window[window_shape]; + window_short_prev = fb->short_window[window_shape_prev]; +#ifdef LD_DEC + } +#endif + + switch(window_sequence) + { + case ONLY_LONG_SEQUENCE: + for (i = nlong-1; i >= 0; i--) + { + windowed_buf[i] = MUL_F(in_data[i], window_long_prev[i]); + windowed_buf[i+nlong] = MUL_F(in_data[i+nlong], window_long[nlong-1-i]); + } + mdct(fb, windowed_buf, out_mdct, 2*nlong); + break; + + case LONG_START_SEQUENCE: + for (i = 0; i < nlong; i++) + windowed_buf[i] = MUL_F(in_data[i], window_long_prev[i]); + for (i = 0; i < nflat_ls; i++) + windowed_buf[i+nlong] = in_data[i+nlong]; + for (i = 0; i < nshort; i++) + windowed_buf[i+nlong+nflat_ls] = MUL_F(in_data[i+nlong+nflat_ls], window_short[nshort-1-i]); + for (i = 0; i < nflat_ls; i++) + windowed_buf[i+nlong+nflat_ls+nshort] = 0; + mdct(fb, windowed_buf, out_mdct, 2*nlong); + break; + + case LONG_STOP_SEQUENCE: + for (i = 0; i < nflat_ls; i++) + windowed_buf[i] = 0; + for (i = 0; i < nshort; i++) + windowed_buf[i+nflat_ls] = MUL_F(in_data[i+nflat_ls], window_short_prev[i]); + for (i = 0; i < nflat_ls; i++) + windowed_buf[i+nflat_ls+nshort] = in_data[i+nflat_ls+nshort]; + for (i = 0; i < nlong; i++) + windowed_buf[i+nlong] = MUL_F(in_data[i+nlong], window_long[nlong-1-i]); + mdct(fb, windowed_buf, out_mdct, 2*nlong); + break; + } +} +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/filtbank.h b/third_party/faad_hdc/upstream/libfaad/filtbank.h new file mode 100644 index 000000000..8fd331d1b --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/filtbank.h @@ -0,0 +1,61 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: filtbank.h,v 1.27 2007/11/01 12:33:30 menno Exp $ +**/ + +#ifndef __FILTBANK_H__ +#define __FILTBANK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +fb_info *filter_bank_init(uint16_t frame_len); +void filter_bank_end(fb_info *fb); + +#ifdef LTP_DEC +void filter_bank_ltp(fb_info *fb, + uint8_t window_sequence, + uint8_t window_shape, + uint8_t window_shape_prev, + real_t *in_data, + real_t *out_mdct, + uint8_t object_type, + uint16_t frame_len); +#endif + +void ifilter_bank(fb_info *fb, uint8_t window_sequence, uint8_t window_shape, + uint8_t window_shape_prev, real_t *freq_in, + real_t *time_out, real_t *overlap, + uint8_t object_type, uint16_t frame_len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/fixed.h b/third_party/faad_hdc/upstream/libfaad/fixed.h new file mode 100644 index 000000000..117cfeac6 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/fixed.h @@ -0,0 +1,293 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: fixed.h,v 1.32 2007/11/01 12:33:30 menno Exp $ +**/ + +#ifndef __FIXED_H__ +#define __FIXED_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_WIN32_WCE) && defined(_ARM_) +#include +#endif + + +#define COEF_BITS 28 +#define COEF_PRECISION (1 << COEF_BITS) +#define REAL_BITS 14 // MAXIMUM OF 14 FOR FIXED POINT SBR +#define REAL_PRECISION (1 << REAL_BITS) + +/* FRAC is the fractional only part of the fixed point number [0.0..1.0) */ +#define FRAC_SIZE 32 /* frac is a 32 bit integer */ +#define FRAC_BITS 31 +/* Multiplication by power of 2 will be compiled to left-shift */ +#define FRAC_MUL (1u << (FRAC_SIZE - FRAC_BITS)) +#define FRAC_PRECISION ((uint32_t)(1u << FRAC_BITS)) +#define FRAC_MAX 0x7FFFFFFF + +typedef int32_t real_t; + + +#define REAL_CONST(A) (((A) >= 0) ? ((real_t)((A)*(REAL_PRECISION)+0.5)) : ((real_t)((A)*(REAL_PRECISION)-0.5))) +#define COEF_CONST(A) (((A) >= 0) ? ((real_t)((A)*(COEF_PRECISION)+0.5)) : ((real_t)((A)*(COEF_PRECISION)-0.5))) +#define FRAC_CONST(A) (((A) == 1.00) ? ((real_t)FRAC_MAX) : (((A) >= 0) ? ((real_t)((A)*(FRAC_PRECISION)+0.5)) : ((real_t)((A)*(FRAC_PRECISION)-0.5)))) +//#define FRAC_CONST(A) (((A) >= 0) ? ((real_t)((A)*(FRAC_PRECISION)+0.5)) : ((real_t)((A)*(FRAC_PRECISION)-0.5))) + +#define Q2_BITS 22 +#define Q2_PRECISION (1 << Q2_BITS) +#define Q2_CONST(A) (((A) >= 0) ? ((real_t)((A)*(Q2_PRECISION)+0.5)) : ((real_t)((A)*(Q2_PRECISION)-0.5))) + +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__GNUC__) && !defined(_WIN64) + +/* multiply with real shift */ +static INLINE real_t MUL_R(real_t A, real_t B) +{ + _asm { + mov eax,A + imul B + shrd eax,edx,REAL_BITS + } +} + +/* multiply with coef shift */ +static INLINE real_t MUL_C(real_t A, real_t B) +{ + _asm { + mov eax,A + imul B + shrd eax,edx,COEF_BITS + } +} + +static INLINE real_t MUL_Q2(real_t A, real_t B) +{ + _asm { + mov eax,A + imul B + shrd eax,edx,Q2_BITS + } +} + +static INLINE real_t MUL_SHIFT6(real_t A, real_t B) +{ + _asm { + mov eax,A + imul B + shrd eax,edx,6 + } +} + +static INLINE real_t MUL_SHIFT23(real_t A, real_t B) +{ + _asm { + mov eax,A + imul B + shrd eax,edx,23 + } +} + +#if 1 +static INLINE real_t _MulHigh(real_t A, real_t B) +{ + _asm { + mov eax,A + imul B + mov eax,edx + } +} + +/* multiply with fractional shift */ +static INLINE real_t MUL_F(real_t A, real_t B) +{ + return _MulHigh(A,B) << (FRAC_SIZE-FRAC_BITS); +} + +/* Complex multiplication */ +static INLINE void ComplexMult(real_t *y1, real_t *y2, + real_t x1, real_t x2, real_t c1, real_t c2) +{ + *y1 = (_MulHigh(x1, c1) + _MulHigh(x2, c2))<<(FRAC_SIZE-FRAC_BITS); + *y2 = (_MulHigh(x2, c1) - _MulHigh(x1, c2))<<(FRAC_SIZE-FRAC_BITS); +} +#else +static INLINE real_t MUL_F(real_t A, real_t B) +{ + _asm { + mov eax,A + imul B + shrd eax,edx,FRAC_BITS + } +} + +/* Complex multiplication */ +static INLINE void ComplexMult(real_t *y1, real_t *y2, + real_t x1, real_t x2, real_t c1, real_t c2) +{ + *y1 = MUL_F(x1, c1) + MUL_F(x2, c2); + *y2 = MUL_F(x2, c1) - MUL_F(x1, c2); +} +#endif + +#elif defined(__GNUC__) && defined (__arm__) + +/* taken from MAD */ +#define arm_mul(x, y, SCALEBITS) \ +({ \ + uint32_t __hi; \ + uint32_t __lo; \ + uint32_t __result; \ + asm("smull %0, %1, %3, %4\n\t" \ + "movs %0, %0, lsr %5\n\t" \ + "adc %2, %0, %1, lsl %6" \ + : "=&r" (__lo), "=&r" (__hi), "=r" (__result) \ + : "%r" (x), "r" (y), \ + "M" (SCALEBITS), "M" (32 - (SCALEBITS)) \ + : "cc"); \ + __result; \ +}) + +static INLINE real_t MUL_R(real_t A, real_t B) +{ + return arm_mul(A, B, REAL_BITS); +} + +static INLINE real_t MUL_C(real_t A, real_t B) +{ + return arm_mul(A, B, COEF_BITS); +} + +static INLINE real_t MUL_Q2(real_t A, real_t B) +{ + return arm_mul(A, B, Q2_BITS); +} + +static INLINE real_t MUL_SHIFT6(real_t A, real_t B) +{ + return arm_mul(A, B, 6); +} + +static INLINE real_t MUL_SHIFT23(real_t A, real_t B) +{ + return arm_mul(A, B, 23); +} + +static INLINE real_t _MulHigh(real_t x, real_t y) +{ + uint32_t __lo; + uint32_t __hi; + asm("smull\t%0, %1, %2, %3" + : "=&r"(__lo),"=&r"(__hi) + : "%r"(x),"r"(y) + : "cc"); + return __hi; +} + +static INLINE real_t MUL_F(real_t A, real_t B) +{ + return _MulHigh(A, B) << (FRAC_SIZE-FRAC_BITS); +} + +/* Complex multiplication */ +static INLINE void ComplexMult(real_t *y1, real_t *y2, + real_t x1, real_t x2, real_t c1, real_t c2) +{ + int32_t tmp, yt1, yt2; + asm("smull %0, %1, %4, %6\n\t" + "smlal %0, %1, %5, %7\n\t" + "rsb %3, %4, #0\n\t" + "smull %0, %2, %5, %6\n\t" + "smlal %0, %2, %3, %7" + : "=&r" (tmp), "=&r" (yt1), "=&r" (yt2), "=r" (x1) + : "3" (x1), "r" (x2), "r" (c1), "r" (c2) + : "cc" ); + *y1 = yt1 << (FRAC_SIZE-FRAC_BITS); + *y2 = yt2 << (FRAC_SIZE-FRAC_BITS); +} + +#else + + /* multiply with real shift */ + #define MUL_R(A,B) (real_t)(((int64_t)(A)*(int64_t)(B)+(1 << (REAL_BITS-1))) >> REAL_BITS) + /* multiply with coef shift */ + #define MUL_C(A,B) (real_t)(((int64_t)(A)*(int64_t)(B)+(1 << (COEF_BITS-1))) >> COEF_BITS) + /* multiply with fractional shift */ +#if defined(_WIN32_WCE) && defined(_ARM_) + /* eVC for PocketPC has an intrinsic function that returns only the high 32 bits of a 32x32 bit multiply */ + static INLINE real_t MUL_F(real_t A, real_t B) + { + return _MulHigh(A,B) << (32-FRAC_BITS); + } +#else +#ifdef __BFIN__ +#define _MulHigh(X,Y) ({ int __xxo; \ + asm ( \ + "a1 = %2.H * %1.L (IS,M);\n\t" \ + "a0 = %1.H * %2.H, a1+= %1.H * %2.L (IS,M);\n\t"\ + "a1 = a1 >>> 16;\n\t" \ + "%0 = (a0 += a1);\n\t" \ + : "=d" (__xxo) : "d" (X), "d" (Y) : "A0","A1"); __xxo; }) + +#define MUL_F(X,Y) ({ int __xxo; \ + asm ( \ + "a1 = %2.H * %1.L (M);\n\t" \ + "a0 = %1.H * %2.H, a1+= %1.H * %2.L (M);\n\t" \ + "a1 = a1 >>> 16;\n\t" \ + "%0 = (a0 += a1);\n\t" \ + : "=d" (__xxo) : "d" (X), "d" (Y) : "A0","A1"); __xxo; }) +#else + #define _MulHigh(A,B) (real_t)(((int64_t)(A)*(int64_t)(B)+(1u << (FRAC_SIZE-1))) >> FRAC_SIZE) + #define MUL_F(A,B) (real_t)(((int64_t)(A)*(int64_t)(B)+(1u << (FRAC_BITS-1))) >> FRAC_BITS) +#endif +#endif + #define MUL_Q2(A,B) (real_t)(((int64_t)(A)*(int64_t)(B)+(1 << (Q2_BITS-1))) >> Q2_BITS) + #define MUL_SHIFT6(A,B) (real_t)(((int64_t)(A)*(int64_t)(B)+(1 << (6-1))) >> 6) + #define MUL_SHIFT23(A,B) (real_t)(((int64_t)(A)*(int64_t)(B)+(1 << (23-1))) >> 23) + +/* Complex multiplication */ +static INLINE void ComplexMult(real_t *y1, real_t *y2, + real_t x1, real_t x2, real_t c1, real_t c2) +{ + *y1 = (_MulHigh(x1, c1) + _MulHigh(x2, c2)) * FRAC_MUL; + *y2 = (_MulHigh(x2, c1) - _MulHigh(x1, c2)) * FRAC_MUL; +} + +#endif + +/* Saturated left shift */ +#define SAT_SHIFT_MASK(E) (~0u << (31u - (E))) +#define SAT_SHIFT(V,E,M) (((((V) >> ((E) + 1)) ^ (V)) & (M)) \ + ? (((V) < 0) ? (int32_t)0x80000000 : 0x7FFFFFFF) \ + : ((int32_t)((uint32_t)(V) << (E)))) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/hcr.c b/third_party/faad_hdc/upstream/libfaad/hcr.c new file mode 100644 index 000000000..71fa48016 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/hcr.c @@ -0,0 +1,433 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: hcr.c,v 1.26 2009/01/26 23:51:15 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include + +#include "specrec.h" +#include "huffman.h" + +/* ISO/IEC 14496-3/Amd.1 + * 8.5.3.3: Huffman Codeword Reordering for AAC spectral data (HCR) + * + * HCR devides the spectral data in known fixed size segments, and + * sorts it by the importance of the data. The importance is firstly + * the (lower) position in the spectrum, and secondly the largest + * value in the used codebook. + * The most important data is written at the start of each segment + * (at known positions), the remaining data is interleaved inbetween, + * with the writing direction alternating. + * Data length is not increased. +*/ + +#ifdef ERROR_RESILIENCE + +/* 8.5.3.3.1 Pre-sorting */ + +#define NUM_CB 6 +#define NUM_CB_ER 22 +#define MAX_CB 32 +#define VCB11_FIRST 16 +#define VCB11_LAST 31 + +static const uint8_t PreSortCB_STD[NUM_CB] = + { 11, 9, 7, 5, 3, 1}; + +static const uint8_t PreSortCB_ER[NUM_CB_ER] = + { 11, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 9, 7, 5, 3, 1}; + +/* 8.5.3.3.2 Derivation of segment width */ + +static const uint8_t maxCwLen[MAX_CB] = {0, 11, 9, 20, 16, 13, 11, 14, 12, 17, 14, 49, + 0, 0, 0, 0, 14, 17, 21, 21, 25, 25, 29, 29, 29, 29, 33, 33, 33, 37, 37, 41}; + +#define segmentWidth(cb) min(maxCwLen[cb], ics->length_of_longest_codeword) + +/* bit-twiddling helpers */ +static const uint8_t S[] = {1, 2, 4, 8, 16}; +static const uint32_t B[] = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF}; + +typedef struct +{ + uint8_t cb; + uint8_t decoded; + uint16_t sp_offset; + bits_t bits; +} codeword_t; + +static uint32_t reverse_word(uint32_t v) +{ + v = ((v >> S[0]) & B[0]) | ((v << S[0]) & ~B[0]); + v = ((v >> S[1]) & B[1]) | ((v << S[1]) & ~B[1]); + v = ((v >> S[2]) & B[2]) | ((v << S[2]) & ~B[2]); + v = ((v >> S[3]) & B[3]) | ((v << S[3]) & ~B[3]); + v = ((v >> S[4]) & B[4]) | ((v << S[4]) & ~B[4]); + return v; +} + +/* bits_t version */ +static void rewrev_bits(bits_t *bits) +{ + if (bits->len == 0) return; + if (bits->len <= 32) { + bits->bufb = 0; + bits->bufa = reverse_word(bits->bufa) >> (32 - bits->len); + } else { + /* last 32<>32 bit swap via rename */ + uint32_t lo = reverse_word(bits->bufb); + uint32_t hi = reverse_word(bits->bufa); + + if (bits->len == 64) { + bits->bufb = hi; + bits->bufa = lo; + } else { + /* shift off low bits (this is really only one 64 bit shift) */ + bits->bufb = hi >> (64 - bits->len); + bits->bufa = (lo >> (64 - bits->len)) | (hi << (bits->len - 32)); + } + } +} + + +/* merge bits of a to b */ +/* precondition: a->len + b->len <= 64 */ +static void concat_bits(bits_t *b, bits_t *a) +{ + uint32_t bl, bh, al, ah; + + /* empty addend */ + if (a->len == 0) return; + + /* addend becomes result */ + if (b->len == 0) + { + *b = *a; + return; + } + + al = a->bufa; + ah = a->bufb; + + if (b->len > 32) + { + /* (b->len - 32) is 1..31 */ + /* maskoff superfluous high b bits */ + bl = b->bufa; + bh = b->bufb & ((1u << (b->len-32)) - 1); + /* left shift a b->len bits */ + ah = al << (b->len - 32); + al = 0; + } else if (b->len == 32) { + bl = b->bufa; + bh = 0; + ah = al; + al = 0; + } else { + /* b->len is 1..31, (32 - b->len) is 1..31 */ + bl = b->bufa & ((1u << (b->len)) - 1); + bh = 0; + ah = (ah << (b->len)) | (al >> (32 - b->len)); + al = al << b->len; + } + + /* merge */ + b->bufa = bl | al; + b->bufb = bh | ah; + + b->len += a->len; +} + +static uint8_t is_good_cb(uint8_t this_CB, uint8_t this_sec_CB) +{ + /* only want spectral data CB's */ + if ((this_sec_CB > ZERO_HCB && this_sec_CB <= ESC_HCB) || (this_sec_CB >= VCB11_FIRST && this_sec_CB <= VCB11_LAST)) + { + if (this_CB < ESC_HCB) + { + /* normal codebook pairs */ + return ((this_sec_CB == this_CB) || (this_sec_CB == this_CB + 1)); + } else + { + /* escape codebook */ + return (this_sec_CB == this_CB); + } + } + return 0; +} + +static void read_segment(bits_t *segment, uint8_t segwidth, bitfile *ld) +{ + segment->len = segwidth; + + if (segwidth > 32) + { + segment->bufb = faad_getbits(ld, segwidth - 32); + segment->bufa = faad_getbits(ld, 32); + + } else { + segment->bufb = 0; + segment->bufa = faad_getbits(ld, segwidth); + } +} + +static void fill_in_codeword(codeword_t *codeword, uint16_t index, uint16_t sp, uint8_t cb) +{ + codeword[index].sp_offset = sp; + codeword[index].cb = cb; + codeword[index].decoded = 0; + codeword[index].bits.len = 0; +} + +uint8_t reordered_spectral_data(NeAACDecStruct *hDecoder, ic_stream *ics, + bitfile *ld, int16_t *spectral_data) +{ + uint16_t PCWs_done; + uint16_t numberOfSegments, numberOfSets, numberOfCodewords; + + codeword_t codeword[512]; + bits_t segment[512]; + + uint16_t sp_offset[8]; + uint16_t g, i, sortloop, set, bitsread; + /*uint16_t bitsleft, codewordsleft*/; + uint8_t w_idx, sfb, this_CB, last_CB, this_sec_CB; + + const uint16_t nshort = hDecoder->frameLength/8; + const uint16_t sp_data_len = ics->length_of_reordered_spectral_data; + + const uint8_t *PreSortCb; + + /* no data (e.g. silence) */ + if (sp_data_len == 0) + return 0; + + /* since there is spectral data, at least one codeword has nonzero length */ + if (ics->length_of_longest_codeword == 0) + return 10; + + if (sp_data_len < ics->length_of_longest_codeword) + return 10; + + sp_offset[0] = 0; + for (g = 1; g < ics->num_window_groups; g++) + { + sp_offset[g] = sp_offset[g-1] + nshort*ics->window_group_length[g-1]; + } + + PCWs_done = 0; + numberOfSegments = 0; + numberOfCodewords = 0; + bitsread = 0; + + /* VCB11 code books in use */ + if (hDecoder->aacSectionDataResilienceFlag) + { + PreSortCb = PreSortCB_ER; + last_CB = NUM_CB_ER; + } else + { + PreSortCb = PreSortCB_STD; + last_CB = NUM_CB; + } + + /* step 1: decode PCW's (set 0), and stuff data in easier-to-use format */ + for (sortloop = 0; sortloop < last_CB; sortloop++) + { + /* select codebook to process this pass */ + this_CB = PreSortCb[sortloop]; + + /* loop over sfbs */ + for (sfb = 0; sfb < ics->max_sfb; sfb++) + { + /* loop over all in this sfb, 4 lines per loop */ + for (w_idx = 0; 4*w_idx < (min(ics->swb_offset[sfb+1], ics->swb_offset_max) - ics->swb_offset[sfb]); w_idx++) + { + for(g = 0; g < ics->num_window_groups; g++) + { + for (i = 0; i < ics->num_sec[g]; i++) + { + /* check whether sfb used here is the one we want to process */ + if ((ics->sect_start[g][i] <= sfb) && (ics->sect_end[g][i] > sfb)) + { + /* check whether codebook used here is the one we want to process */ + this_sec_CB = ics->sect_cb[g][i]; + + if (is_good_cb(this_CB, this_sec_CB)) + { + /* precalculate some stuff */ + uint16_t sect_sfb_size = ics->sect_sfb_offset[g][sfb+1] - ics->sect_sfb_offset[g][sfb]; + uint8_t inc = (this_sec_CB < FIRST_PAIR_HCB) ? QUAD_LEN : PAIR_LEN; + uint16_t group_cws_count = (4*ics->window_group_length[g])/inc; + uint8_t segwidth = segmentWidth(this_sec_CB); + uint16_t cws; + + /* read codewords until end of sfb or end of window group (shouldn't only 1 trigger?) */ + for (cws = 0; (cws < group_cws_count) && ((cws + w_idx*group_cws_count) < sect_sfb_size); cws++) + { + uint16_t sp = sp_offset[g] + ics->sect_sfb_offset[g][sfb] + inc * (cws + w_idx*group_cws_count); + + /* read and decode PCW */ + if (!PCWs_done) + { + /* read in normal segments */ + if (bitsread + segwidth <= sp_data_len) + { + read_segment(&segment[numberOfSegments], segwidth, ld); + bitsread += segwidth; + + huffman_spectral_data_2(this_sec_CB, &segment[numberOfSegments], &spectral_data[sp]); + + /* keep leftover bits */ + rewrev_bits(&segment[numberOfSegments]); + + numberOfSegments++; + } else { // sp_data_len - bitsread < segwidth + /* remaining stuff after last segment, we unfortunately couldn't read + this in earlier because it might not fit in 64 bits. since we already + decoded (and removed) the PCW it is now should fit */ + if (bitsread < sp_data_len) + { + const uint8_t additional_bits = (uint8_t)(sp_data_len - bitsread); + + read_segment(&segment[numberOfSegments], additional_bits, ld); + segment[numberOfSegments].len += segment[numberOfSegments-1].len; + if (segment[numberOfSegments].len > 64) + return 10; + rewrev_bits(&segment[numberOfSegments]); + + if (segment[numberOfSegments-1].len > 32) + { + segment[numberOfSegments-1].bufb = segment[numberOfSegments].bufb + + showbits_hcr(&segment[numberOfSegments-1], segment[numberOfSegments-1].len - 32); + segment[numberOfSegments-1].bufa = segment[numberOfSegments].bufa + + showbits_hcr(&segment[numberOfSegments-1], 32); + } else { + segment[numberOfSegments-1].bufa = segment[numberOfSegments].bufa + + showbits_hcr(&segment[numberOfSegments-1], segment[numberOfSegments-1].len); + segment[numberOfSegments-1].bufb = segment[numberOfSegments].bufb; + } + segment[numberOfSegments-1].len += additional_bits; + } + bitsread = sp_data_len; + PCWs_done = 1; + + fill_in_codeword(codeword, 0, sp, this_sec_CB); + } + } else { + fill_in_codeword(codeword, numberOfCodewords - numberOfSegments, sp, this_sec_CB); + } + numberOfCodewords++; + } + } + } + } + } + } + } + } + + if (numberOfSegments == 0) + return 10; + + numberOfSets = numberOfCodewords / numberOfSegments; + + /* step 2: decode nonPCWs */ + for (set = 1; set <= numberOfSets; set++) + { + uint16_t trial; + + for (trial = 0; trial < numberOfSegments; trial++) + { + uint16_t codewordBase; + + for (codewordBase = 0; codewordBase < numberOfSegments; codewordBase++) + { + const uint16_t segment_idx = (trial + codewordBase) % numberOfSegments; + const uint16_t codeword_idx = codewordBase + set*numberOfSegments - numberOfSegments; + + /* data up */ + if (codeword_idx >= numberOfCodewords - numberOfSegments) break; + + if (!codeword[codeword_idx].decoded && segment[segment_idx].len > 0) + { + uint8_t tmplen = segment[segment_idx].len + codeword[codeword_idx].bits.len; + + if (tmplen > 64) + { + // Drop bits that do not fit concatenation result. + flushbits_hcr(&codeword[codeword_idx].bits, tmplen - 64); + } + + if (codeword[codeword_idx].bits.len != 0) + concat_bits(&segment[segment_idx], &codeword[codeword_idx].bits); + + tmplen = segment[segment_idx].len; + + if (huffman_spectral_data_2(codeword[codeword_idx].cb, &segment[segment_idx], + &spectral_data[codeword[codeword_idx].sp_offset]) >= 0) + { + codeword[codeword_idx].decoded = 1; + } else + { + codeword[codeword_idx].bits = segment[segment_idx]; + codeword[codeword_idx].bits.len = tmplen; + } + + } + } + } + for (i = 0; i < numberOfSegments; i++) + rewrev_bits(&segment[i]); + } + +#if 0 // Seems to give false errors + bitsleft = 0; + + for (i = 0; i < numberOfSegments && !bitsleft; i++) + bitsleft += segment[i].len; + + if (bitsleft) return 10; + + codewordsleft = 0; + + for (i = 0; (i < numberOfCodewords - numberOfSegments) && (!codewordsleft); i++) + if (!codeword[i].decoded) + codewordsleft++; + + if (codewordsleft) return 10; +#endif + + + return 0; + +} +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/huffman.c b/third_party/faad_hdc/upstream/libfaad/huffman.c new file mode 100644 index 000000000..22e057ba9 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/huffman.c @@ -0,0 +1,594 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: huffman.c,v 1.26 2007/11/01 12:33:30 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include +#ifdef ANALYSIS +#include +#endif + +#include "bits.h" +#include "huffman.h" +#include "codebook/hcb.h" + + +/* static function declarations */ +static INLINE void huffman_sign_bits(bitfile *ld, int16_t *sp, uint8_t len); +static INLINE uint8_t huffman_getescape(bitfile *ld, int16_t *sp); +static uint8_t huffman_2step_quad(uint8_t cb, bitfile *ld, int16_t *sp); +static uint8_t huffman_2step_quad_sign(uint8_t cb, bitfile *ld, int16_t *sp); +static uint8_t huffman_2step_pair(uint8_t cb, bitfile *ld, int16_t *sp); +static uint8_t huffman_2step_pair_sign(uint8_t cb, bitfile *ld, int16_t *sp); +static uint8_t huffman_binary_quad(uint8_t cb, bitfile *ld, int16_t *sp); +static uint8_t huffman_binary_quad_sign(uint8_t cb, bitfile *ld, int16_t *sp); +static uint8_t huffman_binary_pair(uint8_t cb, bitfile *ld, int16_t *sp); +static uint8_t huffman_binary_pair_sign(uint8_t cb, bitfile *ld, int16_t *sp); +#if 0 +static int16_t huffman_codebook(uint8_t i); +#endif +static void vcb11_check_LAV(uint8_t cb, int16_t *sp); + +int8_t huffman_scale_factor(bitfile *ld) +{ + uint16_t offset = 0; + + while (hcb_sf[offset][1]) + { + uint8_t b = faad_get1bit(ld + DEBUGVAR(1,255,"huffman_scale_factor()")); + offset += hcb_sf[offset][b]; + } + + return hcb_sf[offset][0]; +} + + +static const uint8_t hcbN[LAST_CB_IDX + 1] = +{ 0, 5, 5, 0, 5, 0, 5, 0, 5, 0, 6, 5}; +static const hcb* hcb_table[LAST_CB_IDX + 1] = +{NULL, hcb1_1, hcb2_1, NULL, hcb4_1, NULL, hcb6_1, NULL, hcb8_1, NULL, hcb10_1, hcb11_1}; +static const hcb_2_quad* hcb_2_quad_table[LAST_CB_IDX + 1] = +{NULL, hcb1_2, hcb2_2, NULL, hcb4_2, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +static const hcb_2_pair* hcb_2_pair_table[LAST_CB_IDX + 1] = +{NULL, NULL, NULL, NULL, NULL, NULL, hcb6_2, NULL, hcb8_2, NULL, hcb10_2, hcb11_2}; +static const hcb_bin_pair* hcb_bin_table[LAST_CB_IDX + 1] = +{NULL, NULL, NULL, NULL, NULL, hcb5, NULL, hcb7, NULL, hcb9, NULL, NULL}; +/* hcb3 is the unique case */ + +/* defines whether a huffman codebook is unsigned or not */ +/* Table 4.6.2 */ +static uint8_t unsigned_cb[32] = { 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, + /* codebook 16 to 31 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +static INLINE void huffman_sign_bits(bitfile *ld, int16_t *sp, uint8_t len) +{ + uint8_t i; + + for (i = 0; i < len; i++) + { + if(sp[i]) + { + if(faad_get1bit(ld + DEBUGVAR(1,5,"huffman_sign_bits(): sign bit")) & 1) + { + sp[i] = -sp[i]; + } + } + } +} + +static INLINE uint8_t huffman_getescape(bitfile *ld, int16_t *sp) +{ + uint8_t neg, i; + int16_t j; + int16_t off; + int16_t x = *sp; + + if (x < 0) + { + if (x != -16) + return 0; + neg = 1; + } else { + if (x != 16) + return 0; + neg = 0; + } + + for (i = 4; i < 16; i++) + { + if (faad_get1bit(ld + DEBUGVAR(1,6,"huffman_getescape(): escape size")) == 0) + { + break; + } + } + if (i >= 16) + return 10; + + off = (int16_t)faad_getbits(ld, i + DEBUGVAR(1,9,"huffman_getescape(): escape")); + + j = off | (1<> 16) & 0xFFFF; + else return (int16_t)data & 0xFFFF; +} +#endif + +static void vcb11_check_LAV(uint8_t cb, int16_t *sp) +{ + static const uint16_t vcb11_LAV_tab[] = { + 16, 31, 47, 63, 95, 127, 159, 191, 223, + 255, 319, 383, 511, 767, 1023, 2047 + }; + uint16_t max = 0; + + if (cb < 16 || cb > 31) + return; + + max = vcb11_LAV_tab[cb - 16]; + + if ((abs(sp[0]) > max) || (abs(sp[1]) > max)) + { + sp[0] = 0; + sp[1] = 0; + } +} + +uint8_t huffman_spectral_data(uint8_t cb, bitfile *ld, int16_t *sp) +{ + switch (cb) + { + case 1: /* 2-step method for data quadruples */ + case 2: + return huffman_2step_quad(cb, ld, sp); + case 3: /* binary search for data quadruples */ + return huffman_binary_quad_sign(cb, ld, sp); + case 4: /* 2-step method for data quadruples */ + return huffman_2step_quad_sign(cb, ld, sp); + case 5: /* binary search for data pairs */ + return huffman_binary_pair(cb, ld, sp); + case 6: /* 2-step method for data pairs */ + return huffman_2step_pair(cb, ld, sp); + case 7: /* binary search for data pairs */ + case 9: + return huffman_binary_pair_sign(cb, ld, sp); + case 8: /* 2-step method for data pairs */ + case 10: + return huffman_2step_pair_sign(cb, ld, sp); + /* Codebook 12 is disallowed, see `section_data` */ +#if 0 + case 12: { + uint8_t err = huffman_2step_pair(11, ld, sp); + sp[0] = huffman_codebook(0); sp[1] = huffman_codebook(1); + return err; } +#endif + case 11: + { + uint8_t err = huffman_2step_pair_sign(11, ld, sp); + if (!err) + err = huffman_getescape(ld, &sp[0]); + if (!err) + err = huffman_getescape(ld, &sp[1]); + return err; + } +#ifdef ERROR_RESILIENCE + /* VCB11 uses codebook 11 */ + case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: + case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: + { + uint8_t err = huffman_2step_pair_sign(11, ld, sp); + if (!err) + err = huffman_getescape(ld, &sp[0]); + if (!err) + err = huffman_getescape(ld, &sp[1]); + + /* check LAV (Largest Absolute Value) */ + /* this finds errors in the ESCAPE signal */ + vcb11_check_LAV(cb, sp); + + return err; + } +#endif + default: + /* Non existent codebook number, something went wrong */ + return 11; + } + + /* return 0; */ +} + + +#ifdef ERROR_RESILIENCE + +/* Special version of huffman_spectral_data +Will not read from a bitfile but a bits_t structure. +Will keep track of the bits decoded and return the number of bits remaining. +Do not read more than ld->len, return -1 if codeword would be longer */ + +int8_t huffman_spectral_data_2(uint8_t cb, bits_t *ld, int16_t *sp) +{ + uint32_t cw; + uint16_t offset = 0; + uint8_t extra_bits; + uint8_t vcb11 = 0; + + + switch (cb) + { + case 1: /* 2-step method for data quadruples */ + case 2: + case 4: { + const hcb* root; + uint8_t root_bits; + const hcb_2_quad* table; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (hcbN[cb] == 0) __builtin_trap(); + if (hcb_table[cb] == NULL) __builtin_trap(); + if (hcb_2_quad_table[cb] == NULL) __builtin_trap(); + // In other words, `cb` is one of [1, 2, 4]. +#endif + root = hcb_table[cb]; + root_bits = hcbN[cb]; + table = hcb_2_quad_table[cb]; + + cw = showbits_hcr(ld, root_bits); + offset = root[cw].offset; + extra_bits = root[cw].extra_bits; + + if (extra_bits) + { + /* We know for sure it's more than root_bits bits long. */ + if (flushbits_hcr(ld, root_bits)) return -1; + offset += (uint16_t)showbits_hcr(ld, extra_bits); + if (flushbits_hcr(ld, table[offset].bits - root_bits)) return -1; + } else { + if (flushbits_hcr(ld, table[offset].bits)) return -1; + } + + sp[0] = table[offset].x; + sp[1] = table[offset].y; + sp[2] = table[offset].v; + sp[3] = table[offset].w; + break; + } + case 6: /* 2-step method for data pairs */ + case 8: + case 10: + case 11: + /* VCB11 uses codebook 11 */ + case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: + case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: { + const hcb* root; + uint8_t root_bits; + const hcb_2_pair* table; + + if (cb >= 16) + { + /* store the virtual codebook */ + vcb11 = cb; + cb = 11; + } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (hcbN[cb] == 0) __builtin_trap(); + if (hcb_table[cb] == NULL) __builtin_trap(); + if (hcb_2_pair_table[cb] == NULL) __builtin_trap(); + // In other words, `cb` is one of [6, 8, 10, 11]. +#endif + root = hcb_table[cb]; + root_bits = hcbN[cb]; + table = hcb_2_pair_table[cb]; + + cw = showbits_hcr(ld, root_bits); + offset = root[cw].offset; + extra_bits = root[cw].extra_bits; + + if (extra_bits) + { + /* we know for sure it's more than hcbN[cb] bits long */ + if (flushbits_hcr(ld, root_bits)) return -1; + offset += (uint16_t)showbits_hcr(ld, extra_bits); + if (flushbits_hcr(ld, table[offset].bits - root_bits)) return -1; + } else { + if ( flushbits_hcr(ld, table[offset].bits)) return -1; + } + sp[0] = table[offset].x; + sp[1] = table[offset].y; + break; + } + case 3: { /* binary search for data quadruples */ + const hcb_bin_quad* table = hcb3; + while (!table[offset].is_leaf) + { + uint8_t b; + if (get1bit_hcr(ld, &b)) return -1; + offset += table[offset].data[b]; + } + + sp[0] = table[offset].data[0]; + sp[1] = table[offset].data[1]; + sp[2] = table[offset].data[2]; + sp[3] = table[offset].data[3]; + + break; + } + + case 5: /* binary search for data pairs */ + case 7: + case 9: { + const hcb_bin_pair* table = hcb_bin_table[cb]; + while (!table[offset].is_leaf) + { + uint8_t b; + + if (get1bit_hcr(ld, &b) ) return -1; + offset += table[offset].data[b]; + } + + sp[0] = table[offset].data[0]; + sp[1] = table[offset].data[1]; + + break; + }} + + /* decode sign bits */ + if (unsigned_cb[cb]) + { + uint8_t i; + for(i = 0; i < ((cb < FIRST_PAIR_HCB) ? QUAD_LEN : PAIR_LEN); i++) + { + if(sp[i]) + { + uint8_t b; + if ( get1bit_hcr(ld, &b) ) return -1; + if (b != 0) { + sp[i] = -sp[i]; + } + } + } + } + + /* decode huffman escape bits */ + if ((cb == ESC_HCB) || (cb >= 16)) + { + uint8_t k; + for (k = 0; k < 2; k++) + { + if ((sp[k] == 16) || (sp[k] == -16)) + { + uint8_t neg, i; + int32_t j; + uint32_t off; + + neg = (sp[k] < 0) ? 1 : 0; + + for (i = 4; ; i++) + { + uint8_t b; + if (get1bit_hcr(ld, &b)) + return -1; + if (b == 0) + break; + } + + if (i > 32) + return -1; + + if (getbits_hcr(ld, i, &off)) + return -1; + j = off + (1<len; +} + +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/huffman.h b/third_party/faad_hdc/upstream/libfaad/huffman.h new file mode 100644 index 000000000..662f359ba --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/huffman.h @@ -0,0 +1,47 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: huffman.h,v 1.28 2007/11/01 12:33:30 menno Exp $ +**/ + +#ifndef __HUFFMAN_H__ +#define __HUFFMAN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +int8_t huffman_scale_factor(bitfile *ld); +uint8_t huffman_spectral_data(uint8_t cb, bitfile *ld, int16_t *sp); +#ifdef ERROR_RESILIENCE +int8_t huffman_spectral_data_2(uint8_t cb, bits_t *ld, int16_t *sp); +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ic_predict.c b/third_party/faad_hdc/upstream/libfaad/ic_predict.c new file mode 100644 index 000000000..c2b62bb5e --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ic_predict.c @@ -0,0 +1,281 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ic_predict.c,v 1.28 2007/11/01 12:33:31 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#ifdef MAIN_DEC + +#include "syntax.h" +#include "ic_predict.h" +#include "pns.h" + + +static uint32_t float_to_bits(float32_t f32) { + uint32_t u32; + memcpy(&u32, &f32, 4); + return u32; +} + +static float32_t bits_to_float(uint32_t u32) { + float32_t f32; + memcpy(&f32, &u32, 4); + return f32; +} + +static float32_t flt_round(float32_t pf) +{ + int32_t flg; + uint32_t tmp, tmp1, tmp2; + + tmp = float_to_bits(pf); + flg = tmp & (uint32_t)0x00008000; + tmp &= (uint32_t)0xffff0000; + tmp1 = tmp; + /* round 1/2 lsb toward infinity */ + if (flg) + { + tmp &= (uint32_t)0xff800000; /* extract exponent and sign */ + tmp |= (uint32_t)0x00010000; /* insert 1 lsb */ + tmp2 = tmp; /* add 1 lsb and elided one */ + tmp &= (uint32_t)0xff800000; /* extract exponent and sign */ + + return bits_to_float(tmp1) + bits_to_float(tmp2) - bits_to_float(tmp); + } else { + return bits_to_float(tmp); + } +} + +static int16_t quant_pred(float32_t x) +{ + return (int16_t)(float_to_bits(x) >> 16); +} + +static float32_t inv_quant_pred(int16_t q) +{ + uint16_t u16 = (uint16_t)q; + return bits_to_float((uint32_t)u16 << 16); +} + +static void ic_predict(pred_state *state, real_t input, real_t *output, uint8_t pred) +{ +#ifdef FIXED_POINT + // main codepath is simply not ready for FIXED_POINT, better not to run it at all. + if (pred) + *output = input; +#else + uint16_t tmp; + int16_t i, j; + real_t dr1; + float32_t predictedvalue; + real_t e0, e1; + real_t k1, k2; + + real_t r[2]; + real_t COR[2]; + real_t VAR[2]; + + r[0] = inv_quant_pred(state->r[0]); + r[1] = inv_quant_pred(state->r[1]); + COR[0] = inv_quant_pred(state->COR[0]); + COR[1] = inv_quant_pred(state->COR[1]); + VAR[0] = inv_quant_pred(state->VAR[0]); + VAR[1] = inv_quant_pred(state->VAR[1]); + + +#if 1 + tmp = state->VAR[0]; + j = (tmp >> 7); + i = tmp & 0x7f; + if (j >= 128) + { + j -= 128; + k1 = COR[0] * exp_table[j] * mnt_table[i]; + } else { + k1 = REAL_CONST(0); + } +#else + + { +#define B 0.953125 + real_t c = COR[0]; + real_t v = VAR[0]; + float32_t tmp; + if (c == 0 || v <= 1) + { + k1 = 0; + } else { + tmp = B / v; + flt_round(&tmp); + k1 = c * tmp; + } + } +#endif + + if (pred) + { +#if 1 + tmp = state->VAR[1]; + j = (tmp >> 7); + i = tmp & 0x7f; + if (j >= 128) + { + j -= 128; + k2 = COR[1] * exp_table[j] * mnt_table[i]; + } else { + k2 = REAL_CONST(0); + } +#else + +#define B 0.953125 + real_t c = COR[1]; + real_t v = VAR[1]; + float32_t tmp; + if (c == 0 || v <= 1) + { + k2 = 0; + } else { + tmp = B / v; + flt_round(&tmp); + k2 = c * tmp; + } +#endif + + predictedvalue = k1*r[0] + k2*r[1]; + predictedvalue = flt_round(predictedvalue); + *output = input + predictedvalue; + } + + /* calculate new state data */ + e0 = *output; + e1 = e0 - k1*r[0]; + dr1 = k1*e0; + + VAR[0] = ALPHA*VAR[0] + 0.5f * (r[0]*r[0] + e0*e0); + COR[0] = ALPHA*COR[0] + r[0]*e0; + VAR[1] = ALPHA*VAR[1] + 0.5f * (r[1]*r[1] + e1*e1); + COR[1] = ALPHA*COR[1] + r[1]*e1; + + r[1] = A * (r[0]-dr1); + r[0] = A * e0; + + state->r[0] = quant_pred(r[0]); + state->r[1] = quant_pred(r[1]); + state->COR[0] = quant_pred(COR[0]); + state->COR[1] = quant_pred(COR[1]); + state->VAR[0] = quant_pred(VAR[0]); + state->VAR[1] = quant_pred(VAR[1]); +#endif +} + +static void reset_pred_state(pred_state *state) +{ + state->r[0] = 0; + state->r[1] = 0; + state->COR[0] = 0; + state->COR[1] = 0; + state->VAR[0] = 0x3F80; + state->VAR[1] = 0x3F80; +} + +void pns_reset_pred_state(ic_stream *ics, pred_state *state) +{ + uint8_t sfb, g, b; + uint16_t i, offs, offs2; + + /* prediction only for long blocks */ + if (ics->window_sequence == EIGHT_SHORT_SEQUENCE) + return; + + for (g = 0; g < ics->num_window_groups; g++) + { + for (b = 0; b < ics->window_group_length[g]; b++) + { + for (sfb = 0; sfb < ics->max_sfb; sfb++) + { + if (is_noise(ics, g, sfb)) + { + offs = ics->swb_offset[sfb]; + offs2 = min(ics->swb_offset[sfb+1], ics->swb_offset_max); + + for (i = offs; i < offs2; i++) + reset_pred_state(&state[i]); + } + } + } + } +} + +void reset_all_predictors(pred_state *state, uint16_t frame_len) +{ + uint16_t i; + + for (i = 0; i < frame_len; i++) + reset_pred_state(&state[i]); +} + +/* intra channel prediction */ +void ic_prediction(ic_stream *ics, real_t *spec, pred_state *state, + uint16_t frame_len, uint8_t sf_index) +{ + uint8_t sfb; + uint16_t bin; + + if (ics->window_sequence == EIGHT_SHORT_SEQUENCE) + { + reset_all_predictors(state, frame_len); + } else { + for (sfb = 0; sfb < max_pred_sfb(sf_index); sfb++) + { + uint16_t low = ics->swb_offset[sfb]; + uint16_t high = min(ics->swb_offset[sfb+1], ics->swb_offset_max); + + for (bin = low; bin < high; bin++) + { + ic_predict(&state[bin], spec[bin], &spec[bin], + (ics->predictor_data_present && ics->pred.prediction_used[sfb])); + } + } + + if (ics->predictor_data_present) + { + if (ics->pred.predictor_reset) + { + for (bin = ics->pred.predictor_reset_group_number - 1; + bin < frame_len; bin += 30) + { + reset_pred_state(&state[bin]); + } + } + } + } +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ic_predict.h b/third_party/faad_hdc/upstream/libfaad/ic_predict.h new file mode 100644 index 000000000..60cf6a849 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ic_predict.h @@ -0,0 +1,252 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ic_predict.h,v 1.23 2007/11/01 12:33:31 menno Exp $ +**/ + +#ifdef MAIN_DEC + +#ifndef __IC_PREDICT_H__ +#define __IC_PREDICT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ALPHA REAL_CONST(0.90625) +#define A REAL_CONST(0.953125) + + +void pns_reset_pred_state(ic_stream *ics, pred_state *state); +void reset_all_predictors(pred_state *state, uint16_t frame_len); +void ic_prediction(ic_stream *ics, real_t *spec, pred_state *state, + uint16_t frame_len, uint8_t sf_index); + +ALIGN static const real_t mnt_table[128] = { + COEF_CONST(0.9531250000), COEF_CONST(0.9453125000), + COEF_CONST(0.9375000000), COEF_CONST(0.9296875000), + COEF_CONST(0.9257812500), COEF_CONST(0.9179687500), + COEF_CONST(0.9101562500), COEF_CONST(0.9023437500), + COEF_CONST(0.8984375000), COEF_CONST(0.8906250000), + COEF_CONST(0.8828125000), COEF_CONST(0.8789062500), + COEF_CONST(0.8710937500), COEF_CONST(0.8671875000), + COEF_CONST(0.8593750000), COEF_CONST(0.8515625000), + COEF_CONST(0.8476562500), COEF_CONST(0.8398437500), + COEF_CONST(0.8359375000), COEF_CONST(0.8281250000), + COEF_CONST(0.8242187500), COEF_CONST(0.8203125000), + COEF_CONST(0.8125000000), COEF_CONST(0.8085937500), + COEF_CONST(0.8007812500), COEF_CONST(0.7968750000), + COEF_CONST(0.7929687500), COEF_CONST(0.7851562500), + COEF_CONST(0.7812500000), COEF_CONST(0.7773437500), + COEF_CONST(0.7734375000), COEF_CONST(0.7656250000), + COEF_CONST(0.7617187500), COEF_CONST(0.7578125000), + COEF_CONST(0.7539062500), COEF_CONST(0.7500000000), + COEF_CONST(0.7421875000), COEF_CONST(0.7382812500), + COEF_CONST(0.7343750000), COEF_CONST(0.7304687500), + COEF_CONST(0.7265625000), COEF_CONST(0.7226562500), + COEF_CONST(0.7187500000), COEF_CONST(0.7148437500), + COEF_CONST(0.7109375000), COEF_CONST(0.7070312500), + COEF_CONST(0.6992187500), COEF_CONST(0.6953125000), + COEF_CONST(0.6914062500), COEF_CONST(0.6875000000), + COEF_CONST(0.6835937500), COEF_CONST(0.6796875000), + COEF_CONST(0.6796875000), COEF_CONST(0.6757812500), + COEF_CONST(0.6718750000), COEF_CONST(0.6679687500), + COEF_CONST(0.6640625000), COEF_CONST(0.6601562500), + COEF_CONST(0.6562500000), COEF_CONST(0.6523437500), + COEF_CONST(0.6484375000), COEF_CONST(0.6445312500), + COEF_CONST(0.6406250000), COEF_CONST(0.6406250000), + COEF_CONST(0.6367187500), COEF_CONST(0.6328125000), + COEF_CONST(0.6289062500), COEF_CONST(0.6250000000), + COEF_CONST(0.6210937500), COEF_CONST(0.6210937500), + COEF_CONST(0.6171875000), COEF_CONST(0.6132812500), + COEF_CONST(0.6093750000), COEF_CONST(0.6054687500), + COEF_CONST(0.6054687500), COEF_CONST(0.6015625000), + COEF_CONST(0.5976562500), COEF_CONST(0.5937500000), + COEF_CONST(0.5937500000), COEF_CONST(0.5898437500), + COEF_CONST(0.5859375000), COEF_CONST(0.5820312500), + COEF_CONST(0.5820312500), COEF_CONST(0.5781250000), + COEF_CONST(0.5742187500), COEF_CONST(0.5742187500), + COEF_CONST(0.5703125000), COEF_CONST(0.5664062500), + COEF_CONST(0.5664062500), COEF_CONST(0.5625000000), + COEF_CONST(0.5585937500), COEF_CONST(0.5585937500), + COEF_CONST(0.5546875000), COEF_CONST(0.5507812500), + COEF_CONST(0.5507812500), COEF_CONST(0.5468750000), + COEF_CONST(0.5429687500), COEF_CONST(0.5429687500), + COEF_CONST(0.5390625000), COEF_CONST(0.5390625000), + COEF_CONST(0.5351562500), COEF_CONST(0.5312500000), + COEF_CONST(0.5312500000), COEF_CONST(0.5273437500), + COEF_CONST(0.5273437500), COEF_CONST(0.5234375000), + COEF_CONST(0.5195312500), COEF_CONST(0.5195312500), + COEF_CONST(0.5156250000), COEF_CONST(0.5156250000), + COEF_CONST(0.5117187500), COEF_CONST(0.5117187500), + COEF_CONST(0.5078125000), COEF_CONST(0.5078125000), + COEF_CONST(0.5039062500), COEF_CONST(0.5039062500), + COEF_CONST(0.5000000000), COEF_CONST(0.4980468750), + COEF_CONST(0.4960937500), COEF_CONST(0.4941406250), + COEF_CONST(0.4921875000), COEF_CONST(0.4902343750), + COEF_CONST(0.4882812500), COEF_CONST(0.4863281250), + COEF_CONST(0.4843750000), COEF_CONST(0.4824218750), + COEF_CONST(0.4804687500), COEF_CONST(0.4785156250) +}; + +ALIGN static const real_t exp_table[128] = { + COEF_CONST(0.50000000000000000000000000000000000000000000000000), + COEF_CONST(0.25000000000000000000000000000000000000000000000000), + COEF_CONST(0.12500000000000000000000000000000000000000000000000), + COEF_CONST(0.06250000000000000000000000000000000000000000000000), + COEF_CONST(0.03125000000000000000000000000000000000000000000000), + COEF_CONST(0.01562500000000000000000000000000000000000000000000), + COEF_CONST(0.00781250000000000000000000000000000000000000000000), + COEF_CONST(0.00390625000000000000000000000000000000000000000000), + COEF_CONST(0.00195312500000000000000000000000000000000000000000), + COEF_CONST(0.00097656250000000000000000000000000000000000000000), + COEF_CONST(0.00048828125000000000000000000000000000000000000000), + COEF_CONST(0.00024414062500000000000000000000000000000000000000), + COEF_CONST(0.00012207031250000000000000000000000000000000000000), + COEF_CONST(0.00006103515625000000000000000000000000000000000000), + COEF_CONST(0.00003051757812500000000000000000000000000000000000), + COEF_CONST(0.00001525878906250000000000000000000000000000000000), + COEF_CONST(0.00000762939453125000000000000000000000000000000000), + COEF_CONST(0.00000381469726562500000000000000000000000000000000), + COEF_CONST(0.00000190734863281250000000000000000000000000000000), + COEF_CONST(0.00000095367431640625000000000000000000000000000000), + COEF_CONST(0.00000047683715820312500000000000000000000000000000), + COEF_CONST(0.00000023841857910156250000000000000000000000000000), + COEF_CONST(0.00000011920928955078125000000000000000000000000000), + COEF_CONST(0.00000005960464477539062500000000000000000000000000), + COEF_CONST(0.00000002980232238769531300000000000000000000000000), + COEF_CONST(0.00000001490116119384765600000000000000000000000000), + COEF_CONST(0.00000000745058059692382810000000000000000000000000), + COEF_CONST(0.00000000372529029846191410000000000000000000000000), + COEF_CONST(0.00000000186264514923095700000000000000000000000000), + COEF_CONST(0.00000000093132257461547852000000000000000000000000), + COEF_CONST(0.00000000046566128730773926000000000000000000000000), + COEF_CONST(0.00000000023283064365386963000000000000000000000000), + COEF_CONST(0.00000000011641532182693481000000000000000000000000), + COEF_CONST(0.00000000005820766091346740700000000000000000000000), + COEF_CONST(0.00000000002910383045673370400000000000000000000000), + COEF_CONST(0.00000000001455191522836685200000000000000000000000), + COEF_CONST(0.00000000000727595761418342590000000000000000000000), + COEF_CONST(0.00000000000363797880709171300000000000000000000000), + COEF_CONST(0.00000000000181898940354585650000000000000000000000), + COEF_CONST(0.00000000000090949470177292824000000000000000000000), + COEF_CONST(0.00000000000045474735088646412000000000000000000000), + COEF_CONST(0.00000000000022737367544323206000000000000000000000), + COEF_CONST(0.00000000000011368683772161603000000000000000000000), + COEF_CONST(0.00000000000005684341886080801500000000000000000000), + COEF_CONST(0.00000000000002842170943040400700000000000000000000), + COEF_CONST(0.00000000000001421085471520200400000000000000000000), + COEF_CONST(0.00000000000000710542735760100190000000000000000000), + COEF_CONST(0.00000000000000355271367880050090000000000000000000), + COEF_CONST(0.00000000000000177635683940025050000000000000000000), + COEF_CONST(0.00000000000000088817841970012523000000000000000000), + COEF_CONST(0.00000000000000044408920985006262000000000000000000), + COEF_CONST(0.00000000000000022204460492503131000000000000000000), + COEF_CONST(0.00000000000000011102230246251565000000000000000000), + COEF_CONST(0.00000000000000005551115123125782700000000000000000), + COEF_CONST(0.00000000000000002775557561562891400000000000000000), + COEF_CONST(0.00000000000000001387778780781445700000000000000000), + COEF_CONST(0.00000000000000000693889390390722840000000000000000), + COEF_CONST(0.00000000000000000346944695195361420000000000000000), + COEF_CONST(0.00000000000000000173472347597680710000000000000000), + COEF_CONST(0.00000000000000000086736173798840355000000000000000), + COEF_CONST(0.00000000000000000043368086899420177000000000000000), + COEF_CONST(0.00000000000000000021684043449710089000000000000000), + COEF_CONST(0.00000000000000000010842021724855044000000000000000), + COEF_CONST(0.00000000000000000005421010862427522200000000000000), + COEF_CONST(0.00000000000000000002710505431213761100000000000000), + COEF_CONST(0.00000000000000000001355252715606880500000000000000), + COEF_CONST(0.00000000000000000000677626357803440270000000000000), + COEF_CONST(0.00000000000000000000338813178901720140000000000000), + COEF_CONST(0.00000000000000000000169406589450860070000000000000), + COEF_CONST(0.00000000000000000000084703294725430034000000000000), + COEF_CONST(0.00000000000000000000042351647362715017000000000000), + COEF_CONST(0.00000000000000000000021175823681357508000000000000), + COEF_CONST(0.00000000000000000000010587911840678754000000000000), + COEF_CONST(0.00000000000000000000005293955920339377100000000000), + COEF_CONST(0.00000000000000000000002646977960169688600000000000), + COEF_CONST(0.00000000000000000000001323488980084844300000000000), + COEF_CONST(0.00000000000000000000000661744490042422140000000000), + COEF_CONST(0.00000000000000000000000330872245021211070000000000), + COEF_CONST(0.00000000000000000000000165436122510605530000000000), + COEF_CONST(0.00000000000000000000000082718061255302767000000000), + COEF_CONST(0.00000000000000000000000041359030627651384000000000), + COEF_CONST(0.00000000000000000000000020679515313825692000000000), + COEF_CONST(0.00000000000000000000000010339757656912846000000000), + COEF_CONST(0.00000000000000000000000005169878828456423000000000), + COEF_CONST(0.00000000000000000000000002584939414228211500000000), + COEF_CONST(0.00000000000000000000000001292469707114105700000000), + COEF_CONST(0.00000000000000000000000000646234853557052870000000), + COEF_CONST(0.00000000000000000000000000323117426778526440000000), + COEF_CONST(0.00000000000000000000000000161558713389263220000000), + COEF_CONST(0.00000000000000000000000000080779356694631609000000), + COEF_CONST(0.00000000000000000000000000040389678347315804000000), + COEF_CONST(0.00000000000000000000000000020194839173657902000000), + COEF_CONST(0.00000000000000000000000000010097419586828951000000), + COEF_CONST(0.00000000000000000000000000005048709793414475600000), + COEF_CONST(0.00000000000000000000000000002524354896707237800000), + COEF_CONST(0.00000000000000000000000000001262177448353618900000), + COEF_CONST(0.00000000000000000000000000000631088724176809440000), + COEF_CONST(0.00000000000000000000000000000315544362088404720000), + COEF_CONST(0.00000000000000000000000000000157772181044202360000), + COEF_CONST(0.00000000000000000000000000000078886090522101181000), + COEF_CONST(0.00000000000000000000000000000039443045261050590000), + COEF_CONST(0.00000000000000000000000000000019721522630525295000), + COEF_CONST(0.00000000000000000000000000000009860761315262647600), + COEF_CONST(0.00000000000000000000000000000004930380657631323800), + COEF_CONST(0.00000000000000000000000000000002465190328815661900), + COEF_CONST(0.00000000000000000000000000000001232595164407830900), + COEF_CONST(0.00000000000000000000000000000000616297582203915470), + COEF_CONST(0.00000000000000000000000000000000308148791101957740), + COEF_CONST(0.00000000000000000000000000000000154074395550978870), + COEF_CONST(0.00000000000000000000000000000000077037197775489434), + COEF_CONST(0.00000000000000000000000000000000038518598887744717), + COEF_CONST(0.00000000000000000000000000000000019259299443872359), + COEF_CONST(0.00000000000000000000000000000000009629649721936179), + COEF_CONST(0.00000000000000000000000000000000004814824860968090), + COEF_CONST(0.00000000000000000000000000000000002407412430484045), + COEF_CONST(0.00000000000000000000000000000000001203706215242022), + COEF_CONST(0.00000000000000000000000000000000000601853107621011), + COEF_CONST(0.00000000000000000000000000000000000300926553810506), + COEF_CONST(0.00000000000000000000000000000000000150463276905253), + COEF_CONST(0.00000000000000000000000000000000000075231638452626), + COEF_CONST(0.00000000000000000000000000000000000037615819226313), + COEF_CONST(0.00000000000000000000000000000000000018807909613157), + COEF_CONST(0.00000000000000000000000000000000000009403954806578), + COEF_CONST(0.00000000000000000000000000000000000004701977403289), + COEF_CONST(0.00000000000000000000000000000000000002350988701645), + COEF_CONST(0.00000000000000000000000000000000000001175494350822), + COEF_CONST(0.0 /* 0000000000000000000000000000000000000587747175411 "floating point underflow" */), + COEF_CONST(0.0) +}; + +#ifdef __cplusplus +} +#endif +#endif + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/iq_table.h b/third_party/faad_hdc/upstream/libfaad/iq_table.h new file mode 100644 index 000000000..feac6a664 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/iq_table.h @@ -0,0 +1,16458 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: iq_table.h,v 1.20 2007/11/01 12:33:31 menno Exp $ +**/ + +#ifndef IQ_TABLE_H__ +#define IQ_TABLE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* !!!DON'T CHANGE IQ_TABLE_SIZE!!! */ + + +#ifndef FIXED_POINT + +#define IQ_TABLE_SIZE 8192 + +#ifdef _MSC_VER +#pragma warning(disable:4305) +#pragma warning(disable:4244) +#endif + +ALIGN static const real_t iq_table[IQ_TABLE_SIZE] = +{ + 0, + 1, + 2.5198420997897464, + 4.3267487109222245, + 6.3496042078727974, + 8.5498797333834844, + 10.902723556992836, + 13.390518279406722, + 15.999999999999998, + 18.720754407467133, + 21.544346900318832, + 24.463780996262464, + 27.47314182127996, + 30.567350940369842, + 33.741991698453212, + 36.993181114957046, + 40.317473596635935, + 43.711787041189993, + 47.173345095760126, + 50.699631325716943, + 54.288352331898118, + 57.937407704003519, + 61.6448652744185, + 65.408940536585988, + 69.227979374755591, + 73.100443455321638, + 77.024897778591622, + 80.999999999999986, + 85.024491212518527, + 89.097187944889555, + 93.216975178615741, + 97.382800224133163, + 101.59366732596474, + 105.84863288986224, + 110.14680124343441, + 114.4873208566006, + 118.86938096020653, + 123.29220851090024, + 127.75506545836058, + 132.25724627755247, + 136.79807573413572, + 141.37690685569191, + 145.99311908523086, + 150.6461165966291, + 155.33532675434674, + 160.06019870205279, + 164.82020206673349, + 169.61482576651861, + 174.44357691188537, + 179.30597979112557, + 184.20157493201927, + 189.12991823257562, + 194.09058015449685, + 199.08314497371677, + 204.1072100829694, + 209.16238534187647, + 214.24829247050752, + 219.36456448277784, + 224.51084515641216, + 229.6867885365223, + 234.89205847013176, + 240.12632816923249, + 245.38927980018505, + 250.68060409747261, + 255.99999999999991, + 261.34717430828869, + 266.72184136106449, + 272.12372272986045, + 277.55254693037961, + 283.0080491494619, + 288.48997098659891, + 293.99806020902247, + 299.53207051947408, + 305.0917613358298, + 310.67689758182206, + 316.28724948815585, + 321.92259240337177, + 327.58270661385535, + 333.26737717243742, + 338.97639373507025, + 344.70955040510125, + 350.46664558470013, + 356.24748183302603, + 362.05186573075139, + 367.87960775058258, + 373.73052213344511, + 379.60442677002078, + 385.50114308734607, + 391.42049594019937, + 397.36231350702371, + 403.32642719014467, + 409.31267152006262, + 415.32088406360799, + 421.35090533576471, + 427.40257871497619, + 433.4757503617617, + 439.5702691404793, + 445.68598654408271, + 451.82275662172759, + 457.98043590909128, + 464.15888336127773, + 470.35796028818726, + 476.5775302922363, + 482.81745920832043, + 489.07761504591741, + 495.35786793323581, + 501.65809006331688, + 507.97815564200368, + 514.31794083769648, + 520.67732373281672, + 527.05618427690604, + 533.45440424129174, + 539.87186717525128, + 546.30845836361505, + 552.76406478574609, + 559.23857507584194, + 565.73187948450413, + 572.24386984152341, + 578.77443951983378, + 585.32348340058843, + 591.89089783931263, + 598.47658063309257, + 605.08043098876044, + 611.70234949203643, + 618.3422380775919, + 624.99999999999977, + 631.67553980553748, + 638.36876330481164, + 645.07957754617485, + 651.80789078990415, + 658.55361248311499, + 665.31665323538357, + 672.09692479505225, + 678.8943400261943, + 685.70881288621433, + 692.540258404062, + 699.38859265903977, + 706.25373276018058, + 713.13559682617972, + 720.03410396586037, + 726.94917425915435, + 733.88072873858209, + 740.82868937121543, + 747.79297904110535, + 754.77352153216191, + 761.77024151147043, + 768.78306451302956, + 775.81191692189896, + 782.85672595874246, + 789.91741966475445, + 796.99392688695798, + 804.08617726386274, + 811.19410121147098, + 818.31762990962227, + 825.45669528866563, + 832.61123001644864, + 839.78116748561604, + 846.96644180120552, + 854.16698776853514, + 861.38274088137143, + 868.61363731036977, + 875.85961389178203, + 883.12060811641959, + 890.39655811886757, + 897.68740266694181, + 904.99308115138172, + 912.31353357577188, + 919.64870054668756, + 926.99852326405619, + 934.36294351172899, + 941.74190364825859, + 949.13534659787422, + 956.54321584165211, + 963.96545540887348, + 971.40200986856541, + 978.85282432122176, + 986.31784439069588, + 993.7970162162635, + 1001.29028644485, + 1008.797602223418, + 1016.3189111915103, + 1023.8541614739464, + 1031.4033016736653, + 1038.9662808647138, + 1046.5430485853758, + 1054.1335548314366, + 1061.7377500495838, + 1069.3555851309357, + 1076.9870114046978, + 1084.6319806319441, + 1092.2904449995174, + 1099.9623571140482, + 1107.6476699960892, + 1115.3463370743607, + 1123.058312180106, + 1130.7835495415541, + 1138.5220037784854, + 1146.273629896901, + 1154.0383832837879, + 1161.816219701986, + 1169.607095285146, + 1177.4109665327808, + 1185.2277903054078, + 1193.0575238197798, + 1200.9001246442001, + 1208.7555506939248, + 1216.6237602266442, + 1224.5047118380478, + 1232.3983644574657, + 1240.3046773435874, + 1248.2236100802568, + 1256.1551225723395, + 1264.099175041662, + 1272.0557280230228, + 1280.0247423602691, + 1288.0061792024444, + 1295.9999999999995, + 1304.006166501068, + 1312.0246407478062, + 1320.0553850727929, + 1328.0983620954903, + 1336.1535347187651, + 1344.2208661254647, + 1352.3003197750522, + 1360.3918594002962, + 1368.4954490040145, + 1376.6110528558709, + 1384.7386354892244, + 1392.8781616980295, + 1401.0295965337855, + 1409.1929053025353, + 1417.3680535619119, + 1425.5550071182327, + 1433.7537320236374, + 1441.9641945732744, + 1450.1863613025282, + 1458.4201989842913, + 1466.6656746262797, + 1474.9227554683875, + 1483.1914089800841, + 1491.4716028578516, + 1499.7633050226596, + 1508.0664836174794, + 1516.3811070048375, + 1524.7071437644029, + 1533.0445626906128, + 1541.3933327903342, + 1549.7534232805581, + 1558.1248035861302, + 1566.507443337515, + 1574.9013123685909, + 1583.3063807144795, + 1591.7226186094069, + 1600.1499964845941, + 1608.58848496618, + 1617.0380548731737, + 1625.4986772154357, + 1633.9703231916887, + 1642.4529641875577, + 1650.9465717736346, + 1659.4511177035752, + 1667.9665739122186, + 1676.4929125137353, + 1685.030105799801, + 1693.5781262377957, + 1702.136946469027, + 1710.7065393069795, + 1719.2868777355877, + 1727.8779349075323, + 1736.4796841425596, + 1745.092098925825, + 1753.7151529062583, + 1762.3488198949503, + 1770.9930738635628, + 1779.6478889427597, + 1788.3132394206564, + 1796.9890997412947, + 1805.6754445031333, + 1814.3722484575621, + 1823.0794865074322, + 1831.7971337056094, + 1840.5251652535437, + 1849.2635564998579, + 1858.0122829389563, + 1866.7713202096493, + 1875.5406440937966, + 1884.3202305149687, + 1893.110055537124, + 1901.9100953633042, + 1910.7203263343454, + 1919.5407249276057, + 1928.3712677557098, + 1937.2119315653083, + 1946.0626932358525, + 1954.923529778386, + 1963.79441833435, + 1972.6753361744036, + 1981.5662606972594, + 1990.467169428533, + 1999.3780400196069, + 2008.2988502465078, + 2017.2295780087982, + 2026.1702013284819, + 2035.1206983489212, + 2044.0810473337688, + 2053.0512266659125, + 2062.0312148464309, + 2071.0209904935646, + 2080.0205323416958, + 2089.0298192403443, + 2098.0488301531714, + 2107.0775441569995, + 2116.115940440839, + 2125.1639983049317, + 2134.2216971597995, + 2143.2890165253098, + 2152.3659360297484, + 2161.4524354089031, + 2170.5484945051617, + 2179.6540932666144, + 2188.7692117461711, + 2197.8938301006888, + 2207.0279285901042, + 2216.1714875765838, + 2225.324487523676, + 2234.4869089954782, + 2243.6587326558101, + 2252.8399392673982, + 2262.0305096910702, + 2271.2304248849537, + 2280.4396659036897, + 2289.6582138976523, + 2298.8860501121762, + 2308.1231558867926, + 2317.3695126544767, + 2326.6251019409005, + 2335.8899053636933, + 2345.1639046317132, + 2354.4470815443233, + 2363.7394179906792, + 2373.0408959490205, + 2382.3514974859731, + 2391.6712047558558, + 2400.9999999999991, + 2410.3378655460651, + 2419.6847838073813, + 2429.0407372822747, + 2438.4057085534191, + 2447.7796802871858, + 2457.1626352330004, + 2466.5545562227112, + 2475.9554261699564, + 2485.3652280695474, + 2494.7839449968492, + 2504.2115601071737, + 2513.6480566351788, + 2523.0934178942675, + 2532.5476272760025, + 2542.0106682495189, + 2551.482524360948, + 2560.9631792328441, + 2570.4526165636184, + 2579.9508201269791, + 2589.4577737713744, + 2598.9734614194458, + 2608.4978670674823, + 2618.0309747848837, + 2627.5727687136259, + 2637.1232330677353, + 2646.6823521327647, + 2656.2501102652768, + 2665.8264918923328, + 2675.4114815109842, + 2685.0050636877722, + 2694.6072230582295, + 2704.2179443263894, + 2713.8372122642972, + 2723.4650117115279, + 2733.1013275747096, + 2742.7461448270483, + 2752.3994485078601, + 2762.0612237221085, + 2771.7314556399419, + 2781.4101294962406, + 2791.0972305901655, + 2800.7927442847094, + 2810.4966560062589, + 2820.2089512441521, + 2829.9296155502466, + 2839.6586345384894, + 2849.3959938844923, + 2859.1416793251065, + 2868.8956766580086, + 2878.6579717412847, + 2888.4285504930212, + 2898.2073988908974, + 2907.9945029717837, + 2917.789848831344, + 2927.5934226236377, + 2937.4052105607311, + 2947.2251989123079, + 2957.0533740052865, + 2966.8897222234368, + 2976.734230007005, + 2986.5868838523397, + 2996.4476703115197, + 3006.3165759919889, + 3016.1935875561908, + 3026.0786917212095, + 3035.9718752584108, + 3045.8731249930906, + 3055.7824278041207, + 3065.6997706236039, + 3075.625140436528, + 3085.5585242804245, + 3095.4999092450298, + 3105.4492824719491, + 3115.4066311543256, + 3125.3719425365089, + 3135.3452039137287, + 3145.3264026317715, + 3155.3155260866592, + 3165.3125617243295, + 3175.3174970403229, + 3185.3303195794679, + 3195.35101693557, + 3205.3795767511078, + 3215.4159867169251, + 3225.460234571929, + 3235.5123081027928, + 3245.5721951436558, + 3255.63988357583, + 3265.7153613275095, + 3275.7986163734795, + 3285.8896367348289, + 3295.9884104786665, + 3306.0949257178395, + 3316.2091706106517, + 3326.331133360588, + 3336.4608022160378, + 3346.5981654700231, + 3356.7432114599264, + 3366.8959285672249, + 3377.0563052172211, + 3387.2243298787821, + 3397.3999910640764, + 3407.5832773283128, + 3417.7741772694862, + 3427.9726795281199, + 3438.1787727870123, + 3448.3924457709873, + 3458.6136872466445, + 3468.8424860221107, + 3479.0788309467976, + 3489.3227109111554, + 3499.5741148464344, + 3509.8330317244445, + 3520.0994505573185, + 3530.3733603972751, + 3540.6547503363886, + 3550.9436095063534, + 3561.239927078258, + 3571.5436922623535, + 3581.8548943078308, + 3592.1735225025936, + 3602.4995661730372, + 3612.8330146838275, + 3623.1738574376814, + 3633.5220838751502, + 3643.8776834744031, + 3654.2406457510142, + 3664.6109602577494, + 3674.9886165843564, + 3685.3736043573545, + 3695.7659132398294, + 3706.1655329312248, + 3716.5724531671399, + 3726.9866637191262, + 3737.4081543944876, + 3747.8369150360782, + 3758.2729355221072, + 3768.7162057659411, + 3779.1667157159077, + 3789.6244553551055, + 3800.0894147012082, + 3810.5615838062768, + 3821.0409527565694, + 3831.5275116723533, + 3842.0212507077194, + 3852.522160050396, + 3863.0302299215673, + 3873.5454505756893, + 3884.0678123003108, + 3894.5973054158922, + 3905.1339202756285, + 3915.6776472652732, + 3926.2284768029604, + 3936.7863993390338, + 3947.3514053558706, + 3957.9234853677135, + 3968.5026299204969, + 3979.0888295916798, + 3989.6820749900776, + 4000.2823567556948, + 4010.8896655595613, + 4021.5039921035655, + 4032.1253271202945, + 4042.7536613728694, + 4053.3889856547858, + 4064.0312907897551, + 4074.6805676315448, + 4085.3368070638221, + 4095.9999999999982, + 4106.6701373830711, + 4117.347210185475, + 4128.0312094089259, + 4138.722126084268, + 4149.4199512713267, + 4160.1246760587583, + 4170.8362915638982, + 4181.5547889326181, + 4192.2801593391769, + 4203.0123939860741, + 4213.7514841039101, + 4224.4974209512384, + 4235.2501958144258, + 4246.0098000075095, + 4256.7762248720574, + 4267.549461777031, + 4278.3295021186423, + 4289.1163373202198, + 4299.9099588320714, + 4310.7103581313495, + 4321.5175267219138, + 4332.3314561342004, + 4343.152137925088, + 4353.9795636777671, + 4364.8137250016052, + 4375.6546135320223, + 4386.5022209303588, + 4397.3565388837469, + 4408.2175591049827, + 4419.0852733324018, + 4429.9596733297531, + 4440.8407508860728, + 4451.7284978155603, + 4462.6229059574571, + 4473.5239671759227, + 4484.4316733599126, + 4495.3460164230582, + 4506.2669883035496, + 4517.1945809640119, + 4528.1287863913894, + 4539.069596596828, + 4550.0170036155587, + 4560.9709995067806, + 4571.931576353546, + 4582.898726262647, + 4593.8724413645004, + 4604.8527138130348, + 4615.8395357855816, + 4626.8328994827571, + 4637.8327971283588, + 4648.8392209692511, + 4659.8521632752563, + 4670.8716163390473, + 4681.8975724760394, + 4692.9300240242837, + 4703.9689633443595, + 4715.0143828192668, + 4726.0662748543255, + 4737.1246318770682, + 4748.1894463371373, + 4759.2607107061804, + 4770.3384174777493, + 4781.4225591671993, + 4792.5131283115852, + 4803.6101174695614, + 4814.7135192212854, + 4825.8233261683154, + 4836.9395309335096, + 4848.0621261609349, + 4859.1911045157631, + 4870.3264586841779, + 4881.4681813732768, + 4892.6162653109768, + 4903.7707032459193, + 4914.931487947375, + 4926.0986122051509, + 4937.2720688294967, + 4948.4518506510112, + 4959.637950520555, + 4970.8303613091521, + 4982.0290759079044, + 4993.2340872278974, + 5004.4453882001153, + 5015.6629717753467, + 5026.8868309241007, + 5038.1169586365131, + 5049.353347922266, + 5060.5959918104927, + 5071.8448833496996, + 5083.1000156076734, + 5094.3613816713996, + 5105.6289746469747, + 5116.9027876595246, + 5128.18281385312, + 5139.4690463906918, + 5150.7614784539473, + 5162.0601032432933, + 5173.3649139777472, + 5184.6759038948594, + 5195.9930662506322, + 5207.3163943194386, + 5218.6458813939435, + 5229.9815207850224, + 5241.3233058216847, + 5252.6712298509919, + 5264.025286237983, + 5275.3854683655954, + 5286.7517696345885, + 5298.1241834634639, + 5309.5027032883945, + 5320.887322563146, + 5332.2780347589978, + 5343.6748333646756, + 5355.0777118862716, + 5366.4866638471722, + 5377.901682787985, + 5389.3227622664635, + 5400.749895857437, + 5412.1830771527357, + 5423.622299761123, + 5435.067557308219, + 5446.5188434364318, + 5457.9761518048872, + 5469.4394760893592, + 5480.9088099821975, + 5492.3841471922606, + 5503.8654814448455, + 5515.3528064816201, + 5526.846116060552, + 5538.3454039558474, + 5549.8506639578736, + 5561.3618898731029, + 5572.8790755240361, + 5584.4022147491451, + 5595.9313014027975, + 5607.4663293552012, + 5619.0072924923297, + 5630.5541847158656, + 5642.1069999431284, + 5653.665732107017, + 5665.230375155943, + 5676.8009230537655, + 5688.3773697797333, + 5699.9597093284156, + 5711.5479357096474, + 5723.1420429484588, + 5734.7420250850209, + 5746.347876174581, + 5757.9595902874016, + 5769.5771615087006, + 5781.2005839385911, + 5792.8298516920213, + 5804.4649588987149, + 5816.1058997031105, + 5827.7526682643065, + 5839.4052587559972, + 5851.0636653664196, + 5862.7278822982908, + 5874.3979037687541, + 5886.0737240093204, + 5897.7553372658094, + 5909.4427377982956, + 5921.1359198810505, + 5932.8348778024874, + 5944.5396058651031, + 5956.2500983854261, + 5967.9663496939575, + 5979.6883541351208, + 5991.4161060672022, + 6003.1495998623004, + 6014.8888299062692, + 6026.6337905986684, + 6038.3844763527022, + 6050.1408815951781, + 6061.9030007664414, + 6073.6708283203316, + 6085.4443587241267, + 6097.2235864584891, + 6109.0085060174197, + 6120.7991119081998, + 6132.595398651345, + 6144.3973607805519, + 6156.2049928426459, + 6168.0182893975361, + 6179.8372450181578, + 6191.6618542904307, + 6203.4921118132024, + 6215.3280121982016, + 6227.1695500699925, + 6239.0167200659189, + 6250.8695168360628, + 6262.7279350431891, + 6274.5919693627056, + 6286.4616144826068, + 6298.3368651034316, + 6310.2177159382172, + 6322.1041617124456, + 6333.9961971640032, + 6345.8938170431311, + 6357.7970161123785, + 6369.7057891465583, + 6381.6201309327007, + 6393.5400362700075, + 6405.4654999698032, + 6417.3965168554978, + 6429.3330817625329, + 6441.2751895383453, + 6453.2228350423138, + 6465.176013145724, + 6477.134718731716, + 6489.0989466952469, + 6501.0686919430445, + 6513.0439493935628, + 6525.0247139769417, + 6537.010980634961, + 6549.002744321001, + 6560.9999999999973, + 6573.0027426483985, + 6585.0109672541284, + 6597.0246688165371, + 6609.0438423463656, + 6621.0684828657004, + 6633.0985854079354, + 6645.134145017727, + 6657.1751567509573, + 6669.2216156746908, + 6681.2735168671343, + 6693.3308554176001, + 6705.3936264264594, + 6717.461825005108, + 6729.535446275926, + 6741.6144853722335, + 6753.6989374382601, + 6765.7887976290967, + 6777.8840611106634, + 6789.9847230596661, + 6802.0907786635626, + 6814.2022231205201, + 6826.3190516393797, + 6838.4412594396181, + 6850.5688417513074, + 6862.701793815083, + 6874.840110882099, + 6886.9837882139991, + 6899.1328210828724, + 6911.2872047712199, + 6923.4469345719199, + 6935.6120057881863, + 6947.7824137335365, + 6959.9581537317536, + 6972.1392211168532, + 6984.3256112330409, + 6996.5173194346862, + 7008.7143410862773, + 7020.9166715623942, + 7033.1243062476678, + 7045.3372405367481, + 7057.5554698342685, + 7069.7789895548103, + 7082.0077951228714, + 7094.2418819728273, + 7106.4812455489018, + 7118.7258813051285, + 7130.9757847053224, + 7143.2309512230404, + 7155.4913763415516, + 7167.7570555538041, + 7180.0279843623894, + 7192.3041582795131, + 7204.5855728269571, + 7216.8722235360519, + 7229.1641059476406, + 7241.4612156120484, + 7253.7635480890503, + 7266.0710989478375, + 7278.3838637669869, + 7290.7018381344296, + 7303.0250176474174, + 7315.3533979124932, + 7327.6869745454596, + 7340.0257431713462, + 7352.3696994243801, + 7364.7188389479543, + 7377.0731573945968, + 7389.4326504259407, + 7401.7973137126937, + 7414.1671429346061, + 7426.5421337804428, + 7438.922281947951, + 7451.3075831438346, + 7463.6980330837177, + 7476.0936274921214, + 7488.4943621024304, + 7500.9002326568652, + 7513.3112349064522, + 7525.7273646109943, + 7538.1486175390446, + 7550.5749894678729, + 7563.0064761834419, + 7575.4430734803736, + 7587.8847771619248, + 7600.3315830399597, + 7612.7834869349153, + 7625.24048467578, + 7637.7025721000637, + 7650.1697450537677, + 7662.6419993913596, + 7675.1193309757446, + 7687.6017356782404, + 7700.0892093785433, + 7712.5817479647112, + 7725.079347333125, + 7737.5820033884729, + 7750.0897120437139, + 7762.6024692200581, + 7775.1202708469355, + 7787.6431128619733, + 7800.1709912109645, + 7812.7039018478481, + 7825.2418407346768, + 7837.7848038415968, + 7850.3327871468155, + 7862.8857866365806, + 7875.4437983051539, + 7888.006818154784, + 7900.5748421956796, + 7913.1478664459901, + 7925.725886931772, + 7938.3088996869719, + 7950.8969007533951, + 7963.4898861806851, + 7976.0878520262959, + 7988.6907943554688, + 8001.2987092412086, + 8013.911592764257, + 8026.5294410130691, + 8039.1522500837891, + 8051.7800160802271, + 8064.412735113835, + 8077.0504033036796, + 8089.6930167764222, + 8102.3405716662946, + 8114.9930641150731, + 8127.6504902720571, + 8140.3128462940449, + 8152.9801283453098, + 8165.6523325975786, + 8178.3294552300049, + 8191.0114924291529, + 8203.6984403889655, + 8216.3902953107463, + 8229.0870534031419, + 8241.7887108821069, + 8254.4952639708936, + 8267.2067089000211, + 8279.9230419072574, + 8292.6442592375952, + 8305.3703571432306, + 8318.101331883543, + 8330.8371797250657, + 8343.577896941475, + 8356.3234798135582, + 8369.0739246291978, + 8381.8292276833508, + 8394.5893852780209, + 8407.3543937222421, + 8420.1242493320569, + 8432.8989484304948, + 8445.6784873475499, + 8458.4628624201578, + 8471.2520699921806, + 8484.0461064143838, + 8496.8449680444082, + 8509.6486512467636, + 8522.4571523927953, + 8535.270467860666, + 8548.0885940353437, + 8560.9115273085663, + 8573.7392640788403, + 8586.5718007514006, + 8599.4091337382069, + 8612.2512594579148, + 8625.0981743358552, + 8637.9498748040205, + 8650.8063573010386, + 8663.6676182721567, + 8676.533654169225, + 8689.4044614506638, + 8702.2800365814601, + 8715.1603760331418, + 8728.0454762837508, + 8740.9353338178389, + 8753.8299451264356, + 8766.7293067070332, + 8779.6334150635721, + 8792.5422667064158, + 8805.4558581523324, + 8818.3741859244819, + 8831.2972465523908, + 8844.2250365719356, + 8857.1575525253265, + 8870.0947909610859, + 8883.0367484340295, + 8895.9834215052524, + 8908.934806742107, + 8921.8909007181846, + 8934.8517000132997, + 8947.817201213471, + 8960.7874009109, + 8973.7622957039603, + 8986.7418821971733, + 8999.7261570011924, + 9012.7151167327884, + 9025.7087580148236, + 9038.7070774762469, + 9051.7100717520643, + 9064.7177374833282, + 9077.7300713171153, + 9090.7470699065179, + 9103.7687299106146, + 9116.7950479944648, + 9129.8260208290812, + 9142.8616450914233, + 9155.9019174643727, + 9168.9468346367157, + 9181.9963933031358, + 9195.0505901641845, + 9208.1094219262741, + 9221.1728853016557, + 9234.240977008405, + 9247.3136937704076, + 9260.3910323173386, + 9273.472989384647, + 9286.5595617135423, + 9299.6507460509747, + 9312.7465391496207, + 9325.8469377678684, + 9338.9519386698012, + 9352.0615386251757, + 9365.1757344094131, + 9378.2945228035842, + 9391.4179005943843, + 9404.5458645741273, + 9417.6784115407263, + 9430.8155382976747, + 9443.9572416540359, + 9457.1035184244265, + 9470.2543654290002, + 9483.4097794934296, + 9496.5697574488931, + 9509.7342961320664, + 9522.9033923850911, + 9536.0770430555804, + 9549.2552449965824, + 9562.4379950665825, + 9575.6252901294793, + 9588.8171270545736, + 9602.0135027165488, + 9615.2144139954635, + 9628.4198577767274, + 9641.629830951093, + 9654.844330414644, + 9668.0633530687719, + 9681.286895820167, + 9694.5149555808002, + 9707.7475292679192, + 9720.9846138040157, + 9734.2262061168276, + 9747.4723031393187, + 9760.7229018096641, + 9773.9779990712323, + 9787.2375918725811, + 9800.5016771674327, + 9813.7702519146696, + 9827.0433130783094, + 9840.3208576275028, + 9853.602882536512, + 9866.8893847846994, + 9880.1803613565116, + 9893.4758092414686, + 9906.7757254341523, + 9920.0801069341851, + 9933.3889507462245, + 9946.7022538799429, + 9960.0200133500221, + 9973.3422261761298, + 9986.6688893829159, + 9999.9999999999945, + 10013.335555061929, + 10026.675551608221, + 10040.019986683301, + 10053.368857336509, + 10066.722160622081, + 10080.079893599144, + 10093.442053331697, + 10106.808636888598, + 10120.179641343551, + 10133.555063775095, + 10146.934901266595, + 10160.31915090622, + 10173.707809786936, + 10187.100875006496, + 10200.498343667417, + 10213.900212876984, + 10227.306479747222, + 10240.717141394889, + 10254.132194941467, + 10267.551637513146, + 10280.975466240814, + 10294.40367826004, + 10307.836270711066, + 10321.273240738796, + 10334.71458549278, + 10348.160302127204, + 10361.610387800878, + 10375.064839677221, + 10388.523654924258, + 10401.986830714593, + 10415.454364225412, + 10428.926252638465, + 10442.402493140049, + 10455.883082921007, + 10469.368019176709, + 10482.85729910704, + 10496.350919916393, + 10509.848878813653, + 10523.351173012188, + 10536.857799729838, + 10550.3687561889, + 10563.884039616123, + 10577.403647242685, + 10590.927576304197, + 10604.455824040679, + 10617.988387696556, + 10631.525264520642, + 10645.066451766135, + 10658.611946690598, + 10672.161746555956, + 10685.715848628475, + 10699.274250178762, + 10712.836948481747, + 10726.403940816675, + 10739.975224467091, + 10753.550796720834, + 10767.130654870027, + 10780.714796211059, + 10794.303218044579, + 10807.895917675487, + 10821.492892412922, + 10835.094139570248, + 10848.699656465047, + 10862.309440419107, + 10875.923488758415, + 10889.541798813138, + 10903.16436791762, + 10916.791193410372, + 10930.422272634056, + 10944.05760293548, + 10957.697181665582, + 10971.341006179427, + 10984.98907383619, + 10998.641381999149, + 11012.297928035676, + 11025.958709317223, + 11039.623723219316, + 11053.292967121541, + 11066.966438407539, + 11080.64413446499, + 11094.326052685608, + 11108.012190465128, + 11121.702545203296, + 11135.397114303863, + 11149.095895174571, + 11162.798885227143, + 11176.506081877278, + 11190.217482544635, + 11203.933084652828, + 11217.652885629415, + 11231.376882905886, + 11245.105073917659, + 11258.837456104062, + 11272.574026908333, + 11286.314783777601, + 11300.059724162888, + 11313.808845519083, + 11327.562145304952, + 11341.319620983111, + 11355.081270020033, + 11368.847089886023, + 11382.617078055218, + 11396.391232005579, + 11410.169549218874, + 11423.952027180676, + 11437.738663380349, + 11451.529455311042, + 11465.324400469679, + 11479.123496356951, + 11492.926740477304, + 11506.734130338931, + 11520.545663453764, + 11534.361337337466, + 11548.181149509423, + 11562.005097492724, + 11575.83317881417, + 11589.665391004253, + 11603.501731597149, + 11617.342198130715, + 11631.186788146468, + 11645.035499189589, + 11658.888328808911, + 11672.745274556904, + 11686.606333989675, + 11700.471504666955, + 11714.340784152086, + 11728.214170012021, + 11742.091659817312, + 11755.973251142101, + 11769.858941564111, + 11783.748728664636, + 11797.642610028539, + 11811.540583244237, + 11825.442645903697, + 11839.34879560242, + 11853.259029939445, + 11867.173346517333, + 11881.091742942155, + 11895.014216823492, + 11908.940765774427, + 11922.871387411526, + 11936.806079354839, + 11950.744839227897, + 11964.687664657684, + 11978.634553274653, + 11992.585502712702, + 12006.540510609168, + 12020.499574604828, + 12034.462692343877, + 12048.429861473938, + 12062.401079646032, + 12076.376344514589, + 12090.355653737433, + 12104.339004975769, + 12118.326395894188, + 12132.317824160644, + 12146.313287446457, + 12160.312783426305, + 12174.316309778205, + 12188.323864183525, + 12202.335444326955, + 12216.351047896511, + 12230.370672583531, + 12244.394316082657, + 12258.421976091831, + 12272.453650312296, + 12286.489336448574, + 12300.529032208471, + 12314.572735303058, + 12328.620443446678, + 12342.672154356922, + 12356.727865754638, + 12370.787575363909, + 12384.851280912055, + 12398.918980129623, + 12412.990670750381, + 12427.066350511306, + 12441.146017152583, + 12455.229668417589, + 12469.317302052901, + 12483.40891580827, + 12497.50450743663, + 12511.604074694078, + 12525.707615339878, + 12539.815127136444, + 12553.926607849342, + 12568.042055247275, + 12582.161467102082, + 12596.284841188726, + 12610.41217528529, + 12624.543467172971, + 12638.678714636069, + 12652.817915461985, + 12666.961067441209, + 12681.108168367316, + 12695.259216036962, + 12709.414208249869, + 12723.573142808827, + 12737.736017519681, + 12751.902830191326, + 12766.073578635704, + 12780.248260667788, + 12794.426874105588, + 12808.609416770132, + 12822.795886485468, + 12836.986281078653, + 12851.180598379744, + 12865.378836221802, + 12879.580992440871, + 12893.787064875984, + 12907.997051369144, + 12922.210949765335, + 12936.428757912496, + 12950.650473661524, + 12964.876094866273, + 12979.105619383534, + 12993.339045073039, + 13007.576369797454, + 13021.817591422368, + 13036.062707816285, + 13050.311716850629, + 13064.564616399723, + 13078.821404340792, + 13093.082078553954, + 13107.346636922217, + 13121.615077331464, + 13135.887397670458, + 13150.163595830827, + 13164.44366970706, + 13178.727617196502, + 13193.015436199352, + 13207.307124618648, + 13221.602680360265, + 13235.902101332911, + 13250.205385448118, + 13264.512530620239, + 13278.823534766434, + 13293.138395806676, + 13307.457111663734, + 13321.779680263176, + 13336.106099533356, + 13350.436367405409, + 13364.77048181325, + 13379.108440693562, + 13393.450241985796, + 13407.795883632158, + 13422.145363577607, + 13436.498679769853, + 13450.855830159346, + 13465.216812699266, + 13479.581625345529, + 13493.950266056772, + 13508.32273279435, + 13522.699023522329, + 13537.079136207483, + 13551.463068819286, + 13565.850819329906, + 13580.2423857142, + 13594.63776594971, + 13609.036958016657, + 13623.439959897927, + 13637.846769579081, + 13652.257385048335, + 13666.67180429656, + 13681.090025317284, + 13695.512046106669, + 13709.937864663521, + 13724.367478989278, + 13738.800887088004, + 13753.238086966385, + 13767.679076633727, + 13782.123854101939, + 13796.572417385545, + 13811.024764501659, + 13825.480893469998, + 13839.94080231286, + 13854.404489055134, + 13868.871951724283, + 13883.34318835034, + 13897.818196965914, + 13912.296975606168, + 13926.779522308825, + 13941.26583511416, + 13955.755912064991, + 13970.249751206682, + 13984.747350587126, + 13999.248708256751, + 14013.753822268511, + 14028.262690677873, + 14042.775311542828, + 14057.291682923867, + 14071.811802883994, + 14086.335669488704, + 14100.863280805994, + 14115.394634906341, + 14129.92972986271, + 14144.468563750548, + 14159.01113464777, + 14173.55744063476, + 14188.107479794369, + 14202.661250211901, + 14217.218749975118, + 14231.779977174227, + 14246.344929901879, + 14260.913606253163, + 14275.486004325601, + 14290.062122219146, + 14304.641958036171, + 14319.225509881464, + 14333.812775862236, + 14348.403754088098, + 14362.998442671067, + 14377.59683972556, + 14392.198943368388, + 14406.804751718748, + 14421.414262898223, + 14436.027475030774, + 14450.64438624274, + 14465.264994662828, + 14479.889298422106, + 14494.517295654005, + 14509.148984494313, + 14523.784363081166, + 14538.423429555049, + 14553.066182058781, + 14567.712618737527, + 14582.362737738777, + 14597.016537212348, + 14611.674015310382, + 14626.33517018734, + 14640.999999999993, + 14655.668502907418, + 14670.340677071003, + 14685.016520654426, + 14699.696031823671, + 14714.379208746999, + 14729.066049594967, + 14743.756552540408, + 14758.45071575843, + 14773.148537426418, + 14787.850015724018, + 14802.555148833142, + 14817.263934937961, + 14831.976372224897, + 14846.692458882624, + 14861.41219310206, + 14876.135573076363, + 14890.862597000923, + 14905.593263073371, + 14920.327569493558, + 14935.065514463557, + 14949.807096187662, + 14964.552312872382, + 14979.301162726431, + 14994.053643960735, + 15008.809754788414, + 15023.569493424788, + 15038.332858087369, + 15053.099846995858, + 15067.870458372134, + 15082.644690440264, + 15097.422541426484, + 15112.204009559202, + 15126.989093068994, + 15141.777790188597, + 15156.570099152905, + 15171.366018198967, + 15186.165545565986, + 15200.968679495301, + 15215.775418230402, + 15230.585760016909, + 15245.399703102579, + 15260.217245737298, + 15275.038386173073, + 15289.863122664035, + 15304.691453466432, + 15319.523376838621, + 15334.358891041069, + 15349.197994336346, + 15364.040684989128, + 15378.886961266177, + 15393.736821436356, + 15408.590263770609, + 15423.447286541972, + 15438.307888025554, + 15453.172066498542, + 15468.039820240196, + 15482.91114753184, + 15497.786046656869, + 15512.664515900733, + 15527.546553550939, + 15542.432157897045, + 15557.32132723066, + 15572.214059845435, + 15587.110354037064, + 15602.010208103273, + 15616.913620343823, + 15631.820589060506, + 15646.731112557136, + 15661.645189139546, + 15676.562817115593, + 15691.483994795139, + 15706.408720490062, + 15721.336992514242, + 15736.268809183561, + 15751.204168815901, + 15766.143069731135, + 15781.085510251132, + 15796.03148869974, + 15810.981003402798, + 15825.934052688119, + 15840.890634885489, + 15855.850748326673, + 15870.814391345401, + 15885.781562277361, + 15900.752259460214, + 15915.726481233565, + 15930.704225938984, + 15945.685491919978, + 15960.670277522009, + 15975.658581092481, + 15990.65040098073, + 16005.645735538035, + 16020.644583117599, + 16035.646942074556, + 16050.652810765967, + 16065.662187550806, + 16080.675070789974, + 16095.691458846273, + 16110.711350084424, + 16125.734742871053, + 16140.761635574685, + 16155.792026565747, + 16170.825914216561, + 16185.863296901338, + 16200.904172996183, + 16215.948540879079, + 16230.996398929899, + 16246.047745530386, + 16261.102579064163, + 16276.160897916721, + 16291.22270047542, + 16306.287985129484, + 16321.356750269995, + 16336.428994289896, + 16351.504715583982, + 16366.5839125489, + 16381.666583583141, + 16396.752727087041, + 16411.842341462776, + 16426.935425114363, + 16442.031976447644, + 16457.131993870298, + 16472.235475791829, + 16487.342420623561, + 16502.452826778641, + 16517.566692672033, + 16532.684016720516, + 16547.804797342676, + 16562.929032958902, + 16578.056721991394, + 16593.18786286415, + 16608.322454002962, + 16623.460493835417, + 16638.601980790896, + 16653.746913300558, + 16668.895289797354, + 16684.047108716015, + 16699.202368493046, + 16714.361067566726, + 16729.523204377107, + 16744.688777366009, + 16759.857784977012, + 16775.030225655464, + 16790.206097848466, + 16805.385400004874, + 16820.568130575302, + 16835.754288012104, + 16850.943870769381, + 16866.136877302983, + 16881.333306070494, + 16896.53315553123, + 16911.736424146249, + 16926.943110378332, + 16942.153212691992, + 16957.366729553454, + 16972.583659430682, + 16987.804000793338, + 17003.027752112816, + 17018.254911862205, + 17033.485478516312, + 17048.719450551645, + 17063.956826446421, + 17079.197604680547, + 17094.44178373563, + 17109.689362094967, + 17124.940338243552, + 17140.194710668064, + 17155.452477856852, + 17170.713638299967, + 17185.978190489128, + 17201.246132917724, + 17216.517464080825, + 17231.792182475165, + 17247.070286599141, + 17262.351774952826, + 17277.636646037936, + 17292.924898357855, + 17308.216530417623, + 17323.511540723921, + 17338.809927785089, + 17354.111690111105, + 17369.416826213594, + 17384.725334605821, + 17400.037213802683, + 17415.352462320716, + 17430.67107867809, + 17445.993061394587, + 17461.318408991636, + 17476.647119992274, + 17491.979192921168, + 17507.314626304586, + 17522.653418670423, + 17537.995568548187, + 17553.341074468986, + 17568.689934965536, + 17584.042148572156, + 17599.397713824768, + 17614.75662926089, + 17630.118893419625, + 17645.484504841683, + 17660.853462069354, + 17676.225763646511, + 17691.601408118619, + 17706.980394032718, + 17722.362719937424, + 17737.748384382936, + 17753.137385921014, + 17768.529723104999, + 17783.92539448979, + 17799.324398631856, + 17814.726734089225, + 17830.13239942148, + 17845.541393189767, + 17860.95371395678, + 17876.369360286772, + 17891.788330745527, + 17907.210623900395, + 17922.636238320254, + 17938.065172575527, + 17953.497425238176, + 17968.932994881692, + 17984.371880081104, + 17999.814079412972, + 18015.259591455371, + 18030.708414787914, + 18046.160547991731, + 18061.615989649465, + 18077.074738345284, + 18092.536792664861, + 18108.002151195393, + 18123.470812525571, + 18138.942775245599, + 18154.418037947191, + 18169.896599223546, + 18185.37845766938, + 18200.863611880886, + 18216.352060455767, + 18231.843801993204, + 18247.338835093873, + 18262.837158359936, + 18278.338770395032, + 18293.84366980429, + 18309.351855194309, + 18324.863325173166, + 18340.378078350412, + 18355.896113337069, + 18371.417428745623, + 18386.942023190033, + 18402.469895285718, + 18418.00104364955, + 18433.53546689987, + 18449.073163656474, + 18464.614132540602, + 18480.158372174956, + 18495.705881183676, + 18511.256658192357, + 18526.810701828035, + 18542.368010719183, + 18557.928583495715, + 18573.492418788985, + 18589.059515231773, + 18604.629871458303, + 18620.203486104212, + 18635.78035780658, + 18651.360485203899, + 18666.943866936086, + 18682.53050164448, + 18698.120387971841, + 18713.713524562332, + 18729.30991006154, + 18744.909543116457, + 18760.512422375479, + 18776.118546488418, + 18791.727914106479, + 18807.340523882274, + 18822.95637446981, + 18838.575464524489, + 18854.197792703111, + 18869.823357663863, + 18885.452158066328, + 18901.08419257147, + 18916.719459841639, + 18932.357958540564, + 18947.999687333362, + 18963.644644886521, + 18979.292829867907, + 18994.944240946759, + 19010.598876793687, + 19026.256736080668, + 19041.917817481048, + 19057.582119669532, + 19073.2496413222, + 19088.920381116473, + 19104.594337731145, + 19120.271509846356, + 19135.951896143604, + 19151.635495305738, + 19167.322306016948, + 19183.012326962784, + 19198.705556830122, + 19214.401994307198, + 19230.101638083579, + 19245.804486850167, + 19261.510539299208, + 19277.219794124274, + 19292.932250020265, + 19308.647905683421, + 19324.366759811302, + 19340.088811102793, + 19355.8140582581, + 19371.542499978754, + 19387.2741349676, + 19403.008961928797, + 19418.746979567823, + 19434.488186591469, + 19450.232581707827, + 19465.980163626304, + 19481.730931057613, + 19497.484882713761, + 19513.242017308068, + 19529.002333555141, + 19544.765830170898, + 19560.532505872539, + 19576.302359378566, + 19592.075389408761, + 19607.851594684209, + 19623.630973927269, + 19639.41352586159, + 19655.199249212103, + 19670.988142705017, + 19686.780205067826, + 19702.575435029288, + 19718.373831319448, + 19734.175392669615, + 19749.980117812371, + 19765.788005481569, + 19781.599054412323, + 19797.413263341008, + 19813.230631005274, + 19829.051156144014, + 19844.874837497395, + 19860.701673806827, + 19876.531663814985, + 19892.364806265789, + 19908.201099904403, + 19924.040543477258, + 19939.883135732012, + 19955.728875417579, + 19971.577761284105, + 19987.429792082985, + 20003.284966566847, + 20019.14328348956, + 20035.004741606219, + 20050.869339673161, + 20066.737076447946, + 20082.607950689362, + 20098.481961157428, + 20114.359106613385, + 20130.239385819699, + 20146.122797540058, + 20162.009340539353, + 20177.899013583716, + 20193.791815440476, + 20209.687744878182, + 20225.586800666591, + 20241.488981576669, + 20257.394286380597, + 20273.302713851754, + 20289.214262764715, + 20305.128931895277, + 20321.046720020415, + 20336.967625918318, + 20352.891648368361, + 20368.818786151114, + 20384.749038048347, + 20400.682402843009, + 20416.618879319249, + 20432.558466262391, + 20448.501162458953, + 20464.446966696629, + 20480.395877764302, + 20496.347894452025, + 20512.303015551031, + 20528.261239853735, + 20544.22256615372, + 20560.186993245738, + 20576.15451992572, + 20592.125144990758, + 20608.098867239107, + 20624.075685470198, + 20640.055598484618, + 20656.038605084115, + 20672.024704071595, + 20688.013894251126, + 20704.006174427926, + 20720.001543408373, + 20735.999999999989, + 20752.001543011454, + 20768.006171252597, + 20784.013883534382, + 20800.024678668931, + 20816.038555469506, + 20832.055512750507, + 20848.075549327474, + 20864.098664017085, + 20880.124855637161, + 20896.154123006647, + 20912.186464945626, + 20928.221880275312, + 20944.260367818049, + 20960.301926397311, + 20976.346554837684, + 20992.394251964895, + 21008.445016605787, + 21024.498847588318, + 21040.555743741574, + 21056.615703895754, + 21072.678726882168, + 21088.744811533252, + 21104.813956682538, + 21120.886161164683, + 21136.961423815443, + 21153.039743471683, + 21169.121118971379, + 21185.205549153605, + 21201.293032858535, + 21217.383568927453, + 21233.477156202731, + 21249.573793527841, + 21265.673479747358, + 21281.776213706937, + 21297.881994253334, + 21313.990820234398, + 21330.102690499054, + 21346.21760389733, + 21362.335559280327, + 21378.456555500241, + 21394.580591410333, + 21410.707665864964, + 21426.83777771956, + 21442.970925830628, + 21459.107109055756, + 21475.246326253604, + 21491.388576283895, + 21507.533858007431, + 21523.682170286087, + 21539.833511982797, + 21555.987881961566, + 21572.145279087465, + 21588.305702226615, + 21604.469150246216, + 21620.635622014521, + 21636.805116400832, + 21652.977632275521, + 21669.153168510009, + 21685.331723976764, + 21701.513297549318, + 21717.697888102244, + 21733.885494511167, + 21750.076115652759, + 21766.269750404736, + 21782.466397645861, + 21798.666056255934, + 21814.868725115801, + 21831.074403107345, + 21847.283089113484, + 21863.494782018177, + 21879.709480706417, + 21895.927184064229, + 21912.147890978667, + 21928.371600337818, + 21944.598311030797, + 21960.828021947746, + 21977.060731979829, + 21993.296440019243, + 22009.535144959198, + 22025.77684569393, + 22042.021541118691, + 22058.269230129757, + 22074.519911624411, + 22090.773584500959, + 22107.030247658717, + 22123.289899998013, + 22139.552540420187, + 22155.818167827587, + 22172.086781123569, + 22188.358379212495, + 22204.632960999726, + 22220.910525391639, + 22237.191071295601, + 22253.474597619981, + 22269.761103274148, + 22286.050587168469, + 22302.343048214312, + 22318.638485324027, + 22334.936897410968, + 22351.23828338947, + 22367.542642174871, + 22383.849972683485, + 22400.160273832618, + 22416.473544540564, + 22432.789783726603, + 22449.108990310986, + 22465.431163214958, + 22481.75630136074, + 22498.084403671528, + 22514.415469071497, + 22530.749496485802, + 22547.086484840562, + 22563.426433062879, + 22579.769340080824, + 22596.115204823436, + 22612.464026220721, + 22628.815803203655, + 22645.170534704179, + 22661.5282196552, + 22677.888856990587, + 22694.252445645168, + 22710.618984554734, + 22726.988472656034, + 22743.360908886778, + 22759.736292185622, + 22776.114621492186, + 22792.495895747044, + 22808.880113891719, + 22825.267274868678, + 22841.657377621348, + 22858.050421094096, + 22874.446404232243, + 22890.845325982053, + 22907.247185290722, + 22923.651981106406, + 22940.059712378195, + 22956.470378056114, + 22972.883977091129, + 22989.300508435153, + 23005.719971041017, + 23022.142363862498, + 23038.567685854305, + 23054.995935972078, + 23071.427113172387, + 23087.86121641273, + 23104.298244651531, + 23120.738196848146, + 23137.181071962848, + 23153.626868956846, + 23170.075586792263, + 23186.527224432142, + 23202.981780840448, + 23219.439254982066, + 23235.899645822796, + 23252.362952329357, + 23268.829173469378, + 23285.298308211408, + 23301.770355524899, + 23318.245314380223, + 23334.723183748658, + 23351.203962602387, + 23367.687649914504, + 23384.174244659007, + 23400.663745810798, + 23417.15615234568, + 23433.651463240367, + 23450.149677472462, + 23466.650794020472, + 23483.154811863806, + 23499.661729982763, + 23516.171547358543, + 23532.684262973235, + 23549.199875809823, + 23565.718384852185, + 23582.239789085092, + 23598.764087494197, + 23615.291279066041, + 23631.821362788058, + 23648.354337648565, + 23664.890202636761, + 23681.428956742733, + 23697.970598957443, + 23714.515128272738, + 23731.062543681343, + 23747.612844176863, + 23764.166028753778, + 23780.72209640744, + 23797.281046134085, + 23813.842876930816, + 23830.407587795606, + 23846.975177727301, + 23863.545645725622, + 23880.11899079115, + 23896.695211925336, + 23913.274308130498, + 23929.856278409821, + 23946.441121767348, + 23963.028837207989, + 23979.619423737513, + 23996.212880362549, + 24012.809206090584, + 24029.408399929966, + 24046.010460889898, + 24062.615387980433, + 24079.223180212492, + 24095.833836597827, + 24112.447356149063, + 24129.063737879667, + 24145.682980803951, + 24162.305083937081, + 24178.930046295067, + 24195.557866894767, + 24212.188544753884, + 24228.822078890964, + 24245.458468325389, + 24262.097712077397, + 24278.739809168052, + 24295.384758619261, + 24312.032559453768, + 24328.683210695162, + 24345.336711367858, + 24361.993060497109, + 24378.652257108995, + 24395.314300230442, + 24411.979188889192, + 24428.646922113825, + 24445.317498933746, + 24461.990918379193, + 24478.667179481225, + 24495.346281271726, + 24512.028222783407, + 24528.713003049801, + 24545.400621105266, + 24562.091075984976, + 24578.784366724925, + 24595.480492361927, + 24612.179451933614, + 24628.881244478438, + 24645.585869035654, + 24662.293324645343, + 24679.003610348394, + 24695.716725186514, + 24712.432668202211, + 24729.151438438807, + 24745.873034940436, + 24762.597456752032, + 24779.324702919344, + 24796.054772488926, + 24812.787664508123, + 24829.5233780251, + 24846.261912088819, + 24863.003265749034, + 24879.747438056307, + 24896.494428062004, + 24913.244234818278, + 24929.996857378079, + 24946.752294795166, + 24963.510546124078, + 24980.271610420157, + 24997.035486739525, + 25013.802174139113, + 25030.571671676629, + 25047.343978410572, + 25064.119093400237, + 25080.897015705697, + 25097.677744387816, + 25114.461278508239, + 25131.2476171294, + 25148.036759314517, + 25164.828704127583, + 25181.623450633375, + 25198.42099789745, + 25215.221344986145, + 25232.024490966574, + 25248.830434906627, + 25265.639175874974, + 25282.450712941049, + 25299.265045175071, + 25316.082171648024, + 25332.902091431668, + 25349.724803598532, + 25366.550307221914, + 25383.378601375884, + 25400.209685135269, + 25417.043557575678, + 25433.880217773472, + 25450.719664805783, + 25467.561897750507, + 25484.406915686297, + 25501.254717692573, + 25518.105302849512, + 25534.958670238051, + 25551.814818939893, + 25568.67374803748, + 25585.535456614027, + 25602.399943753502, + 25619.267208540619, + 25636.137250060852, + 25653.010067400432, + 25669.885659646327, + 25686.76402588627, + 25703.645165208734, + 25720.529076702944, + 25737.415759458876, + 25754.305212567244, + 25771.197435119517, + 25788.092426207899, + 25804.990184925344, + 25821.890710365547, + 25838.794001622944, + 25855.700057792714, + 25872.608877970775, + 25889.520461253778, + 25906.434806739118, + 25923.351913524923, + 25940.271780710063, + 25957.194407394138, + 25974.11979267748, + 25991.047935661154, + 26007.978835446964, + 26024.912491137442, + 26041.848901835841, + 26058.788066646157, + 26075.729984673108, + 26092.674655022136, + 26109.622076799409, + 26126.572249111829, + 26143.525171067016, + 26160.480841773315, + 26177.43926033979, + 26194.400425876229, + 26211.364337493149, + 26228.330994301767, + 26245.30039541404, + 26262.272539942627, + 26279.247427000919, + 26296.225055703002, + 26313.205425163702, + 26330.188534498539, + 26347.174382823756, + 26364.162969256304, + 26381.154292913852, + 26398.148352914774, + 26415.145148378149, + 26432.144678423778, + 26449.146942172156, + 26466.151938744493, + 26483.159667262702, + 26500.170126849403, + 26517.183316627921, + 26534.199235722277, + 26551.217883257199, + 26568.239258358124, + 26585.263360151173, + 26602.290187763181, + 26619.319740321676, + 26636.352016954883, + 26653.387016791727, + 26670.424738961825, + 26687.465182595493, + 26704.508346823739, + 26721.554230778267, + 26738.602833591467, + 26755.65415439643, + 26772.708192326929, + 26789.764946517433, + 26806.824416103096, + 26823.886600219761, + 26840.95149800396, + 26858.019108592915, + 26875.089431124517, + 26892.162464737365, + 26909.238208570721, + 26926.316661764544, + 26943.397823459472, + 26960.481692796813, + 26977.568268918571, + 26994.657550967422, + 27011.749538086722, + 27028.844229420498, + 27045.941624113464, + 27063.041721311005, + 27080.144520159181, + 27097.250019804727, + 27114.35821939505, + 27131.469118078236, + 27148.582715003027, + 27165.699009318858, + 27182.818000175819, + 27199.939686724665, + 27217.064068116837, + 27234.191143504428, + 27251.320912040203, + 27268.453372877593, + 27285.588525170693, + 27302.726368074269, + 27319.866900743735, + 27337.010122335181, + 27354.156032005358, + 27371.304628911668, + 27388.455912212183, + 27405.609881065626, + 27422.766534631384, + 27439.925872069507, + 27457.087892540683, + 27474.252595206275, + 27491.419979228293, + 27508.5900437694, + 27525.762787992917, + 27542.93821106281, + 27560.116312143706, + 27577.297090400876, + 27594.480545000242, + 27611.666675108383, + 27628.855479892518, + 27646.046958520514, + 27663.241110160889, + 27680.437933982801, + 27697.637429156068, + 27714.839594851132, + 27732.04443023909, + 27749.251934491687, + 27766.462106781299, + 27783.674946280949, + 27800.890452164302, + 27818.108623605654, + 27835.329459779954, + 27852.55295986278, + 27869.779123030345, + 27887.007948459504, + 27904.239435327745, + 27921.473582813196, + 27938.710390094613, + 27955.949856351392, + 27973.19198076355, + 27990.436762511745, + 28007.684200777272, + 28024.934294742041, + 28042.187043588601, + 28059.442446500128, + 28076.700502660427, + 28093.961211253929, + 28111.224571465693, + 28128.490582481401, + 28145.759243487362, + 28163.030553670509, + 28180.304512218394, + 28197.581118319198, + 28214.860371161725, + 28232.14226993539, + 28249.42681383024, + 28266.71400203693, + 28284.003833746745, + 28301.296308151585, + 28318.591424443959, + 28335.889181817001, + 28353.189579464462, + 28370.492616580705, + 28387.798292360701, + 28405.106606000048, + 28422.417556694945, + 28439.731143642206, + 28457.047366039264, + 28474.366223084147, + 28491.687713975512, + 28509.011837912611, + 28526.338594095305, + 28543.667981724069, + 28560.999999999982, + 28578.334648124732, + 28595.671925300605, + 28613.011830730498, + 28630.354363617909, + 28647.699523166943, + 28665.0473085823, + 28682.397719069289, + 28699.750753833818, + 28717.10641208239, + 28734.464693022121, + 28751.825595860708, + 28769.189119806462, + 28786.55526406828, + 28803.924027855664, + 28821.295410378701, + 28838.669410848088, + 28856.046028475103, + 28873.425262471628, + 28890.80711205013, + 28908.191576423673, + 28925.578654805915, + 28942.968346411097, + 28960.360650454055, + 28977.755566150216, + 28995.153092715591, + 29012.553229366786, + 29029.955975320987, + 29047.361329795975, + 29064.769292010107, + 29082.179861182336, + 29099.593036532187, + 29117.00881727978, + 29134.427202645813, + 29151.848191851568, + 29169.271784118911, + 29186.697978670283, + 29204.126774728706, + 29221.55817151779, + 29238.992168261717, + 29256.42876418525, + 29273.867958513725, + 29291.309750473058, + 29308.754139289747, + 29326.201124190855, + 29343.65070440403, + 29361.102879157483, + 29378.557647680012, + 29396.015009200975, + 29413.474962950309, + 29430.937508158524, + 29448.402644056692, + 29465.870369876469, + 29483.340684850071, + 29500.81358821028, + 29518.289079190454, + 29535.767157024511, + 29553.247820946945, + 29570.731070192807, + 29588.216903997723, + 29605.70532159787, + 29623.19632223, + 29640.689905131429, + 29658.186069540028, + 29675.684814694236, + 29693.186139833047, + 29710.690044196028, + 29728.196527023298, + 29745.705587555527, + 29763.217225033964, + 29780.731438700397, + 29798.248227797183, + 29815.76759156723, + 29833.289529254005, + 29850.81404010153, + 29868.341123354381, + 29885.870778257693, + 29903.403004057145, + 29920.937799998974, + 29938.475165329975, + 29956.015099297485, + 29973.557601149394, + 29991.102670134147, + 30008.650305500738, + 30026.200506498706, + 30043.753272378144, + 30061.308602389683, + 30078.866495784507, + 30096.426951814352, + 30113.989969731494, + 30131.55554878875, + 30149.123688239491, + 30166.694387337629, + 30184.267645337608, + 30201.843461494434, + 30219.42183506364, + 30237.002765301309, + 30254.586251464058, + 30272.172292809046, + 30289.760888593977, + 30307.35203807709, + 30324.94574051716, + 30342.541995173502, + 30360.140801305966, + 30377.742158174944, + 30395.346065041358, + 30412.952521166666, + 30430.561525812864, + 30448.173078242475, + 30465.787177718561, + 30483.403823504719, + 30501.02301486507, + 30518.644751064272, + 30536.269031367516, + 30553.895855040515, + 30571.525221349519, + 30589.157129561307, + 30606.791578943175, + 30624.428568762964, + 30642.06809828903, + 30659.710166790261, + 30677.35477353607, + 30695.001917796391, + 30712.651598841687, + 30730.303815942945, + 30747.958568371676, + 30765.615855399912, + 30783.275676300211, + 30800.938030345646, + 30818.602916809814, + 30836.270334966837, + 30853.940284091354, + 30871.612763458521, + 30889.287772344011, + 30906.965310024025, + 30924.645375775272, + 30942.327968874983, + 30960.013088600903, + 30977.700734231294, + 30995.390905044929, + 31013.083600321101, + 31030.778819339619, + 31048.476561380798, + 31066.17682572547, + 31083.879611654978, + 31101.584918451179, + 31119.29274539644, + 31137.003091773637, + 31154.715956866155, + 31172.431339957893, + 31190.14924033326, + 31207.869657277162, + 31225.592590075023, + 31243.318038012771, + 31261.046000376838, + 31278.776476454172, + 31296.50946553221, + 31314.24496689891, + 31331.98297984272, + 31349.7235036526, + 31367.466537618013, + 31385.212081028923, + 31402.960133175795, + 31420.710693349596, + 31438.463760841791, + 31456.219334944351, + 31473.977414949743, + 31491.738000150934, + 31509.501089841389, + 31527.266683315069, + 31545.034779866437, + 31562.80537879045, + 31580.578479382562, + 31598.35408093872, + 31616.132182755369, + 31633.91278412945, + 31651.695884358396, + 31669.481482740131, + 31687.269578573076, + 31705.060171156143, + 31722.853259788735, + 31740.648843770748, + 31758.446922402567, + 31776.247494985066, + 31794.050560819614, + 31811.85611920806, + 31829.664169452753, + 31847.474710856521, + 31865.287742722685, + 31883.103264355046, + 31900.921275057899, + 31918.741774136019, + 31936.564760894671, + 31954.390234639599, + 31972.21819467704, + 31990.048640313704, + 32007.881570856793, + 32025.716985613984, + 32043.554883893445, + 32061.395265003815, + 32079.238128254223, + 32097.083472954269, + 32114.931298414049, + 32132.781603944117, + 32150.634388855524, + 32168.48965245979, + 32186.347394068915, + 32204.207612995371, + 32222.07030855212, + 32239.935480052583, + 32257.803126810672, + 32275.673248140767, + 32293.545843357719, + 32311.420911776862, + 32329.298452713996, + 32347.178465485395, + 32365.060949407813, + 32382.945903798463, + 32400.83332797504, + 32418.723221255706, + 32436.615582959093, + 32454.510412404306, + 32472.407708910916, + 32490.307471798966, + 32508.209700388961, + 32526.114394001877, + 32544.021551959166, + 32561.931173582732, + 32579.843258194956, + 32597.757805118679, + 32615.674813677211, + 32633.594283194328, + 32651.516212994258, + 32669.440602401712, + 32687.367450741847, + 32705.296757340297, + 32723.228521523146, + 32741.162742616943, + 32759.099419948703, + 32777.038552845901, + 32794.980140636464, + 32812.924182648792, + 32830.87067821173, + 32848.819626654593, + 32866.77102730715, + 32884.724879499619, + 32902.681182562686, + 32920.639935827494, + 32938.601138625643, + 32956.56479028918, + 32974.530890150607, + 32992.499437542894, + 33010.470431799447, + 33028.443872254145, + 33046.419758241311, + 33064.39808909571, + 33082.378864152583, + 33100.36208274759, + 33118.347744216881, + 33136.335847897026, + 33154.326393125062, + 33172.31937923847, + 33190.314805575174, + 33208.312671473555, + 33226.312976272442, + 33244.315719311111, + 33262.320899929284, + 33280.328517467125, + 33298.33857126526, + 33316.351060664747, + 33334.365985007091, + 33352.383343634239, + 33370.403135888591, + 33388.42536111299, + 33406.450018650721, + 33424.477107845501, + 33442.506628041512, + 33460.53857858335, + 33478.572958816083, + 33496.609768085189, + 33514.649005736617, + 33532.690671116739, + 33550.734763572356, + 33568.781282450735, + 33586.830227099563, + 33604.881596866973, + 33622.935391101528, + 33640.991609152239, + 33659.050250368542, + 33677.111314100322, + 33695.174799697881, + 33713.240706511984, + 33731.309033893805, + 33749.37978119497, + 33767.452947767531, + 33785.528532963974, + 33803.606536137209, + 33821.686956640602, + 33839.769793827938, + 33857.855047053425, + 33875.942715671707, + 33894.032799037872, + 33912.125296507431, + 33930.220207436316, + 33948.317531180888, + 33966.417267097961, + 33984.519414544746, + 34002.623972878901, + 34020.730941458511, + 34038.840319642077, + 34056.952106788536, + 34075.066302257255, + 34093.182905408015, + 34111.301915601027, + 34129.42333219693, + 34147.547154556785, + 34165.673382042078, + 34183.80201401472, + 34201.933049837033, + 34220.06648887178, + 34238.202330482141, + 34256.340574031703, + 34274.481218884495, + 34292.624264404949, + 34310.769709957938, + 34328.91755490873, + 34347.067798623029, + 34365.220440466954, + 34383.375479807051, + 34401.532916010263, + 34419.692748443973, + 34437.854976475966, + 34456.01959947445, + 34474.18661680806, + 34492.356027845817, + 34510.527831957188, + 34528.702028512052, + 34546.878616880676, + 34565.05759643377, + 34583.238966542449, + 34601.422726578232, + 34619.608875913065, + 34637.797413919296, + 34655.988339969692, + 34674.181653437423, + 34692.37735369608, + 34710.575440119668, + 34728.775912082579, + 34746.978768959649, + 34765.184010126082, + 34783.391634957537, + 34801.60164283005, + 34819.814033120063, + 34838.028805204456, + 34856.24595846048, + 34874.465492265823, + 34892.687405998557, + 34910.911699037177, + 34929.138370760564, + 34947.367420548027, + 34965.598847779271, + 34983.832651834389, + 35002.068832093908, + 35020.307387938738, + 35038.548318750189, + 35056.79162390998, + 35075.03730280025, + 35093.285354803513, + 35111.535779302685, + 35129.788575681116, + 35148.043743322516, + 35166.301281611013, + 35184.561189931141, + 35202.823467667826, + 35221.088114206388, + 35239.355128932555, + 35257.624511232447, + 35275.896260492584, + 35294.170376099886, + 35312.446857441668, + 35330.725703905628, + 35349.006914879887, + 35367.290489752944, + 35385.576427913686, + 35403.864728751418, + 35422.155391655811, + 35440.448416016967, + 35458.743801225341, + 35477.041546671804, + 35495.341651747622, + 35513.644115844436, + 35531.948938354304, + 35550.256118669655, + 35568.565656183309, + 35586.877550288496, + 35605.191800378816, + 35623.508405848268, + 35641.827366091238, + 35660.148680502505, + 35678.472348477233, + 35696.798369410979, + 35715.126742699678, + 35733.457467739659, + 35751.790543927644, + 35770.125970660738, + 35788.46374733642, + 35806.803873352568, + 35825.146348107453, + 35843.49117099971, + 35861.838341428367, + 35880.187858792851, + 35898.539722492955, + 35916.893931928862, + 35935.250486501129, + 35953.609385610718, + 35971.970628658957, + 35990.334215047558, + 36008.700144178612, + 36027.068415454596, + 36045.439028278372, + 36063.811982053165, + 36082.187276182609, + 36100.564910070694, + 36118.944883121789, + 36137.327194740654, + 36155.711844332429, + 36174.098831302617, + 36192.488155057115, + 36210.87981500219, + 36229.273810544473, + 36247.670141091003, + 36266.068806049167, + 36284.469804826738, + 36302.873136831862, + 36321.278801473069, + 36339.686798159251, + 36358.097126299683, + 36376.509785304013, + 36394.924774582258, + 36413.342093544816, + 36431.761741602444, + 36450.183718166292, + 36468.608022647859, + 36487.034654459028, + 36505.463613012063, + 36523.894897719583, + 36542.328507994578, + 36560.764443250409, + 36579.202702900831, + 36597.643286359926, + 36616.086193042182, + 36634.531422362437, + 36652.978973735895, + 36671.428846578143, + 36689.881040305125, + 36708.335554333149, + 36726.792388078902, + 36745.251540959427, + 36763.713012392138, + 36782.176801794812, + 36800.642908585593, + 36819.111332182983, + 36837.582072005869, + 36856.055127473483, + 36874.530498005421, + 36893.008183021651, + 36911.488181942506, + 36929.970494188674, + 36948.455119181206, + 36966.942056341519, + 36985.431305091392, + 37003.922864852961, + 37022.416735048733, + 37040.912915101559, + 37059.411404434657, + 37077.91220247162, + 37096.415308636388, + 37114.920722353243, + 37133.428443046862, + 37151.938470142253, + 37170.450803064785, + 37188.965441240209, + 37207.482384094597, + 37226.001631054402, + 37244.523181546429, + 37263.047034997842, + 37281.573190836149, + 37300.101648489224, + 37318.632407385296, + 37337.165466952945, + 37355.700826621112, + 37374.238485819085, + 37392.778443976509, + 37411.320700523385, + 37429.865254890057, + 37448.412106507232, + 37466.961254805974, + 37485.512699217681, + 37504.066439174116, + 37522.622474107404, + 37541.180803449992, + 37559.741426634704, + 37578.304343094693, + 37596.869552263488, + 37615.43705357494, + 37634.006846463279, + 37652.578930363044, + 37671.153304709165, + 37689.729968936896, + 37708.308922481847, + 37726.890164779965, + 37745.473695267559, + 37764.059513381275, + 37782.647618558112, + 37801.238010235415, + 37819.830687850859, + 37838.425650842495, + 37857.022898648691, + 37875.622430708172, + 37894.224246460013, + 37912.828345343616, + 37931.434726798747, + 37950.043390265506, + 37968.654335184328, + 37987.267560995999, + 38005.883067141665, + 38024.500853062775, + 38043.120918201159, + 38061.743261998963, + 38080.367883898682, + 38098.994783343158, + 38117.623959775563, + 38136.255412639417, + 38154.889141378575, + 38173.525145437234, + 38192.163424259939, + 38210.803977291551, + 38229.446803977284, + 38248.091903762703, + 38266.739276093685, + 38285.388920416466, + 38304.040836177606, + 38322.695022824002, + 38341.351479802899, + 38360.010206561863, + 38378.671202548816, + 38397.334467211993, + 38415.999999999978, + 38434.667800361683, + 38453.33786774637, + 38472.010201603611, + 38490.684801383337, + 38509.361666535784, + 38528.040796511552, + 38546.722190761553, + 38565.405848737035, + 38584.091769889594, + 38602.779953671132, + 38621.470399533908, + 38640.163106930493, + 38658.858075313794, + 38677.555304137059, + 38696.254792853862, + 38714.956540918094, + 38733.660547783991, + 38752.366812906112, + 38771.075335739348, + 38789.78611573892, + 38808.499152360368, + 38827.214445059573, + 38845.931993292739, + 38864.651796516388, + 38883.373854187383, + 38902.098165762916, + 38920.824730700486, + 38939.553548457938, + 38958.284618493431, + 38977.017940265461, + 38995.753513232834, + 39014.491336854699, + 39033.231410590517, + 39051.973733900079, + 39070.718306243485, + 39089.465127081188, + 39108.214195873945, + 39126.965512082832, + 39145.719075169261, + 39164.474884594965, + 39183.232939821988, + 39201.99324031271, + 39220.755785529815, + 39239.52057493633, + 39258.287607995589, + 39277.056884171245, + 39295.828402927284, + 39314.602163728006, + 39333.378166038019, + 39352.15640932227, + 39370.936893046004, + 39389.719616674811, + 39408.504579674584, + 39427.291781511522, + 39446.081221652174, + 39464.872899563372, + 39483.666814712291, + 39502.462966566411, + 39521.261354593538, + 39540.06197826178, + 39558.864837039568, + 39577.669930395656, + 39596.47725779911, + 39615.286818719302, + 39634.098612625923, + 39652.912638988993, + 39671.728897278823, + 39690.547386966064, + 39709.368107521652, + 39728.191058416858, + 39747.016239123259, + 39765.84364911275, + 39784.673287857528, + 39803.505154830105, + 39822.339249503319, + 39841.175571350293, + 39860.014119844491, + 39878.854894459677, + 39897.697894669909, + 39916.54311994958, + 39935.390569773372, + 39954.240243616303, + 39973.092140953675, + 39991.946261261117, + 40010.802604014549, + 40029.661168690225, + 40048.521954764678, + 40067.384961714779, + 40086.250189017679, + 40105.117636150855, + 40123.98730259209, + 40142.859187819471, + 40161.733291311379, + 40180.609612546526, + 40199.488151003912, + 40218.368906162854, + 40237.25187750296, + 40256.137064504153, + 40275.024466646668, + 40293.914083411029, + 40312.805914278084, + 40331.699958728961, + 40350.596216245103, + 40369.494686308273, + 40388.39536840051, + 40407.298262004173, + 40426.20336660192, + 40445.110681676706, + 40464.020206711793, + 40482.931941190756, + 40501.845884597446, + 40520.762036416032, + 40539.680396130985, + 40558.600963227072, + 40577.523737189367, + 40596.448717503234, + 40615.375903654342, + 40634.305295128659, + 40653.236891412453, + 40672.170691992294, + 40691.106696355047, + 40710.044903987873, + 40728.985314378238, + 40747.927927013901, + 40766.872741382918, + 40785.819756973651, + 40804.768973274746, + 40823.720389775161, + 40842.674005964131, + 40861.629821331211, + 40880.587835366234, + 40899.548047559321, + 40918.510457400931, + 40937.475064381761, + 40956.441867992849, + 40975.410867725499, + 40994.382063071331, + 41013.355453522236, + 41032.331038570417, + 41051.308817708363, + 41070.288790428858, + 41089.270956224987, + 41108.255314590111, + 41127.241865017888, + 41146.23060700229, + 41165.221540037543, + 41184.214663618193, + 41203.209977239079, + 41222.207480395307, + 41241.207172582297, + 41260.209053295752, + 41279.213122031659, + 41298.219378286303, + 41317.227821556255, + 41336.23845133838, + 41355.251267129832, + 41374.266268428037, + 41393.283454730743, + 41412.302825535953, + 41431.324380341983, + 41450.348118647416, + 41469.374039951144, + 41488.402143752326, + 41507.432429550427, + 41526.464896845187, + 41545.499545136627, + 41564.536373925075, + 41583.575382711126, + 41602.616570995662, + 41621.659938279874, + 41640.705484065205, + 41659.753207853406, + 41678.803109146495, + 41697.855187446803, + 41716.909442256911, + 41735.965873079709, + 41755.02447941836, + 41774.085260776315, + 41793.148216657297, + 41812.213346565331, + 41831.280650004708, + 41850.350126480014, + 41869.421775496106, + 41888.495596558132, + 41907.571589171515, + 41926.649752841957, + 41945.730087075463, + 41964.812591378286, + 41983.897265256979, + 42002.984108218378, + 42022.073119769593, + 42041.164299418015, + 42060.257646671307, + 42079.353161037419, + 42098.450842024591, + 42117.550689141324, + 42136.652701896404, + 42155.756879798893, + 42174.863222358137, + 42193.971729083758, + 42213.082399485655, + 42232.195233074002, + 42251.310229359246, + 42270.427387852127, + 42289.546708063644, + 42308.668189505079, + 42327.791831687995, + 42346.917634124227, + 42366.045596325886, + 42385.175717805352, + 42404.307998075295, + 42423.442436648642, + 42442.579033038608, + 42461.717786758672, + 42480.858697322597, + 42500.001764244422, + 42519.146987038446, + 42538.294365219248, + 42557.443898301688, + 42576.595585800882, + 42595.749427232236, + 42614.90542211142, + 42634.063569954378, + 42653.223870277317, + 42672.386322596729, + 42691.55092642938, + 42710.717681292292, + 42729.886586702756, + 42749.057642178363, + 42768.23084723694, + 42787.406201396603, + 42806.58370417574, + 42825.76335509299, + 42844.945153667286, + 42864.129099417805, + 42883.315191864014, + 42902.503430525649, + 42921.693814922692, + 42940.88634457541, + 42960.081019004348, + 42979.277837730297, + 42998.476800274322, + 43017.677906157769, + 43036.881154902228, + 43056.086546029583, + 43075.294079061961, + 43094.503753521763, + 43113.715568931671, + 43132.929524814601, + 43152.145620693766, + 43171.363856092619, + 43190.584230534907, + 43209.806743544621, + 43229.031394646016, + 43248.258183363621, + 43267.487109222224, + 43286.718171746885, + 43305.951370462906, + 43325.186704895881, + 43344.42417457165, + 43363.663779016322, + 43382.905517756262, + 43402.149390318104, + 43421.395396228749, + 43440.643535015348, + 43459.89380620532, + 43479.146209326354, + 43498.400743906379, + 43517.657409473606, + 43536.916205556496, + 43556.177131683784, + 43575.44018738444, + 43594.705372187724, + 43613.972685623135, + 43633.242127220445, + 43652.513696509668, + 43671.787393021099, + 43691.063216285271, + 43710.341165833001, + 43729.621241195346, + 43748.903441903625, + 43768.187767489413, + 43787.474217484552, + 43806.762791421126, + 43826.053488831501, + 43845.346309248278, + 43864.641252204325, + 43883.938317232765, + 43903.237503866971, + 43922.538811640596, + 43941.842240087513, + 43961.147788741881, + 43980.455457138101, + 43999.765244810835, + 44019.077151295001, + 44038.391176125755, + 44057.70731883854, + 44077.02557896902, + 44096.345956053141, + 44115.668449627083, + 44134.993059227287, + 44154.319784390456, + 44173.648624653535, + 44192.979579553728, + 44212.312648628489, + 44231.647831415532, + 44250.985127452805, + 44270.324536278538, + 44289.666057431183, + 44309.009690449464, + 44328.355434872348, + 44347.703290239064, + 44367.053256089079, + 44386.405331962109, + 44405.759517398139, + 44425.115811937387, + 44444.474215120332, + 44463.834726487694, + 44483.197345580462, + 44502.562071939843, + 44521.928905107328, + 44541.297844624634, + 44560.668890033732, + 44580.042040876848, + 44599.417296696454, + 44618.794657035272, + 44638.174121436256, + 44657.555689442641, + 44676.939360597877, + 44696.325134445673, + 44715.713010530002, + 44735.102988395054, + 44754.495067585296, + 44773.88924764542, + 44793.285528120374, + 44812.683908555344, + 44832.084388495779, + 44851.486967487363, + 44870.891645076015, + 44890.298420807922, + 44909.707294229491, + 44929.118264887409, + 44948.531332328566, + 44967.946496100136, + 44987.363755749502, + 45006.783110824319, + 45026.204560872473, + 45045.628105442098, + 45065.053744081561, + 45084.48147633949, + 45103.911301764747, + 45123.343219906426, + 45142.777230313885, + 45162.21333253671, + 45181.651526124733, + 45201.091810628037, + 45220.534185596924, + 45239.978650581965, + 45259.425205133957, + 45278.873848803938, + 45298.324581143192, + 45317.777401703235, + 45337.232310035848, + 45356.68930569302, + 45376.148388226997, + 45395.60955719027, + 45415.072812135557, + 45434.538152615823, + 45454.005578184282, + 45473.475088394356, + 45492.946682799746, + 45512.420360954362, + 45531.896122412363, + 45551.373966728155, + 45570.853893456362, + 45590.33590215187, + 45609.819992369776, + 45629.306163665438, + 45648.794415594442, + 45668.284747712612, + 45687.777159576006, + 45707.27165074092, + 45726.768220763894, + 45746.266869201696, + 45765.767595611323, + 45785.270399550034, + 45804.775280575297, + 45824.282238244828, + 45843.79127211657, + 45863.302381748719, + 45882.815566699683, + 45902.33082652813, + 45921.848160792935, + 45941.367569053225, + 45960.889050868354, + 45980.41260579793, + 45999.938233401757, + 46019.465933239902, + 46038.995704872657, + 46058.527547860547, + 46078.06146176433, + 46097.597446144995, + 46117.135500563774, + 46136.675624582109, + 46156.217817761702, + 46175.762079664462, + 46195.308409852543, + 46214.856807888333, + 46234.407273334444, + 46253.959805753715, + 46273.51440470924, + 46293.071069764315, + 46312.629800482478, + 46332.190596427499, + 46351.753457163381, + 46371.318382254351, + 46390.885371264863, + 46410.45442375962, + 46430.025539303526, + 46449.598717461733, + 46469.17395779962, + 46488.751259882782, + 46508.33062327707, + 46527.912047548532, + 46547.495532263471, + 46567.081076988397, + 46586.668681290059, + 46606.258344735434, + 46625.850066891719, + 46645.443847326351, + 46665.039685606986, + 46684.637581301497, + 46704.237533978005, + 46723.839543204842, + 46743.443608550573, + 46763.049729583989, + 46782.657905874104, + 46802.268136990162, + 46821.880422501628, + 46841.494761978196, + 46861.111154989776, + 46880.729601106526, + 46900.350099898795, + 46919.97265093719, + 46939.597253792526, + 46959.223908035841, + 46978.852613238392, + 46998.483368971691, + 47018.11617480743, + 47037.751030317551, + 47057.387935074221, + 47077.026888649809, + 47096.66789061694, + 47116.310940548428, + 47135.956038017328, + 47155.603182596918, + 47175.252373860698, + 47194.903611382375, + 47214.556894735892, + 47234.212223495422, + 47253.869597235338, + 47273.52901553025, + 47293.19047795498, + 47312.853984084577, + 47332.519533494306, + 47352.187125759658, + 47371.856760456343, + 47391.528437160297, + 47411.202155447652, + 47430.877914894787, + 47450.555715078299, + 47470.235555574982, + 47489.917435961863, + 47509.601355816201, + 47529.287314715453, + 47548.975312237308, + 47568.665347959672, + 47588.357421460656, + 47608.051532318605, + 47627.747680112072, + 47647.445864419846, + 47667.14608482091, + 47686.848340894474, + 47706.552632219973, + 47726.258958377046, + 47745.967318945557, + 47765.677713505589, + 47785.390141637428, + 47805.104602921601, + 47824.821096938824, + 47844.539623270044, + 47864.260181496429, + 47883.982771199349, + 47903.707391960394, + 47923.434043361369, + 47943.162724984308, + 47962.893436411439, + 47982.626177225218, + 48002.36094700831, + 48022.097745343599, + 48041.836571814172, + 48061.57742600335, + 48081.32030749465, + 48101.065215871815, + 48120.81215071879, + 48140.56111161974, + 48160.312098159047, + 48180.065109921306, + 48199.820146491307, + 48219.577207454073, + 48239.336292394844, + 48259.097400899045, + 48278.860532552339, + 48298.625686940592, + 48318.392863649875, + 48338.162062266485, + 48357.933282376915, + 48377.706523567889, + 48397.481785426316, + 48417.259067539344, + 48437.038369494308, + 48456.819690878765, + 48476.603031280487, + 48496.388390287451, + 48516.175767487839, + 48535.965162470042, + 48555.756574822684, + 48575.550004134566, + 48595.345449994718, + 48615.142911992378, + 48634.942389716991, + 48654.743882758201, + 48674.547390705877, + 48694.352913150084, + 48714.160449681112, + 48733.969999889443, + 48753.781563365759, + 48773.595139700978, + 48793.410728486211, + 48813.228329312769, + 48833.047941772187, + 48852.869565456189, + 48872.693199956717, + 48892.518844865925, + 48912.346499776155, + 48932.176164279976, + 48952.007837970152, + 48971.841520439666, + 48991.677211281676, + 49011.514910089587, + 49031.354616456978, + 49051.196329977654, + 49071.04005024561, + 49090.885776855059, + 49110.733509400408, + 49130.583247476279, + 49150.434990677488, + 49170.288738599062, + 49190.144490836232, + 49210.002246984441, + 49229.86200663932, + 49249.723769396718, + 49269.587534852675, + 49289.453302603448, + 49309.32107224549, + 49329.190843375451, + 49349.062615590192, + 49368.936388486785, + 49388.812161662492, + 49408.689934714785, + 49428.569707241324, + 49448.45147883999, + 49468.335249108866, + 49488.22101764621, + 49508.108784050521, + 49527.99854792047, + 49547.890308854934, + 49567.784066453009, + 49587.679820313977, + 49607.57757003732, + 49627.477315222721, + 49647.379055470075, + 49667.28279037946, + 49687.188519551179, + 49707.096242585707, + 49727.005959083741, + 49746.917668646165, + 49766.831370874068, + 49786.747065368734, + 49806.66475173166, + 49826.584429564515, + 49846.506098469203, + 49866.429758047794, + 49886.355407902578, + 49906.283047636032, + 49926.212676850846, + 49946.144295149883, + 49966.077902136225, + 49986.013497413151, + 50005.951080584135, + 50025.890651252834, + 50045.832209023123, + 50065.775753499074, + 50085.721284284933, + 50105.668800985164, + 50125.618303204428, + 50145.569790547575, + 50165.523262619652, + 50185.478719025901, + 50205.436159371769, + 50225.395583262893, + 50245.356990305103, + 50265.320380104429, + 50285.285752267104, + 50305.253106399534, + 50325.222442108337, + 50345.193759000336, + 50365.16705668252, + 50385.142334762102, + 50405.119592846473, + 50425.098830543218, + 50445.080047460127, + 50465.063243205179, + 50485.048417386541, + 50505.035569612577, + 50525.024699491856, + 50545.015806633128, + 50565.008890645338, + 50585.003951137631, + 50605.00098771933, + 50624.999999999971, + 50645.000987589265, + 50665.003950097132, + 50685.008887133677, + 50705.015798309192, + 50725.024683234165, + 50745.035541519283, + 50765.048372775411, + 50785.063176613621, + 50805.079952645159, + 50825.098700481489, + 50845.119419734241, + 50865.142110015244, + 50885.166770936521, + 50905.193402110279, + 50925.222003148934, + 50945.252573665071, + 50965.285113271471, + 50985.319621581119, + 51005.356098207172, + 51025.394542762981, + 51045.434954862096, + 51065.477334118244, + 51085.521680145357, + 51105.567992557546, + 51125.616270969113, + 51145.66651499454, + 51165.718724248516, + 51185.772898345916, + 51205.829036901778, + 51225.887139531362, + 51245.947205850105, + 51266.009235473619, + 51286.073228017718, + 51306.139183098399, + 51326.207100331856, + 51346.276979334456, + 51366.348819722756, + 51386.42262111351, + 51406.498383123653, + 51426.57610537031, + 51446.655787470787, + 51466.737429042587, + 51486.82102970338, + 51506.906589071048, + 51526.994106763632, + 51547.083582399391, + 51567.175015596738, + 51587.268405974297, + 51607.363753150858, + 51627.461056745415, + 51647.56031637713, + 51667.661531665362, + 51687.764702229651, + 51707.869827689727, + 51727.976907665499, + 51748.085941777055, + 51768.196929644677, + 51788.309870888836, + 51808.42476513017, + 51828.541611989524, + 51848.660411087905, + 51868.781162046515, + 51888.90386448674, + 51909.028518030143, + 51929.155122298485, + 51949.283676913685, + 51969.414181497872, + 51989.546635673345, + 52009.681039062583, + 52029.817391288263, + 52049.955691973213, + 52070.095940740481, + 52090.238137213273, + 52110.382281014987, + 52130.5283717692, + 52150.676409099666, + 52170.826392630333, + 52190.97832198532, + 52211.132196788931, + 52231.288016665654, + 52251.445781240145, + 52271.60549013727, + 52291.76714298204, + 52311.930739399664, + 52332.096279015546, + 52352.263761455244, + 52372.433186344519, + 52392.604553309284, + 52412.777861975665, + 52432.953111969946, + 52453.130302918595, + 52473.309434448267, + 52493.490506185793, + 52513.67351775818, + 52533.858468792605, + 52554.045358916446, + 52574.234187757254, + 52594.42495494274, + 52614.617660100812, + 52634.812302859558, + 52655.008882847229, + 52675.20739969227, + 52695.407853023295, + 52715.610242469098, + 52735.814567658657, + 52756.02082822111, + 52776.229023785803, + 52796.439153982225, + 52816.651218440056, + 52836.865216789171, + 52857.081148659599, + 52877.29901368155, + 52897.518811485425, + 52917.740541701773, + 52937.964203961354, + 52958.18979789508, + 52978.417323134046, + 52998.646779309529, + 53018.878166052978, + 53039.111482996006, + 53059.346729770419, + 53079.583906008193, + 53099.823011341483, + 53120.0640454026, + 53140.307007824063, + 53160.551898238533, + 53180.79871627887, + 53201.047461578091, + 53221.2981337694, + 53241.550732486176, + 53261.805257361964, + 53282.061708030487, + 53302.32008412564, + 53322.580385281493, + 53342.842611132299, + 53363.106761312469, + 53383.372835456597, + 53403.640833199453, + 53423.910754175973, + 53444.18259802126, + 53464.456364370613, + 53484.732052859479, + 53505.009663123499, + 53525.289194798468, + 53545.570647520362, + 53565.854020925333, + 53586.139314649699, + 53606.426528329954, + 53626.715661602764, + 53647.006714104959, + 53667.299685473547, + 53687.59457534572, + 53707.891383358816, + 53728.190109150361, + 53748.490752358055, + 53768.793312619753, + 53789.09778957349, + 53809.404182857485, + 53829.712492110106, + 53850.022716969899, + 53870.334857075584, + 53890.648912066055, + 53910.964881580367, + 53931.28276525774, + 53951.602562737586, + 53971.924273659461, + 53992.24789766311, + 54012.57343438844, + 54032.90088347553, + 54053.23024456462, + 54073.561517296133, + 54093.894701310644, + 54114.22979624891, + 54134.566801751855, + 54154.90571746057, + 54175.246543016314, + 54195.589278060506, + 54215.933922234755, + 54236.280475180814, + 54256.628936540626, + 54276.97930595628, + 54297.331583070045, + 54317.685767524359, + 54338.041858961828, + 54358.399857025215, + 54378.759761357462, + 54399.121571601667, + 54419.485287401105, + 54439.850908399218, + 54460.218434239614, + 54480.587864566056, + 54500.95919902248, + 54521.332437252997, + 54541.707578901878, + 54562.084623613555, + 54582.46357103264, + 54602.844420803893, + 54623.227172572246, + 54643.611825982807, + 54663.998380680838, + 54684.386836311773, + 54704.777192521207, + 54725.169448954897, + 54745.563605258772, + 54765.959661078923, + 54786.357616061614, + 54806.757469853255, + 54827.159222100439, + 54847.562872449904, + 54867.968420548583, + 54888.375866043534, + 54908.785208582012, + 54929.196447811417, + 54949.609583379322, + 54970.024614933463, + 54990.441542121727, + 55010.86036459219, + 55031.28108199306, + 55051.703693972733, + 55072.128200179759, + 55092.554600262847, + 55112.982893870874, + 55133.413080652877, + 55153.845160258061, + 55174.279132335789, + 55194.714996535586, + 55215.152752507143, + 55235.592399900306, + 55256.033938365079, + 55276.477367551655, + 55296.92268711036, + 55317.369896691685, + 55337.818995946305, + 55358.269984525024, + 55378.72286207883, + 55399.177628258869, + 55419.634282716441, + 55440.092825103013, + 55460.553255070205, + 55481.015572269804, + 55501.479776353764, + 55521.945866974187, + 55542.413843783339, + 55562.883706433655, + 55583.355454577715, + 55603.82908786826, + 55624.304605958219, + 55644.782008500639, + 55665.261295148754, + 55685.742465555952, + 55706.225519375774, + 55726.710456261928, + 55747.197275868275, + 55767.685977848843, + 55788.176561857814, + 55808.669027549528, + 55829.163374578478, + 55849.659602599328, + 55870.157711266889, + 55890.657700236145, + 55911.159569162221, + 55931.663317700411, + 55952.168945506164, + 55972.676452235086, + 55993.185837542944, + 56013.697101085651, + 56034.210242519301, + 56054.72526150012, + 56075.242157684508, + 56095.760930729011, + 56116.281580290342, + 56136.804106025367, + 56157.328507591104, + 56177.85478464474, + 56198.382936843598, + 56218.912963845185, + 56239.444865307138, + 56259.978640887268, + 56280.514290243525, + 56301.051813034042, + 56321.591208917082, + 56342.13247755108, + 56362.675618594607, + 56383.220631706419, + 56403.767516545398, + 56424.316272770608, + 56444.866900041241, + 56465.419398016667, + 56485.973766356394, + 56506.530004720102, + 56527.088112767611, + 56547.648090158902, + 56568.209936554107, + 56588.773651613519, + 56609.339234997584, + 56629.9066863669, + 56650.47600538221, + 56671.04719170442, + 56691.620244994599, + 56712.195164913959, + 56732.771951123868, + 56753.350603285835, + 56773.931121061541, + 56794.513504112823, + 56815.097752101647, + 56835.683864690152, + 56856.271841540627, + 56876.86168231551, + 56897.453386677393, + 56918.046954289028, + 56938.642384813298, + 56959.239677913261, + 56979.838833252121, + 57000.439850493225, + 57021.04272930009, + 57041.647469336371, + 57062.254070265873, + 57082.862531752558, + 57103.472853460553, + 57124.085035054108, + 57144.699076197649, + 57165.314976555739, + 57185.932735793103, + 57206.552353574611, + 57227.173829565276, + 57247.797163430281, + 57268.42235483494, + 57289.049403444733, + 57309.678308925286, + 57330.30907094237, + 57350.941689161911, + 57371.576163249985, + 57392.212492872815, + 57412.850677696784, + 57433.490717388406, + 57454.132611614368, + 57474.776360041491, + 57495.421962336746, + 57516.069418167266, + 57536.718727200314, + 57557.36988910332, + 57578.022903543861, + 57598.677770189643, + 57619.334488708548, + 57639.993058768589, + 57660.653480037938, + 57681.315752184906, + 57701.979874877965, + 57722.64584778573, + 57743.31367057695, + 57763.983342920546, + 57784.654864485572, + 57805.328234941233, + 57826.003453956881, + 57846.680521202026, + 57867.359436346305, + 57888.040199059527, + 57908.722809011633, + 57929.407265872709, + 57950.093569313001, + 57970.781719002895, + 57991.471714612911, + 58012.16355581375, + 58032.857242276223, + 58053.552773671312, + 58074.25014967013, + 58094.949369943948, + 58115.650434164185, + 58136.353342002389, + 58157.058093130276, + 58177.764687219693, + 58198.47312394264, + 58219.183402971255, + 58239.895523977837, + 58260.609486634821, + 58281.325290614775, + 58302.042935590434, + 58322.762421234678, + 58343.483747220511, + 58364.206913221096, + 58384.931918909751, + 58405.658763959924, + 58426.3874480452, + 58447.117970839339, + 58467.85033201622, + 58488.584531249864, + 58509.320568214462, + 58530.058442584334, + 58550.798154033931, + 58571.539702237875, + 58592.283086870906, + 58613.028307607929, + 58633.775364123983, + 58654.52425609425, + 58675.274983194053, + 58696.027545098877, + 58716.781941484325, + 58737.538172026158, + 58758.296236400274, + 58779.056134282728, + 58799.817865349694, + 58820.581429277503, + 58841.346825742643, + 58862.114054421712, + 58882.883114991484, + 58903.654007128847, + 58924.426730510851, + 58945.201284814684, + 58965.977669717664, + 58986.755884897269, + 59007.535930031117, + 59028.317804796949, + 59049.101508872664, + 59069.887041936301, + 59090.674403666046, + 59111.463593740213, + 59132.254611837263, + 59153.047457635803, + 59173.84213081457, + 59194.638631052461, + 59215.436958028506, + 59236.237111421855, + 59257.039090911829, + 59277.842896177877, + 59298.648526899589, + 59319.455982756685, + 59340.26526342905, + 59361.076368596696, + 59381.889297939757, + 59402.704051138542, + 59423.520627873484, + 59444.339027825139, + 59465.159250674224, + 59485.9812961016, + 59506.805163788253, + 59527.630853415307, + 59548.458364664046, + 59569.287697215863, + 59590.118850752311, + 59610.951824955089, + 59631.786619506012, + 59652.623234087048, + 59673.461668380311, + 59694.301922068029, + 59715.143994832593, + 59735.987886356525, + 59756.833596322482, + 59777.681124413255, + 59798.530470311794, + 59819.381633701159, + 59840.234614264569, + 59861.089411685381, + 59881.94602564707, + 59902.804455833269, + 59923.664701927737, + 59944.526763614384, + 59965.390640577243, + 59986.256332500488, + 60007.123839068438, + 60027.993159965539, + 60048.864294876381, + 60069.737243485688, + 60090.612005478324, + 60111.488580539284, + 60132.366968353708, + 60153.247168606867, + 60174.129180984164, + 60195.013005171153, + 60215.898640853513, + 60236.786087717061, + 60257.675345447751, + 60278.566413731671, + 60299.459292255044, + 60320.353980704247, + 60341.25047876576, + 60362.148786126229, + 60383.048902472423, + 60403.950827491237, + 60424.854560869717, + 60445.76010229504, + 60466.667451454516, + 60487.57660803559, + 60508.487571725847, + 60529.400342212997, + 60550.314919184893, + 60571.231302329521, + 60592.149491335003, + 60613.069485889588, + 60633.991285681674, + 60654.914890399785, + 60675.840299732568, + 60696.767513368832, + 60717.696530997484, + 60738.627352307602, + 60759.55997698837, + 60780.494404729128, + 60801.430635219323, + 60822.368668148556, + 60843.308503206565, + 60864.250140083204, + 60885.193578468468, + 60906.138818052495, + 60927.085858525541, + 60948.034699578006, + 60968.985340900421, + 60989.937782183442, + 61010.892023117864, + 61031.848063394616, + 61052.805902704764, + 61073.765540739492, + 61094.726977190134, + 61115.690211748137, + 61136.655244105103, + 61157.622073952742, + 61178.590700982917, + 61199.561124887616, + 61220.533345358948, + 61241.507362089171, + 61262.483174770663, + 61283.460783095943, + 61304.440186757645, + 61325.421385448557, + 61346.404378861582, + 61367.389166689762, + 61388.375748626262, + 61409.364124364387, + 61430.354293597571, + 61451.346256019373, + 61472.340011323497, + 61493.335559203762, + 61514.332899354122, + 61535.332031468672, + 61556.332955241618, + 61577.335670367313, + 61598.340176540238, + 61619.346473454993, + 61640.354560806329, + 61661.3644382891, + 61682.376105598312, + 61703.389562429089, + 61724.404808476691, + 61745.42184343651, + 61766.440667004063, + 61787.461278874987, + 61808.483678745069, + 61829.507866310203, + 61850.533841266435, + 61871.561603309929, + 61892.591152136971, + 61913.622487443987, + 61934.655608927525, + 61955.690516284267, + 61976.727209211022, + 61997.765687404724, + 62018.805950562448, + 62039.847998381381, + 62060.891830558845, + 62081.93744679229, + 62102.984846779298, + 62124.034030217575, + 62145.084996804966, + 62166.137746239416, + 62187.19227821903, + 62208.248592442025, + 62229.306688606739, + 62250.366566411656, + 62271.428225555377, + 62292.491665736627, + 62313.556886654267, + 62334.623888007271, + 62355.692669494762, + 62376.763230815974, + 62397.835571670272, + 62418.909691757144, + 62439.98559077621, + 62461.063268427228, + 62482.142724410049, + 62503.223958424685, + 62524.306970171267, + 62545.39175935003, + 62566.478325661366, + 62587.566668805768, + 62608.656788483881, + 62629.748684396451, + 62650.842356244357, + 62671.937803728622, + 62693.035026550366, + 62714.134024410858, + 62735.234797011479, + 62756.337344053733, + 62777.441665239276, + 62798.547760269852, + 62819.655628847358, + 62840.765270673801, + 62861.876685451323, + 62882.989872882186, + 62904.104832668774, + 62925.221564513602, + 62946.340068119309, + 62967.460343188657, + 62988.582389424526, + 63009.70620652994, + 63030.831794208025, + 63051.959152162039, + 63073.08828009537, + 63094.219177711529, + 63115.351844714154, + 63136.486280806988, + 63157.622485693922, + 63178.760459078956, + 63199.900200666219, + 63221.041710159967, + 63242.184987264569, + 63263.330031684534, + 63284.476843124474, + 63305.625421289144, + 63326.775765883409, + 63347.927876612259, + 63369.081753180813, + 63390.237395294316, + 63411.39480265812, + 63432.553974977716, + 63453.714911958712, + 63474.877613306839, + 63496.042078727944, + 63517.208307927998, + 63538.376300613119, + 63559.546056489504, + 63580.717575263516, + 63601.890856641607, + 63623.065900330374, + 63644.242706036515, + 63665.421273466869, + 63686.601602328381, + 63707.783692328136, + 63728.967543173334, + 63750.153154571279, + 63771.340526229418, + 63792.529657855317, + 63813.720549156649, + 63834.913199841227, + 63856.107609616978, + 63877.303778191941, + 63898.501705274284, + 63919.7013905723, + 63940.902833794404, + 63962.106034649114, + 63983.310992845094, + 64004.517708091109, + 64025.726180096048, + 64046.936408568938, + 64068.1483932189, + 64089.362133755196, + 64110.577629887193, + 64131.794881324393, + 64153.013887776404, + 64174.234648952966, + 64195.457164563937, + 64216.681434319289, + 64237.907457929112, + 64259.135235103626, + 64280.36476555316, + 64301.596048988169, + 64322.829085119236, + 64344.06387365704, + 64365.300414312398, + 64386.538706796251, + 64407.778750819634, + 64429.020546093721, + 64450.26409232981, + 64471.509389239291, + 64492.756436533709, + 64514.005233924705, + 64535.255781124033, + 64556.50807784358, + 64577.762123795357, + 64599.017918691468, + 64620.275462244172, + 64641.534754165805, + 64662.795794168844, + 64684.058581965895, + 64705.323117269661, + 64726.589399792974, + 64747.857429248776, + 64769.127205350138, + 64790.398727810236, + 64811.671996342375, + 64832.947010659969, + 64854.223770476558, + 64875.502275505794, + 64896.782525461451, + 64918.064520057414, + 64939.348259007682, + 64960.633742026388, + 64981.920968827762, + 65003.209939126165, + 65024.500652636067, + 65045.793109072067, + 65067.087308148861, + 65088.383249581282, + 65109.680933084259, + 65130.980358372864, + 65152.28152516226, + 65173.584433167736, + 65194.889082104703, + 65216.195471688683, + 65237.503601635319, + 65258.813471660353, + 65280.125081479666, + 65301.438430809241, + 65322.753519365178, + 65344.070346863708, + 65365.388913021146, + 65386.709217553958, + 65408.031260178701, + 65429.355040612056, + 65450.680558570821, + 65472.00781377191, + 65493.336805932355, + 65514.66753476928, + 65535.999999999956, + 65557.334201341757, + 65578.670138512171, + 65600.007811228788, + 65621.347219209332, + 65642.688362171626, + 65664.031239833639, + 65685.375851913413, + 65706.722198129137, + 65728.070278199084, + 65749.420091841661, + 65770.771638775404, + 65792.124918718939, + 65813.479931391004, + 65834.836676510458, + 65856.195153796303, + 65877.5553629676, + 65898.917303743554, + 65920.280975843489, + 65941.646378986843, + 65963.013512893158, + 65984.382377282076, + 66005.752971873386, + 66027.125296386963, + 66048.499350542799, + 66069.875134061018, + 66091.252646661844, + 66112.631888065618, + 66134.01285799277, + 66155.395556163887, + 66176.779982299631, + 66198.166136120795, + 66219.554017348273, + 66240.943625703105, + 66262.334960906388, + 66283.728022679396, + 66305.122810743444, + 66326.519324820023, + 66347.917564630698, + 66369.317529897162, + 66390.719220341227, + 66412.122635684791, + 66433.527775649884, + 66454.934639958636, + 66476.343228333324, + 66497.753540496284, + 66519.165576169995, + 66540.57933507704, + 66561.994816940118, + 66583.412021482043, + 66604.830948425733, + 66626.251597494222, + 66647.673968410629, + 66669.098060898235, + 66690.523874680381, + 66711.951409480564, + 66733.380665022371, + 66754.811641029475, + 66776.244337225711, + 66797.678753334985, + 66819.11488908132, + 66840.552744188884, + 66861.992318381905, + 66883.433611384738, + 66904.876622921889, + 66926.321352717903, + 66947.767800497502, + 66969.215965985466, + 66990.665848906734, + 67012.117448986304, + 67033.570765949335, + 67055.025799521056, + 67076.482549426815, + 67097.941015392076, + 67119.401197142433, + 67140.863094403554, + 67162.326706901222, + 67183.792034361351, + 67205.259076509959, + 67226.72783307315, + 67248.198303777172, + 67269.670488348347, + 67291.144386513144, + 67312.619997998088, + 67334.09732252988, + 67355.576359835293, + 67377.057109641188, + 67398.53957167457, + 67420.023745662547, + 67441.50963133233, + 67462.99722841123, + 67484.486536626689, + 67505.977555706224, + 67527.470285377494, + 67548.964725368263, + 67570.460875406367, + 67591.9587352198, + 67613.458304536631, + 67634.95958308503, + 67656.462570593329, + 67677.967266789899, + 67699.473671403248, + 67720.981784162024, + 67742.491604794923, + 67764.003133030797, + 67785.516368598575, + 67807.031311227314, + 67828.547960646174, + 67850.066316584402, + 67871.58637877139, + 67893.108146936589, + 67914.63162080961, + 67936.156800120138, + 67957.683684597971, + 67979.212273973011, + 68000.742567975263, + 68022.274566334876, + 68043.808268782057, + 68065.343675047145, + 68086.880784860579, + 68108.419597952918, + 68129.960114054789, + 68151.502332896969, + 68173.04625421032, + 68194.591877725834, + 68216.139203174564, + 68237.688230287706, + 68259.238958796544, + 68280.791388432481, + 68302.345518927032, + 68323.901350011787, + 68345.458881418483, + 68367.018112878912, + 68388.579044125028, + 68410.141674888844, + 68431.706004902502, + 68453.272033898262, + 68474.839761608455, + 68496.409187765545, + 68517.980312102081, + 68539.553134350732, + 68561.127654244279, + 68582.70387151558, + 68604.281785897634, + 68625.861397123503, + 68647.44270492639, + 68669.025709039604, + 68690.610409196524, + 68712.196805130661, + 68733.784896575627, + 68755.374683265123, + 68776.966164932994, + 68798.559341313128, + 68820.154212139591, + 68841.750777146473, + 68863.349036068044, + 68884.948988638629, + 68906.550634592684, + 68928.153973664739, + 68949.75900558944, + 68971.365730101577, + 68992.974146935987, + 69014.584255827634, + 69036.196056511588, + 69057.809548723017, + 69079.424732197207, + 69101.041606669532, + 69122.660171875468, + 69144.280427550606, + 69165.902373430625, + 69187.526009251334, + 69209.151334748618, + 69230.778349658474, + 69252.40705371699, + 69274.037446660412, + 69295.669528225, + 69317.303298147192, + 69338.938756163494, + 69360.575902010532, + 69382.214735425005, + 69403.855256143754, + 69425.497463903681, + 69447.141358441833, + 69468.78693949533, + 69490.434206801394, + 69512.083160097391, + 69533.733799120717, + 69555.386123608929, + 69577.04013329967, + 69598.695827930685, + 69620.353207239794, + 69642.012270964973, + 69663.67301884426, + 69685.335450615792, + 69706.999566017839, + 69728.665364788743, + 69750.332846666963, + 69772.002011391058, + 69793.672858699691, + 69815.345388331611, + 69837.019600025669, + 69858.695493520849, + 69880.373068556204, + 69902.052324870907, + 69923.733262204216, + 69945.415880295492, + 69967.100178884211, + 69988.786157709939, + 70010.473816512356, + 70032.163155031216, + 70053.854173006403, + 70075.546870177874, + 70097.241246285717, + 70118.937301070109, + 70140.635034271298, + 70162.334445629691, + 70184.035534885741, + 70205.738301780017, + 70227.442746053217, + 70249.1488674461, + 70270.856665699539, + 70292.566140554511, + 70314.277291752107, + 70335.990119033493, + 70357.704622139936, + 70379.420800812819, + 70401.138654793613, + 70422.85818382389, + 70444.579387645339, + 70466.302265999722, + 70488.026818628918, + 70509.753045274876, + 70531.480945679708, + 70553.210519585555, + 70574.941766734701, + 70596.674686869505, + 70618.409279732456, + 70640.145545066101, + 70661.883482613106, + 70683.623092116264, + 70705.364373318414, + 70727.107325962526, + 70748.851949791671, + 70770.598244549008, + 70792.346209977783, + 70814.095845821372, + 70835.847151823225, + 70857.600127726895, + 70879.354773276034, + 70901.111088214413, + 70922.869072285859, + 70944.628725234332, + 70966.390046803877, + 70988.153036738629, + 71009.917694782853, + 71031.684020680885, + 71053.45201417715, + 71075.221675016204, + 71096.993002942661, + 71118.765997701266, + 71140.540659036851, + 71162.316986694335, + 71184.09498041874, + 71205.874639955218, + 71227.655965048951, + 71249.438955445294, + 71271.223610889632, + 71293.009931127483, + 71314.797915904477, + 71336.587564966307, + 71358.378878058764, + 71380.171854927772, + 71401.966495319313, + 71423.762798979486, + 71445.560765654489, + 71467.360395090596, + 71489.161687034211, + 71510.964641231811, + 71532.769257429973, + 71554.575535375348, + 71576.383474814749, + 71598.19307549503, + 71620.004337163133, + 71641.817259566145, + 71663.631842451214, + 71685.4480855656, + 71707.26598865664, + 71729.085551471784, + 71750.906773758586, + 71772.729655264673, + 71794.554195737772, + 71816.380394925713, + 71838.208252576442, + 71860.037768437964, + 71881.868942258385, + 71903.701773785942, + 71925.536262768932, + 71947.372408955751, + 71969.210212094898, + 71991.049671934976, + 72012.890788224686, + 72034.73356071279, + 72056.577989148165, + 72078.424073279821, + 72100.271812856794, + 72122.121207628254, + 72143.97225734347, + 72165.824961751801, + 72187.679320602692, + 72209.53533364569, + 72231.393000630429, + 72253.252321306645, + 72275.113295424177, + 72296.975922732949, + 72318.840202982959, + 72340.706135924338, + 72362.573721307272, + 72384.442958882093, + 72406.313848399179, + 72428.186389609036, + 72450.060582262216, + 72471.936426109431, + 72493.813920901433, + 72515.693066389096, + 72537.573862323392, + 72559.456308455352, + 72581.340404536139, + 72603.226150316987, + 72625.113545549248, + 72647.002589984331, + 72668.893283373764, + 72690.785625469172, + 72712.679616022273, + 72734.575254784853, + 72756.472541508803, + 72778.371475946144, + 72800.272057848939, + 72822.174286969355, + 72844.07816305969, + 72865.983685872285, + 72887.890855159596, + 72909.799670674183, + 72931.710132168693, + 72953.622239395845, + 72975.535992108475, + 72997.451390059519, + 73019.368433001961, + 73041.287120688925, + 73063.207452873612, + 73085.129429309294, + 73107.053049749389, + 73128.978313947344, + 73150.905221656736, + 73172.833772631217, + 73194.763966624567, + 73216.695803390612, + 73238.62928268328, + 73260.564404256627, + 73282.501167864757, + 73304.439573261901, + 73326.379620202337, + 73348.321308440485, + 73370.264637730841, + 73392.209607827957, + 73414.156218486532, + 73436.104469461323, + 73458.054360507173, + 73480.005891379056, + 73501.959061831993, + 73523.913871621116, + 73545.870320501665, + 73567.828408228932, + 73589.78813455833, + 73611.749499245358, + 73633.712502045615, + 73655.677142714747, + 73677.643421008557, + 73699.611336682879, + 73721.580889493693, + 73743.552079197019, + 73765.524905548999, + 73787.499368305856, + 73809.475467223907, + 73831.453202059551, + 73853.432572569291, + 73875.413578509717, + 73897.396219637507, + 73919.380495709411, + 73941.36640648231, + 73963.353951713143, + 73985.343131158952, + 74007.333944576865, + 74029.326391724098, + 74051.320472357969, + 74073.316186235883, + 74095.313533115303, + 74117.312512753837, + 74139.313124909138, + 74161.315369338976, + 74183.319245801191, + 74205.324754053727, + 74227.331893854629, + 74249.340664961986, + 74271.351067134034, + 74293.363100129049, + 74315.376763705441, + 74337.392057621662, + 74359.408981636298, + 74381.427535508003, + 74403.447718995507, + 74425.469531857671, + 74447.492973853383, + 74469.518044741693, + 74491.54474428168, + 74513.573072232539, + 74535.603028353551, + 74557.634612404087, + 74579.667824143602, + 74601.702663331642, + 74623.739129727837, + 74645.777223091936, + 74667.816943183716, + 74689.858289763113, + 74711.901262590094, + 74733.945861424741, + 74755.992086027225, + 74778.039936157802, + 74800.089411576817, + 74822.140512044702, + 74844.193237321961, + 74866.24758716923, + 74888.303561347187, + 74910.36115961663, + 74932.420381738411, + 74954.481227473516, + 74976.543696582972, + 74998.607788827925, + 75020.673503969607, + 75042.740841769322, + 75064.809801988464, + 75086.88038438854, + 75108.952588731103, + 75131.026414777836, + 75153.101862290467, + 75175.178931030852, + 75197.257620760924, + 75219.33793124267, + 75241.419862238225, + 75263.503413509738, + 75285.588584819503, + 75307.675375929874, + 75329.763786603318, + 75351.853816602365, + 75373.945465689612, + 75396.038733627807, + 75418.133620179724, + 75440.230125108254, + 75462.32824817636, + 75484.427989147109, + 75506.529347783653, + 75528.63232384919, + 75550.736917107075, + 75572.843127320695, + 75594.950954253538, + 75617.060397669193, + 75639.171457331307, + 75661.284133003646, + 75683.398424450032, + 75705.514331434402, + 75727.631853720741, + 75749.750991073175, + 75771.871743255862, + 75793.994110033076, + 75816.118091169177, + 75838.243686428585, + 75860.370895575848, + 75882.499718375562, + 75904.630154592422, + 75926.762203991224, + 75948.895866336825, + 75971.031141394182, + 75993.168028928325, + 76015.306528704401, + 76037.4466404876, + 76059.588364043215, + 76081.731699136653, + 76103.876645533353, + 76126.023202998884, + 76148.171371298871, + 76170.321150199044, + 76192.472539465205, + 76214.625538863256, + 76236.780148159174, + 76258.936367119008, + 76281.094195508922, + 76303.253633095141, + 76325.414679643975, + 76347.577334921851, + 76369.741598695226, + 76391.907470730686, + 76414.074950794879, + 76436.244038654564, + 76458.414734076548, + 76480.587036827754, + 76502.760946675175, + 76524.936463385893, + 76547.11358672705, + 76569.292316465915, + 76591.472652369819, + 76613.654594206164, + 76635.838141742468, + 76658.023294746308, + 76680.210052985349, + 76702.398416227341, + 76724.588384240138, + 76746.779956791637, + 76768.973133649866, + 76791.167914582897, + 76813.364299358902, + 76835.562287746157, + 76857.761879512967, + 76879.963074427797, + 76902.165872259109, + 76924.37027277553, + 76946.576275745727, + 76968.783880938441, + 76990.993088122515, + 77013.203897066895, + 77035.416307540567, + 77057.630319312622, + 77079.845932152239, + 77102.063145828695, + 77124.281960111301, + 77146.50237476948, + 77168.724389572759, + 77190.948004290723, + 77213.173218693031, + 77235.400032549442, + 77257.628445629802, + 77279.858457704031, + 77302.090068542122, + 77324.323277914169, + 77346.558085590339, + 77368.794491340886, + 77391.032494936138, + 77413.272096146524, + 77435.51329474253, + 77457.756090494731, + 77480.000483173804, + 77502.246472550498, + 77524.494058395634, + 77546.743240480107, + 77568.994018574944, + 77591.246392451198, + 77613.500361880026, + 77635.755926632657, + 77658.013086480438, + 77680.271841194757, + 77702.532190547092, + 77724.794134309021, + 77747.057672252195, + 77769.322804148323, + 77791.589529769248, + 77813.857848886837, + 77836.127761273063, + 77858.399266699998, + 77880.67236493979, + 77902.947055764627, + 77925.223338946831, + 77947.50121425878, + 77969.780681472927, + 77992.061740361838, + 78014.344390698127, + 78036.628632254491, + 78058.914464803747, + 78081.201888118725, + 78103.490901972415, + 78125.781506137821, + 78148.073700388064, + 78170.367484496339, + 78192.662858235926, + 78214.959821380166, + 78237.258373702498, + 78259.558514976452, + 78281.860244975614, + 78304.163563473659, + 78326.468470244363, + 78348.77496506153, + 78371.083047699125, + 78393.392717931114, + 78415.703975531578, + 78438.016820274701, + 78460.331251934695, + 78482.647270285903, + 78504.964875102727, + 78527.284066159627, + 78549.604843231195, + 78571.927206092048, + 78594.251154516911, + 78616.576688280606, + 78638.903807157985, + 78661.232510924034, + 78683.562799353778, + 78705.894672222363, + 78728.228129304945, + 78750.563170376859, + 78772.899795213423, + 78795.238003590101, + 78817.577795282399, + 78839.919170065928, + 78862.262127716356, + 78884.606668009452, + 78906.952790721043, + 78929.300495627045, + 78951.64978250346, + 78974.000651126378, + 78996.353101271932, + 79018.707132716358, + 79041.062745235977, + 79063.41993860717, + 79085.778712606436, + 79108.139067010285, + 79130.501001595389, + 79152.864516138419, + 79175.22961041618, + 79197.596284205531, + 79219.96453728342, + 79242.33436942687, + 79264.705780412987, + 79287.078770018954, + 79309.453338022009, + 79331.829484199508, + 79354.207208328866, + 79376.586510187582, + 79398.967389553218, + 79421.349846203433, + 79443.733879915948, + 79466.119490468584, + 79488.50667763922, + 79510.895441205823, + 79533.285780946433, + 79555.677696639163, + 79578.071188062226, + 79600.466254993895, + 79622.862897212515, + 79645.261114496549, + 79667.660906624471, + 79690.062273374875, + 79712.465214526455, + 79734.869729857935, + 79757.275819148126, + 79779.683482175955, + 79802.092718720378, + 79824.503528560454, + 79846.915911475327, + 79869.329867244203, + 79891.745395646343, + 79914.162496461155, + 79936.581169468045, + 79959.001414446553, + 79981.423231176261, + 80003.846619436852, + 80026.271579008084, + 80048.698109669771, + 80071.12621120183, + 80093.555883384237, + 80115.987125997053, + 80138.419938820414, + 80160.854321634528, + 80183.290274219689, + 80205.727796356281, + 80228.166887824715, + 80250.607548405547, + 80273.049777879336, + 80295.493576026798, + 80317.938942628651, + 80340.385877465727, + 80362.834380318949, + 80385.28445096928, + 80407.736089197788, + 80430.189294785596, + 80452.644067513917, + 80475.100407164035, + 80497.558313517322, + 80520.017786355209, + 80542.478825459213, + 80564.941430610925, + 80587.405601592007, + 80609.871338184195, + 80632.338640169342, + 80654.8075073293, + 80677.277939446067, + 80699.749936301683, + 80722.223497678278, + 80744.698623358039, + 80767.17531312324, + 80789.653566756242, + 80812.133384039465, + 80834.614764755403, + 80857.097708686648, + 80879.582215615854, + 80902.068285325731, + 80924.555917599093, + 80947.045112218824, + 80969.535868967869, + 80992.028187629272, + 81014.522067986123, + 81037.017509821613, + 81059.514512919006, + 81082.013077061609, + 81104.513202032831, + 81127.014887616184, + 81149.518133595193, + 81172.022939753486, + 81194.529305874807, + 81217.037231742899, + 81239.546717141639, + 81262.057761854958, + 81284.570365666848, + 81307.084528361403, + 81329.600249722775, + 81352.117529535186, + 81374.636367582949, + 81397.156763650448, + 81419.678717522125, + 81442.202228982511, + 81464.727297816222, + 81487.253923807933, + 81509.782106742379, + 81532.311846404409, + 81554.843142578902, + 81577.375995050839, + 81599.910403605274, + 81622.446368027333, + 81644.983888102215, + 81667.522963615178, + 81690.063594351581, + 81712.605780096841, + 81735.149520636449, + 81757.694815755967, + 81780.241665241047, + 81802.79006887741, + 81825.340026450824, + 81847.891537747171, + 81870.444602552379, + 81892.999220652477, + 81915.555391833506, + 81938.113115881672, + 81960.672392583176, + 81983.233221724338, + 82005.795603091537, + 82028.359536471224, + 82050.925021649906, + 82073.492058414209, + 82096.060646550788, + 82118.630785846399, + 82141.202476087841, + 82163.775717062032, + 82186.35050855593, + 82208.926850356569, + 82231.504742251054, + 82254.084184026578, + 82276.665175470393, + 82299.24771636985, + 82321.831806512317, + 82344.417445685307, + 82367.004633676348, + 82389.593370273054, + 82412.183655263143, + 82434.775488434374, + 82457.368869574595, + 82479.963798471697, + 82502.560274913689, + 82525.158298688606, + 82547.757869584602, + 82570.35898738986, + 82592.961651892678, + 82615.565862881398, + 82638.171620144421, + 82660.778923470265, + 82683.387772647475, + 82705.998167464713, + 82728.610107710658, + 82751.223593174116, + 82773.83862364394, + 82796.45519890904, + 82819.073318758441, + 82841.692982981185, + 82864.314191366429, + 82886.936943703375, + 82909.561239781324, + 82932.187079389638, + 82954.814462317736, + 82977.443388355125, + 83000.073857291369, + 83022.70586891612, + 83045.339423019104, + 83067.974519390089, + 83090.611157818959, + 83113.249338095629, + 83135.8890600101, + 83158.530323352461, + 83181.173127912858, + 83203.817473481497, + 83226.463359848669, + 83249.11078680474, + 83271.759754140134, + 83294.410261645375, + 83317.062309111003, + 83339.715896327703, + 83362.371023086147, + 83385.027689177165, + 83407.685894391587, + 83430.345638520361, + 83453.006921354478, + 83475.669742685001, + 83498.334102303095, + 83520.999999999942, + 83543.667435566866, + 83566.336408795192, + 83589.006919476349, + 83611.678967401851, + 83634.352552363242, + 83657.027674152167, + 83679.704332560359, + 83702.382527379552, + 83725.062258401638, + 83747.743525418511, + 83770.42632822218, + 83793.110666604684, + 83815.796540358162, + 83838.483949274829, + 83861.172893146941, + 83883.863371766842, + 83906.555384926964, + 83929.248932419752, + 83951.944014037799, + 83974.640629573696, + 83997.338778820151, + 84020.038461569929, + 84042.739677615857, + 84065.442426750829, + 84088.146708767847, + 84110.852523459922, + 84133.559870620171, + 84156.268750041796, + 84178.979161518029, + 84201.691104842204, + 84224.404579807713, + 84247.119586208006, + 84269.83612383662, + 84292.55419248715, + 84315.273791953281, + 84337.994922028738, + 84360.717582507335, + 84383.441773182945, + 84406.167493849513, + 84428.894744301069, + 84451.623524331691, + 84474.353833735542, + 84497.085672306828, + 84519.819039839858, + 84542.553936128999, + 84565.290360968676, + 84588.028314153402, + 84610.767795477717, + 84633.508804736295, + 84656.251341723822, + 84678.995406235073, + 84701.740998064924, + 84724.488117008252, + 84747.236762860062, + 84769.986935415407, + 84792.73863446941, + 84815.491859817252, + 84838.246611254188, + 84861.002888575575, + 84883.760691576768, + 84906.520020053256, + 84929.28087380057, + 84952.043252614312, + 84974.807156290146, + 84997.572584623806, + 85020.339537411113, + 85043.108014447949, + 85065.878015530237, + 85088.649540453989, + 85111.422589015303, + 85134.197161010321, + 85156.973256235244, + 85179.750874486374, + 85202.530015560071, + 85225.310679252725, + 85248.092865360857, + 85270.876573681016, + 85293.661804009811, + 85316.448556143951, + 85339.236829880188, + 85362.026625015351, + 85384.817941346351, + 85407.610778670132, + 85430.405136783724, + 85453.201015484257, + 85475.998414568865, + 85498.797333834795, + 85521.597773079353, + 85544.399732099904, + 85567.203210693886, + 85590.008208658808, + 85612.814725792239, + 85635.62276189182, + 85658.432316755265, + 85681.243390180331, + 85704.055981964877, + 85726.870091906807, + 85749.685719804082, + 85772.502865454764, + 85795.321528656961, + 85818.141709208852, + 85840.963406908675, + 85863.78662155474, + 85886.611352945445, + 85909.437600879217, + 85932.26536515457, + 85955.094645570091, + 85977.92544192441, + 86000.757754016275, + 86023.591581644432, + 86046.426924607746, + 86069.263782705122, + 86092.102155735556, + 86114.942043498071, + 86137.783445791807, + 86160.626362415918, + 86183.470793169676, + 86206.316737852379, + 86229.164196263402, + 86252.013168202204, + 86274.863653468303, + 86297.715651861261, + 86320.569163180728, + 86343.424187226425, + 86366.280723798132, + 86389.138772695675, + 86411.998333718977, + 86434.859406668009, + 86457.721991342827, + 86480.586087543532, + 86503.451695070296, + 86526.318813723352, + 86549.187443303032, + 86572.057583609683, + 86594.929234443756, + 86617.802395605773, + 86640.677066896271, + 86663.553248115903, + 86686.43093906538, + 86709.310139545443, + 86732.190849356964, + 86755.073068300815, + 86777.956796177954, + 86800.842032789442, + 86823.728777936354, + 86846.617031419853, + 86869.506793041175, + 86892.398062601613, + 86915.290839902518, + 86938.185124745316, + 86961.080916931489, + 86983.978216262592, + 87006.87702254027, + 87029.777335566177, + 87052.67915514209, + 87075.582481069796, + 87098.487313151185, + 87121.39365118822, + 87144.301494982894, + 87167.210844337285, + 87190.121699053532, + 87213.034058933845, + 87235.947923780506, + 87258.863293395829, + 87281.780167582241, + 87304.698546142172, + 87327.618428878181, + 87350.539815592856, + 87373.462706088845, + 87396.387100168897, + 87419.312997635774, + 87442.240398292357, + 87465.16930194154, + 87488.099708386319, + 87511.031617429733, + 87533.965028874911, + 87556.899942525008, + 87579.836358183282, + 87602.774275653021, + 87625.713694737613, + 87648.654615240492, + 87671.597036965148, + 87694.540959715145, + 87717.486383294105, + 87740.433307505737, + 87763.381732153779, + 87786.331657042057, + 87809.283081974456, + 87832.236006754916, + 87855.190431187453, + 87878.146355076155, + 87901.103778225151, + 87924.062700438633, + 87947.023121520891, + 87969.985041276246, + 87992.948459509105, + 88015.913376023906, + 88038.879790625171, + 88061.847703117513, + 88084.817113305573, + 88107.788020994049, + 88130.760425987726, + 88153.734328091465, + 88176.709727110137, + 88199.686622848749, + 88222.665015112303, + 88245.644903705906, + 88268.626288434709, + 88291.609169103947, + 88314.593545518903, + 88337.579417484914, + 88360.566784807408, + 88383.555647291854, + 88406.546004743795, + 88429.537856968818, + 88452.531203772611, + 88475.52604496089, + 88498.522380339447, + 88521.52020971413, + 88544.519532890874, + 88567.520349675644, + 88590.522659874507, + 88613.526463293543, + 88636.531759738922, + 88659.538549016899, + 88682.546830933745, + 88705.556605295846, + 88728.567871909589, + 88751.580630581491, + 88774.594881118086, + 88797.610623325963, + 88820.62785701183, + 88843.646581982393, + 88866.666798044462, + 88889.688505004888, + 88912.711702670611, + 88935.7363908486, + 88958.762569345898, + 88981.790237969632, + 89004.81939652696, + 89027.850044825114, + 89050.882182671412, + 89073.9158098732, + 89096.950926237885, + 89119.987531572973, + 89143.025625686001, + 89166.065208384563, + 89189.106279476357, + 89212.148838769106, + 89235.192886070581, + 89258.238421188667, + 89281.285443931265, + 89304.333954106376, + 89327.383951522017, + 89350.435435986306, + 89373.488407307406, + 89396.542865293537, + 89419.598809753006, + 89442.656240494165, + 89465.715157325409, + 89488.775560055219, + 89511.837448492137, + 89534.900822444746, + 89557.965681721733, + 89581.032026131812, + 89604.099855483742, + 89627.169169586399, + 89650.239968248672, + 89673.312251279538, + 89696.386018488018, + 89719.461269683205, + 89742.53800467425, + 89765.616223270365, + 89788.69592528083, + 89811.777110514988, + 89834.859778782207, + 89857.943929891975, + 89881.029563653807, + 89904.116679877261, + 89927.205278372014, + 89950.29535894774, + 89973.386921414218, + 89996.479965581268, + 90019.574491258769, + 90042.670498256688, + 90065.767986385021, + 90088.866955453836, + 90111.967405273259, + 90135.069335653476, + 90158.172746404758, + 90181.277637337407, + 90204.384008261797, + 90227.49185898836, + 90250.601189327586, + 90273.711999090039, + 90296.824288086325, + 90319.938056127125, + 90343.053303023189, + 90366.170028585286, + 90389.288232624298, + 90412.407914951138, + 90435.529075376777, + 90458.651713712257, + 90481.775829768681, + 90504.901423357209, + 90528.028494289058, + 90551.157042375504, + 90574.287067427911, + 90597.418569257643, + 90620.551547676194, + 90643.686002495073, + 90666.821933525847, + 90689.959340580186, + 90713.098223469773, + 90736.238582006365, + 90759.380416001804, + 90782.523725267951, + 90805.668509616764, + 90828.814768860233, + 90851.962502810435, + 90875.11171127946, + 90898.262394079517, + 90921.414551022855, + 90944.568181921743, + 90967.72328658856, + 90990.879864835719, + 91014.037916475718, + 91037.19744132107, + 91060.358439184391, + 91083.520909878338, + 91106.684853215629, + 91129.850269009039, + 91153.017157071401, + 91176.185517215621, + 91199.355349254649, + 91222.526653001492, + 91245.699428269247, + 91268.873674871036, + 91292.049392620058, + 91315.226581329553, + 91338.405240812834, + 91361.585370883287, + 91384.766971354344, + 91407.950042039476, + 91431.134582752245, + 91454.320593306256, + 91477.508073515171, + 91500.697023192712, + 91523.887442152685, + 91547.07933020893, + 91570.272687175326, + 91593.467512865856, + 91616.663807094534, + 91639.861569675442, + 91663.060800422725, + 91686.261499150554, + 91709.463665673218, + 91732.66729980502, + 91755.872401360321, + 91779.078970153569, + 91802.287005999257, + 91825.49650871192, + 91848.707478106167, + 91871.91991399668, + 91895.133816198169, + 91918.349184525418, + 91941.566018793281, + 91964.784318816659, + 91988.004084410495, + 92011.22531538982, + 92034.448011569708, + 92057.672172765277, + 92080.897798791746, + 92104.124889464365, + 92127.353444598411, + 92150.58346400928, + 92173.814947512379, + 92197.04789492322, + 92220.282306057314, + 92243.518180730272, + 92266.755518757753, + 92289.994319955469, + 92313.234584139194, + 92336.476311124774, + 92359.719500728082, + 92382.964152765067, + 92406.210267051734, + 92429.457843404161, + 92452.706881638471, + 92475.957381570814, + 92499.209343017443, + 92522.462765794655, + 92545.717649718805, + 92568.973994606305, + 92592.231800273614, + 92615.491066537259, + 92638.751793213814, + 92662.01398011994, + 92685.277627072326, + 92708.54273388772, + 92731.809300382942, + 92755.077326374871, + 92778.346811680414, + 92801.617756116568, + 92824.890159500384, + 92848.164021648947, + 92871.439342379424, + 92894.716121509016, + 92917.994358855023, + 92941.274054234746, + 92964.555207465572, + 92987.837818364962, + 93011.121886750407, + 93034.407412439468, + 93057.694395249753, + 93080.982834998955, + 93104.272731504767, + 93127.564084584999, + 93150.856894057491, + 93174.15115974014, + 93197.446881450916, + 93220.744059007804, + 93244.04269222889, + 93267.342780932304, + 93290.644324936235, + 93313.947324058914, + 93337.251778118633, + 93360.557686933767, + 93383.865050322696, + 93407.173868103928, + 93430.484140095941, + 93453.795866117362, + 93477.109045986799, + 93500.423679522952, + 93523.739766544561, + 93547.057306870454, + 93570.376300319491, + 93593.696746710571, + 93617.018645862699, + 93640.341997594893, + 93663.666801726242, + 93686.993058075881, + 93710.320766463032, + 93733.64992670693, + 93756.980538626914, + 93780.312602042337, + 93803.646116772637, + 93826.981082637285, + 93850.317499455836, + 93873.655367047861, + 93896.994685233032, + 93920.335453831038, + 93943.677672661666, + 93967.021341544707, + 93990.366460300051, + 94013.713028747632, + 94037.061046707429, + 94060.410513999494, + 94083.761430443905, + 94107.113795860845, + 94130.467610070496, + 94153.822872893157, + 94177.179584149111, + 94200.537743658759, + 94223.897351242529, + 94247.25840672091, + 94270.620909914433, + 94293.98486064373, + 94317.350258729421, + 94340.71710399224, + 94364.085396252936, + 94387.455135332348, + 94410.82632105134, + 94434.198953230851, + 94457.573031691878, + 94480.948556255447, + 94504.325526742658, + 94527.70394297468, + 94551.083804772716, + 94574.465111958023, + 94597.847864351934, + 94621.232061775823, + 94644.617704051096, + 94668.004790999272, + 94691.393322441872, + 94714.783298200506, + 94738.174718096794, + 94761.567581952477, + 94784.961889589307, + 94808.357640829097, + 94831.754835493703, + 94855.153473405066, + 94878.553554385173, + 94901.955078256055, + 94925.358044839784, + 94948.762453958523, + 94972.168305434476, + 94995.575599089891, + 95018.984334747074, + 95042.394512228391, + 95065.806131356265, + 95089.219191953176, + 95112.633693841635, + 95136.04963684424, + 95159.467020783617, + 95182.885845482466, + 95206.306110763529, + 95229.727816449609, + 95253.150962363579, + 95276.575548328314, + 95300.001574166803, + 95323.429039702052, + 95346.857944757154, + 95370.288289155214, + 95393.720072719429, + 95417.153295273019, + 95440.587956639298, + 95464.024056641589, + 95487.461595103305, + 95510.900571847902, + 95534.340986698866, + 95557.782839479783, + 95581.226130014256, + 95604.670858125959, + 95628.117023638595, + 95651.564626375985, + 95675.013666161918, + 95698.464142820303, + 95721.916056175076, + 95745.369406050231, + 95768.824192269807, + 95792.280414657915, + 95815.738073038709, + 95839.197167236387, + 95862.657697075221, + 95886.11966237954, + 95909.583062973688, + 95933.047898682111, + 95956.514169329268, + 95979.981874739708, + 96003.451014738006, + 96026.921589148798, + 96050.393597796792, + 96073.867040506724, + 96097.341917103375, + 96120.818227411626, + 96144.295971256375, + 96167.775148462577, + 96191.255758855244, + 96214.737802259449, + 96238.221278500292, + 96261.70618740299, + 96285.192528792715, + 96308.680302494788, + 96332.169508334526, + 96355.660146137321, + 96379.152215728609, + 96402.645716933868, + 96426.14064957868, + 96449.637013488609, + 96473.134808489311, + 96496.63403440651, + 96520.134691065963, + 96543.636778293469, + 96567.140295914898, + 96590.645243756153, + 96614.151621643221, + 96637.659429402134, + 96661.168666858954, + 96684.679333839798, + 96708.191430170875, + 96731.70495567839, + 96755.219910188665, + 96778.736293528011, + 96802.254105522836, + 96825.77334599958, + 96849.29401478474, + 96872.816111704873, + 96896.339636586577, + 96919.864589256511, + 96943.390969541389, + 96966.918777267958, + 96990.448012263048, + 97013.978674353522, + 97037.510763366285, + 97061.044279128328, + 97084.579221466673, + 97108.115590208385, + 97131.653385180587, + 97155.19260621049, + 97178.733253125291, + 97202.2753257523, + 97225.81882391886, + 97249.363747452342, + 97272.910096180189, + 97296.457869929916, + 97320.007068529041, + 97343.557691805196, + 97367.109739586012, + 97390.663211699197, + 97414.218107972498, + 97437.774428233737, + 97461.332172310766, + 97484.891340031507, + 97508.451931223899, + 97532.013945715982, + 97555.577383335811, + 97579.142243911512, + 97602.708527271257, + 97626.276233243261, + 97649.845361655811, + 97673.415912337223, + 97696.987885115886, + 97720.561279820206, + 97744.1360962787, + 97767.712334319876, + 97791.289993772341, + 97814.869074464703, + 97838.449576225685, + 97862.031498883996, + 97885.614842268449, + 97909.199606207883, + 97932.785790531183, + 97956.37339506732, + 97979.962419645264, + 98003.552864094076, + 98027.144728242856, + 98050.738011920766, + 98074.332714956996, + 98097.928837180807, + 98121.526378421506, + 98145.125338508456, + 98168.725717271067, + 98192.327514538789, + 98215.930730141132, + 98239.535363907664, + 98263.141415668011, + 98286.748885251814, + 98310.357772488816, + 98333.968077208759, + 98357.579799241488, + 98381.192938416847, + 98404.807494564782, + 98428.42346751524, + 98452.040857098269, + 98475.659663143917, + 98499.27988548232, + 98522.901523943656, + 98546.524578358163, + 98570.149048556093, + 98593.774934367786, + 98617.402235623624, + 98641.030952154048, + 98664.661083789513, + 98688.292630360564, + 98711.925591697771, + 98735.559967631794, + 98759.195757993293, + 98782.832962613014, + 98806.471581321734, + 98830.111613950285, + 98853.753060329575, + 98877.39592029051, + 98901.040193664099, + 98924.68588028138, + 98948.33297997342, + 98971.981492571387, + 98995.63141790645, + 99019.282755809851, + 99042.935506112874, + 99066.589668646877, + 99090.245243243233, + 99113.902229733401, + 99137.560627948857, + 99161.220437721131, + 99184.881658881859, + 99208.544291262631, + 99232.208334695169, + 99255.87378901121, + 99279.540654042547, + 99303.208929621018, + 99326.878615578535, + 99350.549711746993, + 99374.222217958435, + 99397.896134044888, + 99421.571459838422, + 99445.248195171211, + 99468.926339875441, + 99492.605893783344, + 99516.286856727209, + 99539.969228539398, + 99563.653009052287, + 99587.338198098325, + 99611.024795510006, + 99634.712801119866, + 99658.402214760499, + 99682.093036264545, + 99705.785265464699, + 99729.478902193689, + 99753.173946284325, + 99776.870397569437, + 99800.56825588191, + 99824.267521054688, + 99847.968192920773, + 99871.670271313182, + 99895.373756065004, + 99919.078647009388, + 99942.78494397951, + 99966.492646808634, + 99990.20175533001, + 100013.91226937699, + 100037.62418878295, + 100061.33751338134, + 100085.05224300563, + 100108.76837748935, + 100132.4859166661, + 100156.2048603695, + 100179.92520843323, + 100203.64696069101, + 100227.37011697664, + 100251.09467712394, + 100274.82064096678, + 100298.54800833909, + 100322.27677907483, + 100346.00695300807, + 100369.73852997283, + 100393.47150980328, + 100417.20589233354, + 100440.94167739789, + 100464.67886483055, + 100488.41745446586, + 100512.1574461382, + 100535.89883968196, + 100559.64163493161, + 100583.38583172169, + 100607.13142988674, + 100630.87842926137, + 100654.62682968024, + 100678.37663097809, + 100702.12783298964, + 100725.88043554971, + 100749.63443849317, + 100773.38984165489, + 100797.14664486986, + 100820.90484797307, + 100844.66445079957, + 100868.42545318443, + 100892.18785496285, + 100915.95165596998, + 100939.71685604109, + 100963.48345501146, + 100987.25145271645, + 101011.02084899142, + 101034.79164367182, + 101058.56383659317, + 101082.33742759094, + 101106.11241650078, + 101129.88880315828, + 101153.66658739912, + 101177.44576905905, + 101201.22634797383, + 101225.00832397929, + 101248.7916969113, + 101272.57646660579, + 101296.36263289873, + 101320.15019562612, + 101343.93915462404, + 101367.7295097286, + 101391.52126077596, + 101415.31440760233, + 101439.10895004397, + 101462.9048879372, + 101486.70222111834, + 101510.50094942382, + 101534.30107269008, + 101558.10259075361, + 101581.90550345098, + 101605.70981061876, + 101629.5155120936, + 101653.32260771218, + 101677.13109731126, + 101700.9409807276, + 101724.75225779804, + 101748.56492835947, + 101772.37899224881, + 101796.19444930303, + 101820.01129935916, + 101843.82954225427, + 101867.64917782549, + 101891.47020590997, + 101915.29262634492, + 101939.11643896763, + 101962.94164361537, + 101986.76824012553, + 102010.59622833549, + 102034.42560808272, + 102058.25637920471, + 102082.08854153901, + 102105.9220949232, + 102129.75703919494, + 102153.59337419191, + 102177.43109975185, + 102201.27021571253, + 102225.1107219118, + 102248.95261818753, + 102272.79590437764, + 102296.64058032009, + 102320.48664585294, + 102344.33410081422, + 102368.18294504205, + 102392.03317837461, + 102415.88480065008, + 102439.73781170673, + 102463.59221138287, + 102487.44799951684, + 102511.30517594704, + 102535.1637405119, + 102559.02369304992, + 102582.88503339965, + 102606.74776139967, + 102630.61187688859, + 102654.4773797051, + 102678.34426968795, + 102702.21254667587, + 102726.08221050771, + 102749.95326102231, + 102773.8256980586, + 102797.69952145554, + 102821.57473105213, + 102845.45132668741, + 102869.32930820051, + 102893.20867543056, + 102917.08942821674, + 102940.97156639832, + 102964.85508981455, + 102988.73999830478, + 103012.6262917084, + 103036.51396986481, + 103060.40303261351, + 103084.293479794, + 103108.18531124585, + 103132.07852680866, + 103155.97312632212, + 103179.8691096259, + 103203.76647655977, + 103227.66522696352, + 103251.56536067701, + 103275.46687754011, + 103299.36977739276, + 103323.27406007495, + 103347.1797254267, + 103371.0867732881, + 103394.99520349925, + 103418.90501590034, + 103442.81621033157, + 103466.72878663319, + 103490.64274464553, + 103514.55808420894, + 103538.4748051638, + 103562.39290735057, + 103586.31239060973, + 103610.23325478184, + 103634.15549970744, + 103658.07912522719, + 103682.00413118176, + 103705.93051741188, + 103729.85828375829, + 103753.78743006183, + 103777.71795616332, + 103801.64986190372, + 103825.58314712394, + 103849.51781166498, + 103873.4538553679, + 103897.39127807376, + 103921.33007962372, + 103945.27025985894, + 103969.21181862066, + 103993.15475575015, + 104017.0990710887, + 104041.0447644777, + 104064.99183575854, + 104088.94028477269, + 104112.89011136163, + 104136.84131536692, + 104160.79389663014, + 104184.74785499295, + 104208.70319029699, + 104232.65990238401, + 104256.61799109577, + 104280.57745627411, + 104304.53829776087, + 104328.50051539797, + 104352.46410902737, + 104376.42907849104, + 104400.39542363105, + 104424.36314428948, + 104448.33224030846, + 104472.3027115302, + 104496.27455779689, + 104520.24777895081, + 104544.22237483428, + 104568.19834528965, + 104592.17569015936, + 104616.15440928582, + 104640.13450251156, + 104664.1159696791, + 104688.09881063103, + 104712.08302520998, + 104736.06861325864, + 104760.05557461972, + 104784.043909136, + 104808.03361665027, + 104832.0246970054, + 104856.01715004431, + 104880.01097560991, + 104904.00617354522, + 104928.00274369326, + 104952.00068589712, + 104975.99999999993, + 105000.00068584486, + 105024.00274327511, + 105048.00617213396, + 105072.0109722647, + 105096.0171435107, + 105120.02468571534, + 105144.03359872208, + 105168.04388237436, + 105192.05553651576, + 105216.06856098982, + 105240.08295564017, + 105264.09872031047, + 105288.11585484444, + 105312.13435908582, + 105336.1542328784, + 105360.17547606604, + 105384.19808849262, + 105408.22207000206, + 105432.24742043833, + 105456.27413964548, + 105480.30222746753, + 105504.33168374863, + 105528.36250833291, + 105552.39470106458, + 105576.42826178786, + 105600.46319034706, + 105624.49948658649, + 105648.53715035053, + 105672.5761814836, + 105696.61657983017, + 105720.65834523473, + 105744.70147754184, + 105768.7459765961, + 105792.79184224214, + 105816.83907432464, + 105840.88767268835, + 105864.93763717801, + 105888.98896763846, + 105913.04166391456, + 105937.09572585119, + 105961.15115329332, + 105985.20794608595, + 106009.26610407409, + 106033.32562710284, + 106057.38651501729, + 106081.44876766266, + 106105.51238488412, + 106129.57736652695, + 106153.64371243643, + 106177.71142245791, + 106201.78049643678, + 106225.85093421848, + 106249.92273564848, + 106273.99590057228, + 106298.07042883546, + 106322.14632028362, + 106346.22357476239, + 106370.30219211751, + 106394.38217219469, + 106418.46351483969, + 106442.54621989837, + 106466.63028721658, + 106490.71571664025, + 106514.80250801529, + 106538.89066118775, + 106562.98017600364, + 106587.07105230905, + 106611.16328995011, + 106635.25688877302, + 106659.35184862395, + 106683.44816934918, + 106707.54585079502, + 106731.64489280782, + 106755.74529523395, + 106779.84705791986, + 106803.95018071201, + 106828.05466345693, + 106852.16050600118, + 106876.26770819137, + 106900.37626987413, + 106924.48619089619, + 106948.59747110425, + 106972.71011034511, + 106996.82410846559, + 107020.93946531253, + 107045.05618073288, + 107069.17425457356, + 107093.29368668159, + 107117.41447690397, + 107141.53662508781, + 107165.66013108024, + 107189.7849947284, + 107213.91121587952, + 107238.03879438085, + 107262.16773007967, + 107286.29802282334, + 107310.42967245923, + 107334.56267883476, + 107358.69704179741, + 107382.83276119467, + 107406.96983687414, + 107431.10826868335, + 107455.24805646999, + 107479.38920008171, + 107503.53169936626, + 107527.67555417139, + 107551.82076434491, + 107575.96732973469, + 107600.11525018861, + 107624.26452555459, + 107648.41515568066, + 107672.56714041479, + 107696.72047960508, + 107720.87517309963, + 107745.03122074658, + 107769.18862239413, + 107793.34737789053, + 107817.50748708403, + 107841.66894982298, + 107865.83176595572, + 107889.99593533068, + 107914.16145779629, + 107938.32833320105, + 107962.49656139348, + 107986.66614222217, + 108010.83707553572, + 108035.00936118282, + 108059.18299901215, + 108083.35798887245, + 108107.53433061253, + 108131.71202408121, + 108155.89106912735, + 108180.07146559987, + 108204.25321334775, + 108228.43631221994, + 108252.62076206553, + 108276.80656273357, + 108300.99371407321, + 108325.18221593359, + 108349.37206816394, + 108373.56327061349, + 108397.75582313156, + 108421.94972556747, + 108446.1449777706, + 108470.34157959036, + 108494.53953087622, + 108518.73883147769, + 108542.93948124432, + 108567.14148002568, + 108591.34482767139, + 108615.54952403114, + 108639.75556895464, + 108663.96296229165, + 108688.17170389196, + 108712.38179360541, + 108736.59323128188, + 108760.80601677128, + 108785.02014992358, + 108809.23563058881, + 108833.45245861699, + 108857.67063385822, + 108881.89015616261, + 108906.11102538036, + 108930.33324136167, + 108954.55680395682, + 108978.78171301607, + 109003.00796838976, + 109027.23556992831, + 109051.46451748211, + 109075.69481090162, + 109099.92645003737, + 109124.15943473989, + 109148.39376485976, + 109172.62944024763, + 109196.86646075416, + 109221.10482623006, + 109245.34453652608, + 109269.58559149304, + 109293.82799098175, + 109318.07173484311, + 109342.31682292801, + 109366.56325508743, + 109390.81103117237, + 109415.06015103387, + 109439.31061452301, + 109463.56242149093, + 109487.8155717888, + 109512.07006526781, + 109536.3259017792, + 109560.58308117429, + 109584.8416033044, + 109609.1014680209, + 109633.36267517522, + 109657.62522461878, + 109681.88911620311, + 109706.15434977971, + 109730.4209252002, + 109754.68884231619, + 109778.95810097932, + 109803.22870104131, + 109827.50064235389, + 109851.77392476884, + 109876.04854813802, + 109900.32451231324, + 109924.60181714644, + 109948.88046248957, + 109973.1604481946, + 109997.44177411357, + 110021.72444009855, + 110046.00844600165, + 110070.29379167501, + 110094.58047697082, + 110118.86850174134, + 110143.15786583882, + 110167.44856911557, + 110191.74061142397, + 110216.03399261639, + 110240.32871254528, + 110264.62477106311, + 110288.9221680224, + 110313.22090327571, + 110337.52097667565, + 110361.82238807483, + 110386.12513732594, + 110410.42922428172, + 110434.73464879491, + 110459.04141071832, + 110483.34950990479, + 110507.6589462072, + 110531.96971947847, + 110556.28182957157, + 110580.5952763395, + 110604.91005963532, + 110629.22617931209, + 110653.54363522294, + 110677.86242722106, + 110702.18255515963, + 110726.50401889188, + 110750.82681827113, + 110775.1509531507, + 110799.47642338395, + 110823.80322882428, + 110848.13136932514, + 110872.46084474004, + 110896.79165492248, + 110921.12379972603, + 110945.4572790043, + 110969.79209261097, + 110994.12824039967, + 111018.46572222417, + 111042.80453793822, + 111067.14468739564, + 111091.48617045028, + 111115.82898695602, + 111140.1731367668, + 111164.51861973655, + 111188.86543571933, + 111213.21358456917, + 111237.56306614014, + 111261.91388028639, + 111286.26602686207, + 111310.61950572141, + 111334.97431671864, + 111359.33045970804, + 111383.68793454397, + 111408.04674108078, + 111432.40687917286, + 111456.76834867468, + 111481.13114944073, + 111505.49528132551, + 111529.86074418361, + 111554.22753786964, + 111578.59566223821, + 111602.96511714405, + 111627.33590244185, + 111651.7080179864, + 111676.08146363248, + 111700.45623923496, + 111724.8323446487, + 111749.20977972864, + 111773.58854432974, + 111797.96863830699, + 111822.35006151545, + 111846.73281381019, + 111871.11689504632, + 111895.50230507903, + 111919.8890437635, + 111944.27711095495, + 111968.6665065087, + 111993.05723028004, + 112017.44928212435, + 112041.842661897, + 112066.23736945343, + 112090.63340464912, + 112115.03076733962, + 112139.42945738042, + 112163.82947462716, + 112188.23081893545, + 112212.63349016097, + 112237.03748815943, + 112261.44281278658, + 112285.84946389822, + 112310.25744135017, + 112334.66674499828, + 112359.07737469849, + 112383.48933030672, + 112407.90261167898, + 112432.31721867126, + 112456.73315113965, + 112481.15040894024, + 112505.56899192919, + 112529.98889996267, + 112554.41013289688, + 112578.8326905881, + 112603.25657289263, + 112627.68177966679, + 112652.10831076698, + 112676.53616604958, + 112700.96534537108, + 112725.39584858794, + 112749.82767555672, + 112774.26082613398, + 112798.6953001763, + 112823.13109754038, + 112847.56821808286, + 112872.00666166049, + 112896.44642813003, + 112920.88751734827, + 112945.32992917208, + 112969.77366345831, + 112994.21872006389, + 113018.66509884578, + 113043.11279966099, + 113067.56182236652, + 113092.01216681948, + 113116.46383287695, + 113140.9168203961, + 113165.37112923413, + 113189.82675924824, + 113214.28371029573, + 113238.74198223387, + 113263.20157492002, + 113287.66248821157, + 113312.12472196593, + 113336.58827604055, + 113361.05315029295, + 113385.51934458067, + 113409.98685876124, + 113434.45569269233, + 113458.92584623155, + 113483.39731923661, + 113507.87011156522, + 113532.34422307517, + 113556.81965362425, + 113581.2964030703, + 113605.77447127122, + 113630.25385808491, + 113654.73456336933, + 113679.2165869825, + 113703.69992878241, + 113728.18458862718, + 113752.67056637487, + 113777.15786188368, + 113801.64647501177, + 113826.13640561736, + 113850.62765355874, + 113875.12021869418, + 113899.61410088204, + 113924.1092999807, + 113948.60581584855, + 113973.10364834407, + 113997.60279732574, + 114022.1032626521, + 114046.60504418171, + 114071.10814177318, + 114095.61255528514, + 114120.11828457628, + 114144.62532950533, + 114169.13368993104, + 114193.6433657122, + 114218.15435670764, + 114242.66666277625, + 114267.18028377694, + 114291.69521956862, + 114316.21147001031, + 114340.72903496103, + 114365.24791427983, + 114389.7681078258, + 114414.2896154581, + 114438.81243703589, + 114463.33657241837, + 114487.8620214648, + 114512.38878403447, + 114536.91685998671, + 114561.44624918087, + 114585.97695147636, + 114610.5089667326, + 114635.04229480909, + 114659.57693556532, + 114684.11288886084, + 114708.65015455526, + 114733.18873250818, + 114757.72862257928, + 114782.26982462825, + 114806.81233851484, + 114831.35616409882, + 114855.90130123998, + 114880.44774979822, + 114904.99550963337, + 114929.5445806054, + 114954.09496257425, + 114978.64665539992, + 115003.19965894247, + 115027.75397306195, + 115052.30959761847, + 115076.86653247218, + 115101.42477748329, + 115125.984332512, + 115150.54519741859, + 115175.10737206334, + 115199.67085630659, + 115224.23565000873, + 115248.80175303014, + 115273.3691652313, + 115297.93788647266, + 115322.50791661476, + 115347.07925551817, + 115371.65190304347, + 115396.22585905129, + 115420.80112340231, + 115445.37769595724, + 115469.95557657682, + 115494.53476512182, + 115519.11526145306, + 115543.69706543141, + 115568.28017691776, + 115592.86459577303, + 115617.4503218582, + 115642.03735503425, + 115666.62569516223, + 115691.21534210323, + 115715.80629571836, + 115740.39855586876, + 115764.99212241563, + 115789.58699522018, + 115814.18317414368, + 115838.78065904744, + 115863.37944979276, + 115887.97954624105, + 115912.5809482537, + 115937.18365569216, + 115961.78766841792, + 115986.39298629249, + 116010.99960917742, + 116035.60753693432, + 116060.21676942479, + 116084.82730651053, + 116109.43914805322, + 116134.0522939146, + 116158.66674395646, + 116183.2824980406, + 116207.89955602887, + 116232.51791778316, + 116257.13758316539, + 116281.75855203751, + 116306.38082426153, + 116331.00439969949, + 116355.62927821343, + 116380.25545966547, + 116404.88294391775, + 116429.51173083246, + 116454.14182027178, + 116478.77321209799, + 116503.40590617337, + 116528.03990236025, + 116552.67520052097, + 116577.31180051794, + 116601.94970221359, + 116626.5889054704, + 116651.22941015086, + 116675.87121611751, + 116700.51432323294, + 116725.15873135976, + 116749.8044403606, + 116774.45145009817, + 116799.0997604352, + 116823.74937123443, + 116848.40028235866, + 116873.05249367072, + 116897.70600503348, + 116922.36081630984, + 116947.01692736275, + 116971.67433805518, + 116996.33304825013, + 117020.99305781067, + 117045.65436659988, + 117070.31697448085, + 117094.98088131678, + 117119.64608697082, + 117144.31259130624, + 117168.98039418629, + 117193.64949547425, + 117218.31989503348, + 117242.99159272734, + 117267.66458841923, + 117292.33888197262, + 117317.01447325097, + 117341.6913621178, + 117366.36954843666, + 117391.04903207115, + 117415.72981288488, + 117440.41189074152, + 117465.09526550474, + 117489.77993703831, + 117514.46590520597, + 117539.15316987153, + 117563.84173089883, + 117588.53158815173, + 117613.22274149416, + 117637.91519079007, + 117662.60893590341, + 117687.30397669821, + 117712.00031303853, + 117736.69794478847, + 117761.39687181212, + 117786.09709397367, + 117810.7986111373, + 117835.50142316725, + 117860.20552992777, + 117884.91093128319, + 117909.6176270978, + 117934.32561723603, + 117959.03490156225, + 117983.74547994092, + 118008.45735223651, + 118033.17051831353, + 118057.88497803656, + 118082.60073127014, + 118107.31777787894, + 118132.03611772758, + 118156.75575068076, + 118181.47667660323, + 118206.19889535972, + 118230.92240681504, + 118255.64721083404, + 118280.37330728157, + 118305.10069602253, + 118329.82937692189, + 118354.55934984458, + 118379.29061465565, + 118404.02317122012, + 118428.75701940308, + 118453.49215906965, + 118478.22859008498, + 118502.96631231424, + 118527.70532562268, + 118552.44562987552, + 118577.18722493808, + 118601.93011067568, + 118626.67428695368, + 118651.41975363747, + 118676.16651059251, + 118700.91455768423, + 118725.66389477813, + 118750.41452173979, + 118775.16643843475, + 118799.91964472862, + 118824.67414048707, + 118849.42992557574, + 118874.18699986035, + 118898.94536320666, + 118923.70501548045, + 118948.46595654752, + 118973.22818627374, + 118997.99170452499, + 119022.7565111672, + 119047.52260606633, + 119072.28998908834, + 119097.0586600993, + 119121.82861896523, + 119146.59986555226, + 119171.3723997265, + 119196.14622135412, + 119220.92133030134, + 119245.69772643436, + 119270.47540961947, + 119295.25437972297, + 119320.03463661121, + 119344.81618015055, + 119369.5990102074, + 119394.38312664822, + 119419.16852933947, + 119443.95521814766, + 119468.74319293935, + 119493.53245358112, + 119518.32299993958, + 119543.11483188139, + 119567.90794927324, + 119592.70235198183, + 119617.49803987393, + 119642.29501281632, + 119667.09327067583, + 119691.89281331931, + 119716.69364061367, + 119741.49575242582, + 119766.29914862274, + 119791.10382907141, + 119815.90979363887, + 119840.71704219218, + 119865.52557459843, + 119890.33539072477, + 119915.14649043836, + 119939.95887360642, + 119964.77254009615, + 119989.58748977486, + 120014.40372250983, + 120039.22123816841, + 120064.04003661797, + 120088.86011772591, + 120113.6814813597, + 120138.5041273868, + 120163.3280556747, + 120188.15326609099, + 120212.97975850321, + 120237.807532779, + 120262.63658878599, + 120287.46692639188, + 120312.29854546436, + 120337.13144587121, + 120361.9656274802, + 120386.80109015915, + 120411.63783377589, + 120436.47585819835, + 120461.31516329442, + 120486.15574893207, + 120510.99761497928, + 120535.84076130406, + 120560.68518777451, + 120585.53089425867, + 120610.3778806247, + 120635.22614674074, + 120660.07569247499, + 120684.92651769568, + 120709.77862227106, + 120734.63200606944, + 120759.48666895913, + 120784.3426108085, + 120809.19983148595, + 120834.05833085992, + 120858.91810879884, + 120883.77916517125, + 120908.64149984565, + 120933.5051126906, + 120958.37000357473, + 120983.23617236665, + 121008.10361893504, + 121032.97234314861, + 121057.84234487606, + 121082.71362398617, + 121107.58618034775, + 121132.46001382964, + 121157.33512430069, + 121182.21151162982, + 121207.08917568595, + 121231.96811633807, + 121256.84833345517, + 121281.72982690629, + 121306.61259656049, + 121331.49664228689, + 121356.38196395461, + 121381.26856143285, + 121406.15643459078, + 121431.04558329767, + 121455.93600742276, + 121480.82770683538, + 121505.72068140487, + 121530.61493100057, + 121555.51045549192, + 121580.40725474835, + 121605.30532863933, + 121630.20467703436, + 121655.10529980299, + 121680.00719681478, + 121704.91036793934, + 121729.81481304632, + 121754.72053200539, + 121779.62752468624, + 121804.53579095862, + 121829.44533069231, + 121854.3561437571, + 121879.26823002285, + 121904.1815893594, + 121929.09622163669, + 121954.01212672464, + 121978.92930449323, + 122003.84775481246, + 122028.76747755238, + 122053.68847258303, + 122078.61073977455, + 122103.53427899707, + 122128.45909012076, + 122153.38517301581, + 122178.31252755247, + 122203.24115360099, + 122228.17105103172, + 122253.10221971494, + 122278.03465952107, + 122302.96837032049, + 122327.90335198362, + 122352.83960438096, + 122377.777127383, + 122402.71592086025, + 122427.65598468333, + 122452.59731872278, + 122477.53992284928, + 122502.48379693348, + 122527.42894084606, + 122552.37535445779, + 122577.32303763942, + 122602.27199026172, + 122627.22221219557, + 122652.17370331181, + 122677.12646348133, + 122702.08049257506, + 122727.03579046397, + 122751.99235701906, + 122776.95019211136, + 122801.9092956119, + 122826.8696673918, + 122851.83130732219, + 122876.79421527422, + 122901.75839111909, + 122926.72383472799, + 122951.69054597223, + 122976.65852472307, + 123001.62777085182, + 123026.59828422987, + 123051.57006472857, + 123076.54311221937, + 123101.5174265737, + 123126.49300766307, + 123151.46985535898, + 123176.44796953299, + 123201.42735005668, + 123226.40799680166, + 123251.38990963959, + 123276.37308844214, + 123301.35753308103, + 123326.343243428, + 123351.33021935483, + 123376.31846073334, + 123401.30796743535, + 123426.29873933276, + 123451.29077629748, + 123476.28407820144, + 123501.2786449166, + 123526.27447631498, + 123551.27157226863, + 123576.26993264959, + 123601.26955732999, + 123626.27044618195, + 123651.27259907764, + 123676.27601588926, + 123701.28069648903, + 123726.28664074924, + 123751.29384854218, + 123776.30231974016, + 123801.31205421555, + 123826.32305184075, + 123851.33531248817, + 123876.34883603029, + 123901.36362233957, + 123926.37967128855, + 123951.39698274979, + 123976.41555659588, + 124001.43539269941, + 124026.45649093305, + 124051.47885116948, + 124076.50247328142, + 124101.5273571416, + 124126.55350262282, + 124151.58090959788, + 124176.60957793961, + 124201.63950752091, + 124226.67069821467, + 124251.70314989384, + 124276.73686243138, + 124301.7718357003, + 124326.80806957364, + 124351.84556392446, + 124376.88431862585, + 124401.92433355095, + 124426.96560857294, + 124452.00814356498, + 124477.05193840031, + 124502.0969929522, + 124527.14330709392, + 124552.19088069882, + 124577.23971364023, + 124602.28980579154, + 124627.34115702618, + 124652.3937672176, + 124677.44763623926, + 124702.50276396469, + 124727.55915026742, + 124752.61679502104, + 124777.67569809916, + 124802.73585937542, + 124827.79727872348, + 124852.85995601704, + 124877.92389112986, + 124902.98908393568, + 124928.05553430831, + 124953.1232421216, + 124978.19220724938, + 125003.26242956554, + 125028.33390894404, + 125053.40664525882, + 125078.48063838384, + 125103.55588819318, + 125128.63239456083, + 125153.71015736091, + 125178.78917646752, + 125203.86945175481, + 125228.95098309696, + 125254.03377036817, + 125279.1178134427, + 125304.20311219479, + 125329.28966649878, + 125354.37747622898, + 125379.46654125977, + 125404.55686146552, + 125429.6484367207, + 125454.74126689974, + 125479.83535187715, + 125504.93069152744, + 125530.02728572517, + 125555.12513434493, + 125580.22423726133, + 125605.32459434902, + 125630.4262054827, + 125655.52907053704, + 125680.63318938682, + 125705.73856190679, + 125730.84518797178, + 125755.9530674566, + 125781.06220023613, + 125806.17258618528, + 125831.28422517896, + 125856.39711709213, + 125881.51126179981, + 125906.62665917698, + 125931.74330909875, + 125956.86121144016, + 125981.98036607634, + 126007.10077288245, + 126032.22243173365, + 126057.34534250517, + 126082.46950507225, + 126107.59491931014, + 126132.72158509417, + 126157.84950229966, + 126182.97867080198, + 126208.10909047653, + 126233.24076119871, + 126258.37368284403, + 126283.50785528794, + 126308.64327840599, + 126333.77995207369, + 126358.91787616667, + 126384.0570505605, + 126409.19747513086, + 126434.3391497534, + 126459.48207430386, + 126484.62624865794, + 126509.77167269142, + 126534.9183462801, + 126560.06626929982, + 126585.21544162642, + 126610.36586313581, + 126635.51753370393, + 126660.67045320668, + 126685.82462152008, + 126710.98003852014, + 126736.13670408291, + 126761.29461808444, + 126786.45378040087, + 126811.61419090834, + 126836.77584948298, + 126861.93875600102, + 126887.10291033868, + 126912.26831237224, + 126937.43496197795, + 126962.60285903217, + 126987.77200341123, + 127012.94239499152, + 127038.11403364947, + 127063.2869192615, + 127088.46105170409, + 127113.63643085376, + 127138.81305658702, + 127163.99092878048, + 127189.17004731069, + 127214.35041205429, + 127239.53202288797, + 127264.71487968838, + 127289.89898233226, + 127315.08433069635, + 127340.27092465744, + 127365.45876409234, + 127390.64784887788, + 127415.83817889093, + 127441.02975400841, + 127466.22257410725, + 127491.41663906439, + 127516.61194875685, + 127541.80850306165, + 127567.00630185583, + 127592.20534501647, + 127617.4056324207, + 127642.60716394568, + 127667.80993946856, + 127693.01395886653, + 127718.21922201688, + 127743.42572879682, + 127768.63347908368, + 127793.84247275478, + 127819.05270968749, + 127844.26418975917, + 127869.47691284724, + 127894.69087882918, + 127919.90608758242, + 127945.12253898452, + 127970.34023291297, + 127995.55916924537, + 128020.77934785932, + 128046.00076863244, + 128071.22343144237, + 128096.44733616684, + 128121.67248268353, + 128146.89887087021, + 128172.12650060465, + 128197.35537176467, + 128222.5854842281, + 128247.81683787282, + 128273.04943257671, + 128298.28326821771, + 128323.51834467379, + 128348.75466182294, + 128373.99221954317, + 128399.23101771252, + 128424.47105620909, + 128449.71233491098, + 128474.95485369631, + 128500.19861244329, + 128525.44361103009, + 128550.68984933494, + 128575.93732723613, + 128601.18604461191, + 128626.43600134061, + 128651.68719730059, + 128676.93963237021, + 128702.1933064279, + 128727.44821935208, + 128752.70437102125, + 128777.96176131385, + 128803.22039010846, + 128828.48025728362, + 128853.74136271792, + 128879.00370628996, + 128904.26728787841, + 128929.53210736193, + 128954.79816461923, + 128980.06545952905, + 129005.33399197015, + 129030.60376182134, + 129055.87476896142, + 129081.14701326926, + 129106.42049462376, + 129131.6952129038, + 129156.97116798835, + 129182.24835975636, + 129207.52678808685, + 129232.80645285884, + 129258.08735395141, + 129283.36949124365, + 129308.65286461466, + 129333.9374739436, + 129359.22331910966, + 129384.51039999202, + 129409.79871646997, + 129435.08826842274, + 129460.37905572963, + 129485.67107826998, + 129510.96433592314, + 129536.25882856851, + 129561.55455608548, + 129586.85151835352, + 129612.14971525209, + 129637.4491466607, + 129662.74981245887, + 129688.0517125262, + 129713.35484674224, + 129738.65921498663, + 129763.96481713903, + 129789.27165307909, + 129814.57972268655, + 129839.88902584116, + 129865.19956242264, + 129890.51133231082, + 129915.82433538554, + 129941.13857152662, + 129966.45404061397, + 129991.7707425275, + 130017.08867714716, + 130042.4078443529, + 130067.72824402474, + 130093.04987604271, + 130118.37274028687, + 130143.69683663732, + 130169.02216497416, + 130194.34872517755, + 130219.67651712766, + 130245.0055407047, + 130270.33579578891, + 130295.66728226055, + 130320.99999999991, + 130346.33394888733, + 130371.66912880314, + 130397.00553962773, + 130422.34318124152, + 130447.68205352494, + 130473.02215635845, + 130498.36348962256, + 130523.70605319779, + 130549.0498469647, + 130574.39487080388, + 130599.74112459592, + 130625.08860822149, + 130650.43732156123, + 130675.78726449587, + 130701.13843690613, + 130726.49083867275, + 130751.84446967654, + 130777.19932979831, + 130802.5554189189, + 130827.91273691918, + 130853.27128368006, + 130878.63105908247, + 130903.99206300738, + 130929.35429533575, + 130954.71775594862, + 130980.08244472703, + 131005.44836155206, + 131030.81550630482, + 131056.18387886642, + 131081.55347911804, + 131106.92430694087, + 131132.29636221612, + 131157.66964482504, + 131183.0441546489, + 131208.41989156904, + 131233.79685546676, + 131259.17504622342, + 131284.55446372041, + 131309.93510783918, + 131335.31697846117, + 131360.70007546784, + 131386.0843987407, + 131411.46994816128, + 131436.85672361116, + 131462.24472497194, + 131487.63395212521, + 131513.02440495262, + 131538.41608333588, + 131563.80898715663, + 131589.2031162967, + 131614.59847063778, + 131639.9950500617, + 131665.39285445024, + 131690.79188368531, + 131716.19213764873, + 131741.59361622241, + 131766.99631928833, + 131792.40024672839, + 131817.80539842462, + 131843.21177425905, + 131868.61937411371, + 131894.02819787065, + 131919.43824541202, + 131944.84951661993, + 131970.26201137656, + 131995.67572956407, + 132021.09067106468, + 132046.50683576067, + 132071.9242235343, + 132097.34283426782, + 132122.76266784366, + 132148.1837241441, + 132173.60600305157, + 132199.02950444847, + 132224.45422821722, + 132249.88017424036, + 132275.30734240031, + 132300.73573257966, + 132326.16534466096, + 132351.59617852676, + 132377.02823405969, + 132402.46151114244, + 132427.89600965759, + 132453.33172948789, + 132478.76867051609, + 132504.20683262491, + 132529.64621569714, + 132555.08681961559, + 132580.5286442631, + 132605.97168952253, + 132631.41595527678, + 132656.86144140881, + 132682.30814780149, + 132707.75607433787, + 132733.20522090094, + 132758.65558737374, + 132784.10717363929, + 132809.55997958075, + 132835.01400508118, + 132860.46925002377, + 132885.92571429166, + 132911.38339776811, + 132936.84230033628, + 132962.30242187946, + 132987.76376228096, + 133013.22632142407, + 133038.69009919214, + 133064.15509546854, + 133089.62131013666, + 133115.08874307995, + 133140.55739418184, + 133166.02726332581, + 133191.49835039541, + 133216.97065527414, + 133242.44417784561, + 133267.91891799335, + 133293.39487560102, + 133318.87205055228, + 133344.35044273079, + 133369.83005202023, + 133395.31087830439, + 133420.79292146701, + 133446.27618139185, + 133471.76065796276, + 133497.24635106357, + 133522.73326057816, + 133548.22138639039, + 133573.71072838426, + 133599.20128644365, + 133624.69306045261, + 133650.1860502951, + 133675.68025585517, + 133701.1756770169, + 133726.67231366437, + 133752.17016568172, + 133777.66923295305, + 133803.16951536259, + 133828.67101279454, + 133854.17372513309, + 133879.67765226253, + 133905.18279406714, + 133930.68915043125, + 133956.19672123916, + 133981.70550637526, + 134007.21550572399, + 134032.7267191697, + 134058.23914659687, + 134083.75278789, + 134109.26764293358, + 134134.78371161217, + 134160.30099381026, + 134185.8194894125, + 134211.33919830353, + 134236.8601203679, + 134262.38225549037, + 134287.90560355558, + 134313.43016444831, + 134338.95593805326, + 134364.48292425525, + 134390.01112293909, + 134415.54053398955, + 134441.07115729159, + 134466.60299273001, + 134492.1360401898, + 134517.67029955584, + 134543.20577071316, + 134568.74245354676, + 134594.28034794159, + 134619.81945378278, + 134645.35977095537, + 134670.90129934452, + 134696.4440388353, + 134721.98798931291, + 134747.53315066252, + 134773.07952276937, + 134798.62710551871, + 134824.17589879577, + 134849.72590248589, + 134875.27711647438, + 134900.82954064661, + 134926.38317488792, + 134951.93801908373, + 134977.49407311951, + 135003.05133688069, + 135028.60981025276, + 135054.16949312127, + 135079.73038537172, + 135105.29248688967, + 135130.85579756077, + 135156.42031727062, + 135181.98604590484, + 135207.55298334916, + 135233.12112948924, + 135258.69048421088, + 135284.26104739975, + 135309.83281894168, + 135335.4057987225, + 135360.97998662802, + 135386.55538254412, + 135412.13198635669, + 135437.70979795168, + 135463.28881721498, + 135488.86904403262, + 135514.45047829056, + 135540.03311987486, + 135565.61696867159, + 135591.20202456677, + 135616.78828744654, + 135642.37575719706, + 135667.96443370447, + 135693.55431685498, + 135719.14540653475, + 135744.73770263011, + 135770.33120502727, + 135795.92591361253, + 135821.52182827223, + 135847.11894889272, + 135872.7172753604, + 135898.31680756161, + 135923.91754538284, + 135949.51948871053, + 135975.12263743114, + 136000.72699143123, + 136026.33255059729, + 136051.93931481591, + 136077.54728397369, + 136103.15645795723, + 136128.76683665317, + 136154.37841994822, + 136179.99120772901, + 136205.60519988232, + 136231.2203962949, + 136256.83679685349, + 136282.45440144493, + 136308.07320995603, + 136333.69322227367, + 136359.31443828469, + 136384.93685787608, + 136410.56048093468, + 136436.18530734754, + 136461.81133700156, + 136487.43856978384, + 136513.06700558143, + 136538.6966442813, + 136564.32748577066, + 136589.95952993655, + 136615.59277666616, + 136641.22722584667, + 136666.86287736523, + 136692.49973110916, + 136718.13778696564, + 136743.77704482197, + 136769.41750456547, + 136795.05916608346, + 136820.70202926331, + 136846.34609399244, + 136871.99136015819, + 136897.63782764805, + 136923.28549634948, + 136948.93436614997, + 136974.58443693706, + 137000.23570859825, + 137025.88818102115, + 137051.54185409332, + 137077.19672770242, + 137102.85280173609, + 137128.51007608202, + 137154.16855062786, + 137179.82822526142, + 137205.48909987041, + 137231.15117434258, + 137256.8144485658, + 137282.47892242789, + 137308.14459581667, + 137333.81146862009, + 137359.47954072602, + 137385.14881202241, + 137410.81928239719, + 137436.49095173844, + 137462.16381993407, + 137487.83788687221, + 137513.51315244089, + 137539.18961652822, + 137564.86727902229, + 137590.54613981131, + 137616.22619878338, + 137641.90745582676, + 137667.58991082967, + 137693.27356368033, + 137718.95841426702, + 137744.64446247809, + 137770.33170820182, + 137796.02015132661, + 137821.70979174081, + 137847.40062933284, + 137873.09266399115, + 137898.78589560417, + 137924.48032406042, + 137950.17594924837, + 137975.8727710566, + 138001.57078937365, + 138027.27000408815, + 138052.97041508864, + 138078.67202226384, + 138104.3748255024, + 138130.07882469296, + 138155.78401972432, + 138181.49041048516, + 138207.1979968643, + 138232.9067787505, + 138258.61675603263, + 138284.32792859949, + 138310.04029633995, + 138335.75385914298, + 138361.46861689744, + 138387.18456949232, + 138412.90171681659, + 138438.62005875923, + 138464.33959520931, + 138490.06032605586, + 138515.78225118798, + 138541.50537049473, + 138567.2296838653, + 138592.95519118884, + 138618.68189235451, + 138644.40978725153, + 138670.13887576913, + 138695.86915779658, + 138721.60063322316, + 138747.33330193823, + 138773.06716383106, + 138798.80221879104, + 138824.53846670757, + 138850.27590747006, + 138876.01454096794, + 138901.7543670907, + 138927.49538572782, + 138953.2375967688, + 138978.9810001032, + 139004.72559562061, + 139030.47138321059, + 139056.2183627628, + 139081.96653416683, + 139107.71589731239, + 139133.46645208917, + 139159.21819838689, + 139184.97113609532, + 139210.72526510421, + 139236.48058530336, + 139262.23709658257, + 139287.99479883176, + 139313.75369194071, + 139339.51377579942, + 139365.27505029776, + 139391.03751532568, + 139416.80117077316, + 139442.56601653024, + 139468.33205248689, + 139494.09927853322, + 139519.86769455927, + 139545.63730045516, + 139571.408096111, + 139597.18008141697, + 139622.95325626322, + 139648.72762054001, + 139674.5031741375, + 139700.27991694602, + 139726.05784885579, + 139751.83696975713, + 139777.61727954043, + 139803.39877809596, + 139829.18146531415, + 139854.96534108539, + 139880.75040530015, + 139906.53665784886, + 139932.32409862199, + 139958.11272751007, + 139983.90254440365, + 140009.69354919327, + 140035.48574176949, + 140061.27912202294, + 140087.07368984428, + 140112.86944512415, + 140138.66638775321, + 140164.4645176222, + 140190.26383462184, + 140216.06433864293, + 140241.86602957622, + 140267.66890731253, + 140293.47297174268, + 140319.27822275754, + 140345.08466024802, + 140370.89228410498, + 140396.70109421943, + 140422.51109048226, + 140448.32227278448, + 140474.13464101712, + 140499.94819507122, + 140525.76293483781, + 140551.57886020801, + 140577.3959710729, + 140603.21426732364, + 140629.03374885136, + 140654.85441554731, + 140680.67626730262, + 140706.49930400858, + 140732.32352555645, + 140758.1489318375, + 140783.97552274304, + 140809.80329816442, + 140835.63225799298, + 140861.46240212015, + 140887.29373043729, + 140913.12624283586, + 140938.95993920733, + 140964.79481944317, + 140990.63088343487, + 141016.46813107401, + 141042.30656225214, + 141068.14617686081, + 141093.98697479168, + 141119.82895593636, + 141145.6721201865, + 141171.51646743377, + 141197.36199756994, + 141223.20871048668, + 141249.05660607578, + 141274.90568422904, + 141300.75594483822, + 141326.6073877952, + 141352.4600129918, + 141378.31382031992, + 141404.16880967148, + 141430.02498093838, + 141455.8823340126, + 141481.74086878612, + 141507.60058515094, + 141533.46148299909, + 141559.32356222265, + 141585.18682271364, + 141611.05126436421, + 141636.9168870665, + 141662.78369071262, + 141688.65167519479, + 141714.5208404052, + 141740.39118623605, + 141766.26271257963, + 141792.1354193282, + 141818.00930637406, + 141843.88437360956, + 141869.760620927, + 141895.6380482188, + 141921.51665537735, + 141947.39644229505, + 141973.27740886438, + 141999.15955497778, + 142025.04288052776, + 142050.92738540689, + 142076.81306950765, + 142102.69993272264, + 142128.58797494444, + 142154.47719606571, + 142180.36759597904, + 142206.25917457714, + 142232.15193175265, + 142258.04586739838, + 142283.94098140698, + 142309.83727367126, + 142335.73474408401, + 142361.63339253806, + 142387.5332189262, + 142413.43422314132, + 142439.33640507635, + 142465.23976462413, + 142491.14430167765, + 142517.05001612983, + 142542.95690787368, + 142568.86497680223, + 142594.77422280848, + 142620.68464578551, + 142646.5962456264, + 142672.50902222423, + 142698.42297547215, + 142724.33810526333, + 142750.25441149093, + 142776.17189404817, + 142802.09055282827, + 142828.01038772447, + 142853.93139863008, + 142879.85358543837, + 142905.77694804268, + 142931.70148633636, + 142957.62720021277, + 142983.55408956532, + 143009.48215428743, + 143035.41139427255, + 143061.34180941415, + 143087.27339960571, + 143113.20616474075, + 143139.14010471283, + 143165.07521941551, + 143191.01150874238, + 143216.94897258704, + 143242.88761084314, + 143268.82742340435, + 143294.76841016437, + 143320.71057101688, + 143346.65390585564, + 143372.59841457437, + 143398.54409706692, + 143424.49095322701, + 143450.43898294857, + 143476.38818612538, + 143502.33856265133, + 143528.29011242036, + 143554.24283532638, + 143580.19673126334, + 143606.1518001252, + 143632.10804180597, + 143658.06545619969, + 143684.02404320039, + 143709.98380270213, + 143735.944734599, + 143761.90683878519, + 143787.87011515474, + 143813.83456360188, + 143839.8001840208, + 143865.76697630569, + 143891.73494035081, + 143917.7040760504, + 143943.67438329876, + 143969.6458619902, + 143995.61851201905, + 144021.59233327967, + 144047.56732566646, + 144073.54348907378, + 144099.52082339607, + 144125.49932852783, + 144151.4790043635, + 144177.45985079758, + 144203.44186772458, + 144229.42505503909, + 144255.40941263564, + 144281.39494040885, + 144307.38163825331, + 144333.36950606373, + 144359.35854373468, + 144385.34875116093, + 144411.34012823718, + 144437.33267485813, + 144463.32639091855, + 144489.32127631325, + 144515.31733093705, + 144541.31455468474, + 144567.3129474512, + 144593.3125091313, + 144619.31323961995, + 144645.31513881206, + 144671.31820660262, + 144697.32244288657, + 144723.32784755889, + 144749.33442051467, + 144775.34216164888, + 144801.35107085665, + 144827.36114803303, + 144853.37239307314, + 144879.38480587213, + 144905.39838632516, + 144931.41313432742, + 144957.4290497741, + 144983.44613256046, + 145009.46438258173, + 145035.48379973322, + 145061.50438391021, + 145087.52613500805, + 145113.54905292206, + 145139.57313754765, + 145165.59838878017, + 145191.62480651509, + 145217.65239064783, + 145243.68114107384, + 145269.71105768863, + 145295.74214038774, + 145321.77438906668, + 145347.80780362099, + 145373.84238394629, + 145399.87812993818, + 145425.91504149229, + 145451.95311850426, + 145477.9923608698, + 145504.03276848458, + 145530.07434124436, + 145556.11707904484, + 145582.16098178181, + 145608.20604935108, + 145634.25228164849, + 145660.29967856981, + 145686.34824001096, + 145712.39796586783, + 145738.4488560363, + 145764.50091041232, + 145790.55412889185, + 145816.60851137087, + 145842.66405774537, + 145868.72076791141, + 145894.77864176501, + 145920.83767920226, + 145946.89788011924, + 145972.95924441208, + 145999.02177197693, + 146025.08546270995, + 146051.15031650732, + 146077.21633326527, + 146103.28351288004, + 146129.35185524789, + 146155.42136026506, + 146181.49202782792, + 146207.56385783272, + 146233.63685017588, + 146259.71100475377, + 146285.78632146274, + 146311.86280019928, + 146337.94044085976, + 146364.01924334071, + 146390.09920753856, + 146416.18033334985, + 146442.26262067116, + 146468.34606939898, + 146494.43067942993, + 146520.51645066062, + 146546.60338298764, + 146572.69147630769, + 146598.78073051744, + 146624.87114551352, + 146650.96272119274, + 146677.05545745179, + 146703.14935418745, + 146729.2444112965, + 146755.34062867577, + 146781.43800622207, + 146807.53654383228, + 146833.63624140329, + 146859.73709883197, + 146885.83911601527, + 146911.94229285014, + 146938.04662923355, + 146964.15212506248, + 146990.25878023397, + 147016.36659464505, + 147042.47556819281, + 147068.58570077427, + 147094.6969922866, + 147120.80944262692, + 147146.92305169237, + 147173.03781938017, + 147199.15374558745, + 147225.27083021149, + 147251.38907314953, + 147277.50847429881, + 147303.62903355664, + 147329.75075082036, + 147355.87362598727, + 147381.99765895473, + 147408.12284962015, + 147434.24919788091, + 147460.37670363448, + 147486.50536677826, + 147512.63518720976, + 147538.76616482646, + 147564.89829952587, + 147591.03159120557, + 147617.16603976308, + 147643.30164509601, + 147669.43840710199, + 147695.57632567859, + 147721.71540072354, + 147747.85563213445, + 147773.99701980909, + 147800.13956364512, + 147826.28326354033, + 147852.42811939248, + 147878.57413109933, + 147904.72129855872, + 147930.86962166851, + 147957.01910032652, + 147983.16973443062, + 148009.32152387875, + 148035.47446856883, + 148061.62856839882, + 148087.78382326665, + 148113.94023307035, + 148140.09779770792, + 148166.25651707739, + 148192.41639107687, + 148218.57741960438, + 148244.73960255808, + 148270.90293983606, + 148297.0674313365, + 148323.23307695755, + 148349.39987659742, + 148375.56783015432, + 148401.73693752653, + 148427.90719861226, + 148454.07861330983, + 148480.25118151752, + 148506.42490313368, + 148532.59977805667, + 148558.77580618486, + 148584.95298741665, + 148611.13132165043, + 148637.31080878471, + 148663.49144871789, + 148689.6732413485, + 148715.85618657502, + 148742.040284296, + 148768.22553440998, + 148794.41193681557, + 148820.59949141133, + 148846.78819809589, + 148872.97805676793, + 148899.16906732606, + 148925.36122966901, + 148951.55454369547, + 148977.74900930419, + 149003.9446263939, + 149030.1413948634, + 149056.33931461151, + 149082.53838553699, + 149108.73860753875, + 149134.9399805156, + 149161.14250436646, + 149187.34617899026, + 149213.5510042859, + 149239.75698015234, + 149265.96410648854, + 149292.17238319354, + 149318.38181016635, + 149344.59238730598, + 149370.80411451156, + 149397.01699168212, + 149423.23101871679, + 149449.44619551473, + 149475.66252197503, + 149501.87999799693, + 149528.0986234796, + 149554.31839832227, + 149580.53932242419, + 149606.76139568459, + 149632.98461800278, + 149659.20898927809, + 149685.43450940982, + 149711.66117829733, + 149737.88899584001, + 149764.11796193724, + 149790.34807648844, + 149816.57933939309, + 149842.81175055061, + 149869.04530986046, + 149895.28001722222, + 149921.51587253538, + 149947.75287569952, + 149973.99102661415, + 150000.23032517891, + 150026.47077129342, + 150052.71236485732, + 150078.95510577026, + 150105.1989939319, + 150131.444029242, + 150157.69021160025, + 150183.93754090639, + 150210.18601706024, + 150236.43563996154, + 150262.68640951012, + 150288.93832560582, + 150315.19138814852, + 150341.44559703805, + 150367.70095217437, + 150393.95745345735, + 150420.21510078697, + 150446.47389406321, + 150472.73383318601, + 150498.99491805542, + 150525.25714857146, + 150551.52052463419, + 150577.78504614369, + 150604.05071300003, + 150630.31752510337, + 150656.58548235384, + 150682.85458465159, + 150709.1248318968, + 150735.39622398972, + 150761.66876083051, + 150787.9424423195, + 150814.21726835691, + 150840.49323884305, + 150866.77035367821, + 150893.04861276277, + 150919.32801599705, + 150945.60856328148, + 150971.89025451642, + 150998.17308960229, + 151024.45706843957, + 151050.74219092872, + 151077.02845697021, + 151103.31586646455, + 151129.60441931229, + 151155.894115414, + 151182.1849546702, + 151208.47693698155, + 151234.77006224863, + 151261.06433037209, + 151287.35974125259, + 151313.65629479082, + 151339.95399088747, + 151366.25282944329, + 151392.55281035902, + 151418.85393353543, + 151445.1561988733, + 151471.45960627345, + 151497.76415563675, + 151524.06984686397, + 151550.37667985607, + 151576.68465451393, + 151602.99377073845, + 151629.30402843058, + 151655.61542749128, + 151681.92796782157, + 151708.24164932242, + 151734.55647189484, + 151760.87243543993, + 151787.18953985872, + 151813.50778505235, + 151839.82717092187, + 151866.14769736846, + 151892.46936429327, + 151918.79217159748, + 151945.11611918229, + 151971.44120694889, + 151997.76743479856, + 152024.09480263255, + 152050.42331035214, + 152076.75295785864, + 152103.08374505339, + 152129.41567183775, + 152155.74873811303, + 152182.08294378067, + 152208.41828874208, + 152234.75477289871, + 152261.09239615197, + 152287.43115840337, + 152313.77105955439, + 152340.11209950657, + 152366.45427816146, + 152392.79759542056, + 152419.14205118554, + 152445.48764535793, + 152471.8343778394, + 152498.18224853161, + 152524.53125733617, + 152550.88140415482, + 152577.23268888926, + 152603.58511144121, + 152629.93867171241, + 152656.29336960468, + 152682.64920501978, + 152709.00617785956, + 152735.36428802583, + 152761.72353542043, + 152788.08391994529, + 152814.44544150229, + 152840.80809999333, + 152867.17189532038, + 152893.53682738543, + 152919.90289609041, + 152946.27010133737, + 152972.63844302832, + 152999.00792106529, + 153025.37853535041, + 153051.7502857857, + 153078.12317227334, + 153104.4971947154, + 153130.8723530141, + 153157.24864707157, + 153183.62607679001, + 153210.00464207167, + 153236.38434281875, + 153262.76517893354, + 153289.14715031831, + 153315.53025687535, + 153341.91449850702, + 153368.2998751156, + 153394.68638660354, + 153421.07403287315, + 153447.46281382689, + 153473.85272936718, + 153500.24377939643, + 153526.63596381716, + 153553.02928253182, + 153579.42373544298, + 153605.81932245308, + 153632.21604346478, + 153658.61389838057, + 153685.0128871031, + 153711.41300953497, + 153737.81426557881, + 153764.21665513728, + 153790.62017811305, + 153817.02483440886, + 153843.43062392739, + 153869.83754657139, + 153896.24560224367, + 153922.65479084692, + 153949.06511228404, + 153975.4765664578, + 154001.88915327107, + 154028.30287262669, + 154054.71772442761, + 154081.13370857667, + 154107.55082497682, + 154133.96907353101, + 154160.38845414223, + 154186.80896671346, + 154213.23061114774, + 154239.65338734805, + 154266.07729521746, + 154292.50233465908, + 154318.92850557598, + 154345.35580787127, + 154371.7842414481, + 154398.21380620965, + 154424.64450205903, + 154451.07632889951, + 154477.50928663427, + 154503.94337516659, + 154530.37859439969, + 154556.81494423689, + 154583.25242458144, + 154609.69103533673, + 154636.13077640603, + 154662.57164769279, + 154689.01364910032, + 154715.45678053208, + 154741.90104189145, + 154768.34643308193, + 154794.79295400696, + 154821.24060457002, + 154847.68938467462, + 154874.13929422433, + 154900.59033312264, + 154927.04250127316, + 154953.49579857948, + 154979.95022494521, + 155006.40578027396, + 155032.86246446942, + 155059.32027743524, + 155085.77921907514, + 155112.2392892928, + 155138.70048799197, + 155165.16281507642, + 155191.62627044989, + 155218.09085401625, + 155244.55656567923, + 155271.02340534274, + 155297.49137291059, + 155323.96046828668, + 155350.4306913749, + 155376.90204207919, + 155403.37452030348, + 155429.84812595171, + 155456.32285892789, + 155482.79871913602, + 155509.27570648011, + 155535.75382086422, + 155562.23306219239, + 155588.71343036872, + 155615.19492529731, + 155641.67754688227, + 155668.16129502779, + 155694.64616963797, + 155721.13217061706, + 155747.61929786921, + 155774.10755129869, + 155800.59693080973, + 155827.08743630661, + 155853.57906769359, + 155880.07182487496, + 155906.56570775513, + 155933.06071623837, + 155959.55685022907, + 155986.05410963166, + 156012.5524943505, + 156039.05200429002, + 156065.55263935472, + 156092.054399449, + 156118.5572844774, + 156145.06129434443, + 156171.5664289546, + 156198.07268821247, + 156224.5800720226, + 156251.08858028959, + 156277.59821291809, + 156304.10896981266, + 156330.62085087801, + 156357.1338560188, + 156383.64798513969, + 156410.16323814544, + 156436.67961494075, + 156463.1971154304, + 156489.71573951913, + 156516.23548711176, + 156542.75635811311, + 156569.27835242799, + 156595.80146996127, + 156622.32571061782, + 156648.85107430254, + 156675.37756092031, + 156701.90517037612, + 156728.43390257491, + 156754.96375742162, + 156781.49473482129, + 156808.02683467892, + 156834.5600568995, + 156861.09440138817, + 156887.62986804993, + 156914.16645678994, + 156940.70416751326, + 156967.24300012505, + 156993.78295453047, + 157020.32403063469, + 157046.8662283429, + 157073.40954756032, + 157099.9539881922, + 157126.49955014378, + 157153.04623332032, + 157179.59403762716, + 157206.14296296958, + 157232.69300925292, + 157259.24417638258, + 157285.79646426387, + 157312.34987280221, + 157338.90440190304, + 157365.46005147175, + 157392.01682141385, + 157418.57471163478, + 157445.13372204005, + 157471.69385253513, + 157498.25510302564, + 157524.81747341706, + 157551.38096361503, + 157577.9455735251, + 157604.51130305286, + 157631.07815210402, + 157657.64612058419, + 157684.21520839902, + 157710.78541545427, + 157737.35674165559, + 157763.92918690876, + 157790.50275111952, + 157817.07743419363, + 157843.65323603692, + 157870.23015655516, + 157896.80819565422, + 157923.3873532399, + 157949.96762921812, + 157976.54902349479, + 158003.13153597576, + 158029.71516656701, + 158056.29991517449, + 158082.88578170416, + 158109.47276606198, + 158136.06086815402, + 158162.65008788629, + 158189.24042516484, + 158215.83187989573, + 158242.42445198505, + 158269.01814133892, + 158295.61294786347, + 158322.20887146486, + 158348.80591204923, + 158375.4040695228, + 158402.00334379176, + 158428.60373476235, + 158455.2052423408, + 158481.80786643337, + 158508.41160694641, + 158535.01646378616, + 158561.62243685898, + 158588.2295260712, + 158614.8377313292, + 158641.44705253936, + 158668.05748960807, + 158694.66904244179, + 158721.28171094693, + 158747.89549502998, + 158774.5103945974, + 158801.12640955573, + 158827.74353981143, + 158854.36178527112, + 158880.9811458413, + 158907.60162142856, + 158934.22321193956, + 158960.84591728085, + 158987.46973735912, + 159014.09467208097, + 159040.72072135314, + 159067.3478850823, + 159093.97616317519, + 159120.60555553852, + 159147.23606207906, + 159173.8676827036, + 159200.50041731889, + 159227.13426583182, + 159253.76922814918, + 159280.40530417781, + 159307.04249382461, + 159333.68079699649, + 159360.32021360032, + 159386.96074354305, + 159413.60238673165, + 159440.24514307309, + 159466.88901247433, + 159493.53399484244, + 159520.18009008438, + 159546.82729810724, + 159573.47561881805, + 159600.12505212394, + 159626.77559793202, + 159653.42725614941, + 159680.08002668325, + 159706.73390944069, + 159733.38890432892, + 159760.04501125516, + 159786.70223012666, + 159813.36056085059, + 159840.02000333427, + 159866.68055748497, + 159893.34222320997, + 159920.00500041663, + 159946.66888901225, + 159973.33388890422, + 159999.99999999988, + 160026.66722220668, + 160053.33555543202, + 160080.0049995833, + 160106.67555456801, + 160133.3472202936, + 160160.0199966676, + 160186.6938835975, + 160213.36888099083, + 160240.04498875517, + 160266.72220679806, + 160293.40053502709, + 160320.07997334987, + 160346.76052167406, + 160373.44217990729, + 160400.1249479572, + 160426.80882573154, + 160453.49381313793, + 160480.17991008417, + 160506.86711647795, + 160533.55543222709, + 160560.24485723933, + 160586.93539142248, + 160613.62703468435, + 160640.31978693281, + 160667.01364807569, + 160693.70861802087, + 160720.40469667627, + 160747.1018839498, + 160773.80017974938, + 160800.49958398298, + 160827.20009655855, + 160853.90171738411, + 160880.60444636765, + 160907.30828341722, + 160934.01322844089, + 160960.71928134665, + 160987.42644204266, + 161014.13471043704, + 161040.84408643784, + 161067.55456995327, + 161094.26616089148, + 161120.97885916062, + 161147.69266466892, + 161174.40757732463, + 161201.12359703594, + 161227.84072371112, + 161254.55895725847, + 161281.27829758628, + 161307.99874460287, + 161334.72029821656, + 161361.44295833571, + 161388.1667248687, + 161414.89159772391, + 161441.61757680977, + 161468.34466203468, + 161495.07285330712, + 161521.80215053557, + 161548.53255362847, + 161575.26406249436, + 161601.99667704175, + 161628.7303971792, + 161655.46522281526, + 161682.20115385848, + 161708.93819021754, + 161735.67633180099, + 161762.41557851751, + 161789.15593027571, + 161815.89738698432, + 161842.63994855201, + 161869.38361488748, + 161896.1283858995, + 161922.87426149679, + 161949.62124158812, + 161976.36932608229, + 162003.1185148881, + 162029.8688079144, + 162056.62020507001, + 162083.37270626382, + 162110.12631140469, + 162136.88102040152, + 162163.63683316324, + 162190.39374959879, + 162217.15176961714, + 162243.91089312723, + 162270.67112003808, + 162297.43245025873, + 162324.19488369819, + 162350.9584202655, + 162377.72305986975, + 162404.48880242003, + 162431.25564782543, + 162458.02359599507, + 162484.79264683815, + 162511.56280026378, + 162538.33405618116, + 162565.10641449949, + 162591.87987512801, + 162618.65443797593, + 162645.43010295252, + 162672.20686996708, + 162698.98473892888, + 162725.76370974723, + 162752.54378233149, + 162779.32495659095, + 162806.10723243505, + 162832.89060977317, + 162859.67508851466, + 162886.46066856899, + 162913.24734984562, + 162940.03513225398, + 162966.82401570358, + 162993.6140001039, + 163020.40508536444, + 163047.19727139481, + 163073.99055810447, + 163100.78494540305, + 163127.58043320014, + 163154.37702140535, + 163181.17470992831, + 163207.97349867865, + 163234.77338756606, + 163261.57437650024, + 163288.37646539087, + 163315.17965414765, + 163341.98394268038, + 163368.78933089875, + 163395.59581871261, + 163422.40340603172, + 163449.2120927659, + 163476.02187882498, + 163502.83276411882, + 163529.6447485573, + 163556.45783205028, + 163583.2720145077, + 163610.08729583945, + 163636.90367595552, + 163663.72115476584, + 163690.53973218042, + 163717.35940810922, + 163744.18018246227, + 163771.00205514964, + 163797.82502608138, + 163824.64909516752, + 163851.4742623182, + 163878.3005274435, + 163905.12789045356, + 163931.95635125853, + 163958.78590976857, + 163985.61656589387, + 164012.44831954464, + 164039.28117063109, + 164066.11511906344, + 164092.95016475199, + 164119.78630760699, + 164146.62354753874, + 164173.46188445756, + 164200.30131827376, + 164227.14184889771, + 164253.98347623978, + 164280.82620021031, + 164307.67002071979, + 164334.51493767856, + 164361.3609509971, + 164388.20806058586, + 164415.05626635533, + 164441.905568216, + 164468.75596607837, + 164495.607459853, + 164522.4600494504, + 164549.31373478117, + 164576.16851575591, + 164603.02439228518, + 164629.88136427966, + 164656.73943164994, + 164683.59859430668, + 164710.45885216061, + 164737.32020512238, + 164764.1826531027, + 164791.04619601235, + 164817.91083376206, + 164844.77656626256, + 164871.64339342469, + 164898.51131515924, + 164925.38033137703, + 164952.25044198887, + 164979.1216469057, + 165005.9939460383, + 165032.86733929763, + 165059.7418265946, + 165086.61740784015, + 165113.4940829452 +}; + +#else + +#ifdef BIG_IQ_TABLE +#define IQ_TABLE_SIZE 8192 +#else +#define IQ_TABLE_SIZE 1026 +#endif + +ALIGN static const real_t iq_table[IQ_TABLE_SIZE] = +{ + REAL_CONST(0.0), + REAL_CONST(1.0/8.0), + REAL_CONST(2.5198420997897464/8.0), + REAL_CONST(4.3267487109222245/8.0), + REAL_CONST(6.3496042078727974/8.0), + REAL_CONST(8.5498797333834844/8.0), + REAL_CONST(10.902723556992836/8.0), + REAL_CONST(13.390518279406722/8.0), + REAL_CONST(15.999999999999998/8.0), + REAL_CONST(18.720754407467133/8.0), + REAL_CONST(21.544346900318832/8.0), + REAL_CONST(24.463780996262464/8.0), + REAL_CONST(27.47314182127996/8.0), + REAL_CONST(30.567350940369842/8.0), + REAL_CONST(33.741991698453212/8.0), + REAL_CONST(36.993181114957046/8.0), + REAL_CONST(40.317473596635935/8.0), + REAL_CONST(43.711787041189993/8.0), + REAL_CONST(47.173345095760126/8.0), + REAL_CONST(50.699631325716943/8.0), + REAL_CONST(54.288352331898118/8.0), + REAL_CONST(57.937407704003519/8.0), + REAL_CONST(61.6448652744185/8.0), + REAL_CONST(65.408940536585988/8.0), + REAL_CONST(69.227979374755591/8.0), + REAL_CONST(73.100443455321638/8.0), + REAL_CONST(77.024897778591622/8.0), + REAL_CONST(80.999999999999986/8.0), + REAL_CONST(85.024491212518527/8.0), + REAL_CONST(89.097187944889555/8.0), + REAL_CONST(93.216975178615741/8.0), + REAL_CONST(97.382800224133163/8.0), + REAL_CONST(101.59366732596474/8.0), + REAL_CONST(105.84863288986224/8.0), + REAL_CONST(110.14680124343441/8.0), + REAL_CONST(114.4873208566006/8.0), + REAL_CONST(118.86938096020653/8.0), + REAL_CONST(123.29220851090024/8.0), + REAL_CONST(127.75506545836058/8.0), + REAL_CONST(132.25724627755247/8.0), + REAL_CONST(136.79807573413572/8.0), + REAL_CONST(141.37690685569191/8.0), + REAL_CONST(145.99311908523086/8.0), + REAL_CONST(150.6461165966291/8.0), + REAL_CONST(155.33532675434674/8.0), + REAL_CONST(160.06019870205279/8.0), + REAL_CONST(164.82020206673349/8.0), + REAL_CONST(169.61482576651861/8.0), + REAL_CONST(174.44357691188537/8.0), + REAL_CONST(179.30597979112557/8.0), + REAL_CONST(184.20157493201927/8.0), + REAL_CONST(189.12991823257562/8.0), + REAL_CONST(194.09058015449685/8.0), + REAL_CONST(199.08314497371677/8.0), + REAL_CONST(204.1072100829694/8.0), + REAL_CONST(209.16238534187647/8.0), + REAL_CONST(214.24829247050752/8.0), + REAL_CONST(219.36456448277784/8.0), + REAL_CONST(224.51084515641216/8.0), + REAL_CONST(229.6867885365223/8.0), + REAL_CONST(234.89205847013176/8.0), + REAL_CONST(240.12632816923249/8.0), + REAL_CONST(245.38927980018505/8.0), + REAL_CONST(250.68060409747261/8.0), + REAL_CONST(255.99999999999991/8.0), + REAL_CONST(261.34717430828869/8.0), + REAL_CONST(266.72184136106449/8.0), + REAL_CONST(272.12372272986045/8.0), + REAL_CONST(277.55254693037961/8.0), + REAL_CONST(283.0080491494619/8.0), + REAL_CONST(288.48997098659891/8.0), + REAL_CONST(293.99806020902247/8.0), + REAL_CONST(299.53207051947408/8.0), + REAL_CONST(305.0917613358298/8.0), + REAL_CONST(310.67689758182206/8.0), + REAL_CONST(316.28724948815585/8.0), + REAL_CONST(321.92259240337177/8.0), + REAL_CONST(327.58270661385535/8.0), + REAL_CONST(333.26737717243742/8.0), + REAL_CONST(338.97639373507025/8.0), + REAL_CONST(344.70955040510125/8.0), + REAL_CONST(350.46664558470013/8.0), + REAL_CONST(356.24748183302603/8.0), + REAL_CONST(362.05186573075139/8.0), + REAL_CONST(367.87960775058258/8.0), + REAL_CONST(373.73052213344511/8.0), + REAL_CONST(379.60442677002078/8.0), + REAL_CONST(385.50114308734607/8.0), + REAL_CONST(391.42049594019937/8.0), + REAL_CONST(397.36231350702371/8.0), + REAL_CONST(403.32642719014467/8.0), + REAL_CONST(409.31267152006262/8.0), + REAL_CONST(415.32088406360799/8.0), + REAL_CONST(421.35090533576471/8.0), + REAL_CONST(427.40257871497619/8.0), + REAL_CONST(433.4757503617617/8.0), + REAL_CONST(439.5702691404793/8.0), + REAL_CONST(445.68598654408271/8.0), + REAL_CONST(451.82275662172759/8.0), + REAL_CONST(457.98043590909128/8.0), + REAL_CONST(464.15888336127773/8.0), + REAL_CONST(470.35796028818726/8.0), + REAL_CONST(476.5775302922363/8.0), + REAL_CONST(482.81745920832043/8.0), + REAL_CONST(489.07761504591741/8.0), + REAL_CONST(495.35786793323581/8.0), + REAL_CONST(501.65809006331688/8.0), + REAL_CONST(507.97815564200368/8.0), + REAL_CONST(514.31794083769648/8.0), + REAL_CONST(520.67732373281672/8.0), + REAL_CONST(527.05618427690604/8.0), + REAL_CONST(533.45440424129174/8.0), + REAL_CONST(539.87186717525128/8.0), + REAL_CONST(546.30845836361505/8.0), + REAL_CONST(552.76406478574609/8.0), + REAL_CONST(559.23857507584194/8.0), + REAL_CONST(565.73187948450413/8.0), + REAL_CONST(572.24386984152341/8.0), + REAL_CONST(578.77443951983378/8.0), + REAL_CONST(585.32348340058843/8.0), + REAL_CONST(591.89089783931263/8.0), + REAL_CONST(598.47658063309257/8.0), + REAL_CONST(605.08043098876044/8.0), + REAL_CONST(611.70234949203643/8.0), + REAL_CONST(618.3422380775919/8.0), + REAL_CONST(624.99999999999977/8.0), + REAL_CONST(631.67553980553748/8.0), + REAL_CONST(638.36876330481164/8.0), + REAL_CONST(645.07957754617485/8.0), + REAL_CONST(651.80789078990415/8.0), + REAL_CONST(658.55361248311499/8.0), + REAL_CONST(665.31665323538357/8.0), + REAL_CONST(672.09692479505225/8.0), + REAL_CONST(678.8943400261943/8.0), + REAL_CONST(685.70881288621433/8.0), + REAL_CONST(692.540258404062/8.0), + REAL_CONST(699.38859265903977/8.0), + REAL_CONST(706.25373276018058/8.0), + REAL_CONST(713.13559682617972/8.0), + REAL_CONST(720.03410396586037/8.0), + REAL_CONST(726.94917425915435/8.0), + REAL_CONST(733.88072873858209/8.0), + REAL_CONST(740.82868937121543/8.0), + REAL_CONST(747.79297904110535/8.0), + REAL_CONST(754.77352153216191/8.0), + REAL_CONST(761.77024151147043/8.0), + REAL_CONST(768.78306451302956/8.0), + REAL_CONST(775.81191692189896/8.0), + REAL_CONST(782.85672595874246/8.0), + REAL_CONST(789.91741966475445/8.0), + REAL_CONST(796.99392688695798/8.0), + REAL_CONST(804.08617726386274/8.0), + REAL_CONST(811.19410121147098/8.0), + REAL_CONST(818.31762990962227/8.0), + REAL_CONST(825.45669528866563/8.0), + REAL_CONST(832.61123001644864/8.0), + REAL_CONST(839.78116748561604/8.0), + REAL_CONST(846.96644180120552/8.0), + REAL_CONST(854.16698776853514/8.0), + REAL_CONST(861.38274088137143/8.0), + REAL_CONST(868.61363731036977/8.0), + REAL_CONST(875.85961389178203/8.0), + REAL_CONST(883.12060811641959/8.0), + REAL_CONST(890.39655811886757/8.0), + REAL_CONST(897.68740266694181/8.0), + REAL_CONST(904.99308115138172/8.0), + REAL_CONST(912.31353357577188/8.0), + REAL_CONST(919.64870054668756/8.0), + REAL_CONST(926.99852326405619/8.0), + REAL_CONST(934.36294351172899/8.0), + REAL_CONST(941.74190364825859/8.0), + REAL_CONST(949.13534659787422/8.0), + REAL_CONST(956.54321584165211/8.0), + REAL_CONST(963.96545540887348/8.0), + REAL_CONST(971.40200986856541/8.0), + REAL_CONST(978.85282432122176/8.0), + REAL_CONST(986.31784439069588/8.0), + REAL_CONST(993.7970162162635/8.0), + REAL_CONST(1001.29028644485/8.0), + REAL_CONST(1008.797602223418/8.0), + REAL_CONST(1016.3189111915103/8.0), + REAL_CONST(1023.8541614739464/8.0), + REAL_CONST(1031.4033016736653/8.0), + REAL_CONST(1038.9662808647138/8.0), + REAL_CONST(1046.5430485853758/8.0), + REAL_CONST(1054.1335548314366/8.0), + REAL_CONST(1061.7377500495838/8.0), + REAL_CONST(1069.3555851309357/8.0), + REAL_CONST(1076.9870114046978/8.0), + REAL_CONST(1084.6319806319441/8.0), + REAL_CONST(1092.2904449995174/8.0), + REAL_CONST(1099.9623571140482/8.0), + REAL_CONST(1107.6476699960892/8.0), + REAL_CONST(1115.3463370743607/8.0), + REAL_CONST(1123.058312180106/8.0), + REAL_CONST(1130.7835495415541/8.0), + REAL_CONST(1138.5220037784854/8.0), + REAL_CONST(1146.273629896901/8.0), + REAL_CONST(1154.0383832837879/8.0), + REAL_CONST(1161.816219701986/8.0), + REAL_CONST(1169.607095285146/8.0), + REAL_CONST(1177.4109665327808/8.0), + REAL_CONST(1185.2277903054078/8.0), + REAL_CONST(1193.0575238197798/8.0), + REAL_CONST(1200.9001246442001/8.0), + REAL_CONST(1208.7555506939248/8.0), + REAL_CONST(1216.6237602266442/8.0), + REAL_CONST(1224.5047118380478/8.0), + REAL_CONST(1232.3983644574657/8.0), + REAL_CONST(1240.3046773435874/8.0), + REAL_CONST(1248.2236100802568/8.0), + REAL_CONST(1256.1551225723395/8.0), + REAL_CONST(1264.099175041662/8.0), + REAL_CONST(1272.0557280230228/8.0), + REAL_CONST(1280.0247423602691/8.0), + REAL_CONST(1288.0061792024444/8.0), + REAL_CONST(1295.9999999999995/8.0), + REAL_CONST(1304.006166501068/8.0), + REAL_CONST(1312.0246407478062/8.0), + REAL_CONST(1320.0553850727929/8.0), + REAL_CONST(1328.0983620954903/8.0), + REAL_CONST(1336.1535347187651/8.0), + REAL_CONST(1344.2208661254647/8.0), + REAL_CONST(1352.3003197750522/8.0), + REAL_CONST(1360.3918594002962/8.0), + REAL_CONST(1368.4954490040145/8.0), + REAL_CONST(1376.6110528558709/8.0), + REAL_CONST(1384.7386354892244/8.0), + REAL_CONST(1392.8781616980295/8.0), + REAL_CONST(1401.0295965337855/8.0), + REAL_CONST(1409.1929053025353/8.0), + REAL_CONST(1417.3680535619119/8.0), + REAL_CONST(1425.5550071182327/8.0), + REAL_CONST(1433.7537320236374/8.0), + REAL_CONST(1441.9641945732744/8.0), + REAL_CONST(1450.1863613025282/8.0), + REAL_CONST(1458.4201989842913/8.0), + REAL_CONST(1466.6656746262797/8.0), + REAL_CONST(1474.9227554683875/8.0), + REAL_CONST(1483.1914089800841/8.0), + REAL_CONST(1491.4716028578516/8.0), + REAL_CONST(1499.7633050226596/8.0), + REAL_CONST(1508.0664836174794/8.0), + REAL_CONST(1516.3811070048375/8.0), + REAL_CONST(1524.7071437644029/8.0), + REAL_CONST(1533.0445626906128/8.0), + REAL_CONST(1541.3933327903342/8.0), + REAL_CONST(1549.7534232805581/8.0), + REAL_CONST(1558.1248035861302/8.0), + REAL_CONST(1566.507443337515/8.0), + REAL_CONST(1574.9013123685909/8.0), + REAL_CONST(1583.3063807144795/8.0), + REAL_CONST(1591.7226186094069/8.0), + REAL_CONST(1600.1499964845941/8.0), + REAL_CONST(1608.58848496618/8.0), + REAL_CONST(1617.0380548731737/8.0), + REAL_CONST(1625.4986772154357/8.0), + REAL_CONST(1633.9703231916887/8.0), + REAL_CONST(1642.4529641875577/8.0), + REAL_CONST(1650.9465717736346/8.0), + REAL_CONST(1659.4511177035752/8.0), + REAL_CONST(1667.9665739122186/8.0), + REAL_CONST(1676.4929125137353/8.0), + REAL_CONST(1685.030105799801/8.0), + REAL_CONST(1693.5781262377957/8.0), + REAL_CONST(1702.136946469027/8.0), + REAL_CONST(1710.7065393069795/8.0), + REAL_CONST(1719.2868777355877/8.0), + REAL_CONST(1727.8779349075323/8.0), + REAL_CONST(1736.4796841425596/8.0), + REAL_CONST(1745.092098925825/8.0), + REAL_CONST(1753.7151529062583/8.0), + REAL_CONST(1762.3488198949503/8.0), + REAL_CONST(1770.9930738635628/8.0), + REAL_CONST(1779.6478889427597/8.0), + REAL_CONST(1788.3132394206564/8.0), + REAL_CONST(1796.9890997412947/8.0), + REAL_CONST(1805.6754445031333/8.0), + REAL_CONST(1814.3722484575621/8.0), + REAL_CONST(1823.0794865074322/8.0), + REAL_CONST(1831.7971337056094/8.0), + REAL_CONST(1840.5251652535437/8.0), + REAL_CONST(1849.2635564998579/8.0), + REAL_CONST(1858.0122829389563/8.0), + REAL_CONST(1866.7713202096493/8.0), + REAL_CONST(1875.5406440937966/8.0), + REAL_CONST(1884.3202305149687/8.0), + REAL_CONST(1893.110055537124/8.0), + REAL_CONST(1901.9100953633042/8.0), + REAL_CONST(1910.7203263343454/8.0), + REAL_CONST(1919.5407249276057/8.0), + REAL_CONST(1928.3712677557098/8.0), + REAL_CONST(1937.2119315653083/8.0), + REAL_CONST(1946.0626932358525/8.0), + REAL_CONST(1954.923529778386/8.0), + REAL_CONST(1963.79441833435/8.0), + REAL_CONST(1972.6753361744036/8.0), + REAL_CONST(1981.5662606972594/8.0), + REAL_CONST(1990.467169428533/8.0), + REAL_CONST(1999.3780400196069/8.0), + REAL_CONST(2008.2988502465078/8.0), + REAL_CONST(2017.2295780087982/8.0), + REAL_CONST(2026.1702013284819/8.0), + REAL_CONST(2035.1206983489212/8.0), + REAL_CONST(2044.0810473337688/8.0), + REAL_CONST(2053.0512266659125/8.0), + REAL_CONST(2062.0312148464309/8.0), + REAL_CONST(2071.0209904935646/8.0), + REAL_CONST(2080.0205323416958/8.0), + REAL_CONST(2089.0298192403443/8.0), + REAL_CONST(2098.0488301531714/8.0), + REAL_CONST(2107.0775441569995/8.0), + REAL_CONST(2116.115940440839/8.0), + REAL_CONST(2125.1639983049317/8.0), + REAL_CONST(2134.2216971597995/8.0), + REAL_CONST(2143.2890165253098/8.0), + REAL_CONST(2152.3659360297484/8.0), + REAL_CONST(2161.4524354089031/8.0), + REAL_CONST(2170.5484945051617/8.0), + REAL_CONST(2179.6540932666144/8.0), + REAL_CONST(2188.7692117461711/8.0), + REAL_CONST(2197.8938301006888/8.0), + REAL_CONST(2207.0279285901042/8.0), + REAL_CONST(2216.1714875765838/8.0), + REAL_CONST(2225.324487523676/8.0), + REAL_CONST(2234.4869089954782/8.0), + REAL_CONST(2243.6587326558101/8.0), + REAL_CONST(2252.8399392673982/8.0), + REAL_CONST(2262.0305096910702/8.0), + REAL_CONST(2271.2304248849537/8.0), + REAL_CONST(2280.4396659036897/8.0), + REAL_CONST(2289.6582138976523/8.0), + REAL_CONST(2298.8860501121762/8.0), + REAL_CONST(2308.1231558867926/8.0), + REAL_CONST(2317.3695126544767/8.0), + REAL_CONST(2326.6251019409005/8.0), + REAL_CONST(2335.8899053636933/8.0), + REAL_CONST(2345.1639046317132/8.0), + REAL_CONST(2354.4470815443233/8.0), + REAL_CONST(2363.7394179906792/8.0), + REAL_CONST(2373.0408959490205/8.0), + REAL_CONST(2382.3514974859731/8.0), + REAL_CONST(2391.6712047558558/8.0), + REAL_CONST(2400.9999999999991/8.0), + REAL_CONST(2410.3378655460651/8.0), + REAL_CONST(2419.6847838073813/8.0), + REAL_CONST(2429.0407372822747/8.0), + REAL_CONST(2438.4057085534191/8.0), + REAL_CONST(2447.7796802871858/8.0), + REAL_CONST(2457.1626352330004/8.0), + REAL_CONST(2466.5545562227112/8.0), + REAL_CONST(2475.9554261699564/8.0), + REAL_CONST(2485.3652280695474/8.0), + REAL_CONST(2494.7839449968492/8.0), + REAL_CONST(2504.2115601071737/8.0), + REAL_CONST(2513.6480566351788/8.0), + REAL_CONST(2523.0934178942675/8.0), + REAL_CONST(2532.5476272760025/8.0), + REAL_CONST(2542.0106682495189/8.0), + REAL_CONST(2551.482524360948/8.0), + REAL_CONST(2560.9631792328441/8.0), + REAL_CONST(2570.4526165636184/8.0), + REAL_CONST(2579.9508201269791/8.0), + REAL_CONST(2589.4577737713744/8.0), + REAL_CONST(2598.9734614194458/8.0), + REAL_CONST(2608.4978670674823/8.0), + REAL_CONST(2618.0309747848837/8.0), + REAL_CONST(2627.5727687136259/8.0), + REAL_CONST(2637.1232330677353/8.0), + REAL_CONST(2646.6823521327647/8.0), + REAL_CONST(2656.2501102652768/8.0), + REAL_CONST(2665.8264918923328/8.0), + REAL_CONST(2675.4114815109842/8.0), + REAL_CONST(2685.0050636877722/8.0), + REAL_CONST(2694.6072230582295/8.0), + REAL_CONST(2704.2179443263894/8.0), + REAL_CONST(2713.8372122642972/8.0), + REAL_CONST(2723.4650117115279/8.0), + REAL_CONST(2733.1013275747096/8.0), + REAL_CONST(2742.7461448270483/8.0), + REAL_CONST(2752.3994485078601/8.0), + REAL_CONST(2762.0612237221085/8.0), + REAL_CONST(2771.7314556399419/8.0), + REAL_CONST(2781.4101294962406/8.0), + REAL_CONST(2791.0972305901655/8.0), + REAL_CONST(2800.7927442847094/8.0), + REAL_CONST(2810.4966560062589/8.0), + REAL_CONST(2820.2089512441521/8.0), + REAL_CONST(2829.9296155502466/8.0), + REAL_CONST(2839.6586345384894/8.0), + REAL_CONST(2849.3959938844923/8.0), + REAL_CONST(2859.1416793251065/8.0), + REAL_CONST(2868.8956766580086/8.0), + REAL_CONST(2878.6579717412847/8.0), + REAL_CONST(2888.4285504930212/8.0), + REAL_CONST(2898.2073988908974/8.0), + REAL_CONST(2907.9945029717837/8.0), + REAL_CONST(2917.789848831344/8.0), + REAL_CONST(2927.5934226236377/8.0), + REAL_CONST(2937.4052105607311/8.0), + REAL_CONST(2947.2251989123079/8.0), + REAL_CONST(2957.0533740052865/8.0), + REAL_CONST(2966.8897222234368/8.0), + REAL_CONST(2976.734230007005/8.0), + REAL_CONST(2986.5868838523397/8.0), + REAL_CONST(2996.4476703115197/8.0), + REAL_CONST(3006.3165759919889/8.0), + REAL_CONST(3016.1935875561908/8.0), + REAL_CONST(3026.0786917212095/8.0), + REAL_CONST(3035.9718752584108/8.0), + REAL_CONST(3045.8731249930906/8.0), + REAL_CONST(3055.7824278041207/8.0), + REAL_CONST(3065.6997706236039/8.0), + REAL_CONST(3075.625140436528/8.0), + REAL_CONST(3085.5585242804245/8.0), + REAL_CONST(3095.4999092450298/8.0), + REAL_CONST(3105.4492824719491/8.0), + REAL_CONST(3115.4066311543256/8.0), + REAL_CONST(3125.3719425365089/8.0), + REAL_CONST(3135.3452039137287/8.0), + REAL_CONST(3145.3264026317715/8.0), + REAL_CONST(3155.3155260866592/8.0), + REAL_CONST(3165.3125617243295/8.0), + REAL_CONST(3175.3174970403229/8.0), + REAL_CONST(3185.3303195794679/8.0), + REAL_CONST(3195.35101693557/8.0), + REAL_CONST(3205.3795767511078/8.0), + REAL_CONST(3215.4159867169251/8.0), + REAL_CONST(3225.460234571929/8.0), + REAL_CONST(3235.5123081027928/8.0), + REAL_CONST(3245.5721951436558/8.0), + REAL_CONST(3255.63988357583/8.0), + REAL_CONST(3265.7153613275095/8.0), + REAL_CONST(3275.7986163734795/8.0), + REAL_CONST(3285.8896367348289/8.0), + REAL_CONST(3295.9884104786665/8.0), + REAL_CONST(3306.0949257178395/8.0), + REAL_CONST(3316.2091706106517/8.0), + REAL_CONST(3326.331133360588/8.0), + REAL_CONST(3336.4608022160378/8.0), + REAL_CONST(3346.5981654700231/8.0), + REAL_CONST(3356.7432114599264/8.0), + REAL_CONST(3366.8959285672249/8.0), + REAL_CONST(3377.0563052172211/8.0), + REAL_CONST(3387.2243298787821/8.0), + REAL_CONST(3397.3999910640764/8.0), + REAL_CONST(3407.5832773283128/8.0), + REAL_CONST(3417.7741772694862/8.0), + REAL_CONST(3427.9726795281199/8.0), + REAL_CONST(3438.1787727870123/8.0), + REAL_CONST(3448.3924457709873/8.0), + REAL_CONST(3458.6136872466445/8.0), + REAL_CONST(3468.8424860221107/8.0), + REAL_CONST(3479.0788309467976/8.0), + REAL_CONST(3489.3227109111554/8.0), + REAL_CONST(3499.5741148464344/8.0), + REAL_CONST(3509.8330317244445/8.0), + REAL_CONST(3520.0994505573185/8.0), + REAL_CONST(3530.3733603972751/8.0), + REAL_CONST(3540.6547503363886/8.0), + REAL_CONST(3550.9436095063534/8.0), + REAL_CONST(3561.239927078258/8.0), + REAL_CONST(3571.5436922623535/8.0), + REAL_CONST(3581.8548943078308/8.0), + REAL_CONST(3592.1735225025936/8.0), + REAL_CONST(3602.4995661730372/8.0), + REAL_CONST(3612.8330146838275/8.0), + REAL_CONST(3623.1738574376814/8.0), + REAL_CONST(3633.5220838751502/8.0), + REAL_CONST(3643.8776834744031/8.0), + REAL_CONST(3654.2406457510142/8.0), + REAL_CONST(3664.6109602577494/8.0), + REAL_CONST(3674.9886165843564/8.0), + REAL_CONST(3685.3736043573545/8.0), + REAL_CONST(3695.7659132398294/8.0), + REAL_CONST(3706.1655329312248/8.0), + REAL_CONST(3716.5724531671399/8.0), + REAL_CONST(3726.9866637191262/8.0), + REAL_CONST(3737.4081543944876/8.0), + REAL_CONST(3747.8369150360782/8.0), + REAL_CONST(3758.2729355221072/8.0), + REAL_CONST(3768.7162057659411/8.0), + REAL_CONST(3779.1667157159077/8.0), + REAL_CONST(3789.6244553551055/8.0), + REAL_CONST(3800.0894147012082/8.0), + REAL_CONST(3810.5615838062768/8.0), + REAL_CONST(3821.0409527565694/8.0), + REAL_CONST(3831.5275116723533/8.0), + REAL_CONST(3842.0212507077194/8.0), + REAL_CONST(3852.522160050396/8.0), + REAL_CONST(3863.0302299215673/8.0), + REAL_CONST(3873.5454505756893/8.0), + REAL_CONST(3884.0678123003108/8.0), + REAL_CONST(3894.5973054158922/8.0), + REAL_CONST(3905.1339202756285/8.0), + REAL_CONST(3915.6776472652732/8.0), + REAL_CONST(3926.2284768029604/8.0), + REAL_CONST(3936.7863993390338/8.0), + REAL_CONST(3947.3514053558706/8.0), + REAL_CONST(3957.9234853677135/8.0), + REAL_CONST(3968.5026299204969/8.0), + REAL_CONST(3979.0888295916798/8.0), + REAL_CONST(3989.6820749900776/8.0), + REAL_CONST(4000.2823567556948/8.0), + REAL_CONST(4010.8896655595613/8.0), + REAL_CONST(4021.5039921035655/8.0), + REAL_CONST(4032.1253271202945/8.0), + REAL_CONST(4042.7536613728694/8.0), + REAL_CONST(4053.3889856547858/8.0), + REAL_CONST(4064.0312907897551/8.0), + REAL_CONST(4074.6805676315448/8.0), + REAL_CONST(4085.3368070638221/8.0), + REAL_CONST(4095.9999999999982/8.0), + REAL_CONST(4106.6701373830711/8.0), + REAL_CONST(4117.347210185475/8.0), + REAL_CONST(4128.0312094089259/8.0), + REAL_CONST(4138.722126084268/8.0), + REAL_CONST(4149.4199512713267/8.0), + REAL_CONST(4160.1246760587583/8.0), + REAL_CONST(4170.8362915638982/8.0), + REAL_CONST(4181.5547889326181/8.0), + REAL_CONST(4192.2801593391769/8.0), + REAL_CONST(4203.0123939860741/8.0), + REAL_CONST(4213.7514841039101/8.0), + REAL_CONST(4224.4974209512384/8.0), + REAL_CONST(4235.2501958144258/8.0), + REAL_CONST(4246.0098000075095/8.0), + REAL_CONST(4256.7762248720574/8.0), + REAL_CONST(4267.549461777031/8.0), + REAL_CONST(4278.3295021186423/8.0), + REAL_CONST(4289.1163373202198/8.0), + REAL_CONST(4299.9099588320714/8.0), + REAL_CONST(4310.7103581313495/8.0), + REAL_CONST(4321.5175267219138/8.0), + REAL_CONST(4332.3314561342004/8.0), + REAL_CONST(4343.152137925088/8.0), + REAL_CONST(4353.9795636777671/8.0), + REAL_CONST(4364.8137250016052/8.0), + REAL_CONST(4375.6546135320223/8.0), + REAL_CONST(4386.5022209303588/8.0), + REAL_CONST(4397.3565388837469/8.0), + REAL_CONST(4408.2175591049827/8.0), + REAL_CONST(4419.0852733324018/8.0), + REAL_CONST(4429.9596733297531/8.0), + REAL_CONST(4440.8407508860728/8.0), + REAL_CONST(4451.7284978155603/8.0), + REAL_CONST(4462.6229059574571/8.0), + REAL_CONST(4473.5239671759227/8.0), + REAL_CONST(4484.4316733599126/8.0), + REAL_CONST(4495.3460164230582/8.0), + REAL_CONST(4506.2669883035496/8.0), + REAL_CONST(4517.1945809640119/8.0), + REAL_CONST(4528.1287863913894/8.0), + REAL_CONST(4539.069596596828/8.0), + REAL_CONST(4550.0170036155587/8.0), + REAL_CONST(4560.9709995067806/8.0), + REAL_CONST(4571.931576353546/8.0), + REAL_CONST(4582.898726262647/8.0), + REAL_CONST(4593.8724413645004/8.0), + REAL_CONST(4604.8527138130348/8.0), + REAL_CONST(4615.8395357855816/8.0), + REAL_CONST(4626.8328994827571/8.0), + REAL_CONST(4637.8327971283588/8.0), + REAL_CONST(4648.8392209692511/8.0), + REAL_CONST(4659.8521632752563/8.0), + REAL_CONST(4670.8716163390473/8.0), + REAL_CONST(4681.8975724760394/8.0), + REAL_CONST(4692.9300240242837/8.0), + REAL_CONST(4703.9689633443595/8.0), + REAL_CONST(4715.0143828192668/8.0), + REAL_CONST(4726.0662748543255/8.0), + REAL_CONST(4737.1246318770682/8.0), + REAL_CONST(4748.1894463371373/8.0), + REAL_CONST(4759.2607107061804/8.0), + REAL_CONST(4770.3384174777493/8.0), + REAL_CONST(4781.4225591671993/8.0), + REAL_CONST(4792.5131283115852/8.0), + REAL_CONST(4803.6101174695614/8.0), + REAL_CONST(4814.7135192212854/8.0), + REAL_CONST(4825.8233261683154/8.0), + REAL_CONST(4836.9395309335096/8.0), + REAL_CONST(4848.0621261609349/8.0), + REAL_CONST(4859.1911045157631/8.0), + REAL_CONST(4870.3264586841779/8.0), + REAL_CONST(4881.4681813732768/8.0), + REAL_CONST(4892.6162653109768/8.0), + REAL_CONST(4903.7707032459193/8.0), + REAL_CONST(4914.931487947375/8.0), + REAL_CONST(4926.0986122051509/8.0), + REAL_CONST(4937.2720688294967/8.0), + REAL_CONST(4948.4518506510112/8.0), + REAL_CONST(4959.637950520555/8.0), + REAL_CONST(4970.8303613091521/8.0), + REAL_CONST(4982.0290759079044/8.0), + REAL_CONST(4993.2340872278974/8.0), + REAL_CONST(5004.4453882001153/8.0), + REAL_CONST(5015.6629717753467/8.0), + REAL_CONST(5026.8868309241007/8.0), + REAL_CONST(5038.1169586365131/8.0), + REAL_CONST(5049.353347922266/8.0), + REAL_CONST(5060.5959918104927/8.0), + REAL_CONST(5071.8448833496996/8.0), + REAL_CONST(5083.1000156076734/8.0), + REAL_CONST(5094.3613816713996/8.0), + REAL_CONST(5105.6289746469747/8.0), + REAL_CONST(5116.9027876595246/8.0), + REAL_CONST(5128.18281385312/8.0), + REAL_CONST(5139.4690463906918/8.0), + REAL_CONST(5150.7614784539473/8.0), + REAL_CONST(5162.0601032432933/8.0), + REAL_CONST(5173.3649139777472/8.0), + REAL_CONST(5184.6759038948594/8.0), + REAL_CONST(5195.9930662506322/8.0), + REAL_CONST(5207.3163943194386/8.0), + REAL_CONST(5218.6458813939435/8.0), + REAL_CONST(5229.9815207850224/8.0), + REAL_CONST(5241.3233058216847/8.0), + REAL_CONST(5252.6712298509919/8.0), + REAL_CONST(5264.025286237983/8.0), + REAL_CONST(5275.3854683655954/8.0), + REAL_CONST(5286.7517696345885/8.0), + REAL_CONST(5298.1241834634639/8.0), + REAL_CONST(5309.5027032883945/8.0), + REAL_CONST(5320.887322563146/8.0), + REAL_CONST(5332.2780347589978/8.0), + REAL_CONST(5343.6748333646756/8.0), + REAL_CONST(5355.0777118862716/8.0), + REAL_CONST(5366.4866638471722/8.0), + REAL_CONST(5377.901682787985/8.0), + REAL_CONST(5389.3227622664635/8.0), + REAL_CONST(5400.749895857437/8.0), + REAL_CONST(5412.1830771527357/8.0), + REAL_CONST(5423.622299761123/8.0), + REAL_CONST(5435.067557308219/8.0), + REAL_CONST(5446.5188434364318/8.0), + REAL_CONST(5457.9761518048872/8.0), + REAL_CONST(5469.4394760893592/8.0), + REAL_CONST(5480.9088099821975/8.0), + REAL_CONST(5492.3841471922606/8.0), + REAL_CONST(5503.8654814448455/8.0), + REAL_CONST(5515.3528064816201/8.0), + REAL_CONST(5526.846116060552/8.0), + REAL_CONST(5538.3454039558474/8.0), + REAL_CONST(5549.8506639578736/8.0), + REAL_CONST(5561.3618898731029/8.0), + REAL_CONST(5572.8790755240361/8.0), + REAL_CONST(5584.4022147491451/8.0), + REAL_CONST(5595.9313014027975/8.0), + REAL_CONST(5607.4663293552012/8.0), + REAL_CONST(5619.0072924923297/8.0), + REAL_CONST(5630.5541847158656/8.0), + REAL_CONST(5642.1069999431284/8.0), + REAL_CONST(5653.665732107017/8.0), + REAL_CONST(5665.230375155943/8.0), + REAL_CONST(5676.8009230537655/8.0), + REAL_CONST(5688.3773697797333/8.0), + REAL_CONST(5699.9597093284156/8.0), + REAL_CONST(5711.5479357096474/8.0), + REAL_CONST(5723.1420429484588/8.0), + REAL_CONST(5734.7420250850209/8.0), + REAL_CONST(5746.347876174581/8.0), + REAL_CONST(5757.9595902874016/8.0), + REAL_CONST(5769.5771615087006/8.0), + REAL_CONST(5781.2005839385911/8.0), + REAL_CONST(5792.8298516920213/8.0), + REAL_CONST(5804.4649588987149/8.0), + REAL_CONST(5816.1058997031105/8.0), + REAL_CONST(5827.7526682643065/8.0), + REAL_CONST(5839.4052587559972/8.0), + REAL_CONST(5851.0636653664196/8.0), + REAL_CONST(5862.7278822982908/8.0), + REAL_CONST(5874.3979037687541/8.0), + REAL_CONST(5886.0737240093204/8.0), + REAL_CONST(5897.7553372658094/8.0), + REAL_CONST(5909.4427377982956/8.0), + REAL_CONST(5921.1359198810505/8.0), + REAL_CONST(5932.8348778024874/8.0), + REAL_CONST(5944.5396058651031/8.0), + REAL_CONST(5956.2500983854261/8.0), + REAL_CONST(5967.9663496939575/8.0), + REAL_CONST(5979.6883541351208/8.0), + REAL_CONST(5991.4161060672022/8.0), + REAL_CONST(6003.1495998623004/8.0), + REAL_CONST(6014.8888299062692/8.0), + REAL_CONST(6026.6337905986684/8.0), + REAL_CONST(6038.3844763527022/8.0), + REAL_CONST(6050.1408815951781/8.0), + REAL_CONST(6061.9030007664414/8.0), + REAL_CONST(6073.6708283203316/8.0), + REAL_CONST(6085.4443587241267/8.0), + REAL_CONST(6097.2235864584891/8.0), + REAL_CONST(6109.0085060174197/8.0), + REAL_CONST(6120.7991119081998/8.0), + REAL_CONST(6132.595398651345/8.0), + REAL_CONST(6144.3973607805519/8.0), + REAL_CONST(6156.2049928426459/8.0), + REAL_CONST(6168.0182893975361/8.0), + REAL_CONST(6179.8372450181578/8.0), + REAL_CONST(6191.6618542904307/8.0), + REAL_CONST(6203.4921118132024/8.0), + REAL_CONST(6215.3280121982016/8.0), + REAL_CONST(6227.1695500699925/8.0), + REAL_CONST(6239.0167200659189/8.0), + REAL_CONST(6250.8695168360628/8.0), + REAL_CONST(6262.7279350431891/8.0), + REAL_CONST(6274.5919693627056/8.0), + REAL_CONST(6286.4616144826068/8.0), + REAL_CONST(6298.3368651034316/8.0), + REAL_CONST(6310.2177159382172/8.0), + REAL_CONST(6322.1041617124456/8.0), + REAL_CONST(6333.9961971640032/8.0), + REAL_CONST(6345.8938170431311/8.0), + REAL_CONST(6357.7970161123785/8.0), + REAL_CONST(6369.7057891465583/8.0), + REAL_CONST(6381.6201309327007/8.0), + REAL_CONST(6393.5400362700075/8.0), + REAL_CONST(6405.4654999698032/8.0), + REAL_CONST(6417.3965168554978/8.0), + REAL_CONST(6429.3330817625329/8.0), + REAL_CONST(6441.2751895383453/8.0), + REAL_CONST(6453.2228350423138/8.0), + REAL_CONST(6465.176013145724/8.0), + REAL_CONST(6477.134718731716/8.0), + REAL_CONST(6489.0989466952469/8.0), + REAL_CONST(6501.0686919430445/8.0), + REAL_CONST(6513.0439493935628/8.0), + REAL_CONST(6525.0247139769417/8.0), + REAL_CONST(6537.010980634961/8.0), + REAL_CONST(6549.002744321001/8.0), + REAL_CONST(6560.9999999999973/8.0), + REAL_CONST(6573.0027426483985/8.0), + REAL_CONST(6585.0109672541284/8.0), + REAL_CONST(6597.0246688165371/8.0), + REAL_CONST(6609.0438423463656/8.0), + REAL_CONST(6621.0684828657004/8.0), + REAL_CONST(6633.0985854079354/8.0), + REAL_CONST(6645.134145017727/8.0), + REAL_CONST(6657.1751567509573/8.0), + REAL_CONST(6669.2216156746908/8.0), + REAL_CONST(6681.2735168671343/8.0), + REAL_CONST(6693.3308554176001/8.0), + REAL_CONST(6705.3936264264594/8.0), + REAL_CONST(6717.461825005108/8.0), + REAL_CONST(6729.535446275926/8.0), + REAL_CONST(6741.6144853722335/8.0), + REAL_CONST(6753.6989374382601/8.0), + REAL_CONST(6765.7887976290967/8.0), + REAL_CONST(6777.8840611106634/8.0), + REAL_CONST(6789.9847230596661/8.0), + REAL_CONST(6802.0907786635626/8.0), + REAL_CONST(6814.2022231205201/8.0), + REAL_CONST(6826.3190516393797/8.0), + REAL_CONST(6838.4412594396181/8.0), + REAL_CONST(6850.5688417513074/8.0), + REAL_CONST(6862.701793815083/8.0), + REAL_CONST(6874.840110882099/8.0), + REAL_CONST(6886.9837882139991/8.0), + REAL_CONST(6899.1328210828724/8.0), + REAL_CONST(6911.2872047712199/8.0), + REAL_CONST(6923.4469345719199/8.0), + REAL_CONST(6935.6120057881863/8.0), + REAL_CONST(6947.7824137335365/8.0), + REAL_CONST(6959.9581537317536/8.0), + REAL_CONST(6972.1392211168532/8.0), + REAL_CONST(6984.3256112330409/8.0), + REAL_CONST(6996.5173194346862/8.0), + REAL_CONST(7008.7143410862773/8.0), + REAL_CONST(7020.9166715623942/8.0), + REAL_CONST(7033.1243062476678/8.0), + REAL_CONST(7045.3372405367481/8.0), + REAL_CONST(7057.5554698342685/8.0), + REAL_CONST(7069.7789895548103/8.0), + REAL_CONST(7082.0077951228714/8.0), + REAL_CONST(7094.2418819728273/8.0), + REAL_CONST(7106.4812455489018/8.0), + REAL_CONST(7118.7258813051285/8.0), + REAL_CONST(7130.9757847053224/8.0), + REAL_CONST(7143.2309512230404/8.0), + REAL_CONST(7155.4913763415516/8.0), + REAL_CONST(7167.7570555538041/8.0), + REAL_CONST(7180.0279843623894/8.0), + REAL_CONST(7192.3041582795131/8.0), + REAL_CONST(7204.5855728269571/8.0), + REAL_CONST(7216.8722235360519/8.0), + REAL_CONST(7229.1641059476406/8.0), + REAL_CONST(7241.4612156120484/8.0), + REAL_CONST(7253.7635480890503/8.0), + REAL_CONST(7266.0710989478375/8.0), + REAL_CONST(7278.3838637669869/8.0), + REAL_CONST(7290.7018381344296/8.0), + REAL_CONST(7303.0250176474174/8.0), + REAL_CONST(7315.3533979124932/8.0), + REAL_CONST(7327.6869745454596/8.0), + REAL_CONST(7340.0257431713462/8.0), + REAL_CONST(7352.3696994243801/8.0), + REAL_CONST(7364.7188389479543/8.0), + REAL_CONST(7377.0731573945968/8.0), + REAL_CONST(7389.4326504259407/8.0), + REAL_CONST(7401.7973137126937/8.0), + REAL_CONST(7414.1671429346061/8.0), + REAL_CONST(7426.5421337804428/8.0), + REAL_CONST(7438.922281947951/8.0), + REAL_CONST(7451.3075831438346/8.0), + REAL_CONST(7463.6980330837177/8.0), + REAL_CONST(7476.0936274921214/8.0), + REAL_CONST(7488.4943621024304/8.0), + REAL_CONST(7500.9002326568652/8.0), + REAL_CONST(7513.3112349064522/8.0), + REAL_CONST(7525.7273646109943/8.0), + REAL_CONST(7538.1486175390446/8.0), + REAL_CONST(7550.5749894678729/8.0), + REAL_CONST(7563.0064761834419/8.0), + REAL_CONST(7575.4430734803736/8.0), + REAL_CONST(7587.8847771619248/8.0), + REAL_CONST(7600.3315830399597/8.0), + REAL_CONST(7612.7834869349153/8.0), + REAL_CONST(7625.24048467578/8.0), + REAL_CONST(7637.7025721000637/8.0), + REAL_CONST(7650.1697450537677/8.0), + REAL_CONST(7662.6419993913596/8.0), + REAL_CONST(7675.1193309757446/8.0), + REAL_CONST(7687.6017356782404/8.0), + REAL_CONST(7700.0892093785433/8.0), + REAL_CONST(7712.5817479647112/8.0), + REAL_CONST(7725.079347333125/8.0), + REAL_CONST(7737.5820033884729/8.0), + REAL_CONST(7750.0897120437139/8.0), + REAL_CONST(7762.6024692200581/8.0), + REAL_CONST(7775.1202708469355/8.0), + REAL_CONST(7787.6431128619733/8.0), + REAL_CONST(7800.1709912109645/8.0), + REAL_CONST(7812.7039018478481/8.0), + REAL_CONST(7825.2418407346768/8.0), + REAL_CONST(7837.7848038415968/8.0), + REAL_CONST(7850.3327871468155/8.0), + REAL_CONST(7862.8857866365806/8.0), + REAL_CONST(7875.4437983051539/8.0), + REAL_CONST(7888.006818154784/8.0), + REAL_CONST(7900.5748421956796/8.0), + REAL_CONST(7913.1478664459901/8.0), + REAL_CONST(7925.725886931772/8.0), + REAL_CONST(7938.3088996869719/8.0), + REAL_CONST(7950.8969007533951/8.0), + REAL_CONST(7963.4898861806851/8.0), + REAL_CONST(7976.0878520262959/8.0), + REAL_CONST(7988.6907943554688/8.0), + REAL_CONST(8001.2987092412086/8.0), + REAL_CONST(8013.911592764257/8.0), + REAL_CONST(8026.5294410130691/8.0), + REAL_CONST(8039.1522500837891/8.0), + REAL_CONST(8051.7800160802271/8.0), + REAL_CONST(8064.412735113835/8.0), + REAL_CONST(8077.0504033036796/8.0), + REAL_CONST(8089.6930167764222/8.0), + REAL_CONST(8102.3405716662946/8.0), + REAL_CONST(8114.9930641150731/8.0), + REAL_CONST(8127.6504902720571/8.0), + REAL_CONST(8140.3128462940449/8.0), + REAL_CONST(8152.9801283453098/8.0), + REAL_CONST(8165.6523325975786/8.0), + REAL_CONST(8178.3294552300049/8.0), + REAL_CONST(8191.0114924291529/8.0), + REAL_CONST(8203.6984403889655/8.0), + REAL_CONST(8216.3902953107463/8.0), + REAL_CONST(8229.0870534031419/8.0), + REAL_CONST(8241.7887108821069/8.0), + REAL_CONST(8254.4952639708936/8.0), + REAL_CONST(8267.2067089000211/8.0), + REAL_CONST(8279.9230419072574/8.0), + REAL_CONST(8292.6442592375952/8.0), + REAL_CONST(8305.3703571432306/8.0), + REAL_CONST(8318.101331883543/8.0), + REAL_CONST(8330.8371797250657/8.0), + REAL_CONST(8343.577896941475/8.0), + REAL_CONST(8356.3234798135582/8.0), + REAL_CONST(8369.0739246291978/8.0), + REAL_CONST(8381.8292276833508/8.0), + REAL_CONST(8394.5893852780209/8.0), + REAL_CONST(8407.3543937222421/8.0), + REAL_CONST(8420.1242493320569/8.0), + REAL_CONST(8432.8989484304948/8.0), + REAL_CONST(8445.6784873475499/8.0), + REAL_CONST(8458.4628624201578/8.0), + REAL_CONST(8471.2520699921806/8.0), + REAL_CONST(8484.0461064143838/8.0), + REAL_CONST(8496.8449680444082/8.0), + REAL_CONST(8509.6486512467636/8.0), + REAL_CONST(8522.4571523927953/8.0), + REAL_CONST(8535.270467860666/8.0), + REAL_CONST(8548.0885940353437/8.0), + REAL_CONST(8560.9115273085663/8.0), + REAL_CONST(8573.7392640788403/8.0), + REAL_CONST(8586.5718007514006/8.0), + REAL_CONST(8599.4091337382069/8.0), + REAL_CONST(8612.2512594579148/8.0), + REAL_CONST(8625.0981743358552/8.0), + REAL_CONST(8637.9498748040205/8.0), + REAL_CONST(8650.8063573010386/8.0), + REAL_CONST(8663.6676182721567/8.0), + REAL_CONST(8676.533654169225/8.0), + REAL_CONST(8689.4044614506638/8.0), + REAL_CONST(8702.2800365814601/8.0), + REAL_CONST(8715.1603760331418/8.0), + REAL_CONST(8728.0454762837508/8.0), + REAL_CONST(8740.9353338178389/8.0), + REAL_CONST(8753.8299451264356/8.0), + REAL_CONST(8766.7293067070332/8.0), + REAL_CONST(8779.6334150635721/8.0), + REAL_CONST(8792.5422667064158/8.0), + REAL_CONST(8805.4558581523324/8.0), + REAL_CONST(8818.3741859244819/8.0), + REAL_CONST(8831.2972465523908/8.0), + REAL_CONST(8844.2250365719356/8.0), + REAL_CONST(8857.1575525253265/8.0), + REAL_CONST(8870.0947909610859/8.0), + REAL_CONST(8883.0367484340295/8.0), + REAL_CONST(8895.9834215052524/8.0), + REAL_CONST(8908.934806742107/8.0), + REAL_CONST(8921.8909007181846/8.0), + REAL_CONST(8934.8517000132997/8.0), + REAL_CONST(8947.817201213471/8.0), + REAL_CONST(8960.7874009109/8.0), + REAL_CONST(8973.7622957039603/8.0), + REAL_CONST(8986.7418821971733/8.0), + REAL_CONST(8999.7261570011924/8.0), + REAL_CONST(9012.7151167327884/8.0), + REAL_CONST(9025.7087580148236/8.0), + REAL_CONST(9038.7070774762469/8.0), + REAL_CONST(9051.7100717520643/8.0), + REAL_CONST(9064.7177374833282/8.0), + REAL_CONST(9077.7300713171153/8.0), + REAL_CONST(9090.7470699065179/8.0), + REAL_CONST(9103.7687299106146/8.0), + REAL_CONST(9116.7950479944648/8.0), + REAL_CONST(9129.8260208290812/8.0), + REAL_CONST(9142.8616450914233/8.0), + REAL_CONST(9155.9019174643727/8.0), + REAL_CONST(9168.9468346367157/8.0), + REAL_CONST(9181.9963933031358/8.0), + REAL_CONST(9195.0505901641845/8.0), + REAL_CONST(9208.1094219262741/8.0), + REAL_CONST(9221.1728853016557/8.0), + REAL_CONST(9234.240977008405/8.0), + REAL_CONST(9247.3136937704076/8.0), + REAL_CONST(9260.3910323173386/8.0), + REAL_CONST(9273.472989384647/8.0), + REAL_CONST(9286.5595617135423/8.0), + REAL_CONST(9299.6507460509747/8.0), + REAL_CONST(9312.7465391496207/8.0), + REAL_CONST(9325.8469377678684/8.0), + REAL_CONST(9338.9519386698012/8.0), + REAL_CONST(9352.0615386251757/8.0), + REAL_CONST(9365.1757344094131/8.0), + REAL_CONST(9378.2945228035842/8.0), + REAL_CONST(9391.4179005943843/8.0), + REAL_CONST(9404.5458645741273/8.0), + REAL_CONST(9417.6784115407263/8.0), + REAL_CONST(9430.8155382976747/8.0), + REAL_CONST(9443.9572416540359/8.0), + REAL_CONST(9457.1035184244265/8.0), + REAL_CONST(9470.2543654290002/8.0), + REAL_CONST(9483.4097794934296/8.0), + REAL_CONST(9496.5697574488931/8.0), + REAL_CONST(9509.7342961320664/8.0), + REAL_CONST(9522.9033923850911/8.0), + REAL_CONST(9536.0770430555804/8.0), + REAL_CONST(9549.2552449965824/8.0), + REAL_CONST(9562.4379950665825/8.0), + REAL_CONST(9575.6252901294793/8.0), + REAL_CONST(9588.8171270545736/8.0), + REAL_CONST(9602.0135027165488/8.0), + REAL_CONST(9615.2144139954635/8.0), + REAL_CONST(9628.4198577767274/8.0), + REAL_CONST(9641.629830951093/8.0), + REAL_CONST(9654.844330414644/8.0), + REAL_CONST(9668.0633530687719/8.0), + REAL_CONST(9681.286895820167/8.0), + REAL_CONST(9694.5149555808002/8.0), + REAL_CONST(9707.7475292679192/8.0), + REAL_CONST(9720.9846138040157/8.0), + REAL_CONST(9734.2262061168276/8.0), + REAL_CONST(9747.4723031393187/8.0), + REAL_CONST(9760.7229018096641/8.0), + REAL_CONST(9773.9779990712323/8.0), + REAL_CONST(9787.2375918725811/8.0), + REAL_CONST(9800.5016771674327/8.0), + REAL_CONST(9813.7702519146696/8.0), + REAL_CONST(9827.0433130783094/8.0), + REAL_CONST(9840.3208576275028/8.0), + REAL_CONST(9853.602882536512/8.0), + REAL_CONST(9866.8893847846994/8.0), + REAL_CONST(9880.1803613565116/8.0), + REAL_CONST(9893.4758092414686/8.0), + REAL_CONST(9906.7757254341523/8.0), + REAL_CONST(9920.0801069341851/8.0), + REAL_CONST(9933.3889507462245/8.0), + REAL_CONST(9946.7022538799429/8.0), + REAL_CONST(9960.0200133500221/8.0), + REAL_CONST(9973.3422261761298/8.0), + REAL_CONST(9986.6688893829159/8.0), + REAL_CONST(9999.9999999999945/8.0), + REAL_CONST(10013.335555061929/8.0), + REAL_CONST(10026.675551608221/8.0), + REAL_CONST(10040.019986683301/8.0), + REAL_CONST(10053.368857336509/8.0), + REAL_CONST(10066.722160622081/8.0), + REAL_CONST(10080.079893599144/8.0), + REAL_CONST(10093.442053331697/8.0), + REAL_CONST(10106.808636888598/8.0), + REAL_CONST(10120.179641343551/8.0), + REAL_CONST(10133.555063775095/8.0), + REAL_CONST(10146.934901266595/8.0), + REAL_CONST(10160.31915090622/8.0), + REAL_CONST(10173.707809786936/8.0), + REAL_CONST(10187.100875006496/8.0), + REAL_CONST(10200.498343667417/8.0), + REAL_CONST(10213.900212876984/8.0), + REAL_CONST(10227.306479747222/8.0), + REAL_CONST(10240.717141394889/8.0), + REAL_CONST(10254.132194941467/8.0), + REAL_CONST(10267.551637513146/8.0), + REAL_CONST(10280.975466240814/8.0), + REAL_CONST(10294.40367826004/8.0), + REAL_CONST(10307.836270711066/8.0), + REAL_CONST(10321.273240738796/8.0), + REAL_CONST(10334.71458549278/8.0) +#ifdef BIG_IQ_TABLE + ,REAL_CONST(10348.160302127204/8.0), + REAL_CONST(10361.610387800878/8.0), + REAL_CONST(10375.064839677221/8.0), + REAL_CONST(10388.523654924258/8.0), + REAL_CONST(10401.986830714593/8.0), + REAL_CONST(10415.454364225412/8.0), + REAL_CONST(10428.926252638465/8.0), + REAL_CONST(10442.402493140049/8.0), + REAL_CONST(10455.883082921007/8.0), + REAL_CONST(10469.368019176709/8.0), + REAL_CONST(10482.85729910704/8.0), + REAL_CONST(10496.350919916393/8.0), + REAL_CONST(10509.848878813653/8.0), + REAL_CONST(10523.351173012188/8.0), + REAL_CONST(10536.857799729838/8.0), + REAL_CONST(10550.3687561889/8.0), + REAL_CONST(10563.884039616123/8.0), + REAL_CONST(10577.403647242685/8.0), + REAL_CONST(10590.927576304197/8.0), + REAL_CONST(10604.455824040679/8.0), + REAL_CONST(10617.988387696556/8.0), + REAL_CONST(10631.525264520642/8.0), + REAL_CONST(10645.066451766135/8.0), + REAL_CONST(10658.611946690598/8.0), + REAL_CONST(10672.161746555956/8.0), + REAL_CONST(10685.715848628475/8.0), + REAL_CONST(10699.274250178762/8.0), + REAL_CONST(10712.836948481747/8.0), + REAL_CONST(10726.403940816675/8.0), + REAL_CONST(10739.975224467091/8.0), + REAL_CONST(10753.550796720834/8.0), + REAL_CONST(10767.130654870027/8.0), + REAL_CONST(10780.714796211059/8.0), + REAL_CONST(10794.303218044579/8.0), + REAL_CONST(10807.895917675487/8.0), + REAL_CONST(10821.492892412922/8.0), + REAL_CONST(10835.094139570248/8.0), + REAL_CONST(10848.699656465047/8.0), + REAL_CONST(10862.309440419107/8.0), + REAL_CONST(10875.923488758415/8.0), + REAL_CONST(10889.541798813138/8.0), + REAL_CONST(10903.16436791762/8.0), + REAL_CONST(10916.791193410372/8.0), + REAL_CONST(10930.422272634056/8.0), + REAL_CONST(10944.05760293548/8.0), + REAL_CONST(10957.697181665582/8.0), + REAL_CONST(10971.341006179427/8.0), + REAL_CONST(10984.98907383619/8.0), + REAL_CONST(10998.641381999149/8.0), + REAL_CONST(11012.297928035676/8.0), + REAL_CONST(11025.958709317223/8.0), + REAL_CONST(11039.623723219316/8.0), + REAL_CONST(11053.292967121541/8.0), + REAL_CONST(11066.966438407539/8.0), + REAL_CONST(11080.64413446499/8.0), + REAL_CONST(11094.326052685608/8.0), + REAL_CONST(11108.012190465128/8.0), + REAL_CONST(11121.702545203296/8.0), + REAL_CONST(11135.397114303863/8.0), + REAL_CONST(11149.095895174571/8.0), + REAL_CONST(11162.798885227143/8.0), + REAL_CONST(11176.506081877278/8.0), + REAL_CONST(11190.217482544635/8.0), + REAL_CONST(11203.933084652828/8.0), + REAL_CONST(11217.652885629415/8.0), + REAL_CONST(11231.376882905886/8.0), + REAL_CONST(11245.105073917659/8.0), + REAL_CONST(11258.837456104062/8.0), + REAL_CONST(11272.574026908333/8.0), + REAL_CONST(11286.314783777601/8.0), + REAL_CONST(11300.059724162888/8.0), + REAL_CONST(11313.808845519083/8.0), + REAL_CONST(11327.562145304952/8.0), + REAL_CONST(11341.319620983111/8.0), + REAL_CONST(11355.081270020033/8.0), + REAL_CONST(11368.847089886023/8.0), + REAL_CONST(11382.617078055218/8.0), + REAL_CONST(11396.391232005579/8.0), + REAL_CONST(11410.169549218874/8.0), + REAL_CONST(11423.952027180676/8.0), + REAL_CONST(11437.738663380349/8.0), + REAL_CONST(11451.529455311042/8.0), + REAL_CONST(11465.324400469679/8.0), + REAL_CONST(11479.123496356951/8.0), + REAL_CONST(11492.926740477304/8.0), + REAL_CONST(11506.734130338931/8.0), + REAL_CONST(11520.545663453764/8.0), + REAL_CONST(11534.361337337466/8.0), + REAL_CONST(11548.181149509423/8.0), + REAL_CONST(11562.005097492724/8.0), + REAL_CONST(11575.83317881417/8.0), + REAL_CONST(11589.665391004253/8.0), + REAL_CONST(11603.501731597149/8.0), + REAL_CONST(11617.342198130715/8.0), + REAL_CONST(11631.186788146468/8.0), + REAL_CONST(11645.035499189589/8.0), + REAL_CONST(11658.888328808911/8.0), + REAL_CONST(11672.745274556904/8.0), + REAL_CONST(11686.606333989675/8.0), + REAL_CONST(11700.471504666955/8.0), + REAL_CONST(11714.340784152086/8.0), + REAL_CONST(11728.214170012021/8.0), + REAL_CONST(11742.091659817312/8.0), + REAL_CONST(11755.973251142101/8.0), + REAL_CONST(11769.858941564111/8.0), + REAL_CONST(11783.748728664636/8.0), + REAL_CONST(11797.642610028539/8.0), + REAL_CONST(11811.540583244237/8.0), + REAL_CONST(11825.442645903697/8.0), + REAL_CONST(11839.34879560242/8.0), + REAL_CONST(11853.259029939445/8.0), + REAL_CONST(11867.173346517333/8.0), + REAL_CONST(11881.091742942155/8.0), + REAL_CONST(11895.014216823492/8.0), + REAL_CONST(11908.940765774427/8.0), + REAL_CONST(11922.871387411526/8.0), + REAL_CONST(11936.806079354839/8.0), + REAL_CONST(11950.744839227897/8.0), + REAL_CONST(11964.687664657684/8.0), + REAL_CONST(11978.634553274653/8.0), + REAL_CONST(11992.585502712702/8.0), + REAL_CONST(12006.540510609168/8.0), + REAL_CONST(12020.499574604828/8.0), + REAL_CONST(12034.462692343877/8.0), + REAL_CONST(12048.429861473938/8.0), + REAL_CONST(12062.401079646032/8.0), + REAL_CONST(12076.376344514589/8.0), + REAL_CONST(12090.355653737433/8.0), + REAL_CONST(12104.339004975769/8.0), + REAL_CONST(12118.326395894188/8.0), + REAL_CONST(12132.317824160644/8.0), + REAL_CONST(12146.313287446457/8.0), + REAL_CONST(12160.312783426305/8.0), + REAL_CONST(12174.316309778205/8.0), + REAL_CONST(12188.323864183525/8.0), + REAL_CONST(12202.335444326955/8.0), + REAL_CONST(12216.351047896511/8.0), + REAL_CONST(12230.370672583531/8.0), + REAL_CONST(12244.394316082657/8.0), + REAL_CONST(12258.421976091831/8.0), + REAL_CONST(12272.453650312296/8.0), + REAL_CONST(12286.489336448574/8.0), + REAL_CONST(12300.529032208471/8.0), + REAL_CONST(12314.572735303058/8.0), + REAL_CONST(12328.620443446678/8.0), + REAL_CONST(12342.672154356922/8.0), + REAL_CONST(12356.727865754638/8.0), + REAL_CONST(12370.787575363909/8.0), + REAL_CONST(12384.851280912055/8.0), + REAL_CONST(12398.918980129623/8.0), + REAL_CONST(12412.990670750381/8.0), + REAL_CONST(12427.066350511306/8.0), + REAL_CONST(12441.146017152583/8.0), + REAL_CONST(12455.229668417589/8.0), + REAL_CONST(12469.317302052901/8.0), + REAL_CONST(12483.40891580827/8.0), + REAL_CONST(12497.50450743663/8.0), + REAL_CONST(12511.604074694078/8.0), + REAL_CONST(12525.707615339878/8.0), + REAL_CONST(12539.815127136444/8.0), + REAL_CONST(12553.926607849342/8.0), + REAL_CONST(12568.042055247275/8.0), + REAL_CONST(12582.161467102082/8.0), + REAL_CONST(12596.284841188726/8.0), + REAL_CONST(12610.41217528529/8.0), + REAL_CONST(12624.543467172971/8.0), + REAL_CONST(12638.678714636069/8.0), + REAL_CONST(12652.817915461985/8.0), + REAL_CONST(12666.961067441209/8.0), + REAL_CONST(12681.108168367316/8.0), + REAL_CONST(12695.259216036962/8.0), + REAL_CONST(12709.414208249869/8.0), + REAL_CONST(12723.573142808827/8.0), + REAL_CONST(12737.736017519681/8.0), + REAL_CONST(12751.902830191326/8.0), + REAL_CONST(12766.073578635704/8.0), + REAL_CONST(12780.248260667788/8.0), + REAL_CONST(12794.426874105588/8.0), + REAL_CONST(12808.609416770132/8.0), + REAL_CONST(12822.795886485468/8.0), + REAL_CONST(12836.986281078653/8.0), + REAL_CONST(12851.180598379744/8.0), + REAL_CONST(12865.378836221802/8.0), + REAL_CONST(12879.580992440871/8.0), + REAL_CONST(12893.787064875984/8.0), + REAL_CONST(12907.997051369144/8.0), + REAL_CONST(12922.210949765335/8.0), + REAL_CONST(12936.428757912496/8.0), + REAL_CONST(12950.650473661524/8.0), + REAL_CONST(12964.876094866273/8.0), + REAL_CONST(12979.105619383534/8.0), + REAL_CONST(12993.339045073039/8.0), + REAL_CONST(13007.576369797454/8.0), + REAL_CONST(13021.817591422368/8.0), + REAL_CONST(13036.062707816285/8.0), + REAL_CONST(13050.311716850629/8.0), + REAL_CONST(13064.564616399723/8.0), + REAL_CONST(13078.821404340792/8.0), + REAL_CONST(13093.082078553954/8.0), + REAL_CONST(13107.346636922217/8.0), + REAL_CONST(13121.615077331464/8.0), + REAL_CONST(13135.887397670458/8.0), + REAL_CONST(13150.163595830827/8.0), + REAL_CONST(13164.44366970706/8.0), + REAL_CONST(13178.727617196502/8.0), + REAL_CONST(13193.015436199352/8.0), + REAL_CONST(13207.307124618648/8.0), + REAL_CONST(13221.602680360265/8.0), + REAL_CONST(13235.902101332911/8.0), + REAL_CONST(13250.205385448118/8.0), + REAL_CONST(13264.512530620239/8.0), + REAL_CONST(13278.823534766434/8.0), + REAL_CONST(13293.138395806676/8.0), + REAL_CONST(13307.457111663734/8.0), + REAL_CONST(13321.779680263176/8.0), + REAL_CONST(13336.106099533356/8.0), + REAL_CONST(13350.436367405409/8.0), + REAL_CONST(13364.77048181325/8.0), + REAL_CONST(13379.108440693562/8.0), + REAL_CONST(13393.450241985796/8.0), + REAL_CONST(13407.795883632158/8.0), + REAL_CONST(13422.145363577607/8.0), + REAL_CONST(13436.498679769853/8.0), + REAL_CONST(13450.855830159346/8.0), + REAL_CONST(13465.216812699266/8.0), + REAL_CONST(13479.581625345529/8.0), + REAL_CONST(13493.950266056772/8.0), + REAL_CONST(13508.32273279435/8.0), + REAL_CONST(13522.699023522329/8.0), + REAL_CONST(13537.079136207483/8.0), + REAL_CONST(13551.463068819286/8.0), + REAL_CONST(13565.850819329906/8.0), + REAL_CONST(13580.2423857142/8.0), + REAL_CONST(13594.63776594971/8.0), + REAL_CONST(13609.036958016657/8.0), + REAL_CONST(13623.439959897927/8.0), + REAL_CONST(13637.846769579081/8.0), + REAL_CONST(13652.257385048335/8.0), + REAL_CONST(13666.67180429656/8.0), + REAL_CONST(13681.090025317284/8.0), + REAL_CONST(13695.512046106669/8.0), + REAL_CONST(13709.937864663521/8.0), + REAL_CONST(13724.367478989278/8.0), + REAL_CONST(13738.800887088004/8.0), + REAL_CONST(13753.238086966385/8.0), + REAL_CONST(13767.679076633727/8.0), + REAL_CONST(13782.123854101939/8.0), + REAL_CONST(13796.572417385545/8.0), + REAL_CONST(13811.024764501659/8.0), + REAL_CONST(13825.480893469998/8.0), + REAL_CONST(13839.94080231286/8.0), + REAL_CONST(13854.404489055134/8.0), + REAL_CONST(13868.871951724283/8.0), + REAL_CONST(13883.34318835034/8.0), + REAL_CONST(13897.818196965914/8.0), + REAL_CONST(13912.296975606168/8.0), + REAL_CONST(13926.779522308825/8.0), + REAL_CONST(13941.26583511416/8.0), + REAL_CONST(13955.755912064991/8.0), + REAL_CONST(13970.249751206682/8.0), + REAL_CONST(13984.747350587126/8.0), + REAL_CONST(13999.248708256751/8.0), + REAL_CONST(14013.753822268511/8.0), + REAL_CONST(14028.262690677873/8.0), + REAL_CONST(14042.775311542828/8.0), + REAL_CONST(14057.291682923867/8.0), + REAL_CONST(14071.811802883994/8.0), + REAL_CONST(14086.335669488704/8.0), + REAL_CONST(14100.863280805994/8.0), + REAL_CONST(14115.394634906341/8.0), + REAL_CONST(14129.92972986271/8.0), + REAL_CONST(14144.468563750548/8.0), + REAL_CONST(14159.01113464777/8.0), + REAL_CONST(14173.55744063476/8.0), + REAL_CONST(14188.107479794369/8.0), + REAL_CONST(14202.661250211901/8.0), + REAL_CONST(14217.218749975118/8.0), + REAL_CONST(14231.779977174227/8.0), + REAL_CONST(14246.344929901879/8.0), + REAL_CONST(14260.913606253163/8.0), + REAL_CONST(14275.486004325601/8.0), + REAL_CONST(14290.062122219146/8.0), + REAL_CONST(14304.641958036171/8.0), + REAL_CONST(14319.225509881464/8.0), + REAL_CONST(14333.812775862236/8.0), + REAL_CONST(14348.403754088098/8.0), + REAL_CONST(14362.998442671067/8.0), + REAL_CONST(14377.59683972556/8.0), + REAL_CONST(14392.198943368388/8.0), + REAL_CONST(14406.804751718748/8.0), + REAL_CONST(14421.414262898223/8.0), + REAL_CONST(14436.027475030774/8.0), + REAL_CONST(14450.64438624274/8.0), + REAL_CONST(14465.264994662828/8.0), + REAL_CONST(14479.889298422106/8.0), + REAL_CONST(14494.517295654005/8.0), + REAL_CONST(14509.148984494313/8.0), + REAL_CONST(14523.784363081166/8.0), + REAL_CONST(14538.423429555049/8.0), + REAL_CONST(14553.066182058781/8.0), + REAL_CONST(14567.712618737527/8.0), + REAL_CONST(14582.362737738777/8.0), + REAL_CONST(14597.016537212348/8.0), + REAL_CONST(14611.674015310382/8.0), + REAL_CONST(14626.33517018734/8.0), + REAL_CONST(14640.999999999993/8.0), + REAL_CONST(14655.668502907418/8.0), + REAL_CONST(14670.340677071003/8.0), + REAL_CONST(14685.016520654426/8.0), + REAL_CONST(14699.696031823671/8.0), + REAL_CONST(14714.379208746999/8.0), + REAL_CONST(14729.066049594967/8.0), + REAL_CONST(14743.756552540408/8.0), + REAL_CONST(14758.45071575843/8.0), + REAL_CONST(14773.148537426418/8.0), + REAL_CONST(14787.850015724018/8.0), + REAL_CONST(14802.555148833142/8.0), + REAL_CONST(14817.263934937961/8.0), + REAL_CONST(14831.976372224897/8.0), + REAL_CONST(14846.692458882624/8.0), + REAL_CONST(14861.41219310206/8.0), + REAL_CONST(14876.135573076363/8.0), + REAL_CONST(14890.862597000923/8.0), + REAL_CONST(14905.593263073371/8.0), + REAL_CONST(14920.327569493558/8.0), + REAL_CONST(14935.065514463557/8.0), + REAL_CONST(14949.807096187662/8.0), + REAL_CONST(14964.552312872382/8.0), + REAL_CONST(14979.301162726431/8.0), + REAL_CONST(14994.053643960735/8.0), + REAL_CONST(15008.809754788414/8.0), + REAL_CONST(15023.569493424788/8.0), + REAL_CONST(15038.332858087369/8.0), + REAL_CONST(15053.099846995858/8.0), + REAL_CONST(15067.870458372134/8.0), + REAL_CONST(15082.644690440264/8.0), + REAL_CONST(15097.422541426484/8.0), + REAL_CONST(15112.204009559202/8.0), + REAL_CONST(15126.989093068994/8.0), + REAL_CONST(15141.777790188597/8.0), + REAL_CONST(15156.570099152905/8.0), + REAL_CONST(15171.366018198967/8.0), + REAL_CONST(15186.165545565986/8.0), + REAL_CONST(15200.968679495301/8.0), + REAL_CONST(15215.775418230402/8.0), + REAL_CONST(15230.585760016909/8.0), + REAL_CONST(15245.399703102579/8.0), + REAL_CONST(15260.217245737298/8.0), + REAL_CONST(15275.038386173073/8.0), + REAL_CONST(15289.863122664035/8.0), + REAL_CONST(15304.691453466432/8.0), + REAL_CONST(15319.523376838621/8.0), + REAL_CONST(15334.358891041069/8.0), + REAL_CONST(15349.197994336346/8.0), + REAL_CONST(15364.040684989128/8.0), + REAL_CONST(15378.886961266177/8.0), + REAL_CONST(15393.736821436356/8.0), + REAL_CONST(15408.590263770609/8.0), + REAL_CONST(15423.447286541972/8.0), + REAL_CONST(15438.307888025554/8.0), + REAL_CONST(15453.172066498542/8.0), + REAL_CONST(15468.039820240196/8.0), + REAL_CONST(15482.91114753184/8.0), + REAL_CONST(15497.786046656869/8.0), + REAL_CONST(15512.664515900733/8.0), + REAL_CONST(15527.546553550939/8.0), + REAL_CONST(15542.432157897045/8.0), + REAL_CONST(15557.32132723066/8.0), + REAL_CONST(15572.214059845435/8.0), + REAL_CONST(15587.110354037064/8.0), + REAL_CONST(15602.010208103273/8.0), + REAL_CONST(15616.913620343823/8.0), + REAL_CONST(15631.820589060506/8.0), + REAL_CONST(15646.731112557136/8.0), + REAL_CONST(15661.645189139546/8.0), + REAL_CONST(15676.562817115593/8.0), + REAL_CONST(15691.483994795139/8.0), + REAL_CONST(15706.408720490062/8.0), + REAL_CONST(15721.336992514242/8.0), + REAL_CONST(15736.268809183561/8.0), + REAL_CONST(15751.204168815901/8.0), + REAL_CONST(15766.143069731135/8.0), + REAL_CONST(15781.085510251132/8.0), + REAL_CONST(15796.03148869974/8.0), + REAL_CONST(15810.981003402798/8.0), + REAL_CONST(15825.934052688119/8.0), + REAL_CONST(15840.890634885489/8.0), + REAL_CONST(15855.850748326673/8.0), + REAL_CONST(15870.814391345401/8.0), + REAL_CONST(15885.781562277361/8.0), + REAL_CONST(15900.752259460214/8.0), + REAL_CONST(15915.726481233565/8.0), + REAL_CONST(15930.704225938984/8.0), + REAL_CONST(15945.685491919978/8.0), + REAL_CONST(15960.670277522009/8.0), + REAL_CONST(15975.658581092481/8.0), + REAL_CONST(15990.65040098073/8.0), + REAL_CONST(16005.645735538035/8.0), + REAL_CONST(16020.644583117599/8.0), + REAL_CONST(16035.646942074556/8.0), + REAL_CONST(16050.652810765967/8.0), + REAL_CONST(16065.662187550806/8.0), + REAL_CONST(16080.675070789974/8.0), + REAL_CONST(16095.691458846273/8.0), + REAL_CONST(16110.711350084424/8.0), + REAL_CONST(16125.734742871053/8.0), + REAL_CONST(16140.761635574685/8.0), + REAL_CONST(16155.792026565747/8.0), + REAL_CONST(16170.825914216561/8.0), + REAL_CONST(16185.863296901338/8.0), + REAL_CONST(16200.904172996183/8.0), + REAL_CONST(16215.948540879079/8.0), + REAL_CONST(16230.996398929899/8.0), + REAL_CONST(16246.047745530386/8.0), + REAL_CONST(16261.102579064163/8.0), + REAL_CONST(16276.160897916721/8.0), + REAL_CONST(16291.22270047542/8.0), + REAL_CONST(16306.287985129484/8.0), + REAL_CONST(16321.356750269995/8.0), + REAL_CONST(16336.428994289896/8.0), + REAL_CONST(16351.504715583982/8.0), + REAL_CONST(16366.5839125489/8.0), + REAL_CONST(16381.666583583141/8.0), + REAL_CONST(16396.752727087041/8.0), + REAL_CONST(16411.842341462776/8.0), + REAL_CONST(16426.935425114363/8.0), + REAL_CONST(16442.031976447644/8.0), + REAL_CONST(16457.131993870298/8.0), + REAL_CONST(16472.235475791829/8.0), + REAL_CONST(16487.342420623561/8.0), + REAL_CONST(16502.452826778641/8.0), + REAL_CONST(16517.566692672033/8.0), + REAL_CONST(16532.684016720516/8.0), + REAL_CONST(16547.804797342676/8.0), + REAL_CONST(16562.929032958902/8.0), + REAL_CONST(16578.056721991394/8.0), + REAL_CONST(16593.18786286415/8.0), + REAL_CONST(16608.322454002962/8.0), + REAL_CONST(16623.460493835417/8.0), + REAL_CONST(16638.601980790896/8.0), + REAL_CONST(16653.746913300558/8.0), + REAL_CONST(16668.895289797354/8.0), + REAL_CONST(16684.047108716015/8.0), + REAL_CONST(16699.202368493046/8.0), + REAL_CONST(16714.361067566726/8.0), + REAL_CONST(16729.523204377107/8.0), + REAL_CONST(16744.688777366009/8.0), + REAL_CONST(16759.857784977012/8.0), + REAL_CONST(16775.030225655464/8.0), + REAL_CONST(16790.206097848466/8.0), + REAL_CONST(16805.385400004874/8.0), + REAL_CONST(16820.568130575302/8.0), + REAL_CONST(16835.754288012104/8.0), + REAL_CONST(16850.943870769381/8.0), + REAL_CONST(16866.136877302983/8.0), + REAL_CONST(16881.333306070494/8.0), + REAL_CONST(16896.53315553123/8.0), + REAL_CONST(16911.736424146249/8.0), + REAL_CONST(16926.943110378332/8.0), + REAL_CONST(16942.153212691992/8.0), + REAL_CONST(16957.366729553454/8.0), + REAL_CONST(16972.583659430682/8.0), + REAL_CONST(16987.804000793338/8.0), + REAL_CONST(17003.027752112816/8.0), + REAL_CONST(17018.254911862205/8.0), + REAL_CONST(17033.485478516312/8.0), + REAL_CONST(17048.719450551645/8.0), + REAL_CONST(17063.956826446421/8.0), + REAL_CONST(17079.197604680547/8.0), + REAL_CONST(17094.44178373563/8.0), + REAL_CONST(17109.689362094967/8.0), + REAL_CONST(17124.940338243552/8.0), + REAL_CONST(17140.194710668064/8.0), + REAL_CONST(17155.452477856852/8.0), + REAL_CONST(17170.713638299967/8.0), + REAL_CONST(17185.978190489128/8.0), + REAL_CONST(17201.246132917724/8.0), + REAL_CONST(17216.517464080825/8.0), + REAL_CONST(17231.792182475165/8.0), + REAL_CONST(17247.070286599141/8.0), + REAL_CONST(17262.351774952826/8.0), + REAL_CONST(17277.636646037936/8.0), + REAL_CONST(17292.924898357855/8.0), + REAL_CONST(17308.216530417623/8.0), + REAL_CONST(17323.511540723921/8.0), + REAL_CONST(17338.809927785089/8.0), + REAL_CONST(17354.111690111105/8.0), + REAL_CONST(17369.416826213594/8.0), + REAL_CONST(17384.725334605821/8.0), + REAL_CONST(17400.037213802683/8.0), + REAL_CONST(17415.352462320716/8.0), + REAL_CONST(17430.67107867809/8.0), + REAL_CONST(17445.993061394587/8.0), + REAL_CONST(17461.318408991636/8.0), + REAL_CONST(17476.647119992274/8.0), + REAL_CONST(17491.979192921168/8.0), + REAL_CONST(17507.314626304586/8.0), + REAL_CONST(17522.653418670423/8.0), + REAL_CONST(17537.995568548187/8.0), + REAL_CONST(17553.341074468986/8.0), + REAL_CONST(17568.689934965536/8.0), + REAL_CONST(17584.042148572156/8.0), + REAL_CONST(17599.397713824768/8.0), + REAL_CONST(17614.75662926089/8.0), + REAL_CONST(17630.118893419625/8.0), + REAL_CONST(17645.484504841683/8.0), + REAL_CONST(17660.853462069354/8.0), + REAL_CONST(17676.225763646511/8.0), + REAL_CONST(17691.601408118619/8.0), + REAL_CONST(17706.980394032718/8.0), + REAL_CONST(17722.362719937424/8.0), + REAL_CONST(17737.748384382936/8.0), + REAL_CONST(17753.137385921014/8.0), + REAL_CONST(17768.529723104999/8.0), + REAL_CONST(17783.92539448979/8.0), + REAL_CONST(17799.324398631856/8.0), + REAL_CONST(17814.726734089225/8.0), + REAL_CONST(17830.13239942148/8.0), + REAL_CONST(17845.541393189767/8.0), + REAL_CONST(17860.95371395678/8.0), + REAL_CONST(17876.369360286772/8.0), + REAL_CONST(17891.788330745527/8.0), + REAL_CONST(17907.210623900395/8.0), + REAL_CONST(17922.636238320254/8.0), + REAL_CONST(17938.065172575527/8.0), + REAL_CONST(17953.497425238176/8.0), + REAL_CONST(17968.932994881692/8.0), + REAL_CONST(17984.371880081104/8.0), + REAL_CONST(17999.814079412972/8.0), + REAL_CONST(18015.259591455371/8.0), + REAL_CONST(18030.708414787914/8.0), + REAL_CONST(18046.160547991731/8.0), + REAL_CONST(18061.615989649465/8.0), + REAL_CONST(18077.074738345284/8.0), + REAL_CONST(18092.536792664861/8.0), + REAL_CONST(18108.002151195393/8.0), + REAL_CONST(18123.470812525571/8.0), + REAL_CONST(18138.942775245599/8.0), + REAL_CONST(18154.418037947191/8.0), + REAL_CONST(18169.896599223546/8.0), + REAL_CONST(18185.37845766938/8.0), + REAL_CONST(18200.863611880886/8.0), + REAL_CONST(18216.352060455767/8.0), + REAL_CONST(18231.843801993204/8.0), + REAL_CONST(18247.338835093873/8.0), + REAL_CONST(18262.837158359936/8.0), + REAL_CONST(18278.338770395032/8.0), + REAL_CONST(18293.84366980429/8.0), + REAL_CONST(18309.351855194309/8.0), + REAL_CONST(18324.863325173166/8.0), + REAL_CONST(18340.378078350412/8.0), + REAL_CONST(18355.896113337069/8.0), + REAL_CONST(18371.417428745623/8.0), + REAL_CONST(18386.942023190033/8.0), + REAL_CONST(18402.469895285718/8.0), + REAL_CONST(18418.00104364955/8.0), + REAL_CONST(18433.53546689987/8.0), + REAL_CONST(18449.073163656474/8.0), + REAL_CONST(18464.614132540602/8.0), + REAL_CONST(18480.158372174956/8.0), + REAL_CONST(18495.705881183676/8.0), + REAL_CONST(18511.256658192357/8.0), + REAL_CONST(18526.810701828035/8.0), + REAL_CONST(18542.368010719183/8.0), + REAL_CONST(18557.928583495715/8.0), + REAL_CONST(18573.492418788985/8.0), + REAL_CONST(18589.059515231773/8.0), + REAL_CONST(18604.629871458303/8.0), + REAL_CONST(18620.203486104212/8.0), + REAL_CONST(18635.78035780658/8.0), + REAL_CONST(18651.360485203899/8.0), + REAL_CONST(18666.943866936086/8.0), + REAL_CONST(18682.53050164448/8.0), + REAL_CONST(18698.120387971841/8.0), + REAL_CONST(18713.713524562332/8.0), + REAL_CONST(18729.30991006154/8.0), + REAL_CONST(18744.909543116457/8.0), + REAL_CONST(18760.512422375479/8.0), + REAL_CONST(18776.118546488418/8.0), + REAL_CONST(18791.727914106479/8.0), + REAL_CONST(18807.340523882274/8.0), + REAL_CONST(18822.95637446981/8.0), + REAL_CONST(18838.575464524489/8.0), + REAL_CONST(18854.197792703111/8.0), + REAL_CONST(18869.823357663863/8.0), + REAL_CONST(18885.452158066328/8.0), + REAL_CONST(18901.08419257147/8.0), + REAL_CONST(18916.719459841639/8.0), + REAL_CONST(18932.357958540564/8.0), + REAL_CONST(18947.999687333362/8.0), + REAL_CONST(18963.644644886521/8.0), + REAL_CONST(18979.292829867907/8.0), + REAL_CONST(18994.944240946759/8.0), + REAL_CONST(19010.598876793687/8.0), + REAL_CONST(19026.256736080668/8.0), + REAL_CONST(19041.917817481048/8.0), + REAL_CONST(19057.582119669532/8.0), + REAL_CONST(19073.2496413222/8.0), + REAL_CONST(19088.920381116473/8.0), + REAL_CONST(19104.594337731145/8.0), + REAL_CONST(19120.271509846356/8.0), + REAL_CONST(19135.951896143604/8.0), + REAL_CONST(19151.635495305738/8.0), + REAL_CONST(19167.322306016948/8.0), + REAL_CONST(19183.012326962784/8.0), + REAL_CONST(19198.705556830122/8.0), + REAL_CONST(19214.401994307198/8.0), + REAL_CONST(19230.101638083579/8.0), + REAL_CONST(19245.804486850167/8.0), + REAL_CONST(19261.510539299208/8.0), + REAL_CONST(19277.219794124274/8.0), + REAL_CONST(19292.932250020265/8.0), + REAL_CONST(19308.647905683421/8.0), + REAL_CONST(19324.366759811302/8.0), + REAL_CONST(19340.088811102793/8.0), + REAL_CONST(19355.8140582581/8.0), + REAL_CONST(19371.542499978754/8.0), + REAL_CONST(19387.2741349676/8.0), + REAL_CONST(19403.008961928797/8.0), + REAL_CONST(19418.746979567823/8.0), + REAL_CONST(19434.488186591469/8.0), + REAL_CONST(19450.232581707827/8.0), + REAL_CONST(19465.980163626304/8.0), + REAL_CONST(19481.730931057613/8.0), + REAL_CONST(19497.484882713761/8.0), + REAL_CONST(19513.242017308068/8.0), + REAL_CONST(19529.002333555141/8.0), + REAL_CONST(19544.765830170898/8.0), + REAL_CONST(19560.532505872539/8.0), + REAL_CONST(19576.302359378566/8.0), + REAL_CONST(19592.075389408761/8.0), + REAL_CONST(19607.851594684209/8.0), + REAL_CONST(19623.630973927269/8.0), + REAL_CONST(19639.41352586159/8.0), + REAL_CONST(19655.199249212103/8.0), + REAL_CONST(19670.988142705017/8.0), + REAL_CONST(19686.780205067826/8.0), + REAL_CONST(19702.575435029288/8.0), + REAL_CONST(19718.373831319448/8.0), + REAL_CONST(19734.175392669615/8.0), + REAL_CONST(19749.980117812371/8.0), + REAL_CONST(19765.788005481569/8.0), + REAL_CONST(19781.599054412323/8.0), + REAL_CONST(19797.413263341008/8.0), + REAL_CONST(19813.230631005274/8.0), + REAL_CONST(19829.051156144014/8.0), + REAL_CONST(19844.874837497395/8.0), + REAL_CONST(19860.701673806827/8.0), + REAL_CONST(19876.531663814985/8.0), + REAL_CONST(19892.364806265789/8.0), + REAL_CONST(19908.201099904403/8.0), + REAL_CONST(19924.040543477258/8.0), + REAL_CONST(19939.883135732012/8.0), + REAL_CONST(19955.728875417579/8.0), + REAL_CONST(19971.577761284105/8.0), + REAL_CONST(19987.429792082985/8.0), + REAL_CONST(20003.284966566847/8.0), + REAL_CONST(20019.14328348956/8.0), + REAL_CONST(20035.004741606219/8.0), + REAL_CONST(20050.869339673161/8.0), + REAL_CONST(20066.737076447946/8.0), + REAL_CONST(20082.607950689362/8.0), + REAL_CONST(20098.481961157428/8.0), + REAL_CONST(20114.359106613385/8.0), + REAL_CONST(20130.239385819699/8.0), + REAL_CONST(20146.122797540058/8.0), + REAL_CONST(20162.009340539353/8.0), + REAL_CONST(20177.899013583716/8.0), + REAL_CONST(20193.791815440476/8.0), + REAL_CONST(20209.687744878182/8.0), + REAL_CONST(20225.586800666591/8.0), + REAL_CONST(20241.488981576669/8.0), + REAL_CONST(20257.394286380597/8.0), + REAL_CONST(20273.302713851754/8.0), + REAL_CONST(20289.214262764715/8.0), + REAL_CONST(20305.128931895277/8.0), + REAL_CONST(20321.046720020415/8.0), + REAL_CONST(20336.967625918318/8.0), + REAL_CONST(20352.891648368361/8.0), + REAL_CONST(20368.818786151114/8.0), + REAL_CONST(20384.749038048347/8.0), + REAL_CONST(20400.682402843009/8.0), + REAL_CONST(20416.618879319249/8.0), + REAL_CONST(20432.558466262391/8.0), + REAL_CONST(20448.501162458953/8.0), + REAL_CONST(20464.446966696629/8.0), + REAL_CONST(20480.395877764302/8.0), + REAL_CONST(20496.347894452025/8.0), + REAL_CONST(20512.303015551031/8.0), + REAL_CONST(20528.261239853735/8.0), + REAL_CONST(20544.22256615372/8.0), + REAL_CONST(20560.186993245738/8.0), + REAL_CONST(20576.15451992572/8.0), + REAL_CONST(20592.125144990758/8.0), + REAL_CONST(20608.098867239107/8.0), + REAL_CONST(20624.075685470198/8.0), + REAL_CONST(20640.055598484618/8.0), + REAL_CONST(20656.038605084115/8.0), + REAL_CONST(20672.024704071595/8.0), + REAL_CONST(20688.013894251126/8.0), + REAL_CONST(20704.006174427926/8.0), + REAL_CONST(20720.001543408373/8.0), + REAL_CONST(20735.999999999989/8.0), + REAL_CONST(20752.001543011454/8.0), + REAL_CONST(20768.006171252597/8.0), + REAL_CONST(20784.013883534382/8.0), + REAL_CONST(20800.024678668931/8.0), + REAL_CONST(20816.038555469506/8.0), + REAL_CONST(20832.055512750507/8.0), + REAL_CONST(20848.075549327474/8.0), + REAL_CONST(20864.098664017085/8.0), + REAL_CONST(20880.124855637161/8.0), + REAL_CONST(20896.154123006647/8.0), + REAL_CONST(20912.186464945626/8.0), + REAL_CONST(20928.221880275312/8.0), + REAL_CONST(20944.260367818049/8.0), + REAL_CONST(20960.301926397311/8.0), + REAL_CONST(20976.346554837684/8.0), + REAL_CONST(20992.394251964895/8.0), + REAL_CONST(21008.445016605787/8.0), + REAL_CONST(21024.498847588318/8.0), + REAL_CONST(21040.555743741574/8.0), + REAL_CONST(21056.615703895754/8.0), + REAL_CONST(21072.678726882168/8.0), + REAL_CONST(21088.744811533252/8.0), + REAL_CONST(21104.813956682538/8.0), + REAL_CONST(21120.886161164683/8.0), + REAL_CONST(21136.961423815443/8.0), + REAL_CONST(21153.039743471683/8.0), + REAL_CONST(21169.121118971379/8.0), + REAL_CONST(21185.205549153605/8.0), + REAL_CONST(21201.293032858535/8.0), + REAL_CONST(21217.383568927453/8.0), + REAL_CONST(21233.477156202731/8.0), + REAL_CONST(21249.573793527841/8.0), + REAL_CONST(21265.673479747358/8.0), + REAL_CONST(21281.776213706937/8.0), + REAL_CONST(21297.881994253334/8.0), + REAL_CONST(21313.990820234398/8.0), + REAL_CONST(21330.102690499054/8.0), + REAL_CONST(21346.21760389733/8.0), + REAL_CONST(21362.335559280327/8.0), + REAL_CONST(21378.456555500241/8.0), + REAL_CONST(21394.580591410333/8.0), + REAL_CONST(21410.707665864964/8.0), + REAL_CONST(21426.83777771956/8.0), + REAL_CONST(21442.970925830628/8.0), + REAL_CONST(21459.107109055756/8.0), + REAL_CONST(21475.246326253604/8.0), + REAL_CONST(21491.388576283895/8.0), + REAL_CONST(21507.533858007431/8.0), + REAL_CONST(21523.682170286087/8.0), + REAL_CONST(21539.833511982797/8.0), + REAL_CONST(21555.987881961566/8.0), + REAL_CONST(21572.145279087465/8.0), + REAL_CONST(21588.305702226615/8.0), + REAL_CONST(21604.469150246216/8.0), + REAL_CONST(21620.635622014521/8.0), + REAL_CONST(21636.805116400832/8.0), + REAL_CONST(21652.977632275521/8.0), + REAL_CONST(21669.153168510009/8.0), + REAL_CONST(21685.331723976764/8.0), + REAL_CONST(21701.513297549318/8.0), + REAL_CONST(21717.697888102244/8.0), + REAL_CONST(21733.885494511167/8.0), + REAL_CONST(21750.076115652759/8.0), + REAL_CONST(21766.269750404736/8.0), + REAL_CONST(21782.466397645861/8.0), + REAL_CONST(21798.666056255934/8.0), + REAL_CONST(21814.868725115801/8.0), + REAL_CONST(21831.074403107345/8.0), + REAL_CONST(21847.283089113484/8.0), + REAL_CONST(21863.494782018177/8.0), + REAL_CONST(21879.709480706417/8.0), + REAL_CONST(21895.927184064229/8.0), + REAL_CONST(21912.147890978667/8.0), + REAL_CONST(21928.371600337818/8.0), + REAL_CONST(21944.598311030797/8.0), + REAL_CONST(21960.828021947746/8.0), + REAL_CONST(21977.060731979829/8.0), + REAL_CONST(21993.296440019243/8.0), + REAL_CONST(22009.535144959198/8.0), + REAL_CONST(22025.77684569393/8.0), + REAL_CONST(22042.021541118691/8.0), + REAL_CONST(22058.269230129757/8.0), + REAL_CONST(22074.519911624411/8.0), + REAL_CONST(22090.773584500959/8.0), + REAL_CONST(22107.030247658717/8.0), + REAL_CONST(22123.289899998013/8.0), + REAL_CONST(22139.552540420187/8.0), + REAL_CONST(22155.818167827587/8.0), + REAL_CONST(22172.086781123569/8.0), + REAL_CONST(22188.358379212495/8.0), + REAL_CONST(22204.632960999726/8.0), + REAL_CONST(22220.910525391639/8.0), + REAL_CONST(22237.191071295601/8.0), + REAL_CONST(22253.474597619981/8.0), + REAL_CONST(22269.761103274148/8.0), + REAL_CONST(22286.050587168469/8.0), + REAL_CONST(22302.343048214312/8.0), + REAL_CONST(22318.638485324027/8.0), + REAL_CONST(22334.936897410968/8.0), + REAL_CONST(22351.23828338947/8.0), + REAL_CONST(22367.542642174871/8.0), + REAL_CONST(22383.849972683485/8.0), + REAL_CONST(22400.160273832618/8.0), + REAL_CONST(22416.473544540564/8.0), + REAL_CONST(22432.789783726603/8.0), + REAL_CONST(22449.108990310986/8.0), + REAL_CONST(22465.431163214958/8.0), + REAL_CONST(22481.75630136074/8.0), + REAL_CONST(22498.084403671528/8.0), + REAL_CONST(22514.415469071497/8.0), + REAL_CONST(22530.749496485802/8.0), + REAL_CONST(22547.086484840562/8.0), + REAL_CONST(22563.426433062879/8.0), + REAL_CONST(22579.769340080824/8.0), + REAL_CONST(22596.115204823436/8.0), + REAL_CONST(22612.464026220721/8.0), + REAL_CONST(22628.815803203655/8.0), + REAL_CONST(22645.170534704179/8.0), + REAL_CONST(22661.5282196552/8.0), + REAL_CONST(22677.888856990587/8.0), + REAL_CONST(22694.252445645168/8.0), + REAL_CONST(22710.618984554734/8.0), + REAL_CONST(22726.988472656034/8.0), + REAL_CONST(22743.360908886778/8.0), + REAL_CONST(22759.736292185622/8.0), + REAL_CONST(22776.114621492186/8.0), + REAL_CONST(22792.495895747044/8.0), + REAL_CONST(22808.880113891719/8.0), + REAL_CONST(22825.267274868678/8.0), + REAL_CONST(22841.657377621348/8.0), + REAL_CONST(22858.050421094096/8.0), + REAL_CONST(22874.446404232243/8.0), + REAL_CONST(22890.845325982053/8.0), + REAL_CONST(22907.247185290722/8.0), + REAL_CONST(22923.651981106406/8.0), + REAL_CONST(22940.059712378195/8.0), + REAL_CONST(22956.470378056114/8.0), + REAL_CONST(22972.883977091129/8.0), + REAL_CONST(22989.300508435153/8.0), + REAL_CONST(23005.719971041017/8.0), + REAL_CONST(23022.142363862498/8.0), + REAL_CONST(23038.567685854305/8.0), + REAL_CONST(23054.995935972078/8.0), + REAL_CONST(23071.427113172387/8.0), + REAL_CONST(23087.86121641273/8.0), + REAL_CONST(23104.298244651531/8.0), + REAL_CONST(23120.738196848146/8.0), + REAL_CONST(23137.181071962848/8.0), + REAL_CONST(23153.626868956846/8.0), + REAL_CONST(23170.075586792263/8.0), + REAL_CONST(23186.527224432142/8.0), + REAL_CONST(23202.981780840448/8.0), + REAL_CONST(23219.439254982066/8.0), + REAL_CONST(23235.899645822796/8.0), + REAL_CONST(23252.362952329357/8.0), + REAL_CONST(23268.829173469378/8.0), + REAL_CONST(23285.298308211408/8.0), + REAL_CONST(23301.770355524899/8.0), + REAL_CONST(23318.245314380223/8.0), + REAL_CONST(23334.723183748658/8.0), + REAL_CONST(23351.203962602387/8.0), + REAL_CONST(23367.687649914504/8.0), + REAL_CONST(23384.174244659007/8.0), + REAL_CONST(23400.663745810798/8.0), + REAL_CONST(23417.15615234568/8.0), + REAL_CONST(23433.651463240367/8.0), + REAL_CONST(23450.149677472462/8.0), + REAL_CONST(23466.650794020472/8.0), + REAL_CONST(23483.154811863806/8.0), + REAL_CONST(23499.661729982763/8.0), + REAL_CONST(23516.171547358543/8.0), + REAL_CONST(23532.684262973235/8.0), + REAL_CONST(23549.199875809823/8.0), + REAL_CONST(23565.718384852185/8.0), + REAL_CONST(23582.239789085092/8.0), + REAL_CONST(23598.764087494197/8.0), + REAL_CONST(23615.291279066041/8.0), + REAL_CONST(23631.821362788058/8.0), + REAL_CONST(23648.354337648565/8.0), + REAL_CONST(23664.890202636761/8.0), + REAL_CONST(23681.428956742733/8.0), + REAL_CONST(23697.970598957443/8.0), + REAL_CONST(23714.515128272738/8.0), + REAL_CONST(23731.062543681343/8.0), + REAL_CONST(23747.612844176863/8.0), + REAL_CONST(23764.166028753778/8.0), + REAL_CONST(23780.72209640744/8.0), + REAL_CONST(23797.281046134085/8.0), + REAL_CONST(23813.842876930816/8.0), + REAL_CONST(23830.407587795606/8.0), + REAL_CONST(23846.975177727301/8.0), + REAL_CONST(23863.545645725622/8.0), + REAL_CONST(23880.11899079115/8.0), + REAL_CONST(23896.695211925336/8.0), + REAL_CONST(23913.274308130498/8.0), + REAL_CONST(23929.856278409821/8.0), + REAL_CONST(23946.441121767348/8.0), + REAL_CONST(23963.028837207989/8.0), + REAL_CONST(23979.619423737513/8.0), + REAL_CONST(23996.212880362549/8.0), + REAL_CONST(24012.809206090584/8.0), + REAL_CONST(24029.408399929966/8.0), + REAL_CONST(24046.010460889898/8.0), + REAL_CONST(24062.615387980433/8.0), + REAL_CONST(24079.223180212492/8.0), + REAL_CONST(24095.833836597827/8.0), + REAL_CONST(24112.447356149063/8.0), + REAL_CONST(24129.063737879667/8.0), + REAL_CONST(24145.682980803951/8.0), + REAL_CONST(24162.305083937081/8.0), + REAL_CONST(24178.930046295067/8.0), + REAL_CONST(24195.557866894767/8.0), + REAL_CONST(24212.188544753884/8.0), + REAL_CONST(24228.822078890964/8.0), + REAL_CONST(24245.458468325389/8.0), + REAL_CONST(24262.097712077397/8.0), + REAL_CONST(24278.739809168052/8.0), + REAL_CONST(24295.384758619261/8.0), + REAL_CONST(24312.032559453768/8.0), + REAL_CONST(24328.683210695162/8.0), + REAL_CONST(24345.336711367858/8.0), + REAL_CONST(24361.993060497109/8.0), + REAL_CONST(24378.652257108995/8.0), + REAL_CONST(24395.314300230442/8.0), + REAL_CONST(24411.979188889192/8.0), + REAL_CONST(24428.646922113825/8.0), + REAL_CONST(24445.317498933746/8.0), + REAL_CONST(24461.990918379193/8.0), + REAL_CONST(24478.667179481225/8.0), + REAL_CONST(24495.346281271726/8.0), + REAL_CONST(24512.028222783407/8.0), + REAL_CONST(24528.713003049801/8.0), + REAL_CONST(24545.400621105266/8.0), + REAL_CONST(24562.091075984976/8.0), + REAL_CONST(24578.784366724925/8.0), + REAL_CONST(24595.480492361927/8.0), + REAL_CONST(24612.179451933614/8.0), + REAL_CONST(24628.881244478438/8.0), + REAL_CONST(24645.585869035654/8.0), + REAL_CONST(24662.293324645343/8.0), + REAL_CONST(24679.003610348394/8.0), + REAL_CONST(24695.716725186514/8.0), + REAL_CONST(24712.432668202211/8.0), + REAL_CONST(24729.151438438807/8.0), + REAL_CONST(24745.873034940436/8.0), + REAL_CONST(24762.597456752032/8.0), + REAL_CONST(24779.324702919344/8.0), + REAL_CONST(24796.054772488926/8.0), + REAL_CONST(24812.787664508123/8.0), + REAL_CONST(24829.5233780251/8.0), + REAL_CONST(24846.261912088819/8.0), + REAL_CONST(24863.003265749034/8.0), + REAL_CONST(24879.747438056307/8.0), + REAL_CONST(24896.494428062004/8.0), + REAL_CONST(24913.244234818278/8.0), + REAL_CONST(24929.996857378079/8.0), + REAL_CONST(24946.752294795166/8.0), + REAL_CONST(24963.510546124078/8.0), + REAL_CONST(24980.271610420157/8.0), + REAL_CONST(24997.035486739525/8.0), + REAL_CONST(25013.802174139113/8.0), + REAL_CONST(25030.571671676629/8.0), + REAL_CONST(25047.343978410572/8.0), + REAL_CONST(25064.119093400237/8.0), + REAL_CONST(25080.897015705697/8.0), + REAL_CONST(25097.677744387816/8.0), + REAL_CONST(25114.461278508239/8.0), + REAL_CONST(25131.2476171294/8.0), + REAL_CONST(25148.036759314517/8.0), + REAL_CONST(25164.828704127583/8.0), + REAL_CONST(25181.623450633375/8.0), + REAL_CONST(25198.42099789745/8.0), + REAL_CONST(25215.221344986145/8.0), + REAL_CONST(25232.024490966574/8.0), + REAL_CONST(25248.830434906627/8.0), + REAL_CONST(25265.639175874974/8.0), + REAL_CONST(25282.450712941049/8.0), + REAL_CONST(25299.265045175071/8.0), + REAL_CONST(25316.082171648024/8.0), + REAL_CONST(25332.902091431668/8.0), + REAL_CONST(25349.724803598532/8.0), + REAL_CONST(25366.550307221914/8.0), + REAL_CONST(25383.378601375884/8.0), + REAL_CONST(25400.209685135269/8.0), + REAL_CONST(25417.043557575678/8.0), + REAL_CONST(25433.880217773472/8.0), + REAL_CONST(25450.719664805783/8.0), + REAL_CONST(25467.561897750507/8.0), + REAL_CONST(25484.406915686297/8.0), + REAL_CONST(25501.254717692573/8.0), + REAL_CONST(25518.105302849512/8.0), + REAL_CONST(25534.958670238051/8.0), + REAL_CONST(25551.814818939893/8.0), + REAL_CONST(25568.67374803748/8.0), + REAL_CONST(25585.535456614027/8.0), + REAL_CONST(25602.399943753502/8.0), + REAL_CONST(25619.267208540619/8.0), + REAL_CONST(25636.137250060852/8.0), + REAL_CONST(25653.010067400432/8.0), + REAL_CONST(25669.885659646327/8.0), + REAL_CONST(25686.76402588627/8.0), + REAL_CONST(25703.645165208734/8.0), + REAL_CONST(25720.529076702944/8.0), + REAL_CONST(25737.415759458876/8.0), + REAL_CONST(25754.305212567244/8.0), + REAL_CONST(25771.197435119517/8.0), + REAL_CONST(25788.092426207899/8.0), + REAL_CONST(25804.990184925344/8.0), + REAL_CONST(25821.890710365547/8.0), + REAL_CONST(25838.794001622944/8.0), + REAL_CONST(25855.700057792714/8.0), + REAL_CONST(25872.608877970775/8.0), + REAL_CONST(25889.520461253778/8.0), + REAL_CONST(25906.434806739118/8.0), + REAL_CONST(25923.351913524923/8.0), + REAL_CONST(25940.271780710063/8.0), + REAL_CONST(25957.194407394138/8.0), + REAL_CONST(25974.11979267748/8.0), + REAL_CONST(25991.047935661154/8.0), + REAL_CONST(26007.978835446964/8.0), + REAL_CONST(26024.912491137442/8.0), + REAL_CONST(26041.848901835841/8.0), + REAL_CONST(26058.788066646157/8.0), + REAL_CONST(26075.729984673108/8.0), + REAL_CONST(26092.674655022136/8.0), + REAL_CONST(26109.622076799409/8.0), + REAL_CONST(26126.572249111829/8.0), + REAL_CONST(26143.525171067016/8.0), + REAL_CONST(26160.480841773315/8.0), + REAL_CONST(26177.43926033979/8.0), + REAL_CONST(26194.400425876229/8.0), + REAL_CONST(26211.364337493149/8.0), + REAL_CONST(26228.330994301767/8.0), + REAL_CONST(26245.30039541404/8.0), + REAL_CONST(26262.272539942627/8.0), + REAL_CONST(26279.247427000919/8.0), + REAL_CONST(26296.225055703002/8.0), + REAL_CONST(26313.205425163702/8.0), + REAL_CONST(26330.188534498539/8.0), + REAL_CONST(26347.174382823756/8.0), + REAL_CONST(26364.162969256304/8.0), + REAL_CONST(26381.154292913852/8.0), + REAL_CONST(26398.148352914774/8.0), + REAL_CONST(26415.145148378149/8.0), + REAL_CONST(26432.144678423778/8.0), + REAL_CONST(26449.146942172156/8.0), + REAL_CONST(26466.151938744493/8.0), + REAL_CONST(26483.159667262702/8.0), + REAL_CONST(26500.170126849403/8.0), + REAL_CONST(26517.183316627921/8.0), + REAL_CONST(26534.199235722277/8.0), + REAL_CONST(26551.217883257199/8.0), + REAL_CONST(26568.239258358124/8.0), + REAL_CONST(26585.263360151173/8.0), + REAL_CONST(26602.290187763181/8.0), + REAL_CONST(26619.319740321676/8.0), + REAL_CONST(26636.352016954883/8.0), + REAL_CONST(26653.387016791727/8.0), + REAL_CONST(26670.424738961825/8.0), + REAL_CONST(26687.465182595493/8.0), + REAL_CONST(26704.508346823739/8.0), + REAL_CONST(26721.554230778267/8.0), + REAL_CONST(26738.602833591467/8.0), + REAL_CONST(26755.65415439643/8.0), + REAL_CONST(26772.708192326929/8.0), + REAL_CONST(26789.764946517433/8.0), + REAL_CONST(26806.824416103096/8.0), + REAL_CONST(26823.886600219761/8.0), + REAL_CONST(26840.95149800396/8.0), + REAL_CONST(26858.019108592915/8.0), + REAL_CONST(26875.089431124517/8.0), + REAL_CONST(26892.162464737365/8.0), + REAL_CONST(26909.238208570721/8.0), + REAL_CONST(26926.316661764544/8.0), + REAL_CONST(26943.397823459472/8.0), + REAL_CONST(26960.481692796813/8.0), + REAL_CONST(26977.568268918571/8.0), + REAL_CONST(26994.657550967422/8.0), + REAL_CONST(27011.749538086722/8.0), + REAL_CONST(27028.844229420498/8.0), + REAL_CONST(27045.941624113464/8.0), + REAL_CONST(27063.041721311005/8.0), + REAL_CONST(27080.144520159181/8.0), + REAL_CONST(27097.250019804727/8.0), + REAL_CONST(27114.35821939505/8.0), + REAL_CONST(27131.469118078236/8.0), + REAL_CONST(27148.582715003027/8.0), + REAL_CONST(27165.699009318858/8.0), + REAL_CONST(27182.818000175819/8.0), + REAL_CONST(27199.939686724665/8.0), + REAL_CONST(27217.064068116837/8.0), + REAL_CONST(27234.191143504428/8.0), + REAL_CONST(27251.320912040203/8.0), + REAL_CONST(27268.453372877593/8.0), + REAL_CONST(27285.588525170693/8.0), + REAL_CONST(27302.726368074269/8.0), + REAL_CONST(27319.866900743735/8.0), + REAL_CONST(27337.010122335181/8.0), + REAL_CONST(27354.156032005358/8.0), + REAL_CONST(27371.304628911668/8.0), + REAL_CONST(27388.455912212183/8.0), + REAL_CONST(27405.609881065626/8.0), + REAL_CONST(27422.766534631384/8.0), + REAL_CONST(27439.925872069507/8.0), + REAL_CONST(27457.087892540683/8.0), + REAL_CONST(27474.252595206275/8.0), + REAL_CONST(27491.419979228293/8.0), + REAL_CONST(27508.5900437694/8.0), + REAL_CONST(27525.762787992917/8.0), + REAL_CONST(27542.93821106281/8.0), + REAL_CONST(27560.116312143706/8.0), + REAL_CONST(27577.297090400876/8.0), + REAL_CONST(27594.480545000242/8.0), + REAL_CONST(27611.666675108383/8.0), + REAL_CONST(27628.855479892518/8.0), + REAL_CONST(27646.046958520514/8.0), + REAL_CONST(27663.241110160889/8.0), + REAL_CONST(27680.437933982801/8.0), + REAL_CONST(27697.637429156068/8.0), + REAL_CONST(27714.839594851132/8.0), + REAL_CONST(27732.04443023909/8.0), + REAL_CONST(27749.251934491687/8.0), + REAL_CONST(27766.462106781299/8.0), + REAL_CONST(27783.674946280949/8.0), + REAL_CONST(27800.890452164302/8.0), + REAL_CONST(27818.108623605654/8.0), + REAL_CONST(27835.329459779954/8.0), + REAL_CONST(27852.55295986278/8.0), + REAL_CONST(27869.779123030345/8.0), + REAL_CONST(27887.007948459504/8.0), + REAL_CONST(27904.239435327745/8.0), + REAL_CONST(27921.473582813196/8.0), + REAL_CONST(27938.710390094613/8.0), + REAL_CONST(27955.949856351392/8.0), + REAL_CONST(27973.19198076355/8.0), + REAL_CONST(27990.436762511745/8.0), + REAL_CONST(28007.684200777272/8.0), + REAL_CONST(28024.934294742041/8.0), + REAL_CONST(28042.187043588601/8.0), + REAL_CONST(28059.442446500128/8.0), + REAL_CONST(28076.700502660427/8.0), + REAL_CONST(28093.961211253929/8.0), + REAL_CONST(28111.224571465693/8.0), + REAL_CONST(28128.490582481401/8.0), + REAL_CONST(28145.759243487362/8.0), + REAL_CONST(28163.030553670509/8.0), + REAL_CONST(28180.304512218394/8.0), + REAL_CONST(28197.581118319198/8.0), + REAL_CONST(28214.860371161725/8.0), + REAL_CONST(28232.14226993539/8.0), + REAL_CONST(28249.42681383024/8.0), + REAL_CONST(28266.71400203693/8.0), + REAL_CONST(28284.003833746745/8.0), + REAL_CONST(28301.296308151585/8.0), + REAL_CONST(28318.591424443959/8.0), + REAL_CONST(28335.889181817001/8.0), + REAL_CONST(28353.189579464462/8.0), + REAL_CONST(28370.492616580705/8.0), + REAL_CONST(28387.798292360701/8.0), + REAL_CONST(28405.106606000048/8.0), + REAL_CONST(28422.417556694945/8.0), + REAL_CONST(28439.731143642206/8.0), + REAL_CONST(28457.047366039264/8.0), + REAL_CONST(28474.366223084147/8.0), + REAL_CONST(28491.687713975512/8.0), + REAL_CONST(28509.011837912611/8.0), + REAL_CONST(28526.338594095305/8.0), + REAL_CONST(28543.667981724069/8.0), + REAL_CONST(28560.999999999982/8.0), + REAL_CONST(28578.334648124732/8.0), + REAL_CONST(28595.671925300605/8.0), + REAL_CONST(28613.011830730498/8.0), + REAL_CONST(28630.354363617909/8.0), + REAL_CONST(28647.699523166943/8.0), + REAL_CONST(28665.0473085823/8.0), + REAL_CONST(28682.397719069289/8.0), + REAL_CONST(28699.750753833818/8.0), + REAL_CONST(28717.10641208239/8.0), + REAL_CONST(28734.464693022121/8.0), + REAL_CONST(28751.825595860708/8.0), + REAL_CONST(28769.189119806462/8.0), + REAL_CONST(28786.55526406828/8.0), + REAL_CONST(28803.924027855664/8.0), + REAL_CONST(28821.295410378701/8.0), + REAL_CONST(28838.669410848088/8.0), + REAL_CONST(28856.046028475103/8.0), + REAL_CONST(28873.425262471628/8.0), + REAL_CONST(28890.80711205013/8.0), + REAL_CONST(28908.191576423673/8.0), + REAL_CONST(28925.578654805915/8.0), + REAL_CONST(28942.968346411097/8.0), + REAL_CONST(28960.360650454055/8.0), + REAL_CONST(28977.755566150216/8.0), + REAL_CONST(28995.153092715591/8.0), + REAL_CONST(29012.553229366786/8.0), + REAL_CONST(29029.955975320987/8.0), + REAL_CONST(29047.361329795975/8.0), + REAL_CONST(29064.769292010107/8.0), + REAL_CONST(29082.179861182336/8.0), + REAL_CONST(29099.593036532187/8.0), + REAL_CONST(29117.00881727978/8.0), + REAL_CONST(29134.427202645813/8.0), + REAL_CONST(29151.848191851568/8.0), + REAL_CONST(29169.271784118911/8.0), + REAL_CONST(29186.697978670283/8.0), + REAL_CONST(29204.126774728706/8.0), + REAL_CONST(29221.55817151779/8.0), + REAL_CONST(29238.992168261717/8.0), + REAL_CONST(29256.42876418525/8.0), + REAL_CONST(29273.867958513725/8.0), + REAL_CONST(29291.309750473058/8.0), + REAL_CONST(29308.754139289747/8.0), + REAL_CONST(29326.201124190855/8.0), + REAL_CONST(29343.65070440403/8.0), + REAL_CONST(29361.102879157483/8.0), + REAL_CONST(29378.557647680012/8.0), + REAL_CONST(29396.015009200975/8.0), + REAL_CONST(29413.474962950309/8.0), + REAL_CONST(29430.937508158524/8.0), + REAL_CONST(29448.402644056692/8.0), + REAL_CONST(29465.870369876469/8.0), + REAL_CONST(29483.340684850071/8.0), + REAL_CONST(29500.81358821028/8.0), + REAL_CONST(29518.289079190454/8.0), + REAL_CONST(29535.767157024511/8.0), + REAL_CONST(29553.247820946945/8.0), + REAL_CONST(29570.731070192807/8.0), + REAL_CONST(29588.216903997723/8.0), + REAL_CONST(29605.70532159787/8.0), + REAL_CONST(29623.19632223/8.0), + REAL_CONST(29640.689905131429/8.0), + REAL_CONST(29658.186069540028/8.0), + REAL_CONST(29675.684814694236/8.0), + REAL_CONST(29693.186139833047/8.0), + REAL_CONST(29710.690044196028/8.0), + REAL_CONST(29728.196527023298/8.0), + REAL_CONST(29745.705587555527/8.0), + REAL_CONST(29763.217225033964/8.0), + REAL_CONST(29780.731438700397/8.0), + REAL_CONST(29798.248227797183/8.0), + REAL_CONST(29815.76759156723/8.0), + REAL_CONST(29833.289529254005/8.0), + REAL_CONST(29850.81404010153/8.0), + REAL_CONST(29868.341123354381/8.0), + REAL_CONST(29885.870778257693/8.0), + REAL_CONST(29903.403004057145/8.0), + REAL_CONST(29920.937799998974/8.0), + REAL_CONST(29938.475165329975/8.0), + REAL_CONST(29956.015099297485/8.0), + REAL_CONST(29973.557601149394/8.0), + REAL_CONST(29991.102670134147/8.0), + REAL_CONST(30008.650305500738/8.0), + REAL_CONST(30026.200506498706/8.0), + REAL_CONST(30043.753272378144/8.0), + REAL_CONST(30061.308602389683/8.0), + REAL_CONST(30078.866495784507/8.0), + REAL_CONST(30096.426951814352/8.0), + REAL_CONST(30113.989969731494/8.0), + REAL_CONST(30131.55554878875/8.0), + REAL_CONST(30149.123688239491/8.0), + REAL_CONST(30166.694387337629/8.0), + REAL_CONST(30184.267645337608/8.0), + REAL_CONST(30201.843461494434/8.0), + REAL_CONST(30219.42183506364/8.0), + REAL_CONST(30237.002765301309/8.0), + REAL_CONST(30254.586251464058/8.0), + REAL_CONST(30272.172292809046/8.0), + REAL_CONST(30289.760888593977/8.0), + REAL_CONST(30307.35203807709/8.0), + REAL_CONST(30324.94574051716/8.0), + REAL_CONST(30342.541995173502/8.0), + REAL_CONST(30360.140801305966/8.0), + REAL_CONST(30377.742158174944/8.0), + REAL_CONST(30395.346065041358/8.0), + REAL_CONST(30412.952521166666/8.0), + REAL_CONST(30430.561525812864/8.0), + REAL_CONST(30448.173078242475/8.0), + REAL_CONST(30465.787177718561/8.0), + REAL_CONST(30483.403823504719/8.0), + REAL_CONST(30501.02301486507/8.0), + REAL_CONST(30518.644751064272/8.0), + REAL_CONST(30536.269031367516/8.0), + REAL_CONST(30553.895855040515/8.0), + REAL_CONST(30571.525221349519/8.0), + REAL_CONST(30589.157129561307/8.0), + REAL_CONST(30606.791578943175/8.0), + REAL_CONST(30624.428568762964/8.0), + REAL_CONST(30642.06809828903/8.0), + REAL_CONST(30659.710166790261/8.0), + REAL_CONST(30677.35477353607/8.0), + REAL_CONST(30695.001917796391/8.0), + REAL_CONST(30712.651598841687/8.0), + REAL_CONST(30730.303815942945/8.0), + REAL_CONST(30747.958568371676/8.0), + REAL_CONST(30765.615855399912/8.0), + REAL_CONST(30783.275676300211/8.0), + REAL_CONST(30800.938030345646/8.0), + REAL_CONST(30818.602916809814/8.0), + REAL_CONST(30836.270334966837/8.0), + REAL_CONST(30853.940284091354/8.0), + REAL_CONST(30871.612763458521/8.0), + REAL_CONST(30889.287772344011/8.0), + REAL_CONST(30906.965310024025/8.0), + REAL_CONST(30924.645375775272/8.0), + REAL_CONST(30942.327968874983/8.0), + REAL_CONST(30960.013088600903/8.0), + REAL_CONST(30977.700734231294/8.0), + REAL_CONST(30995.390905044929/8.0), + REAL_CONST(31013.083600321101/8.0), + REAL_CONST(31030.778819339619/8.0), + REAL_CONST(31048.476561380798/8.0), + REAL_CONST(31066.17682572547/8.0), + REAL_CONST(31083.879611654978/8.0), + REAL_CONST(31101.584918451179/8.0), + REAL_CONST(31119.29274539644/8.0), + REAL_CONST(31137.003091773637/8.0), + REAL_CONST(31154.715956866155/8.0), + REAL_CONST(31172.431339957893/8.0), + REAL_CONST(31190.14924033326/8.0), + REAL_CONST(31207.869657277162/8.0), + REAL_CONST(31225.592590075023/8.0), + REAL_CONST(31243.318038012771/8.0), + REAL_CONST(31261.046000376838/8.0), + REAL_CONST(31278.776476454172/8.0), + REAL_CONST(31296.50946553221/8.0), + REAL_CONST(31314.24496689891/8.0), + REAL_CONST(31331.98297984272/8.0), + REAL_CONST(31349.7235036526/8.0), + REAL_CONST(31367.466537618013/8.0), + REAL_CONST(31385.212081028923/8.0), + REAL_CONST(31402.960133175795/8.0), + REAL_CONST(31420.710693349596/8.0), + REAL_CONST(31438.463760841791/8.0), + REAL_CONST(31456.219334944351/8.0), + REAL_CONST(31473.977414949743/8.0), + REAL_CONST(31491.738000150934/8.0), + REAL_CONST(31509.501089841389/8.0), + REAL_CONST(31527.266683315069/8.0), + REAL_CONST(31545.034779866437/8.0), + REAL_CONST(31562.80537879045/8.0), + REAL_CONST(31580.578479382562/8.0), + REAL_CONST(31598.35408093872/8.0), + REAL_CONST(31616.132182755369/8.0), + REAL_CONST(31633.91278412945/8.0), + REAL_CONST(31651.695884358396/8.0), + REAL_CONST(31669.481482740131/8.0), + REAL_CONST(31687.269578573076/8.0), + REAL_CONST(31705.060171156143/8.0), + REAL_CONST(31722.853259788735/8.0), + REAL_CONST(31740.648843770748/8.0), + REAL_CONST(31758.446922402567/8.0), + REAL_CONST(31776.247494985066/8.0), + REAL_CONST(31794.050560819614/8.0), + REAL_CONST(31811.85611920806/8.0), + REAL_CONST(31829.664169452753/8.0), + REAL_CONST(31847.474710856521/8.0), + REAL_CONST(31865.287742722685/8.0), + REAL_CONST(31883.103264355046/8.0), + REAL_CONST(31900.921275057899/8.0), + REAL_CONST(31918.741774136019/8.0), + REAL_CONST(31936.564760894671/8.0), + REAL_CONST(31954.390234639599/8.0), + REAL_CONST(31972.21819467704/8.0), + REAL_CONST(31990.048640313704/8.0), + REAL_CONST(32007.881570856793/8.0), + REAL_CONST(32025.716985613984/8.0), + REAL_CONST(32043.554883893445/8.0), + REAL_CONST(32061.395265003815/8.0), + REAL_CONST(32079.238128254223/8.0), + REAL_CONST(32097.083472954269/8.0), + REAL_CONST(32114.931298414049/8.0), + REAL_CONST(32132.781603944117/8.0), + REAL_CONST(32150.634388855524/8.0), + REAL_CONST(32168.48965245979/8.0), + REAL_CONST(32186.347394068915/8.0), + REAL_CONST(32204.207612995371/8.0), + REAL_CONST(32222.07030855212/8.0), + REAL_CONST(32239.935480052583/8.0), + REAL_CONST(32257.803126810672/8.0), + REAL_CONST(32275.673248140767/8.0), + REAL_CONST(32293.545843357719/8.0), + REAL_CONST(32311.420911776862/8.0), + REAL_CONST(32329.298452713996/8.0), + REAL_CONST(32347.178465485395/8.0), + REAL_CONST(32365.060949407813/8.0), + REAL_CONST(32382.945903798463/8.0), + REAL_CONST(32400.83332797504/8.0), + REAL_CONST(32418.723221255706/8.0), + REAL_CONST(32436.615582959093/8.0), + REAL_CONST(32454.510412404306/8.0), + REAL_CONST(32472.407708910916/8.0), + REAL_CONST(32490.307471798966/8.0), + REAL_CONST(32508.209700388961/8.0), + REAL_CONST(32526.114394001877/8.0), + REAL_CONST(32544.021551959166/8.0), + REAL_CONST(32561.931173582732/8.0), + REAL_CONST(32579.843258194956/8.0), + REAL_CONST(32597.757805118679/8.0), + REAL_CONST(32615.674813677211/8.0), + REAL_CONST(32633.594283194328/8.0), + REAL_CONST(32651.516212994258/8.0), + REAL_CONST(32669.440602401712/8.0), + REAL_CONST(32687.367450741847/8.0), + REAL_CONST(32705.296757340297/8.0), + REAL_CONST(32723.228521523146/8.0), + REAL_CONST(32741.162742616943/8.0), + REAL_CONST(32759.099419948703/8.0), + REAL_CONST(32777.038552845901/8.0), + REAL_CONST(32794.980140636464/8.0), + REAL_CONST(32812.924182648792/8.0), + REAL_CONST(32830.87067821173/8.0), + REAL_CONST(32848.819626654593/8.0), + REAL_CONST(32866.77102730715/8.0), + REAL_CONST(32884.724879499619/8.0), + REAL_CONST(32902.681182562686/8.0), + REAL_CONST(32920.639935827494/8.0), + REAL_CONST(32938.601138625643/8.0), + REAL_CONST(32956.56479028918/8.0), + REAL_CONST(32974.530890150607/8.0), + REAL_CONST(32992.499437542894/8.0), + REAL_CONST(33010.470431799447/8.0), + REAL_CONST(33028.443872254145/8.0), + REAL_CONST(33046.419758241311/8.0), + REAL_CONST(33064.39808909571/8.0), + REAL_CONST(33082.378864152583/8.0), + REAL_CONST(33100.36208274759/8.0), + REAL_CONST(33118.347744216881/8.0), + REAL_CONST(33136.335847897026/8.0), + REAL_CONST(33154.326393125062/8.0), + REAL_CONST(33172.31937923847/8.0), + REAL_CONST(33190.314805575174/8.0), + REAL_CONST(33208.312671473555/8.0), + REAL_CONST(33226.312976272442/8.0), + REAL_CONST(33244.315719311111/8.0), + REAL_CONST(33262.320899929284/8.0), + REAL_CONST(33280.328517467125/8.0), + REAL_CONST(33298.33857126526/8.0), + REAL_CONST(33316.351060664747/8.0), + REAL_CONST(33334.365985007091/8.0), + REAL_CONST(33352.383343634239/8.0), + REAL_CONST(33370.403135888591/8.0), + REAL_CONST(33388.42536111299/8.0), + REAL_CONST(33406.450018650721/8.0), + REAL_CONST(33424.477107845501/8.0), + REAL_CONST(33442.506628041512/8.0), + REAL_CONST(33460.53857858335/8.0), + REAL_CONST(33478.572958816083/8.0), + REAL_CONST(33496.609768085189/8.0), + REAL_CONST(33514.649005736617/8.0), + REAL_CONST(33532.690671116739/8.0), + REAL_CONST(33550.734763572356/8.0), + REAL_CONST(33568.781282450735/8.0), + REAL_CONST(33586.830227099563/8.0), + REAL_CONST(33604.881596866973/8.0), + REAL_CONST(33622.935391101528/8.0), + REAL_CONST(33640.991609152239/8.0), + REAL_CONST(33659.050250368542/8.0), + REAL_CONST(33677.111314100322/8.0), + REAL_CONST(33695.174799697881/8.0), + REAL_CONST(33713.240706511984/8.0), + REAL_CONST(33731.309033893805/8.0), + REAL_CONST(33749.37978119497/8.0), + REAL_CONST(33767.452947767531/8.0), + REAL_CONST(33785.528532963974/8.0), + REAL_CONST(33803.606536137209/8.0), + REAL_CONST(33821.686956640602/8.0), + REAL_CONST(33839.769793827938/8.0), + REAL_CONST(33857.855047053425/8.0), + REAL_CONST(33875.942715671707/8.0), + REAL_CONST(33894.032799037872/8.0), + REAL_CONST(33912.125296507431/8.0), + REAL_CONST(33930.220207436316/8.0), + REAL_CONST(33948.317531180888/8.0), + REAL_CONST(33966.417267097961/8.0), + REAL_CONST(33984.519414544746/8.0), + REAL_CONST(34002.623972878901/8.0), + REAL_CONST(34020.730941458511/8.0), + REAL_CONST(34038.840319642077/8.0), + REAL_CONST(34056.952106788536/8.0), + REAL_CONST(34075.066302257255/8.0), + REAL_CONST(34093.182905408015/8.0), + REAL_CONST(34111.301915601027/8.0), + REAL_CONST(34129.42333219693/8.0), + REAL_CONST(34147.547154556785/8.0), + REAL_CONST(34165.673382042078/8.0), + REAL_CONST(34183.80201401472/8.0), + REAL_CONST(34201.933049837033/8.0), + REAL_CONST(34220.06648887178/8.0), + REAL_CONST(34238.202330482141/8.0), + REAL_CONST(34256.340574031703/8.0), + REAL_CONST(34274.481218884495/8.0), + REAL_CONST(34292.624264404949/8.0), + REAL_CONST(34310.769709957938/8.0), + REAL_CONST(34328.91755490873/8.0), + REAL_CONST(34347.067798623029/8.0), + REAL_CONST(34365.220440466954/8.0), + REAL_CONST(34383.375479807051/8.0), + REAL_CONST(34401.532916010263/8.0), + REAL_CONST(34419.692748443973/8.0), + REAL_CONST(34437.854976475966/8.0), + REAL_CONST(34456.01959947445/8.0), + REAL_CONST(34474.18661680806/8.0), + REAL_CONST(34492.356027845817/8.0), + REAL_CONST(34510.527831957188/8.0), + REAL_CONST(34528.702028512052/8.0), + REAL_CONST(34546.878616880676/8.0), + REAL_CONST(34565.05759643377/8.0), + REAL_CONST(34583.238966542449/8.0), + REAL_CONST(34601.422726578232/8.0), + REAL_CONST(34619.608875913065/8.0), + REAL_CONST(34637.797413919296/8.0), + REAL_CONST(34655.988339969692/8.0), + REAL_CONST(34674.181653437423/8.0), + REAL_CONST(34692.37735369608/8.0), + REAL_CONST(34710.575440119668/8.0), + REAL_CONST(34728.775912082579/8.0), + REAL_CONST(34746.978768959649/8.0), + REAL_CONST(34765.184010126082/8.0), + REAL_CONST(34783.391634957537/8.0), + REAL_CONST(34801.60164283005/8.0), + REAL_CONST(34819.814033120063/8.0), + REAL_CONST(34838.028805204456/8.0), + REAL_CONST(34856.24595846048/8.0), + REAL_CONST(34874.465492265823/8.0), + REAL_CONST(34892.687405998557/8.0), + REAL_CONST(34910.911699037177/8.0), + REAL_CONST(34929.138370760564/8.0), + REAL_CONST(34947.367420548027/8.0), + REAL_CONST(34965.598847779271/8.0), + REAL_CONST(34983.832651834389/8.0), + REAL_CONST(35002.068832093908/8.0), + REAL_CONST(35020.307387938738/8.0), + REAL_CONST(35038.548318750189/8.0), + REAL_CONST(35056.79162390998/8.0), + REAL_CONST(35075.03730280025/8.0), + REAL_CONST(35093.285354803513/8.0), + REAL_CONST(35111.535779302685/8.0), + REAL_CONST(35129.788575681116/8.0), + REAL_CONST(35148.043743322516/8.0), + REAL_CONST(35166.301281611013/8.0), + REAL_CONST(35184.561189931141/8.0), + REAL_CONST(35202.823467667826/8.0), + REAL_CONST(35221.088114206388/8.0), + REAL_CONST(35239.355128932555/8.0), + REAL_CONST(35257.624511232447/8.0), + REAL_CONST(35275.896260492584/8.0), + REAL_CONST(35294.170376099886/8.0), + REAL_CONST(35312.446857441668/8.0), + REAL_CONST(35330.725703905628/8.0), + REAL_CONST(35349.006914879887/8.0), + REAL_CONST(35367.290489752944/8.0), + REAL_CONST(35385.576427913686/8.0), + REAL_CONST(35403.864728751418/8.0), + REAL_CONST(35422.155391655811/8.0), + REAL_CONST(35440.448416016967/8.0), + REAL_CONST(35458.743801225341/8.0), + REAL_CONST(35477.041546671804/8.0), + REAL_CONST(35495.341651747622/8.0), + REAL_CONST(35513.644115844436/8.0), + REAL_CONST(35531.948938354304/8.0), + REAL_CONST(35550.256118669655/8.0), + REAL_CONST(35568.565656183309/8.0), + REAL_CONST(35586.877550288496/8.0), + REAL_CONST(35605.191800378816/8.0), + REAL_CONST(35623.508405848268/8.0), + REAL_CONST(35641.827366091238/8.0), + REAL_CONST(35660.148680502505/8.0), + REAL_CONST(35678.472348477233/8.0), + REAL_CONST(35696.798369410979/8.0), + REAL_CONST(35715.126742699678/8.0), + REAL_CONST(35733.457467739659/8.0), + REAL_CONST(35751.790543927644/8.0), + REAL_CONST(35770.125970660738/8.0), + REAL_CONST(35788.46374733642/8.0), + REAL_CONST(35806.803873352568/8.0), + REAL_CONST(35825.146348107453/8.0), + REAL_CONST(35843.49117099971/8.0), + REAL_CONST(35861.838341428367/8.0), + REAL_CONST(35880.187858792851/8.0), + REAL_CONST(35898.539722492955/8.0), + REAL_CONST(35916.893931928862/8.0), + REAL_CONST(35935.250486501129/8.0), + REAL_CONST(35953.609385610718/8.0), + REAL_CONST(35971.970628658957/8.0), + REAL_CONST(35990.334215047558/8.0), + REAL_CONST(36008.700144178612/8.0), + REAL_CONST(36027.068415454596/8.0), + REAL_CONST(36045.439028278372/8.0), + REAL_CONST(36063.811982053165/8.0), + REAL_CONST(36082.187276182609/8.0), + REAL_CONST(36100.564910070694/8.0), + REAL_CONST(36118.944883121789/8.0), + REAL_CONST(36137.327194740654/8.0), + REAL_CONST(36155.711844332429/8.0), + REAL_CONST(36174.098831302617/8.0), + REAL_CONST(36192.488155057115/8.0), + REAL_CONST(36210.87981500219/8.0), + REAL_CONST(36229.273810544473/8.0), + REAL_CONST(36247.670141091003/8.0), + REAL_CONST(36266.068806049167/8.0), + REAL_CONST(36284.469804826738/8.0), + REAL_CONST(36302.873136831862/8.0), + REAL_CONST(36321.278801473069/8.0), + REAL_CONST(36339.686798159251/8.0), + REAL_CONST(36358.097126299683/8.0), + REAL_CONST(36376.509785304013/8.0), + REAL_CONST(36394.924774582258/8.0), + REAL_CONST(36413.342093544816/8.0), + REAL_CONST(36431.761741602444/8.0), + REAL_CONST(36450.183718166292/8.0), + REAL_CONST(36468.608022647859/8.0), + REAL_CONST(36487.034654459028/8.0), + REAL_CONST(36505.463613012063/8.0), + REAL_CONST(36523.894897719583/8.0), + REAL_CONST(36542.328507994578/8.0), + REAL_CONST(36560.764443250409/8.0), + REAL_CONST(36579.202702900831/8.0), + REAL_CONST(36597.643286359926/8.0), + REAL_CONST(36616.086193042182/8.0), + REAL_CONST(36634.531422362437/8.0), + REAL_CONST(36652.978973735895/8.0), + REAL_CONST(36671.428846578143/8.0), + REAL_CONST(36689.881040305125/8.0), + REAL_CONST(36708.335554333149/8.0), + REAL_CONST(36726.792388078902/8.0), + REAL_CONST(36745.251540959427/8.0), + REAL_CONST(36763.713012392138/8.0), + REAL_CONST(36782.176801794812/8.0), + REAL_CONST(36800.642908585593/8.0), + REAL_CONST(36819.111332182983/8.0), + REAL_CONST(36837.582072005869/8.0), + REAL_CONST(36856.055127473483/8.0), + REAL_CONST(36874.530498005421/8.0), + REAL_CONST(36893.008183021651/8.0), + REAL_CONST(36911.488181942506/8.0), + REAL_CONST(36929.970494188674/8.0), + REAL_CONST(36948.455119181206/8.0), + REAL_CONST(36966.942056341519/8.0), + REAL_CONST(36985.431305091392/8.0), + REAL_CONST(37003.922864852961/8.0), + REAL_CONST(37022.416735048733/8.0), + REAL_CONST(37040.912915101559/8.0), + REAL_CONST(37059.411404434657/8.0), + REAL_CONST(37077.91220247162/8.0), + REAL_CONST(37096.415308636388/8.0), + REAL_CONST(37114.920722353243/8.0), + REAL_CONST(37133.428443046862/8.0), + REAL_CONST(37151.938470142253/8.0), + REAL_CONST(37170.450803064785/8.0), + REAL_CONST(37188.965441240209/8.0), + REAL_CONST(37207.482384094597/8.0), + REAL_CONST(37226.001631054402/8.0), + REAL_CONST(37244.523181546429/8.0), + REAL_CONST(37263.047034997842/8.0), + REAL_CONST(37281.573190836149/8.0), + REAL_CONST(37300.101648489224/8.0), + REAL_CONST(37318.632407385296/8.0), + REAL_CONST(37337.165466952945/8.0), + REAL_CONST(37355.700826621112/8.0), + REAL_CONST(37374.238485819085/8.0), + REAL_CONST(37392.778443976509/8.0), + REAL_CONST(37411.320700523385/8.0), + REAL_CONST(37429.865254890057/8.0), + REAL_CONST(37448.412106507232/8.0), + REAL_CONST(37466.961254805974/8.0), + REAL_CONST(37485.512699217681/8.0), + REAL_CONST(37504.066439174116/8.0), + REAL_CONST(37522.622474107404/8.0), + REAL_CONST(37541.180803449992/8.0), + REAL_CONST(37559.741426634704/8.0), + REAL_CONST(37578.304343094693/8.0), + REAL_CONST(37596.869552263488/8.0), + REAL_CONST(37615.43705357494/8.0), + REAL_CONST(37634.006846463279/8.0), + REAL_CONST(37652.578930363044/8.0), + REAL_CONST(37671.153304709165/8.0), + REAL_CONST(37689.729968936896/8.0), + REAL_CONST(37708.308922481847/8.0), + REAL_CONST(37726.890164779965/8.0), + REAL_CONST(37745.473695267559/8.0), + REAL_CONST(37764.059513381275/8.0), + REAL_CONST(37782.647618558112/8.0), + REAL_CONST(37801.238010235415/8.0), + REAL_CONST(37819.830687850859/8.0), + REAL_CONST(37838.425650842495/8.0), + REAL_CONST(37857.022898648691/8.0), + REAL_CONST(37875.622430708172/8.0), + REAL_CONST(37894.224246460013/8.0), + REAL_CONST(37912.828345343616/8.0), + REAL_CONST(37931.434726798747/8.0), + REAL_CONST(37950.043390265506/8.0), + REAL_CONST(37968.654335184328/8.0), + REAL_CONST(37987.267560995999/8.0), + REAL_CONST(38005.883067141665/8.0), + REAL_CONST(38024.500853062775/8.0), + REAL_CONST(38043.120918201159/8.0), + REAL_CONST(38061.743261998963/8.0), + REAL_CONST(38080.367883898682/8.0), + REAL_CONST(38098.994783343158/8.0), + REAL_CONST(38117.623959775563/8.0), + REAL_CONST(38136.255412639417/8.0), + REAL_CONST(38154.889141378575/8.0), + REAL_CONST(38173.525145437234/8.0), + REAL_CONST(38192.163424259939/8.0), + REAL_CONST(38210.803977291551/8.0), + REAL_CONST(38229.446803977284/8.0), + REAL_CONST(38248.091903762703/8.0), + REAL_CONST(38266.739276093685/8.0), + REAL_CONST(38285.388920416466/8.0), + REAL_CONST(38304.040836177606/8.0), + REAL_CONST(38322.695022824002/8.0), + REAL_CONST(38341.351479802899/8.0), + REAL_CONST(38360.010206561863/8.0), + REAL_CONST(38378.671202548816/8.0), + REAL_CONST(38397.334467211993/8.0), + REAL_CONST(38415.999999999978/8.0), + REAL_CONST(38434.667800361683/8.0), + REAL_CONST(38453.33786774637/8.0), + REAL_CONST(38472.010201603611/8.0), + REAL_CONST(38490.684801383337/8.0), + REAL_CONST(38509.361666535784/8.0), + REAL_CONST(38528.040796511552/8.0), + REAL_CONST(38546.722190761553/8.0), + REAL_CONST(38565.405848737035/8.0), + REAL_CONST(38584.091769889594/8.0), + REAL_CONST(38602.779953671132/8.0), + REAL_CONST(38621.470399533908/8.0), + REAL_CONST(38640.163106930493/8.0), + REAL_CONST(38658.858075313794/8.0), + REAL_CONST(38677.555304137059/8.0), + REAL_CONST(38696.254792853862/8.0), + REAL_CONST(38714.956540918094/8.0), + REAL_CONST(38733.660547783991/8.0), + REAL_CONST(38752.366812906112/8.0), + REAL_CONST(38771.075335739348/8.0), + REAL_CONST(38789.78611573892/8.0), + REAL_CONST(38808.499152360368/8.0), + REAL_CONST(38827.214445059573/8.0), + REAL_CONST(38845.931993292739/8.0), + REAL_CONST(38864.651796516388/8.0), + REAL_CONST(38883.373854187383/8.0), + REAL_CONST(38902.098165762916/8.0), + REAL_CONST(38920.824730700486/8.0), + REAL_CONST(38939.553548457938/8.0), + REAL_CONST(38958.284618493431/8.0), + REAL_CONST(38977.017940265461/8.0), + REAL_CONST(38995.753513232834/8.0), + REAL_CONST(39014.491336854699/8.0), + REAL_CONST(39033.231410590517/8.0), + REAL_CONST(39051.973733900079/8.0), + REAL_CONST(39070.718306243485/8.0), + REAL_CONST(39089.465127081188/8.0), + REAL_CONST(39108.214195873945/8.0), + REAL_CONST(39126.965512082832/8.0), + REAL_CONST(39145.719075169261/8.0), + REAL_CONST(39164.474884594965/8.0), + REAL_CONST(39183.232939821988/8.0), + REAL_CONST(39201.99324031271/8.0), + REAL_CONST(39220.755785529815/8.0), + REAL_CONST(39239.52057493633/8.0), + REAL_CONST(39258.287607995589/8.0), + REAL_CONST(39277.056884171245/8.0), + REAL_CONST(39295.828402927284/8.0), + REAL_CONST(39314.602163728006/8.0), + REAL_CONST(39333.378166038019/8.0), + REAL_CONST(39352.15640932227/8.0), + REAL_CONST(39370.936893046004/8.0), + REAL_CONST(39389.719616674811/8.0), + REAL_CONST(39408.504579674584/8.0), + REAL_CONST(39427.291781511522/8.0), + REAL_CONST(39446.081221652174/8.0), + REAL_CONST(39464.872899563372/8.0), + REAL_CONST(39483.666814712291/8.0), + REAL_CONST(39502.462966566411/8.0), + REAL_CONST(39521.261354593538/8.0), + REAL_CONST(39540.06197826178/8.0), + REAL_CONST(39558.864837039568/8.0), + REAL_CONST(39577.669930395656/8.0), + REAL_CONST(39596.47725779911/8.0), + REAL_CONST(39615.286818719302/8.0), + REAL_CONST(39634.098612625923/8.0), + REAL_CONST(39652.912638988993/8.0), + REAL_CONST(39671.728897278823/8.0), + REAL_CONST(39690.547386966064/8.0), + REAL_CONST(39709.368107521652/8.0), + REAL_CONST(39728.191058416858/8.0), + REAL_CONST(39747.016239123259/8.0), + REAL_CONST(39765.84364911275/8.0), + REAL_CONST(39784.673287857528/8.0), + REAL_CONST(39803.505154830105/8.0), + REAL_CONST(39822.339249503319/8.0), + REAL_CONST(39841.175571350293/8.0), + REAL_CONST(39860.014119844491/8.0), + REAL_CONST(39878.854894459677/8.0), + REAL_CONST(39897.697894669909/8.0), + REAL_CONST(39916.54311994958/8.0), + REAL_CONST(39935.390569773372/8.0), + REAL_CONST(39954.240243616303/8.0), + REAL_CONST(39973.092140953675/8.0), + REAL_CONST(39991.946261261117/8.0), + REAL_CONST(40010.802604014549/8.0), + REAL_CONST(40029.661168690225/8.0), + REAL_CONST(40048.521954764678/8.0), + REAL_CONST(40067.384961714779/8.0), + REAL_CONST(40086.250189017679/8.0), + REAL_CONST(40105.117636150855/8.0), + REAL_CONST(40123.98730259209/8.0), + REAL_CONST(40142.859187819471/8.0), + REAL_CONST(40161.733291311379/8.0), + REAL_CONST(40180.609612546526/8.0), + REAL_CONST(40199.488151003912/8.0), + REAL_CONST(40218.368906162854/8.0), + REAL_CONST(40237.25187750296/8.0), + REAL_CONST(40256.137064504153/8.0), + REAL_CONST(40275.024466646668/8.0), + REAL_CONST(40293.914083411029/8.0), + REAL_CONST(40312.805914278084/8.0), + REAL_CONST(40331.699958728961/8.0), + REAL_CONST(40350.596216245103/8.0), + REAL_CONST(40369.494686308273/8.0), + REAL_CONST(40388.39536840051/8.0), + REAL_CONST(40407.298262004173/8.0), + REAL_CONST(40426.20336660192/8.0), + REAL_CONST(40445.110681676706/8.0), + REAL_CONST(40464.020206711793/8.0), + REAL_CONST(40482.931941190756/8.0), + REAL_CONST(40501.845884597446/8.0), + REAL_CONST(40520.762036416032/8.0), + REAL_CONST(40539.680396130985/8.0), + REAL_CONST(40558.600963227072/8.0), + REAL_CONST(40577.523737189367/8.0), + REAL_CONST(40596.448717503234/8.0), + REAL_CONST(40615.375903654342/8.0), + REAL_CONST(40634.305295128659/8.0), + REAL_CONST(40653.236891412453/8.0), + REAL_CONST(40672.170691992294/8.0), + REAL_CONST(40691.106696355047/8.0), + REAL_CONST(40710.044903987873/8.0), + REAL_CONST(40728.985314378238/8.0), + REAL_CONST(40747.927927013901/8.0), + REAL_CONST(40766.872741382918/8.0), + REAL_CONST(40785.819756973651/8.0), + REAL_CONST(40804.768973274746/8.0), + REAL_CONST(40823.720389775161/8.0), + REAL_CONST(40842.674005964131/8.0), + REAL_CONST(40861.629821331211/8.0), + REAL_CONST(40880.587835366234/8.0), + REAL_CONST(40899.548047559321/8.0), + REAL_CONST(40918.510457400931/8.0), + REAL_CONST(40937.475064381761/8.0), + REAL_CONST(40956.441867992849/8.0), + REAL_CONST(40975.410867725499/8.0), + REAL_CONST(40994.382063071331/8.0), + REAL_CONST(41013.355453522236/8.0), + REAL_CONST(41032.331038570417/8.0), + REAL_CONST(41051.308817708363/8.0), + REAL_CONST(41070.288790428858/8.0), + REAL_CONST(41089.270956224987/8.0), + REAL_CONST(41108.255314590111/8.0), + REAL_CONST(41127.241865017888/8.0), + REAL_CONST(41146.23060700229/8.0), + REAL_CONST(41165.221540037543/8.0), + REAL_CONST(41184.214663618193/8.0), + REAL_CONST(41203.209977239079/8.0), + REAL_CONST(41222.207480395307/8.0), + REAL_CONST(41241.207172582297/8.0), + REAL_CONST(41260.209053295752/8.0), + REAL_CONST(41279.213122031659/8.0), + REAL_CONST(41298.219378286303/8.0), + REAL_CONST(41317.227821556255/8.0), + REAL_CONST(41336.23845133838/8.0), + REAL_CONST(41355.251267129832/8.0), + REAL_CONST(41374.266268428037/8.0), + REAL_CONST(41393.283454730743/8.0), + REAL_CONST(41412.302825535953/8.0), + REAL_CONST(41431.324380341983/8.0), + REAL_CONST(41450.348118647416/8.0), + REAL_CONST(41469.374039951144/8.0), + REAL_CONST(41488.402143752326/8.0), + REAL_CONST(41507.432429550427/8.0), + REAL_CONST(41526.464896845187/8.0), + REAL_CONST(41545.499545136627/8.0), + REAL_CONST(41564.536373925075/8.0), + REAL_CONST(41583.575382711126/8.0), + REAL_CONST(41602.616570995662/8.0), + REAL_CONST(41621.659938279874/8.0), + REAL_CONST(41640.705484065205/8.0), + REAL_CONST(41659.753207853406/8.0), + REAL_CONST(41678.803109146495/8.0), + REAL_CONST(41697.855187446803/8.0), + REAL_CONST(41716.909442256911/8.0), + REAL_CONST(41735.965873079709/8.0), + REAL_CONST(41755.02447941836/8.0), + REAL_CONST(41774.085260776315/8.0), + REAL_CONST(41793.148216657297/8.0), + REAL_CONST(41812.213346565331/8.0), + REAL_CONST(41831.280650004708/8.0), + REAL_CONST(41850.350126480014/8.0), + REAL_CONST(41869.421775496106/8.0), + REAL_CONST(41888.495596558132/8.0), + REAL_CONST(41907.571589171515/8.0), + REAL_CONST(41926.649752841957/8.0), + REAL_CONST(41945.730087075463/8.0), + REAL_CONST(41964.812591378286/8.0), + REAL_CONST(41983.897265256979/8.0), + REAL_CONST(42002.984108218378/8.0), + REAL_CONST(42022.073119769593/8.0), + REAL_CONST(42041.164299418015/8.0), + REAL_CONST(42060.257646671307/8.0), + REAL_CONST(42079.353161037419/8.0), + REAL_CONST(42098.450842024591/8.0), + REAL_CONST(42117.550689141324/8.0), + REAL_CONST(42136.652701896404/8.0), + REAL_CONST(42155.756879798893/8.0), + REAL_CONST(42174.863222358137/8.0), + REAL_CONST(42193.971729083758/8.0), + REAL_CONST(42213.082399485655/8.0), + REAL_CONST(42232.195233074002/8.0), + REAL_CONST(42251.310229359246/8.0), + REAL_CONST(42270.427387852127/8.0), + REAL_CONST(42289.546708063644/8.0), + REAL_CONST(42308.668189505079/8.0), + REAL_CONST(42327.791831687995/8.0), + REAL_CONST(42346.917634124227/8.0), + REAL_CONST(42366.045596325886/8.0), + REAL_CONST(42385.175717805352/8.0), + REAL_CONST(42404.307998075295/8.0), + REAL_CONST(42423.442436648642/8.0), + REAL_CONST(42442.579033038608/8.0), + REAL_CONST(42461.717786758672/8.0), + REAL_CONST(42480.858697322597/8.0), + REAL_CONST(42500.001764244422/8.0), + REAL_CONST(42519.146987038446/8.0), + REAL_CONST(42538.294365219248/8.0), + REAL_CONST(42557.443898301688/8.0), + REAL_CONST(42576.595585800882/8.0), + REAL_CONST(42595.749427232236/8.0), + REAL_CONST(42614.90542211142/8.0), + REAL_CONST(42634.063569954378/8.0), + REAL_CONST(42653.223870277317/8.0), + REAL_CONST(42672.386322596729/8.0), + REAL_CONST(42691.55092642938/8.0), + REAL_CONST(42710.717681292292/8.0), + REAL_CONST(42729.886586702756/8.0), + REAL_CONST(42749.057642178363/8.0), + REAL_CONST(42768.23084723694/8.0), + REAL_CONST(42787.406201396603/8.0), + REAL_CONST(42806.58370417574/8.0), + REAL_CONST(42825.76335509299/8.0), + REAL_CONST(42844.945153667286/8.0), + REAL_CONST(42864.129099417805/8.0), + REAL_CONST(42883.315191864014/8.0), + REAL_CONST(42902.503430525649/8.0), + REAL_CONST(42921.693814922692/8.0), + REAL_CONST(42940.88634457541/8.0), + REAL_CONST(42960.081019004348/8.0), + REAL_CONST(42979.277837730297/8.0), + REAL_CONST(42998.476800274322/8.0), + REAL_CONST(43017.677906157769/8.0), + REAL_CONST(43036.881154902228/8.0), + REAL_CONST(43056.086546029583/8.0), + REAL_CONST(43075.294079061961/8.0), + REAL_CONST(43094.503753521763/8.0), + REAL_CONST(43113.715568931671/8.0), + REAL_CONST(43132.929524814601/8.0), + REAL_CONST(43152.145620693766/8.0), + REAL_CONST(43171.363856092619/8.0), + REAL_CONST(43190.584230534907/8.0), + REAL_CONST(43209.806743544621/8.0), + REAL_CONST(43229.031394646016/8.0), + REAL_CONST(43248.258183363621/8.0), + REAL_CONST(43267.487109222224/8.0), + REAL_CONST(43286.718171746885/8.0), + REAL_CONST(43305.951370462906/8.0), + REAL_CONST(43325.186704895881/8.0), + REAL_CONST(43344.42417457165/8.0), + REAL_CONST(43363.663779016322/8.0), + REAL_CONST(43382.905517756262/8.0), + REAL_CONST(43402.149390318104/8.0), + REAL_CONST(43421.395396228749/8.0), + REAL_CONST(43440.643535015348/8.0), + REAL_CONST(43459.89380620532/8.0), + REAL_CONST(43479.146209326354/8.0), + REAL_CONST(43498.400743906379/8.0), + REAL_CONST(43517.657409473606/8.0), + REAL_CONST(43536.916205556496/8.0), + REAL_CONST(43556.177131683784/8.0), + REAL_CONST(43575.44018738444/8.0), + REAL_CONST(43594.705372187724/8.0), + REAL_CONST(43613.972685623135/8.0), + REAL_CONST(43633.242127220445/8.0), + REAL_CONST(43652.513696509668/8.0), + REAL_CONST(43671.787393021099/8.0), + REAL_CONST(43691.063216285271/8.0), + REAL_CONST(43710.341165833001/8.0), + REAL_CONST(43729.621241195346/8.0), + REAL_CONST(43748.903441903625/8.0), + REAL_CONST(43768.187767489413/8.0), + REAL_CONST(43787.474217484552/8.0), + REAL_CONST(43806.762791421126/8.0), + REAL_CONST(43826.053488831501/8.0), + REAL_CONST(43845.346309248278/8.0), + REAL_CONST(43864.641252204325/8.0), + REAL_CONST(43883.938317232765/8.0), + REAL_CONST(43903.237503866971/8.0), + REAL_CONST(43922.538811640596/8.0), + REAL_CONST(43941.842240087513/8.0), + REAL_CONST(43961.147788741881/8.0), + REAL_CONST(43980.455457138101/8.0), + REAL_CONST(43999.765244810835/8.0), + REAL_CONST(44019.077151295001/8.0), + REAL_CONST(44038.391176125755/8.0), + REAL_CONST(44057.70731883854/8.0), + REAL_CONST(44077.02557896902/8.0), + REAL_CONST(44096.345956053141/8.0), + REAL_CONST(44115.668449627083/8.0), + REAL_CONST(44134.993059227287/8.0), + REAL_CONST(44154.319784390456/8.0), + REAL_CONST(44173.648624653535/8.0), + REAL_CONST(44192.979579553728/8.0), + REAL_CONST(44212.312648628489/8.0), + REAL_CONST(44231.647831415532/8.0), + REAL_CONST(44250.985127452805/8.0), + REAL_CONST(44270.324536278538/8.0), + REAL_CONST(44289.666057431183/8.0), + REAL_CONST(44309.009690449464/8.0), + REAL_CONST(44328.355434872348/8.0), + REAL_CONST(44347.703290239064/8.0), + REAL_CONST(44367.053256089079/8.0), + REAL_CONST(44386.405331962109/8.0), + REAL_CONST(44405.759517398139/8.0), + REAL_CONST(44425.115811937387/8.0), + REAL_CONST(44444.474215120332/8.0), + REAL_CONST(44463.834726487694/8.0), + REAL_CONST(44483.197345580462/8.0), + REAL_CONST(44502.562071939843/8.0), + REAL_CONST(44521.928905107328/8.0), + REAL_CONST(44541.297844624634/8.0), + REAL_CONST(44560.668890033732/8.0), + REAL_CONST(44580.042040876848/8.0), + REAL_CONST(44599.417296696454/8.0), + REAL_CONST(44618.794657035272/8.0), + REAL_CONST(44638.174121436256/8.0), + REAL_CONST(44657.555689442641/8.0), + REAL_CONST(44676.939360597877/8.0), + REAL_CONST(44696.325134445673/8.0), + REAL_CONST(44715.713010530002/8.0), + REAL_CONST(44735.102988395054/8.0), + REAL_CONST(44754.495067585296/8.0), + REAL_CONST(44773.88924764542/8.0), + REAL_CONST(44793.285528120374/8.0), + REAL_CONST(44812.683908555344/8.0), + REAL_CONST(44832.084388495779/8.0), + REAL_CONST(44851.486967487363/8.0), + REAL_CONST(44870.891645076015/8.0), + REAL_CONST(44890.298420807922/8.0), + REAL_CONST(44909.707294229491/8.0), + REAL_CONST(44929.118264887409/8.0), + REAL_CONST(44948.531332328566/8.0), + REAL_CONST(44967.946496100136/8.0), + REAL_CONST(44987.363755749502/8.0), + REAL_CONST(45006.783110824319/8.0), + REAL_CONST(45026.204560872473/8.0), + REAL_CONST(45045.628105442098/8.0), + REAL_CONST(45065.053744081561/8.0), + REAL_CONST(45084.48147633949/8.0), + REAL_CONST(45103.911301764747/8.0), + REAL_CONST(45123.343219906426/8.0), + REAL_CONST(45142.777230313885/8.0), + REAL_CONST(45162.21333253671/8.0), + REAL_CONST(45181.651526124733/8.0), + REAL_CONST(45201.091810628037/8.0), + REAL_CONST(45220.534185596924/8.0), + REAL_CONST(45239.978650581965/8.0), + REAL_CONST(45259.425205133957/8.0), + REAL_CONST(45278.873848803938/8.0), + REAL_CONST(45298.324581143192/8.0), + REAL_CONST(45317.777401703235/8.0), + REAL_CONST(45337.232310035848/8.0), + REAL_CONST(45356.68930569302/8.0), + REAL_CONST(45376.148388226997/8.0), + REAL_CONST(45395.60955719027/8.0), + REAL_CONST(45415.072812135557/8.0), + REAL_CONST(45434.538152615823/8.0), + REAL_CONST(45454.005578184282/8.0), + REAL_CONST(45473.475088394356/8.0), + REAL_CONST(45492.946682799746/8.0), + REAL_CONST(45512.420360954362/8.0), + REAL_CONST(45531.896122412363/8.0), + REAL_CONST(45551.373966728155/8.0), + REAL_CONST(45570.853893456362/8.0), + REAL_CONST(45590.33590215187/8.0), + REAL_CONST(45609.819992369776/8.0), + REAL_CONST(45629.306163665438/8.0), + REAL_CONST(45648.794415594442/8.0), + REAL_CONST(45668.284747712612/8.0), + REAL_CONST(45687.777159576006/8.0), + REAL_CONST(45707.27165074092/8.0), + REAL_CONST(45726.768220763894/8.0), + REAL_CONST(45746.266869201696/8.0), + REAL_CONST(45765.767595611323/8.0), + REAL_CONST(45785.270399550034/8.0), + REAL_CONST(45804.775280575297/8.0), + REAL_CONST(45824.282238244828/8.0), + REAL_CONST(45843.79127211657/8.0), + REAL_CONST(45863.302381748719/8.0), + REAL_CONST(45882.815566699683/8.0), + REAL_CONST(45902.33082652813/8.0), + REAL_CONST(45921.848160792935/8.0), + REAL_CONST(45941.367569053225/8.0), + REAL_CONST(45960.889050868354/8.0), + REAL_CONST(45980.41260579793/8.0), + REAL_CONST(45999.938233401757/8.0), + REAL_CONST(46019.465933239902/8.0), + REAL_CONST(46038.995704872657/8.0), + REAL_CONST(46058.527547860547/8.0), + REAL_CONST(46078.06146176433/8.0), + REAL_CONST(46097.597446144995/8.0), + REAL_CONST(46117.135500563774/8.0), + REAL_CONST(46136.675624582109/8.0), + REAL_CONST(46156.217817761702/8.0), + REAL_CONST(46175.762079664462/8.0), + REAL_CONST(46195.308409852543/8.0), + REAL_CONST(46214.856807888333/8.0), + REAL_CONST(46234.407273334444/8.0), + REAL_CONST(46253.959805753715/8.0), + REAL_CONST(46273.51440470924/8.0), + REAL_CONST(46293.071069764315/8.0), + REAL_CONST(46312.629800482478/8.0), + REAL_CONST(46332.190596427499/8.0), + REAL_CONST(46351.753457163381/8.0), + REAL_CONST(46371.318382254351/8.0), + REAL_CONST(46390.885371264863/8.0), + REAL_CONST(46410.45442375962/8.0), + REAL_CONST(46430.025539303526/8.0), + REAL_CONST(46449.598717461733/8.0), + REAL_CONST(46469.17395779962/8.0), + REAL_CONST(46488.751259882782/8.0), + REAL_CONST(46508.33062327707/8.0), + REAL_CONST(46527.912047548532/8.0), + REAL_CONST(46547.495532263471/8.0), + REAL_CONST(46567.081076988397/8.0), + REAL_CONST(46586.668681290059/8.0), + REAL_CONST(46606.258344735434/8.0), + REAL_CONST(46625.850066891719/8.0), + REAL_CONST(46645.443847326351/8.0), + REAL_CONST(46665.039685606986/8.0), + REAL_CONST(46684.637581301497/8.0), + REAL_CONST(46704.237533978005/8.0), + REAL_CONST(46723.839543204842/8.0), + REAL_CONST(46743.443608550573/8.0), + REAL_CONST(46763.049729583989/8.0), + REAL_CONST(46782.657905874104/8.0), + REAL_CONST(46802.268136990162/8.0), + REAL_CONST(46821.880422501628/8.0), + REAL_CONST(46841.494761978196/8.0), + REAL_CONST(46861.111154989776/8.0), + REAL_CONST(46880.729601106526/8.0), + REAL_CONST(46900.350099898795/8.0), + REAL_CONST(46919.97265093719/8.0), + REAL_CONST(46939.597253792526/8.0), + REAL_CONST(46959.223908035841/8.0), + REAL_CONST(46978.852613238392/8.0), + REAL_CONST(46998.483368971691/8.0), + REAL_CONST(47018.11617480743/8.0), + REAL_CONST(47037.751030317551/8.0), + REAL_CONST(47057.387935074221/8.0), + REAL_CONST(47077.026888649809/8.0), + REAL_CONST(47096.66789061694/8.0), + REAL_CONST(47116.310940548428/8.0), + REAL_CONST(47135.956038017328/8.0), + REAL_CONST(47155.603182596918/8.0), + REAL_CONST(47175.252373860698/8.0), + REAL_CONST(47194.903611382375/8.0), + REAL_CONST(47214.556894735892/8.0), + REAL_CONST(47234.212223495422/8.0), + REAL_CONST(47253.869597235338/8.0), + REAL_CONST(47273.52901553025/8.0), + REAL_CONST(47293.19047795498/8.0), + REAL_CONST(47312.853984084577/8.0), + REAL_CONST(47332.519533494306/8.0), + REAL_CONST(47352.187125759658/8.0), + REAL_CONST(47371.856760456343/8.0), + REAL_CONST(47391.528437160297/8.0), + REAL_CONST(47411.202155447652/8.0), + REAL_CONST(47430.877914894787/8.0), + REAL_CONST(47450.555715078299/8.0), + REAL_CONST(47470.235555574982/8.0), + REAL_CONST(47489.917435961863/8.0), + REAL_CONST(47509.601355816201/8.0), + REAL_CONST(47529.287314715453/8.0), + REAL_CONST(47548.975312237308/8.0), + REAL_CONST(47568.665347959672/8.0), + REAL_CONST(47588.357421460656/8.0), + REAL_CONST(47608.051532318605/8.0), + REAL_CONST(47627.747680112072/8.0), + REAL_CONST(47647.445864419846/8.0), + REAL_CONST(47667.14608482091/8.0), + REAL_CONST(47686.848340894474/8.0), + REAL_CONST(47706.552632219973/8.0), + REAL_CONST(47726.258958377046/8.0), + REAL_CONST(47745.967318945557/8.0), + REAL_CONST(47765.677713505589/8.0), + REAL_CONST(47785.390141637428/8.0), + REAL_CONST(47805.104602921601/8.0), + REAL_CONST(47824.821096938824/8.0), + REAL_CONST(47844.539623270044/8.0), + REAL_CONST(47864.260181496429/8.0), + REAL_CONST(47883.982771199349/8.0), + REAL_CONST(47903.707391960394/8.0), + REAL_CONST(47923.434043361369/8.0), + REAL_CONST(47943.162724984308/8.0), + REAL_CONST(47962.893436411439/8.0), + REAL_CONST(47982.626177225218/8.0), + REAL_CONST(48002.36094700831/8.0), + REAL_CONST(48022.097745343599/8.0), + REAL_CONST(48041.836571814172/8.0), + REAL_CONST(48061.57742600335/8.0), + REAL_CONST(48081.32030749465/8.0), + REAL_CONST(48101.065215871815/8.0), + REAL_CONST(48120.81215071879/8.0), + REAL_CONST(48140.56111161974/8.0), + REAL_CONST(48160.312098159047/8.0), + REAL_CONST(48180.065109921306/8.0), + REAL_CONST(48199.820146491307/8.0), + REAL_CONST(48219.577207454073/8.0), + REAL_CONST(48239.336292394844/8.0), + REAL_CONST(48259.097400899045/8.0), + REAL_CONST(48278.860532552339/8.0), + REAL_CONST(48298.625686940592/8.0), + REAL_CONST(48318.392863649875/8.0), + REAL_CONST(48338.162062266485/8.0), + REAL_CONST(48357.933282376915/8.0), + REAL_CONST(48377.706523567889/8.0), + REAL_CONST(48397.481785426316/8.0), + REAL_CONST(48417.259067539344/8.0), + REAL_CONST(48437.038369494308/8.0), + REAL_CONST(48456.819690878765/8.0), + REAL_CONST(48476.603031280487/8.0), + REAL_CONST(48496.388390287451/8.0), + REAL_CONST(48516.175767487839/8.0), + REAL_CONST(48535.965162470042/8.0), + REAL_CONST(48555.756574822684/8.0), + REAL_CONST(48575.550004134566/8.0), + REAL_CONST(48595.345449994718/8.0), + REAL_CONST(48615.142911992378/8.0), + REAL_CONST(48634.942389716991/8.0), + REAL_CONST(48654.743882758201/8.0), + REAL_CONST(48674.547390705877/8.0), + REAL_CONST(48694.352913150084/8.0), + REAL_CONST(48714.160449681112/8.0), + REAL_CONST(48733.969999889443/8.0), + REAL_CONST(48753.781563365759/8.0), + REAL_CONST(48773.595139700978/8.0), + REAL_CONST(48793.410728486211/8.0), + REAL_CONST(48813.228329312769/8.0), + REAL_CONST(48833.047941772187/8.0), + REAL_CONST(48852.869565456189/8.0), + REAL_CONST(48872.693199956717/8.0), + REAL_CONST(48892.518844865925/8.0), + REAL_CONST(48912.346499776155/8.0), + REAL_CONST(48932.176164279976/8.0), + REAL_CONST(48952.007837970152/8.0), + REAL_CONST(48971.841520439666/8.0), + REAL_CONST(48991.677211281676/8.0), + REAL_CONST(49011.514910089587/8.0), + REAL_CONST(49031.354616456978/8.0), + REAL_CONST(49051.196329977654/8.0), + REAL_CONST(49071.04005024561/8.0), + REAL_CONST(49090.885776855059/8.0), + REAL_CONST(49110.733509400408/8.0), + REAL_CONST(49130.583247476279/8.0), + REAL_CONST(49150.434990677488/8.0), + REAL_CONST(49170.288738599062/8.0), + REAL_CONST(49190.144490836232/8.0), + REAL_CONST(49210.002246984441/8.0), + REAL_CONST(49229.86200663932/8.0), + REAL_CONST(49249.723769396718/8.0), + REAL_CONST(49269.587534852675/8.0), + REAL_CONST(49289.453302603448/8.0), + REAL_CONST(49309.32107224549/8.0), + REAL_CONST(49329.190843375451/8.0), + REAL_CONST(49349.062615590192/8.0), + REAL_CONST(49368.936388486785/8.0), + REAL_CONST(49388.812161662492/8.0), + REAL_CONST(49408.689934714785/8.0), + REAL_CONST(49428.569707241324/8.0), + REAL_CONST(49448.45147883999/8.0), + REAL_CONST(49468.335249108866/8.0), + REAL_CONST(49488.22101764621/8.0), + REAL_CONST(49508.108784050521/8.0), + REAL_CONST(49527.99854792047/8.0), + REAL_CONST(49547.890308854934/8.0), + REAL_CONST(49567.784066453009/8.0), + REAL_CONST(49587.679820313977/8.0), + REAL_CONST(49607.57757003732/8.0), + REAL_CONST(49627.477315222721/8.0), + REAL_CONST(49647.379055470075/8.0), + REAL_CONST(49667.28279037946/8.0), + REAL_CONST(49687.188519551179/8.0), + REAL_CONST(49707.096242585707/8.0), + REAL_CONST(49727.005959083741/8.0), + REAL_CONST(49746.917668646165/8.0), + REAL_CONST(49766.831370874068/8.0), + REAL_CONST(49786.747065368734/8.0), + REAL_CONST(49806.66475173166/8.0), + REAL_CONST(49826.584429564515/8.0), + REAL_CONST(49846.506098469203/8.0), + REAL_CONST(49866.429758047794/8.0), + REAL_CONST(49886.355407902578/8.0), + REAL_CONST(49906.283047636032/8.0), + REAL_CONST(49926.212676850846/8.0), + REAL_CONST(49946.144295149883/8.0), + REAL_CONST(49966.077902136225/8.0), + REAL_CONST(49986.013497413151/8.0), + REAL_CONST(50005.951080584135/8.0), + REAL_CONST(50025.890651252834/8.0), + REAL_CONST(50045.832209023123/8.0), + REAL_CONST(50065.775753499074/8.0), + REAL_CONST(50085.721284284933/8.0), + REAL_CONST(50105.668800985164/8.0), + REAL_CONST(50125.618303204428/8.0), + REAL_CONST(50145.569790547575/8.0), + REAL_CONST(50165.523262619652/8.0), + REAL_CONST(50185.478719025901/8.0), + REAL_CONST(50205.436159371769/8.0), + REAL_CONST(50225.395583262893/8.0), + REAL_CONST(50245.356990305103/8.0), + REAL_CONST(50265.320380104429/8.0), + REAL_CONST(50285.285752267104/8.0), + REAL_CONST(50305.253106399534/8.0), + REAL_CONST(50325.222442108337/8.0), + REAL_CONST(50345.193759000336/8.0), + REAL_CONST(50365.16705668252/8.0), + REAL_CONST(50385.142334762102/8.0), + REAL_CONST(50405.119592846473/8.0), + REAL_CONST(50425.098830543218/8.0), + REAL_CONST(50445.080047460127/8.0), + REAL_CONST(50465.063243205179/8.0), + REAL_CONST(50485.048417386541/8.0), + REAL_CONST(50505.035569612577/8.0), + REAL_CONST(50525.024699491856/8.0), + REAL_CONST(50545.015806633128/8.0), + REAL_CONST(50565.008890645338/8.0), + REAL_CONST(50585.003951137631/8.0), + REAL_CONST(50605.00098771933/8.0), + REAL_CONST(50624.999999999971/8.0), + REAL_CONST(50645.000987589265/8.0), + REAL_CONST(50665.003950097132/8.0), + REAL_CONST(50685.008887133677/8.0), + REAL_CONST(50705.015798309192/8.0), + REAL_CONST(50725.024683234165/8.0), + REAL_CONST(50745.035541519283/8.0), + REAL_CONST(50765.048372775411/8.0), + REAL_CONST(50785.063176613621/8.0), + REAL_CONST(50805.079952645159/8.0), + REAL_CONST(50825.098700481489/8.0), + REAL_CONST(50845.119419734241/8.0), + REAL_CONST(50865.142110015244/8.0), + REAL_CONST(50885.166770936521/8.0), + REAL_CONST(50905.193402110279/8.0), + REAL_CONST(50925.222003148934/8.0), + REAL_CONST(50945.252573665071/8.0), + REAL_CONST(50965.285113271471/8.0), + REAL_CONST(50985.319621581119/8.0), + REAL_CONST(51005.356098207172/8.0), + REAL_CONST(51025.394542762981/8.0), + REAL_CONST(51045.434954862096/8.0), + REAL_CONST(51065.477334118244/8.0), + REAL_CONST(51085.521680145357/8.0), + REAL_CONST(51105.567992557546/8.0), + REAL_CONST(51125.616270969113/8.0), + REAL_CONST(51145.66651499454/8.0), + REAL_CONST(51165.718724248516/8.0), + REAL_CONST(51185.772898345916/8.0), + REAL_CONST(51205.829036901778/8.0), + REAL_CONST(51225.887139531362/8.0), + REAL_CONST(51245.947205850105/8.0), + REAL_CONST(51266.009235473619/8.0), + REAL_CONST(51286.073228017718/8.0), + REAL_CONST(51306.139183098399/8.0), + REAL_CONST(51326.207100331856/8.0), + REAL_CONST(51346.276979334456/8.0), + REAL_CONST(51366.348819722756/8.0), + REAL_CONST(51386.42262111351/8.0), + REAL_CONST(51406.498383123653/8.0), + REAL_CONST(51426.57610537031/8.0), + REAL_CONST(51446.655787470787/8.0), + REAL_CONST(51466.737429042587/8.0), + REAL_CONST(51486.82102970338/8.0), + REAL_CONST(51506.906589071048/8.0), + REAL_CONST(51526.994106763632/8.0), + REAL_CONST(51547.083582399391/8.0), + REAL_CONST(51567.175015596738/8.0), + REAL_CONST(51587.268405974297/8.0), + REAL_CONST(51607.363753150858/8.0), + REAL_CONST(51627.461056745415/8.0), + REAL_CONST(51647.56031637713/8.0), + REAL_CONST(51667.661531665362/8.0), + REAL_CONST(51687.764702229651/8.0), + REAL_CONST(51707.869827689727/8.0), + REAL_CONST(51727.976907665499/8.0), + REAL_CONST(51748.085941777055/8.0), + REAL_CONST(51768.196929644677/8.0), + REAL_CONST(51788.309870888836/8.0), + REAL_CONST(51808.42476513017/8.0), + REAL_CONST(51828.541611989524/8.0), + REAL_CONST(51848.660411087905/8.0), + REAL_CONST(51868.781162046515/8.0), + REAL_CONST(51888.90386448674/8.0), + REAL_CONST(51909.028518030143/8.0), + REAL_CONST(51929.155122298485/8.0), + REAL_CONST(51949.283676913685/8.0), + REAL_CONST(51969.414181497872/8.0), + REAL_CONST(51989.546635673345/8.0), + REAL_CONST(52009.681039062583/8.0), + REAL_CONST(52029.817391288263/8.0), + REAL_CONST(52049.955691973213/8.0), + REAL_CONST(52070.095940740481/8.0), + REAL_CONST(52090.238137213273/8.0), + REAL_CONST(52110.382281014987/8.0), + REAL_CONST(52130.5283717692/8.0), + REAL_CONST(52150.676409099666/8.0), + REAL_CONST(52170.826392630333/8.0), + REAL_CONST(52190.97832198532/8.0), + REAL_CONST(52211.132196788931/8.0), + REAL_CONST(52231.288016665654/8.0), + REAL_CONST(52251.445781240145/8.0), + REAL_CONST(52271.60549013727/8.0), + REAL_CONST(52291.76714298204/8.0), + REAL_CONST(52311.930739399664/8.0), + REAL_CONST(52332.096279015546/8.0), + REAL_CONST(52352.263761455244/8.0), + REAL_CONST(52372.433186344519/8.0), + REAL_CONST(52392.604553309284/8.0), + REAL_CONST(52412.777861975665/8.0), + REAL_CONST(52432.953111969946/8.0), + REAL_CONST(52453.130302918595/8.0), + REAL_CONST(52473.309434448267/8.0), + REAL_CONST(52493.490506185793/8.0), + REAL_CONST(52513.67351775818/8.0), + REAL_CONST(52533.858468792605/8.0), + REAL_CONST(52554.045358916446/8.0), + REAL_CONST(52574.234187757254/8.0), + REAL_CONST(52594.42495494274/8.0), + REAL_CONST(52614.617660100812/8.0), + REAL_CONST(52634.812302859558/8.0), + REAL_CONST(52655.008882847229/8.0), + REAL_CONST(52675.20739969227/8.0), + REAL_CONST(52695.407853023295/8.0), + REAL_CONST(52715.610242469098/8.0), + REAL_CONST(52735.814567658657/8.0), + REAL_CONST(52756.02082822111/8.0), + REAL_CONST(52776.229023785803/8.0), + REAL_CONST(52796.439153982225/8.0), + REAL_CONST(52816.651218440056/8.0), + REAL_CONST(52836.865216789171/8.0), + REAL_CONST(52857.081148659599/8.0), + REAL_CONST(52877.29901368155/8.0), + REAL_CONST(52897.518811485425/8.0), + REAL_CONST(52917.740541701773/8.0), + REAL_CONST(52937.964203961354/8.0), + REAL_CONST(52958.18979789508/8.0), + REAL_CONST(52978.417323134046/8.0), + REAL_CONST(52998.646779309529/8.0), + REAL_CONST(53018.878166052978/8.0), + REAL_CONST(53039.111482996006/8.0), + REAL_CONST(53059.346729770419/8.0), + REAL_CONST(53079.583906008193/8.0), + REAL_CONST(53099.823011341483/8.0), + REAL_CONST(53120.0640454026/8.0), + REAL_CONST(53140.307007824063/8.0), + REAL_CONST(53160.551898238533/8.0), + REAL_CONST(53180.79871627887/8.0), + REAL_CONST(53201.047461578091/8.0), + REAL_CONST(53221.2981337694/8.0), + REAL_CONST(53241.550732486176/8.0), + REAL_CONST(53261.805257361964/8.0), + REAL_CONST(53282.061708030487/8.0), + REAL_CONST(53302.32008412564/8.0), + REAL_CONST(53322.580385281493/8.0), + REAL_CONST(53342.842611132299/8.0), + REAL_CONST(53363.106761312469/8.0), + REAL_CONST(53383.372835456597/8.0), + REAL_CONST(53403.640833199453/8.0), + REAL_CONST(53423.910754175973/8.0), + REAL_CONST(53444.18259802126/8.0), + REAL_CONST(53464.456364370613/8.0), + REAL_CONST(53484.732052859479/8.0), + REAL_CONST(53505.009663123499/8.0), + REAL_CONST(53525.289194798468/8.0), + REAL_CONST(53545.570647520362/8.0), + REAL_CONST(53565.854020925333/8.0), + REAL_CONST(53586.139314649699/8.0), + REAL_CONST(53606.426528329954/8.0), + REAL_CONST(53626.715661602764/8.0), + REAL_CONST(53647.006714104959/8.0), + REAL_CONST(53667.299685473547/8.0), + REAL_CONST(53687.59457534572/8.0), + REAL_CONST(53707.891383358816/8.0), + REAL_CONST(53728.190109150361/8.0), + REAL_CONST(53748.490752358055/8.0), + REAL_CONST(53768.793312619753/8.0), + REAL_CONST(53789.09778957349/8.0), + REAL_CONST(53809.404182857485/8.0), + REAL_CONST(53829.712492110106/8.0), + REAL_CONST(53850.022716969899/8.0), + REAL_CONST(53870.334857075584/8.0), + REAL_CONST(53890.648912066055/8.0), + REAL_CONST(53910.964881580367/8.0), + REAL_CONST(53931.28276525774/8.0), + REAL_CONST(53951.602562737586/8.0), + REAL_CONST(53971.924273659461/8.0), + REAL_CONST(53992.24789766311/8.0), + REAL_CONST(54012.57343438844/8.0), + REAL_CONST(54032.90088347553/8.0), + REAL_CONST(54053.23024456462/8.0), + REAL_CONST(54073.561517296133/8.0), + REAL_CONST(54093.894701310644/8.0), + REAL_CONST(54114.22979624891/8.0), + REAL_CONST(54134.566801751855/8.0), + REAL_CONST(54154.90571746057/8.0), + REAL_CONST(54175.246543016314/8.0), + REAL_CONST(54195.589278060506/8.0), + REAL_CONST(54215.933922234755/8.0), + REAL_CONST(54236.280475180814/8.0), + REAL_CONST(54256.628936540626/8.0), + REAL_CONST(54276.97930595628/8.0), + REAL_CONST(54297.331583070045/8.0), + REAL_CONST(54317.685767524359/8.0), + REAL_CONST(54338.041858961828/8.0), + REAL_CONST(54358.399857025215/8.0), + REAL_CONST(54378.759761357462/8.0), + REAL_CONST(54399.121571601667/8.0), + REAL_CONST(54419.485287401105/8.0), + REAL_CONST(54439.850908399218/8.0), + REAL_CONST(54460.218434239614/8.0), + REAL_CONST(54480.587864566056/8.0), + REAL_CONST(54500.95919902248/8.0), + REAL_CONST(54521.332437252997/8.0), + REAL_CONST(54541.707578901878/8.0), + REAL_CONST(54562.084623613555/8.0), + REAL_CONST(54582.46357103264/8.0), + REAL_CONST(54602.844420803893/8.0), + REAL_CONST(54623.227172572246/8.0), + REAL_CONST(54643.611825982807/8.0), + REAL_CONST(54663.998380680838/8.0), + REAL_CONST(54684.386836311773/8.0), + REAL_CONST(54704.777192521207/8.0), + REAL_CONST(54725.169448954897/8.0), + REAL_CONST(54745.563605258772/8.0), + REAL_CONST(54765.959661078923/8.0), + REAL_CONST(54786.357616061614/8.0), + REAL_CONST(54806.757469853255/8.0), + REAL_CONST(54827.159222100439/8.0), + REAL_CONST(54847.562872449904/8.0), + REAL_CONST(54867.968420548583/8.0), + REAL_CONST(54888.375866043534/8.0), + REAL_CONST(54908.785208582012/8.0), + REAL_CONST(54929.196447811417/8.0), + REAL_CONST(54949.609583379322/8.0), + REAL_CONST(54970.024614933463/8.0), + REAL_CONST(54990.441542121727/8.0), + REAL_CONST(55010.86036459219/8.0), + REAL_CONST(55031.28108199306/8.0), + REAL_CONST(55051.703693972733/8.0), + REAL_CONST(55072.128200179759/8.0), + REAL_CONST(55092.554600262847/8.0), + REAL_CONST(55112.982893870874/8.0), + REAL_CONST(55133.413080652877/8.0), + REAL_CONST(55153.845160258061/8.0), + REAL_CONST(55174.279132335789/8.0), + REAL_CONST(55194.714996535586/8.0), + REAL_CONST(55215.152752507143/8.0), + REAL_CONST(55235.592399900306/8.0), + REAL_CONST(55256.033938365079/8.0), + REAL_CONST(55276.477367551655/8.0), + REAL_CONST(55296.92268711036/8.0), + REAL_CONST(55317.369896691685/8.0), + REAL_CONST(55337.818995946305/8.0), + REAL_CONST(55358.269984525024/8.0), + REAL_CONST(55378.72286207883/8.0), + REAL_CONST(55399.177628258869/8.0), + REAL_CONST(55419.634282716441/8.0), + REAL_CONST(55440.092825103013/8.0), + REAL_CONST(55460.553255070205/8.0), + REAL_CONST(55481.015572269804/8.0), + REAL_CONST(55501.479776353764/8.0), + REAL_CONST(55521.945866974187/8.0), + REAL_CONST(55542.413843783339/8.0), + REAL_CONST(55562.883706433655/8.0), + REAL_CONST(55583.355454577715/8.0), + REAL_CONST(55603.82908786826/8.0), + REAL_CONST(55624.304605958219/8.0), + REAL_CONST(55644.782008500639/8.0), + REAL_CONST(55665.261295148754/8.0), + REAL_CONST(55685.742465555952/8.0), + REAL_CONST(55706.225519375774/8.0), + REAL_CONST(55726.710456261928/8.0), + REAL_CONST(55747.197275868275/8.0), + REAL_CONST(55767.685977848843/8.0), + REAL_CONST(55788.176561857814/8.0), + REAL_CONST(55808.669027549528/8.0), + REAL_CONST(55829.163374578478/8.0), + REAL_CONST(55849.659602599328/8.0), + REAL_CONST(55870.157711266889/8.0), + REAL_CONST(55890.657700236145/8.0), + REAL_CONST(55911.159569162221/8.0), + REAL_CONST(55931.663317700411/8.0), + REAL_CONST(55952.168945506164/8.0), + REAL_CONST(55972.676452235086/8.0), + REAL_CONST(55993.185837542944/8.0), + REAL_CONST(56013.697101085651/8.0), + REAL_CONST(56034.210242519301/8.0), + REAL_CONST(56054.72526150012/8.0), + REAL_CONST(56075.242157684508/8.0), + REAL_CONST(56095.760930729011/8.0), + REAL_CONST(56116.281580290342/8.0), + REAL_CONST(56136.804106025367/8.0), + REAL_CONST(56157.328507591104/8.0), + REAL_CONST(56177.85478464474/8.0), + REAL_CONST(56198.382936843598/8.0), + REAL_CONST(56218.912963845185/8.0), + REAL_CONST(56239.444865307138/8.0), + REAL_CONST(56259.978640887268/8.0), + REAL_CONST(56280.514290243525/8.0), + REAL_CONST(56301.051813034042/8.0), + REAL_CONST(56321.591208917082/8.0), + REAL_CONST(56342.13247755108/8.0), + REAL_CONST(56362.675618594607/8.0), + REAL_CONST(56383.220631706419/8.0), + REAL_CONST(56403.767516545398/8.0), + REAL_CONST(56424.316272770608/8.0), + REAL_CONST(56444.866900041241/8.0), + REAL_CONST(56465.419398016667/8.0), + REAL_CONST(56485.973766356394/8.0), + REAL_CONST(56506.530004720102/8.0), + REAL_CONST(56527.088112767611/8.0), + REAL_CONST(56547.648090158902/8.0), + REAL_CONST(56568.209936554107/8.0), + REAL_CONST(56588.773651613519/8.0), + REAL_CONST(56609.339234997584/8.0), + REAL_CONST(56629.9066863669/8.0), + REAL_CONST(56650.47600538221/8.0), + REAL_CONST(56671.04719170442/8.0), + REAL_CONST(56691.620244994599/8.0), + REAL_CONST(56712.195164913959/8.0), + REAL_CONST(56732.771951123868/8.0), + REAL_CONST(56753.350603285835/8.0), + REAL_CONST(56773.931121061541/8.0), + REAL_CONST(56794.513504112823/8.0), + REAL_CONST(56815.097752101647/8.0), + REAL_CONST(56835.683864690152/8.0), + REAL_CONST(56856.271841540627/8.0), + REAL_CONST(56876.86168231551/8.0), + REAL_CONST(56897.453386677393/8.0), + REAL_CONST(56918.046954289028/8.0), + REAL_CONST(56938.642384813298/8.0), + REAL_CONST(56959.239677913261/8.0), + REAL_CONST(56979.838833252121/8.0), + REAL_CONST(57000.439850493225/8.0), + REAL_CONST(57021.04272930009/8.0), + REAL_CONST(57041.647469336371/8.0), + REAL_CONST(57062.254070265873/8.0), + REAL_CONST(57082.862531752558/8.0), + REAL_CONST(57103.472853460553/8.0), + REAL_CONST(57124.085035054108/8.0), + REAL_CONST(57144.699076197649/8.0), + REAL_CONST(57165.314976555739/8.0), + REAL_CONST(57185.932735793103/8.0), + REAL_CONST(57206.552353574611/8.0), + REAL_CONST(57227.173829565276/8.0), + REAL_CONST(57247.797163430281/8.0), + REAL_CONST(57268.42235483494/8.0), + REAL_CONST(57289.049403444733/8.0), + REAL_CONST(57309.678308925286/8.0), + REAL_CONST(57330.30907094237/8.0), + REAL_CONST(57350.941689161911/8.0), + REAL_CONST(57371.576163249985/8.0), + REAL_CONST(57392.212492872815/8.0), + REAL_CONST(57412.850677696784/8.0), + REAL_CONST(57433.490717388406/8.0), + REAL_CONST(57454.132611614368/8.0), + REAL_CONST(57474.776360041491/8.0), + REAL_CONST(57495.421962336746/8.0), + REAL_CONST(57516.069418167266/8.0), + REAL_CONST(57536.718727200314/8.0), + REAL_CONST(57557.36988910332/8.0), + REAL_CONST(57578.022903543861/8.0), + REAL_CONST(57598.677770189643/8.0), + REAL_CONST(57619.334488708548/8.0), + REAL_CONST(57639.993058768589/8.0), + REAL_CONST(57660.653480037938/8.0), + REAL_CONST(57681.315752184906/8.0), + REAL_CONST(57701.979874877965/8.0), + REAL_CONST(57722.64584778573/8.0), + REAL_CONST(57743.31367057695/8.0), + REAL_CONST(57763.983342920546/8.0), + REAL_CONST(57784.654864485572/8.0), + REAL_CONST(57805.328234941233/8.0), + REAL_CONST(57826.003453956881/8.0), + REAL_CONST(57846.680521202026/8.0), + REAL_CONST(57867.359436346305/8.0), + REAL_CONST(57888.040199059527/8.0), + REAL_CONST(57908.722809011633/8.0), + REAL_CONST(57929.407265872709/8.0), + REAL_CONST(57950.093569313001/8.0), + REAL_CONST(57970.781719002895/8.0), + REAL_CONST(57991.471714612911/8.0), + REAL_CONST(58012.16355581375/8.0), + REAL_CONST(58032.857242276223/8.0), + REAL_CONST(58053.552773671312/8.0), + REAL_CONST(58074.25014967013/8.0), + REAL_CONST(58094.949369943948/8.0), + REAL_CONST(58115.650434164185/8.0), + REAL_CONST(58136.353342002389/8.0), + REAL_CONST(58157.058093130276/8.0), + REAL_CONST(58177.764687219693/8.0), + REAL_CONST(58198.47312394264/8.0), + REAL_CONST(58219.183402971255/8.0), + REAL_CONST(58239.895523977837/8.0), + REAL_CONST(58260.609486634821/8.0), + REAL_CONST(58281.325290614775/8.0), + REAL_CONST(58302.042935590434/8.0), + REAL_CONST(58322.762421234678/8.0), + REAL_CONST(58343.483747220511/8.0), + REAL_CONST(58364.206913221096/8.0), + REAL_CONST(58384.931918909751/8.0), + REAL_CONST(58405.658763959924/8.0), + REAL_CONST(58426.3874480452/8.0), + REAL_CONST(58447.117970839339/8.0), + REAL_CONST(58467.85033201622/8.0), + REAL_CONST(58488.584531249864/8.0), + REAL_CONST(58509.320568214462/8.0), + REAL_CONST(58530.058442584334/8.0), + REAL_CONST(58550.798154033931/8.0), + REAL_CONST(58571.539702237875/8.0), + REAL_CONST(58592.283086870906/8.0), + REAL_CONST(58613.028307607929/8.0), + REAL_CONST(58633.775364123983/8.0), + REAL_CONST(58654.52425609425/8.0), + REAL_CONST(58675.274983194053/8.0), + REAL_CONST(58696.027545098877/8.0), + REAL_CONST(58716.781941484325/8.0), + REAL_CONST(58737.538172026158/8.0), + REAL_CONST(58758.296236400274/8.0), + REAL_CONST(58779.056134282728/8.0), + REAL_CONST(58799.817865349694/8.0), + REAL_CONST(58820.581429277503/8.0), + REAL_CONST(58841.346825742643/8.0), + REAL_CONST(58862.114054421712/8.0), + REAL_CONST(58882.883114991484/8.0), + REAL_CONST(58903.654007128847/8.0), + REAL_CONST(58924.426730510851/8.0), + REAL_CONST(58945.201284814684/8.0), + REAL_CONST(58965.977669717664/8.0), + REAL_CONST(58986.755884897269/8.0), + REAL_CONST(59007.535930031117/8.0), + REAL_CONST(59028.317804796949/8.0), + REAL_CONST(59049.101508872664/8.0), + REAL_CONST(59069.887041936301/8.0), + REAL_CONST(59090.674403666046/8.0), + REAL_CONST(59111.463593740213/8.0), + REAL_CONST(59132.254611837263/8.0), + REAL_CONST(59153.047457635803/8.0), + REAL_CONST(59173.84213081457/8.0), + REAL_CONST(59194.638631052461/8.0), + REAL_CONST(59215.436958028506/8.0), + REAL_CONST(59236.237111421855/8.0), + REAL_CONST(59257.039090911829/8.0), + REAL_CONST(59277.842896177877/8.0), + REAL_CONST(59298.648526899589/8.0), + REAL_CONST(59319.455982756685/8.0), + REAL_CONST(59340.26526342905/8.0), + REAL_CONST(59361.076368596696/8.0), + REAL_CONST(59381.889297939757/8.0), + REAL_CONST(59402.704051138542/8.0), + REAL_CONST(59423.520627873484/8.0), + REAL_CONST(59444.339027825139/8.0), + REAL_CONST(59465.159250674224/8.0), + REAL_CONST(59485.9812961016/8.0), + REAL_CONST(59506.805163788253/8.0), + REAL_CONST(59527.630853415307/8.0), + REAL_CONST(59548.458364664046/8.0), + REAL_CONST(59569.287697215863/8.0), + REAL_CONST(59590.118850752311/8.0), + REAL_CONST(59610.951824955089/8.0), + REAL_CONST(59631.786619506012/8.0), + REAL_CONST(59652.623234087048/8.0), + REAL_CONST(59673.461668380311/8.0), + REAL_CONST(59694.301922068029/8.0), + REAL_CONST(59715.143994832593/8.0), + REAL_CONST(59735.987886356525/8.0), + REAL_CONST(59756.833596322482/8.0), + REAL_CONST(59777.681124413255/8.0), + REAL_CONST(59798.530470311794/8.0), + REAL_CONST(59819.381633701159/8.0), + REAL_CONST(59840.234614264569/8.0), + REAL_CONST(59861.089411685381/8.0), + REAL_CONST(59881.94602564707/8.0), + REAL_CONST(59902.804455833269/8.0), + REAL_CONST(59923.664701927737/8.0), + REAL_CONST(59944.526763614384/8.0), + REAL_CONST(59965.390640577243/8.0), + REAL_CONST(59986.256332500488/8.0), + REAL_CONST(60007.123839068438/8.0), + REAL_CONST(60027.993159965539/8.0), + REAL_CONST(60048.864294876381/8.0), + REAL_CONST(60069.737243485688/8.0), + REAL_CONST(60090.612005478324/8.0), + REAL_CONST(60111.488580539284/8.0), + REAL_CONST(60132.366968353708/8.0), + REAL_CONST(60153.247168606867/8.0), + REAL_CONST(60174.129180984164/8.0), + REAL_CONST(60195.013005171153/8.0), + REAL_CONST(60215.898640853513/8.0), + REAL_CONST(60236.786087717061/8.0), + REAL_CONST(60257.675345447751/8.0), + REAL_CONST(60278.566413731671/8.0), + REAL_CONST(60299.459292255044/8.0), + REAL_CONST(60320.353980704247/8.0), + REAL_CONST(60341.25047876576/8.0), + REAL_CONST(60362.148786126229/8.0), + REAL_CONST(60383.048902472423/8.0), + REAL_CONST(60403.950827491237/8.0), + REAL_CONST(60424.854560869717/8.0), + REAL_CONST(60445.76010229504/8.0), + REAL_CONST(60466.667451454516/8.0), + REAL_CONST(60487.57660803559/8.0), + REAL_CONST(60508.487571725847/8.0), + REAL_CONST(60529.400342212997/8.0), + REAL_CONST(60550.314919184893/8.0), + REAL_CONST(60571.231302329521/8.0), + REAL_CONST(60592.149491335003/8.0), + REAL_CONST(60613.069485889588/8.0), + REAL_CONST(60633.991285681674/8.0), + REAL_CONST(60654.914890399785/8.0), + REAL_CONST(60675.840299732568/8.0), + REAL_CONST(60696.767513368832/8.0), + REAL_CONST(60717.696530997484/8.0), + REAL_CONST(60738.627352307602/8.0), + REAL_CONST(60759.55997698837/8.0), + REAL_CONST(60780.494404729128/8.0), + REAL_CONST(60801.430635219323/8.0), + REAL_CONST(60822.368668148556/8.0), + REAL_CONST(60843.308503206565/8.0), + REAL_CONST(60864.250140083204/8.0), + REAL_CONST(60885.193578468468/8.0), + REAL_CONST(60906.138818052495/8.0), + REAL_CONST(60927.085858525541/8.0), + REAL_CONST(60948.034699578006/8.0), + REAL_CONST(60968.985340900421/8.0), + REAL_CONST(60989.937782183442/8.0), + REAL_CONST(61010.892023117864/8.0), + REAL_CONST(61031.848063394616/8.0), + REAL_CONST(61052.805902704764/8.0), + REAL_CONST(61073.765540739492/8.0), + REAL_CONST(61094.726977190134/8.0), + REAL_CONST(61115.690211748137/8.0), + REAL_CONST(61136.655244105103/8.0), + REAL_CONST(61157.622073952742/8.0), + REAL_CONST(61178.590700982917/8.0), + REAL_CONST(61199.561124887616/8.0), + REAL_CONST(61220.533345358948/8.0), + REAL_CONST(61241.507362089171/8.0), + REAL_CONST(61262.483174770663/8.0), + REAL_CONST(61283.460783095943/8.0), + REAL_CONST(61304.440186757645/8.0), + REAL_CONST(61325.421385448557/8.0), + REAL_CONST(61346.404378861582/8.0), + REAL_CONST(61367.389166689762/8.0), + REAL_CONST(61388.375748626262/8.0), + REAL_CONST(61409.364124364387/8.0), + REAL_CONST(61430.354293597571/8.0), + REAL_CONST(61451.346256019373/8.0), + REAL_CONST(61472.340011323497/8.0), + REAL_CONST(61493.335559203762/8.0), + REAL_CONST(61514.332899354122/8.0), + REAL_CONST(61535.332031468672/8.0), + REAL_CONST(61556.332955241618/8.0), + REAL_CONST(61577.335670367313/8.0), + REAL_CONST(61598.340176540238/8.0), + REAL_CONST(61619.346473454993/8.0), + REAL_CONST(61640.354560806329/8.0), + REAL_CONST(61661.3644382891/8.0), + REAL_CONST(61682.376105598312/8.0), + REAL_CONST(61703.389562429089/8.0), + REAL_CONST(61724.404808476691/8.0), + REAL_CONST(61745.42184343651/8.0), + REAL_CONST(61766.440667004063/8.0), + REAL_CONST(61787.461278874987/8.0), + REAL_CONST(61808.483678745069/8.0), + REAL_CONST(61829.507866310203/8.0), + REAL_CONST(61850.533841266435/8.0), + REAL_CONST(61871.561603309929/8.0), + REAL_CONST(61892.591152136971/8.0), + REAL_CONST(61913.622487443987/8.0), + REAL_CONST(61934.655608927525/8.0), + REAL_CONST(61955.690516284267/8.0), + REAL_CONST(61976.727209211022/8.0), + REAL_CONST(61997.765687404724/8.0), + REAL_CONST(62018.805950562448/8.0), + REAL_CONST(62039.847998381381/8.0), + REAL_CONST(62060.891830558845/8.0), + REAL_CONST(62081.93744679229/8.0), + REAL_CONST(62102.984846779298/8.0), + REAL_CONST(62124.034030217575/8.0), + REAL_CONST(62145.084996804966/8.0), + REAL_CONST(62166.137746239416/8.0), + REAL_CONST(62187.19227821903/8.0), + REAL_CONST(62208.248592442025/8.0), + REAL_CONST(62229.306688606739/8.0), + REAL_CONST(62250.366566411656/8.0), + REAL_CONST(62271.428225555377/8.0), + REAL_CONST(62292.491665736627/8.0), + REAL_CONST(62313.556886654267/8.0), + REAL_CONST(62334.623888007271/8.0), + REAL_CONST(62355.692669494762/8.0), + REAL_CONST(62376.763230815974/8.0), + REAL_CONST(62397.835571670272/8.0), + REAL_CONST(62418.909691757144/8.0), + REAL_CONST(62439.98559077621/8.0), + REAL_CONST(62461.063268427228/8.0), + REAL_CONST(62482.142724410049/8.0), + REAL_CONST(62503.223958424685/8.0), + REAL_CONST(62524.306970171267/8.0), + REAL_CONST(62545.39175935003/8.0), + REAL_CONST(62566.478325661366/8.0), + REAL_CONST(62587.566668805768/8.0), + REAL_CONST(62608.656788483881/8.0), + REAL_CONST(62629.748684396451/8.0), + REAL_CONST(62650.842356244357/8.0), + REAL_CONST(62671.937803728622/8.0), + REAL_CONST(62693.035026550366/8.0), + REAL_CONST(62714.134024410858/8.0), + REAL_CONST(62735.234797011479/8.0), + REAL_CONST(62756.337344053733/8.0), + REAL_CONST(62777.441665239276/8.0), + REAL_CONST(62798.547760269852/8.0), + REAL_CONST(62819.655628847358/8.0), + REAL_CONST(62840.765270673801/8.0), + REAL_CONST(62861.876685451323/8.0), + REAL_CONST(62882.989872882186/8.0), + REAL_CONST(62904.104832668774/8.0), + REAL_CONST(62925.221564513602/8.0), + REAL_CONST(62946.340068119309/8.0), + REAL_CONST(62967.460343188657/8.0), + REAL_CONST(62988.582389424526/8.0), + REAL_CONST(63009.70620652994/8.0), + REAL_CONST(63030.831794208025/8.0), + REAL_CONST(63051.959152162039/8.0), + REAL_CONST(63073.08828009537/8.0), + REAL_CONST(63094.219177711529/8.0), + REAL_CONST(63115.351844714154/8.0), + REAL_CONST(63136.486280806988/8.0), + REAL_CONST(63157.622485693922/8.0), + REAL_CONST(63178.760459078956/8.0), + REAL_CONST(63199.900200666219/8.0), + REAL_CONST(63221.041710159967/8.0), + REAL_CONST(63242.184987264569/8.0), + REAL_CONST(63263.330031684534/8.0), + REAL_CONST(63284.476843124474/8.0), + REAL_CONST(63305.625421289144/8.0), + REAL_CONST(63326.775765883409/8.0), + REAL_CONST(63347.927876612259/8.0), + REAL_CONST(63369.081753180813/8.0), + REAL_CONST(63390.237395294316/8.0), + REAL_CONST(63411.39480265812/8.0), + REAL_CONST(63432.553974977716/8.0), + REAL_CONST(63453.714911958712/8.0), + REAL_CONST(63474.877613306839/8.0), + REAL_CONST(63496.042078727944/8.0), + REAL_CONST(63517.208307927998/8.0), + REAL_CONST(63538.376300613119/8.0), + REAL_CONST(63559.546056489504/8.0), + REAL_CONST(63580.717575263516/8.0), + REAL_CONST(63601.890856641607/8.0), + REAL_CONST(63623.065900330374/8.0), + REAL_CONST(63644.242706036515/8.0), + REAL_CONST(63665.421273466869/8.0), + REAL_CONST(63686.601602328381/8.0), + REAL_CONST(63707.783692328136/8.0), + REAL_CONST(63728.967543173334/8.0), + REAL_CONST(63750.153154571279/8.0), + REAL_CONST(63771.340526229418/8.0), + REAL_CONST(63792.529657855317/8.0), + REAL_CONST(63813.720549156649/8.0), + REAL_CONST(63834.913199841227/8.0), + REAL_CONST(63856.107609616978/8.0), + REAL_CONST(63877.303778191941/8.0), + REAL_CONST(63898.501705274284/8.0), + REAL_CONST(63919.7013905723/8.0), + REAL_CONST(63940.902833794404/8.0), + REAL_CONST(63962.106034649114/8.0), + REAL_CONST(63983.310992845094/8.0), + REAL_CONST(64004.517708091109/8.0), + REAL_CONST(64025.726180096048/8.0), + REAL_CONST(64046.936408568938/8.0), + REAL_CONST(64068.1483932189/8.0), + REAL_CONST(64089.362133755196/8.0), + REAL_CONST(64110.577629887193/8.0), + REAL_CONST(64131.794881324393/8.0), + REAL_CONST(64153.013887776404/8.0), + REAL_CONST(64174.234648952966/8.0), + REAL_CONST(64195.457164563937/8.0), + REAL_CONST(64216.681434319289/8.0), + REAL_CONST(64237.907457929112/8.0), + REAL_CONST(64259.135235103626/8.0), + REAL_CONST(64280.36476555316/8.0), + REAL_CONST(64301.596048988169/8.0), + REAL_CONST(64322.829085119236/8.0), + REAL_CONST(64344.06387365704/8.0), + REAL_CONST(64365.300414312398/8.0), + REAL_CONST(64386.538706796251/8.0), + REAL_CONST(64407.778750819634/8.0), + REAL_CONST(64429.020546093721/8.0), + REAL_CONST(64450.26409232981/8.0), + REAL_CONST(64471.509389239291/8.0), + REAL_CONST(64492.756436533709/8.0), + REAL_CONST(64514.005233924705/8.0), + REAL_CONST(64535.255781124033/8.0), + REAL_CONST(64556.50807784358/8.0), + REAL_CONST(64577.762123795357/8.0), + REAL_CONST(64599.017918691468/8.0), + REAL_CONST(64620.275462244172/8.0), + REAL_CONST(64641.534754165805/8.0), + REAL_CONST(64662.795794168844/8.0), + REAL_CONST(64684.058581965895/8.0), + REAL_CONST(64705.323117269661/8.0), + REAL_CONST(64726.589399792974/8.0), + REAL_CONST(64747.857429248776/8.0), + REAL_CONST(64769.127205350138/8.0), + REAL_CONST(64790.398727810236/8.0), + REAL_CONST(64811.671996342375/8.0), + REAL_CONST(64832.947010659969/8.0), + REAL_CONST(64854.223770476558/8.0), + REAL_CONST(64875.502275505794/8.0), + REAL_CONST(64896.782525461451/8.0), + REAL_CONST(64918.064520057414/8.0), + REAL_CONST(64939.348259007682/8.0), + REAL_CONST(64960.633742026388/8.0), + REAL_CONST(64981.920968827762/8.0), + REAL_CONST(65003.209939126165/8.0), + REAL_CONST(65024.500652636067/8.0), + REAL_CONST(65045.793109072067/8.0), + REAL_CONST(65067.087308148861/8.0), + REAL_CONST(65088.383249581282/8.0), + REAL_CONST(65109.680933084259/8.0), + REAL_CONST(65130.980358372864/8.0), + REAL_CONST(65152.28152516226/8.0), + REAL_CONST(65173.584433167736/8.0), + REAL_CONST(65194.889082104703/8.0), + REAL_CONST(65216.195471688683/8.0), + REAL_CONST(65237.503601635319/8.0), + REAL_CONST(65258.813471660353/8.0), + REAL_CONST(65280.125081479666/8.0), + REAL_CONST(65301.438430809241/8.0), + REAL_CONST(65322.753519365178/8.0), + REAL_CONST(65344.070346863708/8.0), + REAL_CONST(65365.388913021146/8.0), + REAL_CONST(65386.709217553958/8.0), + REAL_CONST(65408.031260178701/8.0), + REAL_CONST(65429.355040612056/8.0), + REAL_CONST(65450.680558570821/8.0), + REAL_CONST(65472.00781377191/8.0), + REAL_CONST(65493.336805932355/8.0), + REAL_CONST(65514.66753476928/8.0), + REAL_CONST(65535.999999999956/8.0), + REAL_CONST(65557.334201341757/8.0), + REAL_CONST(65578.670138512171/8.0), + REAL_CONST(65600.007811228788/8.0), + REAL_CONST(65621.347219209332/8.0), + REAL_CONST(65642.688362171626/8.0), + REAL_CONST(65664.031239833639/8.0), + REAL_CONST(65685.375851913413/8.0), + REAL_CONST(65706.722198129137/8.0), + REAL_CONST(65728.070278199084/8.0), + REAL_CONST(65749.420091841661/8.0), + REAL_CONST(65770.771638775404/8.0), + REAL_CONST(65792.124918718939/8.0), + REAL_CONST(65813.479931391004/8.0), + REAL_CONST(65834.836676510458/8.0), + REAL_CONST(65856.195153796303/8.0), + REAL_CONST(65877.5553629676/8.0), + REAL_CONST(65898.917303743554/8.0), + REAL_CONST(65920.280975843489/8.0), + REAL_CONST(65941.646378986843/8.0), + REAL_CONST(65963.013512893158/8.0), + REAL_CONST(65984.382377282076/8.0), + REAL_CONST(66005.752971873386/8.0), + REAL_CONST(66027.125296386963/8.0), + REAL_CONST(66048.499350542799/8.0), + REAL_CONST(66069.875134061018/8.0), + REAL_CONST(66091.252646661844/8.0), + REAL_CONST(66112.631888065618/8.0), + REAL_CONST(66134.01285799277/8.0), + REAL_CONST(66155.395556163887/8.0), + REAL_CONST(66176.779982299631/8.0), + REAL_CONST(66198.166136120795/8.0), + REAL_CONST(66219.554017348273/8.0), + REAL_CONST(66240.943625703105/8.0), + REAL_CONST(66262.334960906388/8.0), + REAL_CONST(66283.728022679396/8.0), + REAL_CONST(66305.122810743444/8.0), + REAL_CONST(66326.519324820023/8.0), + REAL_CONST(66347.917564630698/8.0), + REAL_CONST(66369.317529897162/8.0), + REAL_CONST(66390.719220341227/8.0), + REAL_CONST(66412.122635684791/8.0), + REAL_CONST(66433.527775649884/8.0), + REAL_CONST(66454.934639958636/8.0), + REAL_CONST(66476.343228333324/8.0), + REAL_CONST(66497.753540496284/8.0), + REAL_CONST(66519.165576169995/8.0), + REAL_CONST(66540.57933507704/8.0), + REAL_CONST(66561.994816940118/8.0), + REAL_CONST(66583.412021482043/8.0), + REAL_CONST(66604.830948425733/8.0), + REAL_CONST(66626.251597494222/8.0), + REAL_CONST(66647.673968410629/8.0), + REAL_CONST(66669.098060898235/8.0), + REAL_CONST(66690.523874680381/8.0), + REAL_CONST(66711.951409480564/8.0), + REAL_CONST(66733.380665022371/8.0), + REAL_CONST(66754.811641029475/8.0), + REAL_CONST(66776.244337225711/8.0), + REAL_CONST(66797.678753334985/8.0), + REAL_CONST(66819.11488908132/8.0), + REAL_CONST(66840.552744188884/8.0), + REAL_CONST(66861.992318381905/8.0), + REAL_CONST(66883.433611384738/8.0), + REAL_CONST(66904.876622921889/8.0), + REAL_CONST(66926.321352717903/8.0), + REAL_CONST(66947.767800497502/8.0), + REAL_CONST(66969.215965985466/8.0), + REAL_CONST(66990.665848906734/8.0), + REAL_CONST(67012.117448986304/8.0), + REAL_CONST(67033.570765949335/8.0), + REAL_CONST(67055.025799521056/8.0), + REAL_CONST(67076.482549426815/8.0), + REAL_CONST(67097.941015392076/8.0), + REAL_CONST(67119.401197142433/8.0), + REAL_CONST(67140.863094403554/8.0), + REAL_CONST(67162.326706901222/8.0), + REAL_CONST(67183.792034361351/8.0), + REAL_CONST(67205.259076509959/8.0), + REAL_CONST(67226.72783307315/8.0), + REAL_CONST(67248.198303777172/8.0), + REAL_CONST(67269.670488348347/8.0), + REAL_CONST(67291.144386513144/8.0), + REAL_CONST(67312.619997998088/8.0), + REAL_CONST(67334.09732252988/8.0), + REAL_CONST(67355.576359835293/8.0), + REAL_CONST(67377.057109641188/8.0), + REAL_CONST(67398.53957167457/8.0), + REAL_CONST(67420.023745662547/8.0), + REAL_CONST(67441.50963133233/8.0), + REAL_CONST(67462.99722841123/8.0), + REAL_CONST(67484.486536626689/8.0), + REAL_CONST(67505.977555706224/8.0), + REAL_CONST(67527.470285377494/8.0), + REAL_CONST(67548.964725368263/8.0), + REAL_CONST(67570.460875406367/8.0), + REAL_CONST(67591.9587352198/8.0), + REAL_CONST(67613.458304536631/8.0), + REAL_CONST(67634.95958308503/8.0), + REAL_CONST(67656.462570593329/8.0), + REAL_CONST(67677.967266789899/8.0), + REAL_CONST(67699.473671403248/8.0), + REAL_CONST(67720.981784162024/8.0), + REAL_CONST(67742.491604794923/8.0), + REAL_CONST(67764.003133030797/8.0), + REAL_CONST(67785.516368598575/8.0), + REAL_CONST(67807.031311227314/8.0), + REAL_CONST(67828.547960646174/8.0), + REAL_CONST(67850.066316584402/8.0), + REAL_CONST(67871.58637877139/8.0), + REAL_CONST(67893.108146936589/8.0), + REAL_CONST(67914.63162080961/8.0), + REAL_CONST(67936.156800120138/8.0), + REAL_CONST(67957.683684597971/8.0), + REAL_CONST(67979.212273973011/8.0), + REAL_CONST(68000.742567975263/8.0), + REAL_CONST(68022.274566334876/8.0), + REAL_CONST(68043.808268782057/8.0), + REAL_CONST(68065.343675047145/8.0), + REAL_CONST(68086.880784860579/8.0), + REAL_CONST(68108.419597952918/8.0), + REAL_CONST(68129.960114054789/8.0), + REAL_CONST(68151.502332896969/8.0), + REAL_CONST(68173.04625421032/8.0), + REAL_CONST(68194.591877725834/8.0), + REAL_CONST(68216.139203174564/8.0), + REAL_CONST(68237.688230287706/8.0), + REAL_CONST(68259.238958796544/8.0), + REAL_CONST(68280.791388432481/8.0), + REAL_CONST(68302.345518927032/8.0), + REAL_CONST(68323.901350011787/8.0), + REAL_CONST(68345.458881418483/8.0), + REAL_CONST(68367.018112878912/8.0), + REAL_CONST(68388.579044125028/8.0), + REAL_CONST(68410.141674888844/8.0), + REAL_CONST(68431.706004902502/8.0), + REAL_CONST(68453.272033898262/8.0), + REAL_CONST(68474.839761608455/8.0), + REAL_CONST(68496.409187765545/8.0), + REAL_CONST(68517.980312102081/8.0), + REAL_CONST(68539.553134350732/8.0), + REAL_CONST(68561.127654244279/8.0), + REAL_CONST(68582.70387151558/8.0), + REAL_CONST(68604.281785897634/8.0), + REAL_CONST(68625.861397123503/8.0), + REAL_CONST(68647.44270492639/8.0), + REAL_CONST(68669.025709039604/8.0), + REAL_CONST(68690.610409196524/8.0), + REAL_CONST(68712.196805130661/8.0), + REAL_CONST(68733.784896575627/8.0), + REAL_CONST(68755.374683265123/8.0), + REAL_CONST(68776.966164932994/8.0), + REAL_CONST(68798.559341313128/8.0), + REAL_CONST(68820.154212139591/8.0), + REAL_CONST(68841.750777146473/8.0), + REAL_CONST(68863.349036068044/8.0), + REAL_CONST(68884.948988638629/8.0), + REAL_CONST(68906.550634592684/8.0), + REAL_CONST(68928.153973664739/8.0), + REAL_CONST(68949.75900558944/8.0), + REAL_CONST(68971.365730101577/8.0), + REAL_CONST(68992.974146935987/8.0), + REAL_CONST(69014.584255827634/8.0), + REAL_CONST(69036.196056511588/8.0), + REAL_CONST(69057.809548723017/8.0), + REAL_CONST(69079.424732197207/8.0), + REAL_CONST(69101.041606669532/8.0), + REAL_CONST(69122.660171875468/8.0), + REAL_CONST(69144.280427550606/8.0), + REAL_CONST(69165.902373430625/8.0), + REAL_CONST(69187.526009251334/8.0), + REAL_CONST(69209.151334748618/8.0), + REAL_CONST(69230.778349658474/8.0), + REAL_CONST(69252.40705371699/8.0), + REAL_CONST(69274.037446660412/8.0), + REAL_CONST(69295.669528225/8.0), + REAL_CONST(69317.303298147192/8.0), + REAL_CONST(69338.938756163494/8.0), + REAL_CONST(69360.575902010532/8.0), + REAL_CONST(69382.214735425005/8.0), + REAL_CONST(69403.855256143754/8.0), + REAL_CONST(69425.497463903681/8.0), + REAL_CONST(69447.141358441833/8.0), + REAL_CONST(69468.78693949533/8.0), + REAL_CONST(69490.434206801394/8.0), + REAL_CONST(69512.083160097391/8.0), + REAL_CONST(69533.733799120717/8.0), + REAL_CONST(69555.386123608929/8.0), + REAL_CONST(69577.04013329967/8.0), + REAL_CONST(69598.695827930685/8.0), + REAL_CONST(69620.353207239794/8.0), + REAL_CONST(69642.012270964973/8.0), + REAL_CONST(69663.67301884426/8.0), + REAL_CONST(69685.335450615792/8.0), + REAL_CONST(69706.999566017839/8.0), + REAL_CONST(69728.665364788743/8.0), + REAL_CONST(69750.332846666963/8.0), + REAL_CONST(69772.002011391058/8.0), + REAL_CONST(69793.672858699691/8.0), + REAL_CONST(69815.345388331611/8.0), + REAL_CONST(69837.019600025669/8.0), + REAL_CONST(69858.695493520849/8.0), + REAL_CONST(69880.373068556204/8.0), + REAL_CONST(69902.052324870907/8.0), + REAL_CONST(69923.733262204216/8.0), + REAL_CONST(69945.415880295492/8.0), + REAL_CONST(69967.100178884211/8.0), + REAL_CONST(69988.786157709939/8.0), + REAL_CONST(70010.473816512356/8.0), + REAL_CONST(70032.163155031216/8.0), + REAL_CONST(70053.854173006403/8.0), + REAL_CONST(70075.546870177874/8.0), + REAL_CONST(70097.241246285717/8.0), + REAL_CONST(70118.937301070109/8.0), + REAL_CONST(70140.635034271298/8.0), + REAL_CONST(70162.334445629691/8.0), + REAL_CONST(70184.035534885741/8.0), + REAL_CONST(70205.738301780017/8.0), + REAL_CONST(70227.442746053217/8.0), + REAL_CONST(70249.1488674461/8.0), + REAL_CONST(70270.856665699539/8.0), + REAL_CONST(70292.566140554511/8.0), + REAL_CONST(70314.277291752107/8.0), + REAL_CONST(70335.990119033493/8.0), + REAL_CONST(70357.704622139936/8.0), + REAL_CONST(70379.420800812819/8.0), + REAL_CONST(70401.138654793613/8.0), + REAL_CONST(70422.85818382389/8.0), + REAL_CONST(70444.579387645339/8.0), + REAL_CONST(70466.302265999722/8.0), + REAL_CONST(70488.026818628918/8.0), + REAL_CONST(70509.753045274876/8.0), + REAL_CONST(70531.480945679708/8.0), + REAL_CONST(70553.210519585555/8.0), + REAL_CONST(70574.941766734701/8.0), + REAL_CONST(70596.674686869505/8.0), + REAL_CONST(70618.409279732456/8.0), + REAL_CONST(70640.145545066101/8.0), + REAL_CONST(70661.883482613106/8.0), + REAL_CONST(70683.623092116264/8.0), + REAL_CONST(70705.364373318414/8.0), + REAL_CONST(70727.107325962526/8.0), + REAL_CONST(70748.851949791671/8.0), + REAL_CONST(70770.598244549008/8.0), + REAL_CONST(70792.346209977783/8.0), + REAL_CONST(70814.095845821372/8.0), + REAL_CONST(70835.847151823225/8.0), + REAL_CONST(70857.600127726895/8.0), + REAL_CONST(70879.354773276034/8.0), + REAL_CONST(70901.111088214413/8.0), + REAL_CONST(70922.869072285859/8.0), + REAL_CONST(70944.628725234332/8.0), + REAL_CONST(70966.390046803877/8.0), + REAL_CONST(70988.153036738629/8.0), + REAL_CONST(71009.917694782853/8.0), + REAL_CONST(71031.684020680885/8.0), + REAL_CONST(71053.45201417715/8.0), + REAL_CONST(71075.221675016204/8.0), + REAL_CONST(71096.993002942661/8.0), + REAL_CONST(71118.765997701266/8.0), + REAL_CONST(71140.540659036851/8.0), + REAL_CONST(71162.316986694335/8.0), + REAL_CONST(71184.09498041874/8.0), + REAL_CONST(71205.874639955218/8.0), + REAL_CONST(71227.655965048951/8.0), + REAL_CONST(71249.438955445294/8.0), + REAL_CONST(71271.223610889632/8.0), + REAL_CONST(71293.009931127483/8.0), + REAL_CONST(71314.797915904477/8.0), + REAL_CONST(71336.587564966307/8.0), + REAL_CONST(71358.378878058764/8.0), + REAL_CONST(71380.171854927772/8.0), + REAL_CONST(71401.966495319313/8.0), + REAL_CONST(71423.762798979486/8.0), + REAL_CONST(71445.560765654489/8.0), + REAL_CONST(71467.360395090596/8.0), + REAL_CONST(71489.161687034211/8.0), + REAL_CONST(71510.964641231811/8.0), + REAL_CONST(71532.769257429973/8.0), + REAL_CONST(71554.575535375348/8.0), + REAL_CONST(71576.383474814749/8.0), + REAL_CONST(71598.19307549503/8.0), + REAL_CONST(71620.004337163133/8.0), + REAL_CONST(71641.817259566145/8.0), + REAL_CONST(71663.631842451214/8.0), + REAL_CONST(71685.4480855656/8.0), + REAL_CONST(71707.26598865664/8.0), + REAL_CONST(71729.085551471784/8.0), + REAL_CONST(71750.906773758586/8.0), + REAL_CONST(71772.729655264673/8.0), + REAL_CONST(71794.554195737772/8.0), + REAL_CONST(71816.380394925713/8.0), + REAL_CONST(71838.208252576442/8.0), + REAL_CONST(71860.037768437964/8.0), + REAL_CONST(71881.868942258385/8.0), + REAL_CONST(71903.701773785942/8.0), + REAL_CONST(71925.536262768932/8.0), + REAL_CONST(71947.372408955751/8.0), + REAL_CONST(71969.210212094898/8.0), + REAL_CONST(71991.049671934976/8.0), + REAL_CONST(72012.890788224686/8.0), + REAL_CONST(72034.73356071279/8.0), + REAL_CONST(72056.577989148165/8.0), + REAL_CONST(72078.424073279821/8.0), + REAL_CONST(72100.271812856794/8.0), + REAL_CONST(72122.121207628254/8.0), + REAL_CONST(72143.97225734347/8.0), + REAL_CONST(72165.824961751801/8.0), + REAL_CONST(72187.679320602692/8.0), + REAL_CONST(72209.53533364569/8.0), + REAL_CONST(72231.393000630429/8.0), + REAL_CONST(72253.252321306645/8.0), + REAL_CONST(72275.113295424177/8.0), + REAL_CONST(72296.975922732949/8.0), + REAL_CONST(72318.840202982959/8.0), + REAL_CONST(72340.706135924338/8.0), + REAL_CONST(72362.573721307272/8.0), + REAL_CONST(72384.442958882093/8.0), + REAL_CONST(72406.313848399179/8.0), + REAL_CONST(72428.186389609036/8.0), + REAL_CONST(72450.060582262216/8.0), + REAL_CONST(72471.936426109431/8.0), + REAL_CONST(72493.813920901433/8.0), + REAL_CONST(72515.693066389096/8.0), + REAL_CONST(72537.573862323392/8.0), + REAL_CONST(72559.456308455352/8.0), + REAL_CONST(72581.340404536139/8.0), + REAL_CONST(72603.226150316987/8.0), + REAL_CONST(72625.113545549248/8.0), + REAL_CONST(72647.002589984331/8.0), + REAL_CONST(72668.893283373764/8.0), + REAL_CONST(72690.785625469172/8.0), + REAL_CONST(72712.679616022273/8.0), + REAL_CONST(72734.575254784853/8.0), + REAL_CONST(72756.472541508803/8.0), + REAL_CONST(72778.371475946144/8.0), + REAL_CONST(72800.272057848939/8.0), + REAL_CONST(72822.174286969355/8.0), + REAL_CONST(72844.07816305969/8.0), + REAL_CONST(72865.983685872285/8.0), + REAL_CONST(72887.890855159596/8.0), + REAL_CONST(72909.799670674183/8.0), + REAL_CONST(72931.710132168693/8.0), + REAL_CONST(72953.622239395845/8.0), + REAL_CONST(72975.535992108475/8.0), + REAL_CONST(72997.451390059519/8.0), + REAL_CONST(73019.368433001961/8.0), + REAL_CONST(73041.287120688925/8.0), + REAL_CONST(73063.207452873612/8.0), + REAL_CONST(73085.129429309294/8.0), + REAL_CONST(73107.053049749389/8.0), + REAL_CONST(73128.978313947344/8.0), + REAL_CONST(73150.905221656736/8.0), + REAL_CONST(73172.833772631217/8.0), + REAL_CONST(73194.763966624567/8.0), + REAL_CONST(73216.695803390612/8.0), + REAL_CONST(73238.62928268328/8.0), + REAL_CONST(73260.564404256627/8.0), + REAL_CONST(73282.501167864757/8.0), + REAL_CONST(73304.439573261901/8.0), + REAL_CONST(73326.379620202337/8.0), + REAL_CONST(73348.321308440485/8.0), + REAL_CONST(73370.264637730841/8.0), + REAL_CONST(73392.209607827957/8.0), + REAL_CONST(73414.156218486532/8.0), + REAL_CONST(73436.104469461323/8.0), + REAL_CONST(73458.054360507173/8.0), + REAL_CONST(73480.005891379056/8.0), + REAL_CONST(73501.959061831993/8.0), + REAL_CONST(73523.913871621116/8.0), + REAL_CONST(73545.870320501665/8.0), + REAL_CONST(73567.828408228932/8.0), + REAL_CONST(73589.78813455833/8.0), + REAL_CONST(73611.749499245358/8.0), + REAL_CONST(73633.712502045615/8.0), + REAL_CONST(73655.677142714747/8.0), + REAL_CONST(73677.643421008557/8.0), + REAL_CONST(73699.611336682879/8.0), + REAL_CONST(73721.580889493693/8.0), + REAL_CONST(73743.552079197019/8.0), + REAL_CONST(73765.524905548999/8.0), + REAL_CONST(73787.499368305856/8.0), + REAL_CONST(73809.475467223907/8.0), + REAL_CONST(73831.453202059551/8.0), + REAL_CONST(73853.432572569291/8.0), + REAL_CONST(73875.413578509717/8.0), + REAL_CONST(73897.396219637507/8.0), + REAL_CONST(73919.380495709411/8.0), + REAL_CONST(73941.36640648231/8.0), + REAL_CONST(73963.353951713143/8.0), + REAL_CONST(73985.343131158952/8.0), + REAL_CONST(74007.333944576865/8.0), + REAL_CONST(74029.326391724098/8.0), + REAL_CONST(74051.320472357969/8.0), + REAL_CONST(74073.316186235883/8.0), + REAL_CONST(74095.313533115303/8.0), + REAL_CONST(74117.312512753837/8.0), + REAL_CONST(74139.313124909138/8.0), + REAL_CONST(74161.315369338976/8.0), + REAL_CONST(74183.319245801191/8.0), + REAL_CONST(74205.324754053727/8.0), + REAL_CONST(74227.331893854629/8.0), + REAL_CONST(74249.340664961986/8.0), + REAL_CONST(74271.351067134034/8.0), + REAL_CONST(74293.363100129049/8.0), + REAL_CONST(74315.376763705441/8.0), + REAL_CONST(74337.392057621662/8.0), + REAL_CONST(74359.408981636298/8.0), + REAL_CONST(74381.427535508003/8.0), + REAL_CONST(74403.447718995507/8.0), + REAL_CONST(74425.469531857671/8.0), + REAL_CONST(74447.492973853383/8.0), + REAL_CONST(74469.518044741693/8.0), + REAL_CONST(74491.54474428168/8.0), + REAL_CONST(74513.573072232539/8.0), + REAL_CONST(74535.603028353551/8.0), + REAL_CONST(74557.634612404087/8.0), + REAL_CONST(74579.667824143602/8.0), + REAL_CONST(74601.702663331642/8.0), + REAL_CONST(74623.739129727837/8.0), + REAL_CONST(74645.777223091936/8.0), + REAL_CONST(74667.816943183716/8.0), + REAL_CONST(74689.858289763113/8.0), + REAL_CONST(74711.901262590094/8.0), + REAL_CONST(74733.945861424741/8.0), + REAL_CONST(74755.992086027225/8.0), + REAL_CONST(74778.039936157802/8.0), + REAL_CONST(74800.089411576817/8.0), + REAL_CONST(74822.140512044702/8.0), + REAL_CONST(74844.193237321961/8.0), + REAL_CONST(74866.24758716923/8.0), + REAL_CONST(74888.303561347187/8.0), + REAL_CONST(74910.36115961663/8.0), + REAL_CONST(74932.420381738411/8.0), + REAL_CONST(74954.481227473516/8.0), + REAL_CONST(74976.543696582972/8.0), + REAL_CONST(74998.607788827925/8.0), + REAL_CONST(75020.673503969607/8.0), + REAL_CONST(75042.740841769322/8.0), + REAL_CONST(75064.809801988464/8.0), + REAL_CONST(75086.88038438854/8.0), + REAL_CONST(75108.952588731103/8.0), + REAL_CONST(75131.026414777836/8.0), + REAL_CONST(75153.101862290467/8.0), + REAL_CONST(75175.178931030852/8.0), + REAL_CONST(75197.257620760924/8.0), + REAL_CONST(75219.33793124267/8.0), + REAL_CONST(75241.419862238225/8.0), + REAL_CONST(75263.503413509738/8.0), + REAL_CONST(75285.588584819503/8.0), + REAL_CONST(75307.675375929874/8.0), + REAL_CONST(75329.763786603318/8.0), + REAL_CONST(75351.853816602365/8.0), + REAL_CONST(75373.945465689612/8.0), + REAL_CONST(75396.038733627807/8.0), + REAL_CONST(75418.133620179724/8.0), + REAL_CONST(75440.230125108254/8.0), + REAL_CONST(75462.32824817636/8.0), + REAL_CONST(75484.427989147109/8.0), + REAL_CONST(75506.529347783653/8.0), + REAL_CONST(75528.63232384919/8.0), + REAL_CONST(75550.736917107075/8.0), + REAL_CONST(75572.843127320695/8.0), + REAL_CONST(75594.950954253538/8.0), + REAL_CONST(75617.060397669193/8.0), + REAL_CONST(75639.171457331307/8.0), + REAL_CONST(75661.284133003646/8.0), + REAL_CONST(75683.398424450032/8.0), + REAL_CONST(75705.514331434402/8.0), + REAL_CONST(75727.631853720741/8.0), + REAL_CONST(75749.750991073175/8.0), + REAL_CONST(75771.871743255862/8.0), + REAL_CONST(75793.994110033076/8.0), + REAL_CONST(75816.118091169177/8.0), + REAL_CONST(75838.243686428585/8.0), + REAL_CONST(75860.370895575848/8.0), + REAL_CONST(75882.499718375562/8.0), + REAL_CONST(75904.630154592422/8.0), + REAL_CONST(75926.762203991224/8.0), + REAL_CONST(75948.895866336825/8.0), + REAL_CONST(75971.031141394182/8.0), + REAL_CONST(75993.168028928325/8.0), + REAL_CONST(76015.306528704401/8.0), + REAL_CONST(76037.4466404876/8.0), + REAL_CONST(76059.588364043215/8.0), + REAL_CONST(76081.731699136653/8.0), + REAL_CONST(76103.876645533353/8.0), + REAL_CONST(76126.023202998884/8.0), + REAL_CONST(76148.171371298871/8.0), + REAL_CONST(76170.321150199044/8.0), + REAL_CONST(76192.472539465205/8.0), + REAL_CONST(76214.625538863256/8.0), + REAL_CONST(76236.780148159174/8.0), + REAL_CONST(76258.936367119008/8.0), + REAL_CONST(76281.094195508922/8.0), + REAL_CONST(76303.253633095141/8.0), + REAL_CONST(76325.414679643975/8.0), + REAL_CONST(76347.577334921851/8.0), + REAL_CONST(76369.741598695226/8.0), + REAL_CONST(76391.907470730686/8.0), + REAL_CONST(76414.074950794879/8.0), + REAL_CONST(76436.244038654564/8.0), + REAL_CONST(76458.414734076548/8.0), + REAL_CONST(76480.587036827754/8.0), + REAL_CONST(76502.760946675175/8.0), + REAL_CONST(76524.936463385893/8.0), + REAL_CONST(76547.11358672705/8.0), + REAL_CONST(76569.292316465915/8.0), + REAL_CONST(76591.472652369819/8.0), + REAL_CONST(76613.654594206164/8.0), + REAL_CONST(76635.838141742468/8.0), + REAL_CONST(76658.023294746308/8.0), + REAL_CONST(76680.210052985349/8.0), + REAL_CONST(76702.398416227341/8.0), + REAL_CONST(76724.588384240138/8.0), + REAL_CONST(76746.779956791637/8.0), + REAL_CONST(76768.973133649866/8.0), + REAL_CONST(76791.167914582897/8.0), + REAL_CONST(76813.364299358902/8.0), + REAL_CONST(76835.562287746157/8.0), + REAL_CONST(76857.761879512967/8.0), + REAL_CONST(76879.963074427797/8.0), + REAL_CONST(76902.165872259109/8.0), + REAL_CONST(76924.37027277553/8.0), + REAL_CONST(76946.576275745727/8.0), + REAL_CONST(76968.783880938441/8.0), + REAL_CONST(76990.993088122515/8.0), + REAL_CONST(77013.203897066895/8.0), + REAL_CONST(77035.416307540567/8.0), + REAL_CONST(77057.630319312622/8.0), + REAL_CONST(77079.845932152239/8.0), + REAL_CONST(77102.063145828695/8.0), + REAL_CONST(77124.281960111301/8.0), + REAL_CONST(77146.50237476948/8.0), + REAL_CONST(77168.724389572759/8.0), + REAL_CONST(77190.948004290723/8.0), + REAL_CONST(77213.173218693031/8.0), + REAL_CONST(77235.400032549442/8.0), + REAL_CONST(77257.628445629802/8.0), + REAL_CONST(77279.858457704031/8.0), + REAL_CONST(77302.090068542122/8.0), + REAL_CONST(77324.323277914169/8.0), + REAL_CONST(77346.558085590339/8.0), + REAL_CONST(77368.794491340886/8.0), + REAL_CONST(77391.032494936138/8.0), + REAL_CONST(77413.272096146524/8.0), + REAL_CONST(77435.51329474253/8.0), + REAL_CONST(77457.756090494731/8.0), + REAL_CONST(77480.000483173804/8.0), + REAL_CONST(77502.246472550498/8.0), + REAL_CONST(77524.494058395634/8.0), + REAL_CONST(77546.743240480107/8.0), + REAL_CONST(77568.994018574944/8.0), + REAL_CONST(77591.246392451198/8.0), + REAL_CONST(77613.500361880026/8.0), + REAL_CONST(77635.755926632657/8.0), + REAL_CONST(77658.013086480438/8.0), + REAL_CONST(77680.271841194757/8.0), + REAL_CONST(77702.532190547092/8.0), + REAL_CONST(77724.794134309021/8.0), + REAL_CONST(77747.057672252195/8.0), + REAL_CONST(77769.322804148323/8.0), + REAL_CONST(77791.589529769248/8.0), + REAL_CONST(77813.857848886837/8.0), + REAL_CONST(77836.127761273063/8.0), + REAL_CONST(77858.399266699998/8.0), + REAL_CONST(77880.67236493979/8.0), + REAL_CONST(77902.947055764627/8.0), + REAL_CONST(77925.223338946831/8.0), + REAL_CONST(77947.50121425878/8.0), + REAL_CONST(77969.780681472927/8.0), + REAL_CONST(77992.061740361838/8.0), + REAL_CONST(78014.344390698127/8.0), + REAL_CONST(78036.628632254491/8.0), + REAL_CONST(78058.914464803747/8.0), + REAL_CONST(78081.201888118725/8.0), + REAL_CONST(78103.490901972415/8.0), + REAL_CONST(78125.781506137821/8.0), + REAL_CONST(78148.073700388064/8.0), + REAL_CONST(78170.367484496339/8.0), + REAL_CONST(78192.662858235926/8.0), + REAL_CONST(78214.959821380166/8.0), + REAL_CONST(78237.258373702498/8.0), + REAL_CONST(78259.558514976452/8.0), + REAL_CONST(78281.860244975614/8.0), + REAL_CONST(78304.163563473659/8.0), + REAL_CONST(78326.468470244363/8.0), + REAL_CONST(78348.77496506153/8.0), + REAL_CONST(78371.083047699125/8.0), + REAL_CONST(78393.392717931114/8.0), + REAL_CONST(78415.703975531578/8.0), + REAL_CONST(78438.016820274701/8.0), + REAL_CONST(78460.331251934695/8.0), + REAL_CONST(78482.647270285903/8.0), + REAL_CONST(78504.964875102727/8.0), + REAL_CONST(78527.284066159627/8.0), + REAL_CONST(78549.604843231195/8.0), + REAL_CONST(78571.927206092048/8.0), + REAL_CONST(78594.251154516911/8.0), + REAL_CONST(78616.576688280606/8.0), + REAL_CONST(78638.903807157985/8.0), + REAL_CONST(78661.232510924034/8.0), + REAL_CONST(78683.562799353778/8.0), + REAL_CONST(78705.894672222363/8.0), + REAL_CONST(78728.228129304945/8.0), + REAL_CONST(78750.563170376859/8.0), + REAL_CONST(78772.899795213423/8.0), + REAL_CONST(78795.238003590101/8.0), + REAL_CONST(78817.577795282399/8.0), + REAL_CONST(78839.919170065928/8.0), + REAL_CONST(78862.262127716356/8.0), + REAL_CONST(78884.606668009452/8.0), + REAL_CONST(78906.952790721043/8.0), + REAL_CONST(78929.300495627045/8.0), + REAL_CONST(78951.64978250346/8.0), + REAL_CONST(78974.000651126378/8.0), + REAL_CONST(78996.353101271932/8.0), + REAL_CONST(79018.707132716358/8.0), + REAL_CONST(79041.062745235977/8.0), + REAL_CONST(79063.41993860717/8.0), + REAL_CONST(79085.778712606436/8.0), + REAL_CONST(79108.139067010285/8.0), + REAL_CONST(79130.501001595389/8.0), + REAL_CONST(79152.864516138419/8.0), + REAL_CONST(79175.22961041618/8.0), + REAL_CONST(79197.596284205531/8.0), + REAL_CONST(79219.96453728342/8.0), + REAL_CONST(79242.33436942687/8.0), + REAL_CONST(79264.705780412987/8.0), + REAL_CONST(79287.078770018954/8.0), + REAL_CONST(79309.453338022009/8.0), + REAL_CONST(79331.829484199508/8.0), + REAL_CONST(79354.207208328866/8.0), + REAL_CONST(79376.586510187582/8.0), + REAL_CONST(79398.967389553218/8.0), + REAL_CONST(79421.349846203433/8.0), + REAL_CONST(79443.733879915948/8.0), + REAL_CONST(79466.119490468584/8.0), + REAL_CONST(79488.50667763922/8.0), + REAL_CONST(79510.895441205823/8.0), + REAL_CONST(79533.285780946433/8.0), + REAL_CONST(79555.677696639163/8.0), + REAL_CONST(79578.071188062226/8.0), + REAL_CONST(79600.466254993895/8.0), + REAL_CONST(79622.862897212515/8.0), + REAL_CONST(79645.261114496549/8.0), + REAL_CONST(79667.660906624471/8.0), + REAL_CONST(79690.062273374875/8.0), + REAL_CONST(79712.465214526455/8.0), + REAL_CONST(79734.869729857935/8.0), + REAL_CONST(79757.275819148126/8.0), + REAL_CONST(79779.683482175955/8.0), + REAL_CONST(79802.092718720378/8.0), + REAL_CONST(79824.503528560454/8.0), + REAL_CONST(79846.915911475327/8.0), + REAL_CONST(79869.329867244203/8.0), + REAL_CONST(79891.745395646343/8.0), + REAL_CONST(79914.162496461155/8.0), + REAL_CONST(79936.581169468045/8.0), + REAL_CONST(79959.001414446553/8.0), + REAL_CONST(79981.423231176261/8.0), + REAL_CONST(80003.846619436852/8.0), + REAL_CONST(80026.271579008084/8.0), + REAL_CONST(80048.698109669771/8.0), + REAL_CONST(80071.12621120183/8.0), + REAL_CONST(80093.555883384237/8.0), + REAL_CONST(80115.987125997053/8.0), + REAL_CONST(80138.419938820414/8.0), + REAL_CONST(80160.854321634528/8.0), + REAL_CONST(80183.290274219689/8.0), + REAL_CONST(80205.727796356281/8.0), + REAL_CONST(80228.166887824715/8.0), + REAL_CONST(80250.607548405547/8.0), + REAL_CONST(80273.049777879336/8.0), + REAL_CONST(80295.493576026798/8.0), + REAL_CONST(80317.938942628651/8.0), + REAL_CONST(80340.385877465727/8.0), + REAL_CONST(80362.834380318949/8.0), + REAL_CONST(80385.28445096928/8.0), + REAL_CONST(80407.736089197788/8.0), + REAL_CONST(80430.189294785596/8.0), + REAL_CONST(80452.644067513917/8.0), + REAL_CONST(80475.100407164035/8.0), + REAL_CONST(80497.558313517322/8.0), + REAL_CONST(80520.017786355209/8.0), + REAL_CONST(80542.478825459213/8.0), + REAL_CONST(80564.941430610925/8.0), + REAL_CONST(80587.405601592007/8.0), + REAL_CONST(80609.871338184195/8.0), + REAL_CONST(80632.338640169342/8.0), + REAL_CONST(80654.8075073293/8.0), + REAL_CONST(80677.277939446067/8.0), + REAL_CONST(80699.749936301683/8.0), + REAL_CONST(80722.223497678278/8.0), + REAL_CONST(80744.698623358039/8.0), + REAL_CONST(80767.17531312324/8.0), + REAL_CONST(80789.653566756242/8.0), + REAL_CONST(80812.133384039465/8.0), + REAL_CONST(80834.614764755403/8.0), + REAL_CONST(80857.097708686648/8.0), + REAL_CONST(80879.582215615854/8.0), + REAL_CONST(80902.068285325731/8.0), + REAL_CONST(80924.555917599093/8.0), + REAL_CONST(80947.045112218824/8.0), + REAL_CONST(80969.535868967869/8.0), + REAL_CONST(80992.028187629272/8.0), + REAL_CONST(81014.522067986123/8.0), + REAL_CONST(81037.017509821613/8.0), + REAL_CONST(81059.514512919006/8.0), + REAL_CONST(81082.013077061609/8.0), + REAL_CONST(81104.513202032831/8.0), + REAL_CONST(81127.014887616184/8.0), + REAL_CONST(81149.518133595193/8.0), + REAL_CONST(81172.022939753486/8.0), + REAL_CONST(81194.529305874807/8.0), + REAL_CONST(81217.037231742899/8.0), + REAL_CONST(81239.546717141639/8.0), + REAL_CONST(81262.057761854958/8.0), + REAL_CONST(81284.570365666848/8.0), + REAL_CONST(81307.084528361403/8.0), + REAL_CONST(81329.600249722775/8.0), + REAL_CONST(81352.117529535186/8.0), + REAL_CONST(81374.636367582949/8.0), + REAL_CONST(81397.156763650448/8.0), + REAL_CONST(81419.678717522125/8.0), + REAL_CONST(81442.202228982511/8.0), + REAL_CONST(81464.727297816222/8.0), + REAL_CONST(81487.253923807933/8.0), + REAL_CONST(81509.782106742379/8.0), + REAL_CONST(81532.311846404409/8.0), + REAL_CONST(81554.843142578902/8.0), + REAL_CONST(81577.375995050839/8.0), + REAL_CONST(81599.910403605274/8.0), + REAL_CONST(81622.446368027333/8.0), + REAL_CONST(81644.983888102215/8.0), + REAL_CONST(81667.522963615178/8.0), + REAL_CONST(81690.063594351581/8.0), + REAL_CONST(81712.605780096841/8.0), + REAL_CONST(81735.149520636449/8.0), + REAL_CONST(81757.694815755967/8.0), + REAL_CONST(81780.241665241047/8.0), + REAL_CONST(81802.79006887741/8.0), + REAL_CONST(81825.340026450824/8.0), + REAL_CONST(81847.891537747171/8.0), + REAL_CONST(81870.444602552379/8.0), + REAL_CONST(81892.999220652477/8.0), + REAL_CONST(81915.555391833506/8.0), + REAL_CONST(81938.113115881672/8.0), + REAL_CONST(81960.672392583176/8.0), + REAL_CONST(81983.233221724338/8.0), + REAL_CONST(82005.795603091537/8.0), + REAL_CONST(82028.359536471224/8.0), + REAL_CONST(82050.925021649906/8.0), + REAL_CONST(82073.492058414209/8.0), + REAL_CONST(82096.060646550788/8.0), + REAL_CONST(82118.630785846399/8.0), + REAL_CONST(82141.202476087841/8.0), + REAL_CONST(82163.775717062032/8.0), + REAL_CONST(82186.35050855593/8.0), + REAL_CONST(82208.926850356569/8.0), + REAL_CONST(82231.504742251054/8.0), + REAL_CONST(82254.084184026578/8.0), + REAL_CONST(82276.665175470393/8.0), + REAL_CONST(82299.24771636985/8.0), + REAL_CONST(82321.831806512317/8.0), + REAL_CONST(82344.417445685307/8.0), + REAL_CONST(82367.004633676348/8.0), + REAL_CONST(82389.593370273054/8.0), + REAL_CONST(82412.183655263143/8.0), + REAL_CONST(82434.775488434374/8.0), + REAL_CONST(82457.368869574595/8.0), + REAL_CONST(82479.963798471697/8.0), + REAL_CONST(82502.560274913689/8.0), + REAL_CONST(82525.158298688606/8.0), + REAL_CONST(82547.757869584602/8.0), + REAL_CONST(82570.35898738986/8.0), + REAL_CONST(82592.961651892678/8.0), + REAL_CONST(82615.565862881398/8.0), + REAL_CONST(82638.171620144421/8.0), + REAL_CONST(82660.778923470265/8.0), + REAL_CONST(82683.387772647475/8.0), + REAL_CONST(82705.998167464713/8.0), + REAL_CONST(82728.610107710658/8.0), + REAL_CONST(82751.223593174116/8.0), + REAL_CONST(82773.83862364394/8.0), + REAL_CONST(82796.45519890904/8.0), + REAL_CONST(82819.073318758441/8.0), + REAL_CONST(82841.692982981185/8.0), + REAL_CONST(82864.314191366429/8.0), + REAL_CONST(82886.936943703375/8.0), + REAL_CONST(82909.561239781324/8.0), + REAL_CONST(82932.187079389638/8.0), + REAL_CONST(82954.814462317736/8.0), + REAL_CONST(82977.443388355125/8.0), + REAL_CONST(83000.073857291369/8.0), + REAL_CONST(83022.70586891612/8.0), + REAL_CONST(83045.339423019104/8.0), + REAL_CONST(83067.974519390089/8.0), + REAL_CONST(83090.611157818959/8.0), + REAL_CONST(83113.249338095629/8.0), + REAL_CONST(83135.8890600101/8.0), + REAL_CONST(83158.530323352461/8.0), + REAL_CONST(83181.173127912858/8.0), + REAL_CONST(83203.817473481497/8.0), + REAL_CONST(83226.463359848669/8.0), + REAL_CONST(83249.11078680474/8.0), + REAL_CONST(83271.759754140134/8.0), + REAL_CONST(83294.410261645375/8.0), + REAL_CONST(83317.062309111003/8.0), + REAL_CONST(83339.715896327703/8.0), + REAL_CONST(83362.371023086147/8.0), + REAL_CONST(83385.027689177165/8.0), + REAL_CONST(83407.685894391587/8.0), + REAL_CONST(83430.345638520361/8.0), + REAL_CONST(83453.006921354478/8.0), + REAL_CONST(83475.669742685001/8.0), + REAL_CONST(83498.334102303095/8.0), + REAL_CONST(83520.999999999942/8.0), + REAL_CONST(83543.667435566866/8.0), + REAL_CONST(83566.336408795192/8.0), + REAL_CONST(83589.006919476349/8.0), + REAL_CONST(83611.678967401851/8.0), + REAL_CONST(83634.352552363242/8.0), + REAL_CONST(83657.027674152167/8.0), + REAL_CONST(83679.704332560359/8.0), + REAL_CONST(83702.382527379552/8.0), + REAL_CONST(83725.062258401638/8.0), + REAL_CONST(83747.743525418511/8.0), + REAL_CONST(83770.42632822218/8.0), + REAL_CONST(83793.110666604684/8.0), + REAL_CONST(83815.796540358162/8.0), + REAL_CONST(83838.483949274829/8.0), + REAL_CONST(83861.172893146941/8.0), + REAL_CONST(83883.863371766842/8.0), + REAL_CONST(83906.555384926964/8.0), + REAL_CONST(83929.248932419752/8.0), + REAL_CONST(83951.944014037799/8.0), + REAL_CONST(83974.640629573696/8.0), + REAL_CONST(83997.338778820151/8.0), + REAL_CONST(84020.038461569929/8.0), + REAL_CONST(84042.739677615857/8.0), + REAL_CONST(84065.442426750829/8.0), + REAL_CONST(84088.146708767847/8.0), + REAL_CONST(84110.852523459922/8.0), + REAL_CONST(84133.559870620171/8.0), + REAL_CONST(84156.268750041796/8.0), + REAL_CONST(84178.979161518029/8.0), + REAL_CONST(84201.691104842204/8.0), + REAL_CONST(84224.404579807713/8.0), + REAL_CONST(84247.119586208006/8.0), + REAL_CONST(84269.83612383662/8.0), + REAL_CONST(84292.55419248715/8.0), + REAL_CONST(84315.273791953281/8.0), + REAL_CONST(84337.994922028738/8.0), + REAL_CONST(84360.717582507335/8.0), + REAL_CONST(84383.441773182945/8.0), + REAL_CONST(84406.167493849513/8.0), + REAL_CONST(84428.894744301069/8.0), + REAL_CONST(84451.623524331691/8.0), + REAL_CONST(84474.353833735542/8.0), + REAL_CONST(84497.085672306828/8.0), + REAL_CONST(84519.819039839858/8.0), + REAL_CONST(84542.553936128999/8.0), + REAL_CONST(84565.290360968676/8.0), + REAL_CONST(84588.028314153402/8.0), + REAL_CONST(84610.767795477717/8.0), + REAL_CONST(84633.508804736295/8.0), + REAL_CONST(84656.251341723822/8.0), + REAL_CONST(84678.995406235073/8.0), + REAL_CONST(84701.740998064924/8.0), + REAL_CONST(84724.488117008252/8.0), + REAL_CONST(84747.236762860062/8.0), + REAL_CONST(84769.986935415407/8.0), + REAL_CONST(84792.73863446941/8.0), + REAL_CONST(84815.491859817252/8.0), + REAL_CONST(84838.246611254188/8.0), + REAL_CONST(84861.002888575575/8.0), + REAL_CONST(84883.760691576768/8.0), + REAL_CONST(84906.520020053256/8.0), + REAL_CONST(84929.28087380057/8.0), + REAL_CONST(84952.043252614312/8.0), + REAL_CONST(84974.807156290146/8.0), + REAL_CONST(84997.572584623806/8.0), + REAL_CONST(85020.339537411113/8.0), + REAL_CONST(85043.108014447949/8.0), + REAL_CONST(85065.878015530237/8.0), + REAL_CONST(85088.649540453989/8.0), + REAL_CONST(85111.422589015303/8.0), + REAL_CONST(85134.197161010321/8.0), + REAL_CONST(85156.973256235244/8.0), + REAL_CONST(85179.750874486374/8.0), + REAL_CONST(85202.530015560071/8.0), + REAL_CONST(85225.310679252725/8.0), + REAL_CONST(85248.092865360857/8.0), + REAL_CONST(85270.876573681016/8.0), + REAL_CONST(85293.661804009811/8.0), + REAL_CONST(85316.448556143951/8.0), + REAL_CONST(85339.236829880188/8.0), + REAL_CONST(85362.026625015351/8.0), + REAL_CONST(85384.817941346351/8.0), + REAL_CONST(85407.610778670132/8.0), + REAL_CONST(85430.405136783724/8.0), + REAL_CONST(85453.201015484257/8.0), + REAL_CONST(85475.998414568865/8.0), + REAL_CONST(85498.797333834795/8.0), + REAL_CONST(85521.597773079353/8.0), + REAL_CONST(85544.399732099904/8.0), + REAL_CONST(85567.203210693886/8.0), + REAL_CONST(85590.008208658808/8.0), + REAL_CONST(85612.814725792239/8.0), + REAL_CONST(85635.62276189182/8.0), + REAL_CONST(85658.432316755265/8.0), + REAL_CONST(85681.243390180331/8.0), + REAL_CONST(85704.055981964877/8.0), + REAL_CONST(85726.870091906807/8.0), + REAL_CONST(85749.685719804082/8.0), + REAL_CONST(85772.502865454764/8.0), + REAL_CONST(85795.321528656961/8.0), + REAL_CONST(85818.141709208852/8.0), + REAL_CONST(85840.963406908675/8.0), + REAL_CONST(85863.78662155474/8.0), + REAL_CONST(85886.611352945445/8.0), + REAL_CONST(85909.437600879217/8.0), + REAL_CONST(85932.26536515457/8.0), + REAL_CONST(85955.094645570091/8.0), + REAL_CONST(85977.92544192441/8.0), + REAL_CONST(86000.757754016275/8.0), + REAL_CONST(86023.591581644432/8.0), + REAL_CONST(86046.426924607746/8.0), + REAL_CONST(86069.263782705122/8.0), + REAL_CONST(86092.102155735556/8.0), + REAL_CONST(86114.942043498071/8.0), + REAL_CONST(86137.783445791807/8.0), + REAL_CONST(86160.626362415918/8.0), + REAL_CONST(86183.470793169676/8.0), + REAL_CONST(86206.316737852379/8.0), + REAL_CONST(86229.164196263402/8.0), + REAL_CONST(86252.013168202204/8.0), + REAL_CONST(86274.863653468303/8.0), + REAL_CONST(86297.715651861261/8.0), + REAL_CONST(86320.569163180728/8.0), + REAL_CONST(86343.424187226425/8.0), + REAL_CONST(86366.280723798132/8.0), + REAL_CONST(86389.138772695675/8.0), + REAL_CONST(86411.998333718977/8.0), + REAL_CONST(86434.859406668009/8.0), + REAL_CONST(86457.721991342827/8.0), + REAL_CONST(86480.586087543532/8.0), + REAL_CONST(86503.451695070296/8.0), + REAL_CONST(86526.318813723352/8.0), + REAL_CONST(86549.187443303032/8.0), + REAL_CONST(86572.057583609683/8.0), + REAL_CONST(86594.929234443756/8.0), + REAL_CONST(86617.802395605773/8.0), + REAL_CONST(86640.677066896271/8.0), + REAL_CONST(86663.553248115903/8.0), + REAL_CONST(86686.43093906538/8.0), + REAL_CONST(86709.310139545443/8.0), + REAL_CONST(86732.190849356964/8.0), + REAL_CONST(86755.073068300815/8.0), + REAL_CONST(86777.956796177954/8.0), + REAL_CONST(86800.842032789442/8.0), + REAL_CONST(86823.728777936354/8.0), + REAL_CONST(86846.617031419853/8.0), + REAL_CONST(86869.506793041175/8.0), + REAL_CONST(86892.398062601613/8.0), + REAL_CONST(86915.290839902518/8.0), + REAL_CONST(86938.185124745316/8.0), + REAL_CONST(86961.080916931489/8.0), + REAL_CONST(86983.978216262592/8.0), + REAL_CONST(87006.87702254027/8.0), + REAL_CONST(87029.777335566177/8.0), + REAL_CONST(87052.67915514209/8.0), + REAL_CONST(87075.582481069796/8.0), + REAL_CONST(87098.487313151185/8.0), + REAL_CONST(87121.39365118822/8.0), + REAL_CONST(87144.301494982894/8.0), + REAL_CONST(87167.210844337285/8.0), + REAL_CONST(87190.121699053532/8.0), + REAL_CONST(87213.034058933845/8.0), + REAL_CONST(87235.947923780506/8.0), + REAL_CONST(87258.863293395829/8.0), + REAL_CONST(87281.780167582241/8.0), + REAL_CONST(87304.698546142172/8.0), + REAL_CONST(87327.618428878181/8.0), + REAL_CONST(87350.539815592856/8.0), + REAL_CONST(87373.462706088845/8.0), + REAL_CONST(87396.387100168897/8.0), + REAL_CONST(87419.312997635774/8.0), + REAL_CONST(87442.240398292357/8.0), + REAL_CONST(87465.16930194154/8.0), + REAL_CONST(87488.099708386319/8.0), + REAL_CONST(87511.031617429733/8.0), + REAL_CONST(87533.965028874911/8.0), + REAL_CONST(87556.899942525008/8.0), + REAL_CONST(87579.836358183282/8.0), + REAL_CONST(87602.774275653021/8.0), + REAL_CONST(87625.713694737613/8.0), + REAL_CONST(87648.654615240492/8.0), + REAL_CONST(87671.597036965148/8.0), + REAL_CONST(87694.540959715145/8.0), + REAL_CONST(87717.486383294105/8.0), + REAL_CONST(87740.433307505737/8.0), + REAL_CONST(87763.381732153779/8.0), + REAL_CONST(87786.331657042057/8.0), + REAL_CONST(87809.283081974456/8.0), + REAL_CONST(87832.236006754916/8.0), + REAL_CONST(87855.190431187453/8.0), + REAL_CONST(87878.146355076155/8.0), + REAL_CONST(87901.103778225151/8.0), + REAL_CONST(87924.062700438633/8.0), + REAL_CONST(87947.023121520891/8.0), + REAL_CONST(87969.985041276246/8.0), + REAL_CONST(87992.948459509105/8.0), + REAL_CONST(88015.913376023906/8.0), + REAL_CONST(88038.879790625171/8.0), + REAL_CONST(88061.847703117513/8.0), + REAL_CONST(88084.817113305573/8.0), + REAL_CONST(88107.788020994049/8.0), + REAL_CONST(88130.760425987726/8.0), + REAL_CONST(88153.734328091465/8.0), + REAL_CONST(88176.709727110137/8.0), + REAL_CONST(88199.686622848749/8.0), + REAL_CONST(88222.665015112303/8.0), + REAL_CONST(88245.644903705906/8.0), + REAL_CONST(88268.626288434709/8.0), + REAL_CONST(88291.609169103947/8.0), + REAL_CONST(88314.593545518903/8.0), + REAL_CONST(88337.579417484914/8.0), + REAL_CONST(88360.566784807408/8.0), + REAL_CONST(88383.555647291854/8.0), + REAL_CONST(88406.546004743795/8.0), + REAL_CONST(88429.537856968818/8.0), + REAL_CONST(88452.531203772611/8.0), + REAL_CONST(88475.52604496089/8.0), + REAL_CONST(88498.522380339447/8.0), + REAL_CONST(88521.52020971413/8.0), + REAL_CONST(88544.519532890874/8.0), + REAL_CONST(88567.520349675644/8.0), + REAL_CONST(88590.522659874507/8.0), + REAL_CONST(88613.526463293543/8.0), + REAL_CONST(88636.531759738922/8.0), + REAL_CONST(88659.538549016899/8.0), + REAL_CONST(88682.546830933745/8.0), + REAL_CONST(88705.556605295846/8.0), + REAL_CONST(88728.567871909589/8.0), + REAL_CONST(88751.580630581491/8.0), + REAL_CONST(88774.594881118086/8.0), + REAL_CONST(88797.610623325963/8.0), + REAL_CONST(88820.62785701183/8.0), + REAL_CONST(88843.646581982393/8.0), + REAL_CONST(88866.666798044462/8.0), + REAL_CONST(88889.688505004888/8.0), + REAL_CONST(88912.711702670611/8.0), + REAL_CONST(88935.7363908486/8.0), + REAL_CONST(88958.762569345898/8.0), + REAL_CONST(88981.790237969632/8.0), + REAL_CONST(89004.81939652696/8.0), + REAL_CONST(89027.850044825114/8.0), + REAL_CONST(89050.882182671412/8.0), + REAL_CONST(89073.9158098732/8.0), + REAL_CONST(89096.950926237885/8.0), + REAL_CONST(89119.987531572973/8.0), + REAL_CONST(89143.025625686001/8.0), + REAL_CONST(89166.065208384563/8.0), + REAL_CONST(89189.106279476357/8.0), + REAL_CONST(89212.148838769106/8.0), + REAL_CONST(89235.192886070581/8.0), + REAL_CONST(89258.238421188667/8.0), + REAL_CONST(89281.285443931265/8.0), + REAL_CONST(89304.333954106376/8.0), + REAL_CONST(89327.383951522017/8.0), + REAL_CONST(89350.435435986306/8.0), + REAL_CONST(89373.488407307406/8.0), + REAL_CONST(89396.542865293537/8.0), + REAL_CONST(89419.598809753006/8.0), + REAL_CONST(89442.656240494165/8.0), + REAL_CONST(89465.715157325409/8.0), + REAL_CONST(89488.775560055219/8.0), + REAL_CONST(89511.837448492137/8.0), + REAL_CONST(89534.900822444746/8.0), + REAL_CONST(89557.965681721733/8.0), + REAL_CONST(89581.032026131812/8.0), + REAL_CONST(89604.099855483742/8.0), + REAL_CONST(89627.169169586399/8.0), + REAL_CONST(89650.239968248672/8.0), + REAL_CONST(89673.312251279538/8.0), + REAL_CONST(89696.386018488018/8.0), + REAL_CONST(89719.461269683205/8.0), + REAL_CONST(89742.53800467425/8.0), + REAL_CONST(89765.616223270365/8.0), + REAL_CONST(89788.69592528083/8.0), + REAL_CONST(89811.777110514988/8.0), + REAL_CONST(89834.859778782207/8.0), + REAL_CONST(89857.943929891975/8.0), + REAL_CONST(89881.029563653807/8.0), + REAL_CONST(89904.116679877261/8.0), + REAL_CONST(89927.205278372014/8.0), + REAL_CONST(89950.29535894774/8.0), + REAL_CONST(89973.386921414218/8.0), + REAL_CONST(89996.479965581268/8.0), + REAL_CONST(90019.574491258769/8.0), + REAL_CONST(90042.670498256688/8.0), + REAL_CONST(90065.767986385021/8.0), + REAL_CONST(90088.866955453836/8.0), + REAL_CONST(90111.967405273259/8.0), + REAL_CONST(90135.069335653476/8.0), + REAL_CONST(90158.172746404758/8.0), + REAL_CONST(90181.277637337407/8.0), + REAL_CONST(90204.384008261797/8.0), + REAL_CONST(90227.49185898836/8.0), + REAL_CONST(90250.601189327586/8.0), + REAL_CONST(90273.711999090039/8.0), + REAL_CONST(90296.824288086325/8.0), + REAL_CONST(90319.938056127125/8.0), + REAL_CONST(90343.053303023189/8.0), + REAL_CONST(90366.170028585286/8.0), + REAL_CONST(90389.288232624298/8.0), + REAL_CONST(90412.407914951138/8.0), + REAL_CONST(90435.529075376777/8.0), + REAL_CONST(90458.651713712257/8.0), + REAL_CONST(90481.775829768681/8.0), + REAL_CONST(90504.901423357209/8.0), + REAL_CONST(90528.028494289058/8.0), + REAL_CONST(90551.157042375504/8.0), + REAL_CONST(90574.287067427911/8.0), + REAL_CONST(90597.418569257643/8.0), + REAL_CONST(90620.551547676194/8.0), + REAL_CONST(90643.686002495073/8.0), + REAL_CONST(90666.821933525847/8.0), + REAL_CONST(90689.959340580186/8.0), + REAL_CONST(90713.098223469773/8.0), + REAL_CONST(90736.238582006365/8.0), + REAL_CONST(90759.380416001804/8.0), + REAL_CONST(90782.523725267951/8.0), + REAL_CONST(90805.668509616764/8.0), + REAL_CONST(90828.814768860233/8.0), + REAL_CONST(90851.962502810435/8.0), + REAL_CONST(90875.11171127946/8.0), + REAL_CONST(90898.262394079517/8.0), + REAL_CONST(90921.414551022855/8.0), + REAL_CONST(90944.568181921743/8.0), + REAL_CONST(90967.72328658856/8.0), + REAL_CONST(90990.879864835719/8.0), + REAL_CONST(91014.037916475718/8.0), + REAL_CONST(91037.19744132107/8.0), + REAL_CONST(91060.358439184391/8.0), + REAL_CONST(91083.520909878338/8.0), + REAL_CONST(91106.684853215629/8.0), + REAL_CONST(91129.850269009039/8.0), + REAL_CONST(91153.017157071401/8.0), + REAL_CONST(91176.185517215621/8.0), + REAL_CONST(91199.355349254649/8.0), + REAL_CONST(91222.526653001492/8.0), + REAL_CONST(91245.699428269247/8.0), + REAL_CONST(91268.873674871036/8.0), + REAL_CONST(91292.049392620058/8.0), + REAL_CONST(91315.226581329553/8.0), + REAL_CONST(91338.405240812834/8.0), + REAL_CONST(91361.585370883287/8.0), + REAL_CONST(91384.766971354344/8.0), + REAL_CONST(91407.950042039476/8.0), + REAL_CONST(91431.134582752245/8.0), + REAL_CONST(91454.320593306256/8.0), + REAL_CONST(91477.508073515171/8.0), + REAL_CONST(91500.697023192712/8.0), + REAL_CONST(91523.887442152685/8.0), + REAL_CONST(91547.07933020893/8.0), + REAL_CONST(91570.272687175326/8.0), + REAL_CONST(91593.467512865856/8.0), + REAL_CONST(91616.663807094534/8.0), + REAL_CONST(91639.861569675442/8.0), + REAL_CONST(91663.060800422725/8.0), + REAL_CONST(91686.261499150554/8.0), + REAL_CONST(91709.463665673218/8.0), + REAL_CONST(91732.66729980502/8.0), + REAL_CONST(91755.872401360321/8.0), + REAL_CONST(91779.078970153569/8.0), + REAL_CONST(91802.287005999257/8.0), + REAL_CONST(91825.49650871192/8.0), + REAL_CONST(91848.707478106167/8.0), + REAL_CONST(91871.91991399668/8.0), + REAL_CONST(91895.133816198169/8.0), + REAL_CONST(91918.349184525418/8.0), + REAL_CONST(91941.566018793281/8.0), + REAL_CONST(91964.784318816659/8.0), + REAL_CONST(91988.004084410495/8.0), + REAL_CONST(92011.22531538982/8.0), + REAL_CONST(92034.448011569708/8.0), + REAL_CONST(92057.672172765277/8.0), + REAL_CONST(92080.897798791746/8.0), + REAL_CONST(92104.124889464365/8.0), + REAL_CONST(92127.353444598411/8.0), + REAL_CONST(92150.58346400928/8.0), + REAL_CONST(92173.814947512379/8.0), + REAL_CONST(92197.04789492322/8.0), + REAL_CONST(92220.282306057314/8.0), + REAL_CONST(92243.518180730272/8.0), + REAL_CONST(92266.755518757753/8.0), + REAL_CONST(92289.994319955469/8.0), + REAL_CONST(92313.234584139194/8.0), + REAL_CONST(92336.476311124774/8.0), + REAL_CONST(92359.719500728082/8.0), + REAL_CONST(92382.964152765067/8.0), + REAL_CONST(92406.210267051734/8.0), + REAL_CONST(92429.457843404161/8.0), + REAL_CONST(92452.706881638471/8.0), + REAL_CONST(92475.957381570814/8.0), + REAL_CONST(92499.209343017443/8.0), + REAL_CONST(92522.462765794655/8.0), + REAL_CONST(92545.717649718805/8.0), + REAL_CONST(92568.973994606305/8.0), + REAL_CONST(92592.231800273614/8.0), + REAL_CONST(92615.491066537259/8.0), + REAL_CONST(92638.751793213814/8.0), + REAL_CONST(92662.01398011994/8.0), + REAL_CONST(92685.277627072326/8.0), + REAL_CONST(92708.54273388772/8.0), + REAL_CONST(92731.809300382942/8.0), + REAL_CONST(92755.077326374871/8.0), + REAL_CONST(92778.346811680414/8.0), + REAL_CONST(92801.617756116568/8.0), + REAL_CONST(92824.890159500384/8.0), + REAL_CONST(92848.164021648947/8.0), + REAL_CONST(92871.439342379424/8.0), + REAL_CONST(92894.716121509016/8.0), + REAL_CONST(92917.994358855023/8.0), + REAL_CONST(92941.274054234746/8.0), + REAL_CONST(92964.555207465572/8.0), + REAL_CONST(92987.837818364962/8.0), + REAL_CONST(93011.121886750407/8.0), + REAL_CONST(93034.407412439468/8.0), + REAL_CONST(93057.694395249753/8.0), + REAL_CONST(93080.982834998955/8.0), + REAL_CONST(93104.272731504767/8.0), + REAL_CONST(93127.564084584999/8.0), + REAL_CONST(93150.856894057491/8.0), + REAL_CONST(93174.15115974014/8.0), + REAL_CONST(93197.446881450916/8.0), + REAL_CONST(93220.744059007804/8.0), + REAL_CONST(93244.04269222889/8.0), + REAL_CONST(93267.342780932304/8.0), + REAL_CONST(93290.644324936235/8.0), + REAL_CONST(93313.947324058914/8.0), + REAL_CONST(93337.251778118633/8.0), + REAL_CONST(93360.557686933767/8.0), + REAL_CONST(93383.865050322696/8.0), + REAL_CONST(93407.173868103928/8.0), + REAL_CONST(93430.484140095941/8.0), + REAL_CONST(93453.795866117362/8.0), + REAL_CONST(93477.109045986799/8.0), + REAL_CONST(93500.423679522952/8.0), + REAL_CONST(93523.739766544561/8.0), + REAL_CONST(93547.057306870454/8.0), + REAL_CONST(93570.376300319491/8.0), + REAL_CONST(93593.696746710571/8.0), + REAL_CONST(93617.018645862699/8.0), + REAL_CONST(93640.341997594893/8.0), + REAL_CONST(93663.666801726242/8.0), + REAL_CONST(93686.993058075881/8.0), + REAL_CONST(93710.320766463032/8.0), + REAL_CONST(93733.64992670693/8.0), + REAL_CONST(93756.980538626914/8.0), + REAL_CONST(93780.312602042337/8.0), + REAL_CONST(93803.646116772637/8.0), + REAL_CONST(93826.981082637285/8.0), + REAL_CONST(93850.317499455836/8.0), + REAL_CONST(93873.655367047861/8.0), + REAL_CONST(93896.994685233032/8.0), + REAL_CONST(93920.335453831038/8.0), + REAL_CONST(93943.677672661666/8.0), + REAL_CONST(93967.021341544707/8.0), + REAL_CONST(93990.366460300051/8.0), + REAL_CONST(94013.713028747632/8.0), + REAL_CONST(94037.061046707429/8.0), + REAL_CONST(94060.410513999494/8.0), + REAL_CONST(94083.761430443905/8.0), + REAL_CONST(94107.113795860845/8.0), + REAL_CONST(94130.467610070496/8.0), + REAL_CONST(94153.822872893157/8.0), + REAL_CONST(94177.179584149111/8.0), + REAL_CONST(94200.537743658759/8.0), + REAL_CONST(94223.897351242529/8.0), + REAL_CONST(94247.25840672091/8.0), + REAL_CONST(94270.620909914433/8.0), + REAL_CONST(94293.98486064373/8.0), + REAL_CONST(94317.350258729421/8.0), + REAL_CONST(94340.71710399224/8.0), + REAL_CONST(94364.085396252936/8.0), + REAL_CONST(94387.455135332348/8.0), + REAL_CONST(94410.82632105134/8.0), + REAL_CONST(94434.198953230851/8.0), + REAL_CONST(94457.573031691878/8.0), + REAL_CONST(94480.948556255447/8.0), + REAL_CONST(94504.325526742658/8.0), + REAL_CONST(94527.70394297468/8.0), + REAL_CONST(94551.083804772716/8.0), + REAL_CONST(94574.465111958023/8.0), + REAL_CONST(94597.847864351934/8.0), + REAL_CONST(94621.232061775823/8.0), + REAL_CONST(94644.617704051096/8.0), + REAL_CONST(94668.004790999272/8.0), + REAL_CONST(94691.393322441872/8.0), + REAL_CONST(94714.783298200506/8.0), + REAL_CONST(94738.174718096794/8.0), + REAL_CONST(94761.567581952477/8.0), + REAL_CONST(94784.961889589307/8.0), + REAL_CONST(94808.357640829097/8.0), + REAL_CONST(94831.754835493703/8.0), + REAL_CONST(94855.153473405066/8.0), + REAL_CONST(94878.553554385173/8.0), + REAL_CONST(94901.955078256055/8.0), + REAL_CONST(94925.358044839784/8.0), + REAL_CONST(94948.762453958523/8.0), + REAL_CONST(94972.168305434476/8.0), + REAL_CONST(94995.575599089891/8.0), + REAL_CONST(95018.984334747074/8.0), + REAL_CONST(95042.394512228391/8.0), + REAL_CONST(95065.806131356265/8.0), + REAL_CONST(95089.219191953176/8.0), + REAL_CONST(95112.633693841635/8.0), + REAL_CONST(95136.04963684424/8.0), + REAL_CONST(95159.467020783617/8.0), + REAL_CONST(95182.885845482466/8.0), + REAL_CONST(95206.306110763529/8.0), + REAL_CONST(95229.727816449609/8.0), + REAL_CONST(95253.150962363579/8.0), + REAL_CONST(95276.575548328314/8.0), + REAL_CONST(95300.001574166803/8.0), + REAL_CONST(95323.429039702052/8.0), + REAL_CONST(95346.857944757154/8.0), + REAL_CONST(95370.288289155214/8.0), + REAL_CONST(95393.720072719429/8.0), + REAL_CONST(95417.153295273019/8.0), + REAL_CONST(95440.587956639298/8.0), + REAL_CONST(95464.024056641589/8.0), + REAL_CONST(95487.461595103305/8.0), + REAL_CONST(95510.900571847902/8.0), + REAL_CONST(95534.340986698866/8.0), + REAL_CONST(95557.782839479783/8.0), + REAL_CONST(95581.226130014256/8.0), + REAL_CONST(95604.670858125959/8.0), + REAL_CONST(95628.117023638595/8.0), + REAL_CONST(95651.564626375985/8.0), + REAL_CONST(95675.013666161918/8.0), + REAL_CONST(95698.464142820303/8.0), + REAL_CONST(95721.916056175076/8.0), + REAL_CONST(95745.369406050231/8.0), + REAL_CONST(95768.824192269807/8.0), + REAL_CONST(95792.280414657915/8.0), + REAL_CONST(95815.738073038709/8.0), + REAL_CONST(95839.197167236387/8.0), + REAL_CONST(95862.657697075221/8.0), + REAL_CONST(95886.11966237954/8.0), + REAL_CONST(95909.583062973688/8.0), + REAL_CONST(95933.047898682111/8.0), + REAL_CONST(95956.514169329268/8.0), + REAL_CONST(95979.981874739708/8.0), + REAL_CONST(96003.451014738006/8.0), + REAL_CONST(96026.921589148798/8.0), + REAL_CONST(96050.393597796792/8.0), + REAL_CONST(96073.867040506724/8.0), + REAL_CONST(96097.341917103375/8.0), + REAL_CONST(96120.818227411626/8.0), + REAL_CONST(96144.295971256375/8.0), + REAL_CONST(96167.775148462577/8.0), + REAL_CONST(96191.255758855244/8.0), + REAL_CONST(96214.737802259449/8.0), + REAL_CONST(96238.221278500292/8.0), + REAL_CONST(96261.70618740299/8.0), + REAL_CONST(96285.192528792715/8.0), + REAL_CONST(96308.680302494788/8.0), + REAL_CONST(96332.169508334526/8.0), + REAL_CONST(96355.660146137321/8.0), + REAL_CONST(96379.152215728609/8.0), + REAL_CONST(96402.645716933868/8.0), + REAL_CONST(96426.14064957868/8.0), + REAL_CONST(96449.637013488609/8.0), + REAL_CONST(96473.134808489311/8.0), + REAL_CONST(96496.63403440651/8.0), + REAL_CONST(96520.134691065963/8.0), + REAL_CONST(96543.636778293469/8.0), + REAL_CONST(96567.140295914898/8.0), + REAL_CONST(96590.645243756153/8.0), + REAL_CONST(96614.151621643221/8.0), + REAL_CONST(96637.659429402134/8.0), + REAL_CONST(96661.168666858954/8.0), + REAL_CONST(96684.679333839798/8.0), + REAL_CONST(96708.191430170875/8.0), + REAL_CONST(96731.70495567839/8.0), + REAL_CONST(96755.219910188665/8.0), + REAL_CONST(96778.736293528011/8.0), + REAL_CONST(96802.254105522836/8.0), + REAL_CONST(96825.77334599958/8.0), + REAL_CONST(96849.29401478474/8.0), + REAL_CONST(96872.816111704873/8.0), + REAL_CONST(96896.339636586577/8.0), + REAL_CONST(96919.864589256511/8.0), + REAL_CONST(96943.390969541389/8.0), + REAL_CONST(96966.918777267958/8.0), + REAL_CONST(96990.448012263048/8.0), + REAL_CONST(97013.978674353522/8.0), + REAL_CONST(97037.510763366285/8.0), + REAL_CONST(97061.044279128328/8.0), + REAL_CONST(97084.579221466673/8.0), + REAL_CONST(97108.115590208385/8.0), + REAL_CONST(97131.653385180587/8.0), + REAL_CONST(97155.19260621049/8.0), + REAL_CONST(97178.733253125291/8.0), + REAL_CONST(97202.2753257523/8.0), + REAL_CONST(97225.81882391886/8.0), + REAL_CONST(97249.363747452342/8.0), + REAL_CONST(97272.910096180189/8.0), + REAL_CONST(97296.457869929916/8.0), + REAL_CONST(97320.007068529041/8.0), + REAL_CONST(97343.557691805196/8.0), + REAL_CONST(97367.109739586012/8.0), + REAL_CONST(97390.663211699197/8.0), + REAL_CONST(97414.218107972498/8.0), + REAL_CONST(97437.774428233737/8.0), + REAL_CONST(97461.332172310766/8.0), + REAL_CONST(97484.891340031507/8.0), + REAL_CONST(97508.451931223899/8.0), + REAL_CONST(97532.013945715982/8.0), + REAL_CONST(97555.577383335811/8.0), + REAL_CONST(97579.142243911512/8.0), + REAL_CONST(97602.708527271257/8.0), + REAL_CONST(97626.276233243261/8.0), + REAL_CONST(97649.845361655811/8.0), + REAL_CONST(97673.415912337223/8.0), + REAL_CONST(97696.987885115886/8.0), + REAL_CONST(97720.561279820206/8.0), + REAL_CONST(97744.1360962787/8.0), + REAL_CONST(97767.712334319876/8.0), + REAL_CONST(97791.289993772341/8.0), + REAL_CONST(97814.869074464703/8.0), + REAL_CONST(97838.449576225685/8.0), + REAL_CONST(97862.031498883996/8.0), + REAL_CONST(97885.614842268449/8.0), + REAL_CONST(97909.199606207883/8.0), + REAL_CONST(97932.785790531183/8.0), + REAL_CONST(97956.37339506732/8.0), + REAL_CONST(97979.962419645264/8.0), + REAL_CONST(98003.552864094076/8.0), + REAL_CONST(98027.144728242856/8.0), + REAL_CONST(98050.738011920766/8.0), + REAL_CONST(98074.332714956996/8.0), + REAL_CONST(98097.928837180807/8.0), + REAL_CONST(98121.526378421506/8.0), + REAL_CONST(98145.125338508456/8.0), + REAL_CONST(98168.725717271067/8.0), + REAL_CONST(98192.327514538789/8.0), + REAL_CONST(98215.930730141132/8.0), + REAL_CONST(98239.535363907664/8.0), + REAL_CONST(98263.141415668011/8.0), + REAL_CONST(98286.748885251814/8.0), + REAL_CONST(98310.357772488816/8.0), + REAL_CONST(98333.968077208759/8.0), + REAL_CONST(98357.579799241488/8.0), + REAL_CONST(98381.192938416847/8.0), + REAL_CONST(98404.807494564782/8.0), + REAL_CONST(98428.42346751524/8.0), + REAL_CONST(98452.040857098269/8.0), + REAL_CONST(98475.659663143917/8.0), + REAL_CONST(98499.27988548232/8.0), + REAL_CONST(98522.901523943656/8.0), + REAL_CONST(98546.524578358163/8.0), + REAL_CONST(98570.149048556093/8.0), + REAL_CONST(98593.774934367786/8.0), + REAL_CONST(98617.402235623624/8.0), + REAL_CONST(98641.030952154048/8.0), + REAL_CONST(98664.661083789513/8.0), + REAL_CONST(98688.292630360564/8.0), + REAL_CONST(98711.925591697771/8.0), + REAL_CONST(98735.559967631794/8.0), + REAL_CONST(98759.195757993293/8.0), + REAL_CONST(98782.832962613014/8.0), + REAL_CONST(98806.471581321734/8.0), + REAL_CONST(98830.111613950285/8.0), + REAL_CONST(98853.753060329575/8.0), + REAL_CONST(98877.39592029051/8.0), + REAL_CONST(98901.040193664099/8.0), + REAL_CONST(98924.68588028138/8.0), + REAL_CONST(98948.33297997342/8.0), + REAL_CONST(98971.981492571387/8.0), + REAL_CONST(98995.63141790645/8.0), + REAL_CONST(99019.282755809851/8.0), + REAL_CONST(99042.935506112874/8.0), + REAL_CONST(99066.589668646877/8.0), + REAL_CONST(99090.245243243233/8.0), + REAL_CONST(99113.902229733401/8.0), + REAL_CONST(99137.560627948857/8.0), + REAL_CONST(99161.220437721131/8.0), + REAL_CONST(99184.881658881859/8.0), + REAL_CONST(99208.544291262631/8.0), + REAL_CONST(99232.208334695169/8.0), + REAL_CONST(99255.87378901121/8.0), + REAL_CONST(99279.540654042547/8.0), + REAL_CONST(99303.208929621018/8.0), + REAL_CONST(99326.878615578535/8.0), + REAL_CONST(99350.549711746993/8.0), + REAL_CONST(99374.222217958435/8.0), + REAL_CONST(99397.896134044888/8.0), + REAL_CONST(99421.571459838422/8.0), + REAL_CONST(99445.248195171211/8.0), + REAL_CONST(99468.926339875441/8.0), + REAL_CONST(99492.605893783344/8.0), + REAL_CONST(99516.286856727209/8.0), + REAL_CONST(99539.969228539398/8.0), + REAL_CONST(99563.653009052287/8.0), + REAL_CONST(99587.338198098325/8.0), + REAL_CONST(99611.024795510006/8.0), + REAL_CONST(99634.712801119866/8.0), + REAL_CONST(99658.402214760499/8.0), + REAL_CONST(99682.093036264545/8.0), + REAL_CONST(99705.785265464699/8.0), + REAL_CONST(99729.478902193689/8.0), + REAL_CONST(99753.173946284325/8.0), + REAL_CONST(99776.870397569437/8.0), + REAL_CONST(99800.56825588191/8.0), + REAL_CONST(99824.267521054688/8.0), + REAL_CONST(99847.968192920773/8.0), + REAL_CONST(99871.670271313182/8.0), + REAL_CONST(99895.373756065004/8.0), + REAL_CONST(99919.078647009388/8.0), + REAL_CONST(99942.78494397951/8.0), + REAL_CONST(99966.492646808634/8.0), + REAL_CONST(99990.20175533001/8.0), + REAL_CONST(100013.91226937699/8.0), + REAL_CONST(100037.62418878295/8.0), + REAL_CONST(100061.33751338134/8.0), + REAL_CONST(100085.05224300563/8.0), + REAL_CONST(100108.76837748935/8.0), + REAL_CONST(100132.4859166661/8.0), + REAL_CONST(100156.2048603695/8.0), + REAL_CONST(100179.92520843323/8.0), + REAL_CONST(100203.64696069101/8.0), + REAL_CONST(100227.37011697664/8.0), + REAL_CONST(100251.09467712394/8.0), + REAL_CONST(100274.82064096678/8.0), + REAL_CONST(100298.54800833909/8.0), + REAL_CONST(100322.27677907483/8.0), + REAL_CONST(100346.00695300807/8.0), + REAL_CONST(100369.73852997283/8.0), + REAL_CONST(100393.47150980328/8.0), + REAL_CONST(100417.20589233354/8.0), + REAL_CONST(100440.94167739789/8.0), + REAL_CONST(100464.67886483055/8.0), + REAL_CONST(100488.41745446586/8.0), + REAL_CONST(100512.1574461382/8.0), + REAL_CONST(100535.89883968196/8.0), + REAL_CONST(100559.64163493161/8.0), + REAL_CONST(100583.38583172169/8.0), + REAL_CONST(100607.13142988674/8.0), + REAL_CONST(100630.87842926137/8.0), + REAL_CONST(100654.62682968024/8.0), + REAL_CONST(100678.37663097809/8.0), + REAL_CONST(100702.12783298964/8.0), + REAL_CONST(100725.88043554971/8.0), + REAL_CONST(100749.63443849317/8.0), + REAL_CONST(100773.38984165489/8.0), + REAL_CONST(100797.14664486986/8.0), + REAL_CONST(100820.90484797307/8.0), + REAL_CONST(100844.66445079957/8.0), + REAL_CONST(100868.42545318443/8.0), + REAL_CONST(100892.18785496285/8.0), + REAL_CONST(100915.95165596998/8.0), + REAL_CONST(100939.71685604109/8.0), + REAL_CONST(100963.48345501146/8.0), + REAL_CONST(100987.25145271645/8.0), + REAL_CONST(101011.02084899142/8.0), + REAL_CONST(101034.79164367182/8.0), + REAL_CONST(101058.56383659317/8.0), + REAL_CONST(101082.33742759094/8.0), + REAL_CONST(101106.11241650078/8.0), + REAL_CONST(101129.88880315828/8.0), + REAL_CONST(101153.66658739912/8.0), + REAL_CONST(101177.44576905905/8.0), + REAL_CONST(101201.22634797383/8.0), + REAL_CONST(101225.00832397929/8.0), + REAL_CONST(101248.7916969113/8.0), + REAL_CONST(101272.57646660579/8.0), + REAL_CONST(101296.36263289873/8.0), + REAL_CONST(101320.15019562612/8.0), + REAL_CONST(101343.93915462404/8.0), + REAL_CONST(101367.7295097286/8.0), + REAL_CONST(101391.52126077596/8.0), + REAL_CONST(101415.31440760233/8.0), + REAL_CONST(101439.10895004397/8.0), + REAL_CONST(101462.9048879372/8.0), + REAL_CONST(101486.70222111834/8.0), + REAL_CONST(101510.50094942382/8.0), + REAL_CONST(101534.30107269008/8.0), + REAL_CONST(101558.10259075361/8.0), + REAL_CONST(101581.90550345098/8.0), + REAL_CONST(101605.70981061876/8.0), + REAL_CONST(101629.5155120936/8.0), + REAL_CONST(101653.32260771218/8.0), + REAL_CONST(101677.13109731126/8.0), + REAL_CONST(101700.9409807276/8.0), + REAL_CONST(101724.75225779804/8.0), + REAL_CONST(101748.56492835947/8.0), + REAL_CONST(101772.37899224881/8.0), + REAL_CONST(101796.19444930303/8.0), + REAL_CONST(101820.01129935916/8.0), + REAL_CONST(101843.82954225427/8.0), + REAL_CONST(101867.64917782549/8.0), + REAL_CONST(101891.47020590997/8.0), + REAL_CONST(101915.29262634492/8.0), + REAL_CONST(101939.11643896763/8.0), + REAL_CONST(101962.94164361537/8.0), + REAL_CONST(101986.76824012553/8.0), + REAL_CONST(102010.59622833549/8.0), + REAL_CONST(102034.42560808272/8.0), + REAL_CONST(102058.25637920471/8.0), + REAL_CONST(102082.08854153901/8.0), + REAL_CONST(102105.9220949232/8.0), + REAL_CONST(102129.75703919494/8.0), + REAL_CONST(102153.59337419191/8.0), + REAL_CONST(102177.43109975185/8.0), + REAL_CONST(102201.27021571253/8.0), + REAL_CONST(102225.1107219118/8.0), + REAL_CONST(102248.95261818753/8.0), + REAL_CONST(102272.79590437764/8.0), + REAL_CONST(102296.64058032009/8.0), + REAL_CONST(102320.48664585294/8.0), + REAL_CONST(102344.33410081422/8.0), + REAL_CONST(102368.18294504205/8.0), + REAL_CONST(102392.03317837461/8.0), + REAL_CONST(102415.88480065008/8.0), + REAL_CONST(102439.73781170673/8.0), + REAL_CONST(102463.59221138287/8.0), + REAL_CONST(102487.44799951684/8.0), + REAL_CONST(102511.30517594704/8.0), + REAL_CONST(102535.1637405119/8.0), + REAL_CONST(102559.02369304992/8.0), + REAL_CONST(102582.88503339965/8.0), + REAL_CONST(102606.74776139967/8.0), + REAL_CONST(102630.61187688859/8.0), + REAL_CONST(102654.4773797051/8.0), + REAL_CONST(102678.34426968795/8.0), + REAL_CONST(102702.21254667587/8.0), + REAL_CONST(102726.08221050771/8.0), + REAL_CONST(102749.95326102231/8.0), + REAL_CONST(102773.8256980586/8.0), + REAL_CONST(102797.69952145554/8.0), + REAL_CONST(102821.57473105213/8.0), + REAL_CONST(102845.45132668741/8.0), + REAL_CONST(102869.32930820051/8.0), + REAL_CONST(102893.20867543056/8.0), + REAL_CONST(102917.08942821674/8.0), + REAL_CONST(102940.97156639832/8.0), + REAL_CONST(102964.85508981455/8.0), + REAL_CONST(102988.73999830478/8.0), + REAL_CONST(103012.6262917084/8.0), + REAL_CONST(103036.51396986481/8.0), + REAL_CONST(103060.40303261351/8.0), + REAL_CONST(103084.293479794/8.0), + REAL_CONST(103108.18531124585/8.0), + REAL_CONST(103132.07852680866/8.0), + REAL_CONST(103155.97312632212/8.0), + REAL_CONST(103179.8691096259/8.0), + REAL_CONST(103203.76647655977/8.0), + REAL_CONST(103227.66522696352/8.0), + REAL_CONST(103251.56536067701/8.0), + REAL_CONST(103275.46687754011/8.0), + REAL_CONST(103299.36977739276/8.0), + REAL_CONST(103323.27406007495/8.0), + REAL_CONST(103347.1797254267/8.0), + REAL_CONST(103371.0867732881/8.0), + REAL_CONST(103394.99520349925/8.0), + REAL_CONST(103418.90501590034/8.0), + REAL_CONST(103442.81621033157/8.0), + REAL_CONST(103466.72878663319/8.0), + REAL_CONST(103490.64274464553/8.0), + REAL_CONST(103514.55808420894/8.0), + REAL_CONST(103538.4748051638/8.0), + REAL_CONST(103562.39290735057/8.0), + REAL_CONST(103586.31239060973/8.0), + REAL_CONST(103610.23325478184/8.0), + REAL_CONST(103634.15549970744/8.0), + REAL_CONST(103658.07912522719/8.0), + REAL_CONST(103682.00413118176/8.0), + REAL_CONST(103705.93051741188/8.0), + REAL_CONST(103729.85828375829/8.0), + REAL_CONST(103753.78743006183/8.0), + REAL_CONST(103777.71795616332/8.0), + REAL_CONST(103801.64986190372/8.0), + REAL_CONST(103825.58314712394/8.0), + REAL_CONST(103849.51781166498/8.0), + REAL_CONST(103873.4538553679/8.0), + REAL_CONST(103897.39127807376/8.0), + REAL_CONST(103921.33007962372/8.0), + REAL_CONST(103945.27025985894/8.0), + REAL_CONST(103969.21181862066/8.0), + REAL_CONST(103993.15475575015/8.0), + REAL_CONST(104017.0990710887/8.0), + REAL_CONST(104041.0447644777/8.0), + REAL_CONST(104064.99183575854/8.0), + REAL_CONST(104088.94028477269/8.0), + REAL_CONST(104112.89011136163/8.0), + REAL_CONST(104136.84131536692/8.0), + REAL_CONST(104160.79389663014/8.0), + REAL_CONST(104184.74785499295/8.0), + REAL_CONST(104208.70319029699/8.0), + REAL_CONST(104232.65990238401/8.0), + REAL_CONST(104256.61799109577/8.0), + REAL_CONST(104280.57745627411/8.0), + REAL_CONST(104304.53829776087/8.0), + REAL_CONST(104328.50051539797/8.0), + REAL_CONST(104352.46410902737/8.0), + REAL_CONST(104376.42907849104/8.0), + REAL_CONST(104400.39542363105/8.0), + REAL_CONST(104424.36314428948/8.0), + REAL_CONST(104448.33224030846/8.0), + REAL_CONST(104472.3027115302/8.0), + REAL_CONST(104496.27455779689/8.0), + REAL_CONST(104520.24777895081/8.0), + REAL_CONST(104544.22237483428/8.0), + REAL_CONST(104568.19834528965/8.0), + REAL_CONST(104592.17569015936/8.0), + REAL_CONST(104616.15440928582/8.0), + REAL_CONST(104640.13450251156/8.0), + REAL_CONST(104664.1159696791/8.0), + REAL_CONST(104688.09881063103/8.0), + REAL_CONST(104712.08302520998/8.0), + REAL_CONST(104736.06861325864/8.0), + REAL_CONST(104760.05557461972/8.0), + REAL_CONST(104784.043909136/8.0), + REAL_CONST(104808.03361665027/8.0), + REAL_CONST(104832.0246970054/8.0), + REAL_CONST(104856.01715004431/8.0), + REAL_CONST(104880.01097560991/8.0), + REAL_CONST(104904.00617354522/8.0), + REAL_CONST(104928.00274369326/8.0), + REAL_CONST(104952.00068589712/8.0), + REAL_CONST(104975.99999999993/8.0), + REAL_CONST(105000.00068584486/8.0), + REAL_CONST(105024.00274327511/8.0), + REAL_CONST(105048.00617213396/8.0), + REAL_CONST(105072.0109722647/8.0), + REAL_CONST(105096.0171435107/8.0), + REAL_CONST(105120.02468571534/8.0), + REAL_CONST(105144.03359872208/8.0), + REAL_CONST(105168.04388237436/8.0), + REAL_CONST(105192.05553651576/8.0), + REAL_CONST(105216.06856098982/8.0), + REAL_CONST(105240.08295564017/8.0), + REAL_CONST(105264.09872031047/8.0), + REAL_CONST(105288.11585484444/8.0), + REAL_CONST(105312.13435908582/8.0), + REAL_CONST(105336.1542328784/8.0), + REAL_CONST(105360.17547606604/8.0), + REAL_CONST(105384.19808849262/8.0), + REAL_CONST(105408.22207000206/8.0), + REAL_CONST(105432.24742043833/8.0), + REAL_CONST(105456.27413964548/8.0), + REAL_CONST(105480.30222746753/8.0), + REAL_CONST(105504.33168374863/8.0), + REAL_CONST(105528.36250833291/8.0), + REAL_CONST(105552.39470106458/8.0), + REAL_CONST(105576.42826178786/8.0), + REAL_CONST(105600.46319034706/8.0), + REAL_CONST(105624.49948658649/8.0), + REAL_CONST(105648.53715035053/8.0), + REAL_CONST(105672.5761814836/8.0), + REAL_CONST(105696.61657983017/8.0), + REAL_CONST(105720.65834523473/8.0), + REAL_CONST(105744.70147754184/8.0), + REAL_CONST(105768.7459765961/8.0), + REAL_CONST(105792.79184224214/8.0), + REAL_CONST(105816.83907432464/8.0), + REAL_CONST(105840.88767268835/8.0), + REAL_CONST(105864.93763717801/8.0), + REAL_CONST(105888.98896763846/8.0), + REAL_CONST(105913.04166391456/8.0), + REAL_CONST(105937.09572585119/8.0), + REAL_CONST(105961.15115329332/8.0), + REAL_CONST(105985.20794608595/8.0), + REAL_CONST(106009.26610407409/8.0), + REAL_CONST(106033.32562710284/8.0), + REAL_CONST(106057.38651501729/8.0), + REAL_CONST(106081.44876766266/8.0), + REAL_CONST(106105.51238488412/8.0), + REAL_CONST(106129.57736652695/8.0), + REAL_CONST(106153.64371243643/8.0), + REAL_CONST(106177.71142245791/8.0), + REAL_CONST(106201.78049643678/8.0), + REAL_CONST(106225.85093421848/8.0), + REAL_CONST(106249.92273564848/8.0), + REAL_CONST(106273.99590057228/8.0), + REAL_CONST(106298.07042883546/8.0), + REAL_CONST(106322.14632028362/8.0), + REAL_CONST(106346.22357476239/8.0), + REAL_CONST(106370.30219211751/8.0), + REAL_CONST(106394.38217219469/8.0), + REAL_CONST(106418.46351483969/8.0), + REAL_CONST(106442.54621989837/8.0), + REAL_CONST(106466.63028721658/8.0), + REAL_CONST(106490.71571664025/8.0), + REAL_CONST(106514.80250801529/8.0), + REAL_CONST(106538.89066118775/8.0), + REAL_CONST(106562.98017600364/8.0), + REAL_CONST(106587.07105230905/8.0), + REAL_CONST(106611.16328995011/8.0), + REAL_CONST(106635.25688877302/8.0), + REAL_CONST(106659.35184862395/8.0), + REAL_CONST(106683.44816934918/8.0), + REAL_CONST(106707.54585079502/8.0), + REAL_CONST(106731.64489280782/8.0), + REAL_CONST(106755.74529523395/8.0), + REAL_CONST(106779.84705791986/8.0), + REAL_CONST(106803.95018071201/8.0), + REAL_CONST(106828.05466345693/8.0), + REAL_CONST(106852.16050600118/8.0), + REAL_CONST(106876.26770819137/8.0), + REAL_CONST(106900.37626987413/8.0), + REAL_CONST(106924.48619089619/8.0), + REAL_CONST(106948.59747110425/8.0), + REAL_CONST(106972.71011034511/8.0), + REAL_CONST(106996.82410846559/8.0), + REAL_CONST(107020.93946531253/8.0), + REAL_CONST(107045.05618073288/8.0), + REAL_CONST(107069.17425457356/8.0), + REAL_CONST(107093.29368668159/8.0), + REAL_CONST(107117.41447690397/8.0), + REAL_CONST(107141.53662508781/8.0), + REAL_CONST(107165.66013108024/8.0), + REAL_CONST(107189.7849947284/8.0), + REAL_CONST(107213.91121587952/8.0), + REAL_CONST(107238.03879438085/8.0), + REAL_CONST(107262.16773007967/8.0), + REAL_CONST(107286.29802282334/8.0), + REAL_CONST(107310.42967245923/8.0), + REAL_CONST(107334.56267883476/8.0), + REAL_CONST(107358.69704179741/8.0), + REAL_CONST(107382.83276119467/8.0), + REAL_CONST(107406.96983687414/8.0), + REAL_CONST(107431.10826868335/8.0), + REAL_CONST(107455.24805646999/8.0), + REAL_CONST(107479.38920008171/8.0), + REAL_CONST(107503.53169936626/8.0), + REAL_CONST(107527.67555417139/8.0), + REAL_CONST(107551.82076434491/8.0), + REAL_CONST(107575.96732973469/8.0), + REAL_CONST(107600.11525018861/8.0), + REAL_CONST(107624.26452555459/8.0), + REAL_CONST(107648.41515568066/8.0), + REAL_CONST(107672.56714041479/8.0), + REAL_CONST(107696.72047960508/8.0), + REAL_CONST(107720.87517309963/8.0), + REAL_CONST(107745.03122074658/8.0), + REAL_CONST(107769.18862239413/8.0), + REAL_CONST(107793.34737789053/8.0), + REAL_CONST(107817.50748708403/8.0), + REAL_CONST(107841.66894982298/8.0), + REAL_CONST(107865.83176595572/8.0), + REAL_CONST(107889.99593533068/8.0), + REAL_CONST(107914.16145779629/8.0), + REAL_CONST(107938.32833320105/8.0), + REAL_CONST(107962.49656139348/8.0), + REAL_CONST(107986.66614222217/8.0), + REAL_CONST(108010.83707553572/8.0), + REAL_CONST(108035.00936118282/8.0), + REAL_CONST(108059.18299901215/8.0), + REAL_CONST(108083.35798887245/8.0), + REAL_CONST(108107.53433061253/8.0), + REAL_CONST(108131.71202408121/8.0), + REAL_CONST(108155.89106912735/8.0), + REAL_CONST(108180.07146559987/8.0), + REAL_CONST(108204.25321334775/8.0), + REAL_CONST(108228.43631221994/8.0), + REAL_CONST(108252.62076206553/8.0), + REAL_CONST(108276.80656273357/8.0), + REAL_CONST(108300.99371407321/8.0), + REAL_CONST(108325.18221593359/8.0), + REAL_CONST(108349.37206816394/8.0), + REAL_CONST(108373.56327061349/8.0), + REAL_CONST(108397.75582313156/8.0), + REAL_CONST(108421.94972556747/8.0), + REAL_CONST(108446.1449777706/8.0), + REAL_CONST(108470.34157959036/8.0), + REAL_CONST(108494.53953087622/8.0), + REAL_CONST(108518.73883147769/8.0), + REAL_CONST(108542.93948124432/8.0), + REAL_CONST(108567.14148002568/8.0), + REAL_CONST(108591.34482767139/8.0), + REAL_CONST(108615.54952403114/8.0), + REAL_CONST(108639.75556895464/8.0), + REAL_CONST(108663.96296229165/8.0), + REAL_CONST(108688.17170389196/8.0), + REAL_CONST(108712.38179360541/8.0), + REAL_CONST(108736.59323128188/8.0), + REAL_CONST(108760.80601677128/8.0), + REAL_CONST(108785.02014992358/8.0), + REAL_CONST(108809.23563058881/8.0), + REAL_CONST(108833.45245861699/8.0), + REAL_CONST(108857.67063385822/8.0), + REAL_CONST(108881.89015616261/8.0), + REAL_CONST(108906.11102538036/8.0), + REAL_CONST(108930.33324136167/8.0), + REAL_CONST(108954.55680395682/8.0), + REAL_CONST(108978.78171301607/8.0), + REAL_CONST(109003.00796838976/8.0), + REAL_CONST(109027.23556992831/8.0), + REAL_CONST(109051.46451748211/8.0), + REAL_CONST(109075.69481090162/8.0), + REAL_CONST(109099.92645003737/8.0), + REAL_CONST(109124.15943473989/8.0), + REAL_CONST(109148.39376485976/8.0), + REAL_CONST(109172.62944024763/8.0), + REAL_CONST(109196.86646075416/8.0), + REAL_CONST(109221.10482623006/8.0), + REAL_CONST(109245.34453652608/8.0), + REAL_CONST(109269.58559149304/8.0), + REAL_CONST(109293.82799098175/8.0), + REAL_CONST(109318.07173484311/8.0), + REAL_CONST(109342.31682292801/8.0), + REAL_CONST(109366.56325508743/8.0), + REAL_CONST(109390.81103117237/8.0), + REAL_CONST(109415.06015103387/8.0), + REAL_CONST(109439.31061452301/8.0), + REAL_CONST(109463.56242149093/8.0), + REAL_CONST(109487.8155717888/8.0), + REAL_CONST(109512.07006526781/8.0), + REAL_CONST(109536.3259017792/8.0), + REAL_CONST(109560.58308117429/8.0), + REAL_CONST(109584.8416033044/8.0), + REAL_CONST(109609.1014680209/8.0), + REAL_CONST(109633.36267517522/8.0), + REAL_CONST(109657.62522461878/8.0), + REAL_CONST(109681.88911620311/8.0), + REAL_CONST(109706.15434977971/8.0), + REAL_CONST(109730.4209252002/8.0), + REAL_CONST(109754.68884231619/8.0), + REAL_CONST(109778.95810097932/8.0), + REAL_CONST(109803.22870104131/8.0), + REAL_CONST(109827.50064235389/8.0), + REAL_CONST(109851.77392476884/8.0), + REAL_CONST(109876.04854813802/8.0), + REAL_CONST(109900.32451231324/8.0), + REAL_CONST(109924.60181714644/8.0), + REAL_CONST(109948.88046248957/8.0), + REAL_CONST(109973.1604481946/8.0), + REAL_CONST(109997.44177411357/8.0), + REAL_CONST(110021.72444009855/8.0), + REAL_CONST(110046.00844600165/8.0), + REAL_CONST(110070.29379167501/8.0), + REAL_CONST(110094.58047697082/8.0), + REAL_CONST(110118.86850174134/8.0), + REAL_CONST(110143.15786583882/8.0), + REAL_CONST(110167.44856911557/8.0), + REAL_CONST(110191.74061142397/8.0), + REAL_CONST(110216.03399261639/8.0), + REAL_CONST(110240.32871254528/8.0), + REAL_CONST(110264.62477106311/8.0), + REAL_CONST(110288.9221680224/8.0), + REAL_CONST(110313.22090327571/8.0), + REAL_CONST(110337.52097667565/8.0), + REAL_CONST(110361.82238807483/8.0), + REAL_CONST(110386.12513732594/8.0), + REAL_CONST(110410.42922428172/8.0), + REAL_CONST(110434.73464879491/8.0), + REAL_CONST(110459.04141071832/8.0), + REAL_CONST(110483.34950990479/8.0), + REAL_CONST(110507.6589462072/8.0), + REAL_CONST(110531.96971947847/8.0), + REAL_CONST(110556.28182957157/8.0), + REAL_CONST(110580.5952763395/8.0), + REAL_CONST(110604.91005963532/8.0), + REAL_CONST(110629.22617931209/8.0), + REAL_CONST(110653.54363522294/8.0), + REAL_CONST(110677.86242722106/8.0), + REAL_CONST(110702.18255515963/8.0), + REAL_CONST(110726.50401889188/8.0), + REAL_CONST(110750.82681827113/8.0), + REAL_CONST(110775.1509531507/8.0), + REAL_CONST(110799.47642338395/8.0), + REAL_CONST(110823.80322882428/8.0), + REAL_CONST(110848.13136932514/8.0), + REAL_CONST(110872.46084474004/8.0), + REAL_CONST(110896.79165492248/8.0), + REAL_CONST(110921.12379972603/8.0), + REAL_CONST(110945.4572790043/8.0), + REAL_CONST(110969.79209261097/8.0), + REAL_CONST(110994.12824039967/8.0), + REAL_CONST(111018.46572222417/8.0), + REAL_CONST(111042.80453793822/8.0), + REAL_CONST(111067.14468739564/8.0), + REAL_CONST(111091.48617045028/8.0), + REAL_CONST(111115.82898695602/8.0), + REAL_CONST(111140.1731367668/8.0), + REAL_CONST(111164.51861973655/8.0), + REAL_CONST(111188.86543571933/8.0), + REAL_CONST(111213.21358456917/8.0), + REAL_CONST(111237.56306614014/8.0), + REAL_CONST(111261.91388028639/8.0), + REAL_CONST(111286.26602686207/8.0), + REAL_CONST(111310.61950572141/8.0), + REAL_CONST(111334.97431671864/8.0), + REAL_CONST(111359.33045970804/8.0), + REAL_CONST(111383.68793454397/8.0), + REAL_CONST(111408.04674108078/8.0), + REAL_CONST(111432.40687917286/8.0), + REAL_CONST(111456.76834867468/8.0), + REAL_CONST(111481.13114944073/8.0), + REAL_CONST(111505.49528132551/8.0), + REAL_CONST(111529.86074418361/8.0), + REAL_CONST(111554.22753786964/8.0), + REAL_CONST(111578.59566223821/8.0), + REAL_CONST(111602.96511714405/8.0), + REAL_CONST(111627.33590244185/8.0), + REAL_CONST(111651.7080179864/8.0), + REAL_CONST(111676.08146363248/8.0), + REAL_CONST(111700.45623923496/8.0), + REAL_CONST(111724.8323446487/8.0), + REAL_CONST(111749.20977972864/8.0), + REAL_CONST(111773.58854432974/8.0), + REAL_CONST(111797.96863830699/8.0), + REAL_CONST(111822.35006151545/8.0), + REAL_CONST(111846.73281381019/8.0), + REAL_CONST(111871.11689504632/8.0), + REAL_CONST(111895.50230507903/8.0), + REAL_CONST(111919.8890437635/8.0), + REAL_CONST(111944.27711095495/8.0), + REAL_CONST(111968.6665065087/8.0), + REAL_CONST(111993.05723028004/8.0), + REAL_CONST(112017.44928212435/8.0), + REAL_CONST(112041.842661897/8.0), + REAL_CONST(112066.23736945343/8.0), + REAL_CONST(112090.63340464912/8.0), + REAL_CONST(112115.03076733962/8.0), + REAL_CONST(112139.42945738042/8.0), + REAL_CONST(112163.82947462716/8.0), + REAL_CONST(112188.23081893545/8.0), + REAL_CONST(112212.63349016097/8.0), + REAL_CONST(112237.03748815943/8.0), + REAL_CONST(112261.44281278658/8.0), + REAL_CONST(112285.84946389822/8.0), + REAL_CONST(112310.25744135017/8.0), + REAL_CONST(112334.66674499828/8.0), + REAL_CONST(112359.07737469849/8.0), + REAL_CONST(112383.48933030672/8.0), + REAL_CONST(112407.90261167898/8.0), + REAL_CONST(112432.31721867126/8.0), + REAL_CONST(112456.73315113965/8.0), + REAL_CONST(112481.15040894024/8.0), + REAL_CONST(112505.56899192919/8.0), + REAL_CONST(112529.98889996267/8.0), + REAL_CONST(112554.41013289688/8.0), + REAL_CONST(112578.8326905881/8.0), + REAL_CONST(112603.25657289263/8.0), + REAL_CONST(112627.68177966679/8.0), + REAL_CONST(112652.10831076698/8.0), + REAL_CONST(112676.53616604958/8.0), + REAL_CONST(112700.96534537108/8.0), + REAL_CONST(112725.39584858794/8.0), + REAL_CONST(112749.82767555672/8.0), + REAL_CONST(112774.26082613398/8.0), + REAL_CONST(112798.6953001763/8.0), + REAL_CONST(112823.13109754038/8.0), + REAL_CONST(112847.56821808286/8.0), + REAL_CONST(112872.00666166049/8.0), + REAL_CONST(112896.44642813003/8.0), + REAL_CONST(112920.88751734827/8.0), + REAL_CONST(112945.32992917208/8.0), + REAL_CONST(112969.77366345831/8.0), + REAL_CONST(112994.21872006389/8.0), + REAL_CONST(113018.66509884578/8.0), + REAL_CONST(113043.11279966099/8.0), + REAL_CONST(113067.56182236652/8.0), + REAL_CONST(113092.01216681948/8.0), + REAL_CONST(113116.46383287695/8.0), + REAL_CONST(113140.9168203961/8.0), + REAL_CONST(113165.37112923413/8.0), + REAL_CONST(113189.82675924824/8.0), + REAL_CONST(113214.28371029573/8.0), + REAL_CONST(113238.74198223387/8.0), + REAL_CONST(113263.20157492002/8.0), + REAL_CONST(113287.66248821157/8.0), + REAL_CONST(113312.12472196593/8.0), + REAL_CONST(113336.58827604055/8.0), + REAL_CONST(113361.05315029295/8.0), + REAL_CONST(113385.51934458067/8.0), + REAL_CONST(113409.98685876124/8.0), + REAL_CONST(113434.45569269233/8.0), + REAL_CONST(113458.92584623155/8.0), + REAL_CONST(113483.39731923661/8.0), + REAL_CONST(113507.87011156522/8.0), + REAL_CONST(113532.34422307517/8.0), + REAL_CONST(113556.81965362425/8.0), + REAL_CONST(113581.2964030703/8.0), + REAL_CONST(113605.77447127122/8.0), + REAL_CONST(113630.25385808491/8.0), + REAL_CONST(113654.73456336933/8.0), + REAL_CONST(113679.2165869825/8.0), + REAL_CONST(113703.69992878241/8.0), + REAL_CONST(113728.18458862718/8.0), + REAL_CONST(113752.67056637487/8.0), + REAL_CONST(113777.15786188368/8.0), + REAL_CONST(113801.64647501177/8.0), + REAL_CONST(113826.13640561736/8.0), + REAL_CONST(113850.62765355874/8.0), + REAL_CONST(113875.12021869418/8.0), + REAL_CONST(113899.61410088204/8.0), + REAL_CONST(113924.1092999807/8.0), + REAL_CONST(113948.60581584855/8.0), + REAL_CONST(113973.10364834407/8.0), + REAL_CONST(113997.60279732574/8.0), + REAL_CONST(114022.1032626521/8.0), + REAL_CONST(114046.60504418171/8.0), + REAL_CONST(114071.10814177318/8.0), + REAL_CONST(114095.61255528514/8.0), + REAL_CONST(114120.11828457628/8.0), + REAL_CONST(114144.62532950533/8.0), + REAL_CONST(114169.13368993104/8.0), + REAL_CONST(114193.6433657122/8.0), + REAL_CONST(114218.15435670764/8.0), + REAL_CONST(114242.66666277625/8.0), + REAL_CONST(114267.18028377694/8.0), + REAL_CONST(114291.69521956862/8.0), + REAL_CONST(114316.21147001031/8.0), + REAL_CONST(114340.72903496103/8.0), + REAL_CONST(114365.24791427983/8.0), + REAL_CONST(114389.7681078258/8.0), + REAL_CONST(114414.2896154581/8.0), + REAL_CONST(114438.81243703589/8.0), + REAL_CONST(114463.33657241837/8.0), + REAL_CONST(114487.8620214648/8.0), + REAL_CONST(114512.38878403447/8.0), + REAL_CONST(114536.91685998671/8.0), + REAL_CONST(114561.44624918087/8.0), + REAL_CONST(114585.97695147636/8.0), + REAL_CONST(114610.5089667326/8.0), + REAL_CONST(114635.04229480909/8.0), + REAL_CONST(114659.57693556532/8.0), + REAL_CONST(114684.11288886084/8.0), + REAL_CONST(114708.65015455526/8.0), + REAL_CONST(114733.18873250818/8.0), + REAL_CONST(114757.72862257928/8.0), + REAL_CONST(114782.26982462825/8.0), + REAL_CONST(114806.81233851484/8.0), + REAL_CONST(114831.35616409882/8.0), + REAL_CONST(114855.90130123998/8.0), + REAL_CONST(114880.44774979822/8.0), + REAL_CONST(114904.99550963337/8.0), + REAL_CONST(114929.5445806054/8.0), + REAL_CONST(114954.09496257425/8.0), + REAL_CONST(114978.64665539992/8.0), + REAL_CONST(115003.19965894247/8.0), + REAL_CONST(115027.75397306195/8.0), + REAL_CONST(115052.30959761847/8.0), + REAL_CONST(115076.86653247218/8.0), + REAL_CONST(115101.42477748329/8.0), + REAL_CONST(115125.984332512/8.0), + REAL_CONST(115150.54519741859/8.0), + REAL_CONST(115175.10737206334/8.0), + REAL_CONST(115199.67085630659/8.0), + REAL_CONST(115224.23565000873/8.0), + REAL_CONST(115248.80175303014/8.0), + REAL_CONST(115273.3691652313/8.0), + REAL_CONST(115297.93788647266/8.0), + REAL_CONST(115322.50791661476/8.0), + REAL_CONST(115347.07925551817/8.0), + REAL_CONST(115371.65190304347/8.0), + REAL_CONST(115396.22585905129/8.0), + REAL_CONST(115420.80112340231/8.0), + REAL_CONST(115445.37769595724/8.0), + REAL_CONST(115469.95557657682/8.0), + REAL_CONST(115494.53476512182/8.0), + REAL_CONST(115519.11526145306/8.0), + REAL_CONST(115543.69706543141/8.0), + REAL_CONST(115568.28017691776/8.0), + REAL_CONST(115592.86459577303/8.0), + REAL_CONST(115617.4503218582/8.0), + REAL_CONST(115642.03735503425/8.0), + REAL_CONST(115666.62569516223/8.0), + REAL_CONST(115691.21534210323/8.0), + REAL_CONST(115715.80629571836/8.0), + REAL_CONST(115740.39855586876/8.0), + REAL_CONST(115764.99212241563/8.0), + REAL_CONST(115789.58699522018/8.0), + REAL_CONST(115814.18317414368/8.0), + REAL_CONST(115838.78065904744/8.0), + REAL_CONST(115863.37944979276/8.0), + REAL_CONST(115887.97954624105/8.0), + REAL_CONST(115912.5809482537/8.0), + REAL_CONST(115937.18365569216/8.0), + REAL_CONST(115961.78766841792/8.0), + REAL_CONST(115986.39298629249/8.0), + REAL_CONST(116010.99960917742/8.0), + REAL_CONST(116035.60753693432/8.0), + REAL_CONST(116060.21676942479/8.0), + REAL_CONST(116084.82730651053/8.0), + REAL_CONST(116109.43914805322/8.0), + REAL_CONST(116134.0522939146/8.0), + REAL_CONST(116158.66674395646/8.0), + REAL_CONST(116183.2824980406/8.0), + REAL_CONST(116207.89955602887/8.0), + REAL_CONST(116232.51791778316/8.0), + REAL_CONST(116257.13758316539/8.0), + REAL_CONST(116281.75855203751/8.0), + REAL_CONST(116306.38082426153/8.0), + REAL_CONST(116331.00439969949/8.0), + REAL_CONST(116355.62927821343/8.0), + REAL_CONST(116380.25545966547/8.0), + REAL_CONST(116404.88294391775/8.0), + REAL_CONST(116429.51173083246/8.0), + REAL_CONST(116454.14182027178/8.0), + REAL_CONST(116478.77321209799/8.0), + REAL_CONST(116503.40590617337/8.0), + REAL_CONST(116528.03990236025/8.0), + REAL_CONST(116552.67520052097/8.0), + REAL_CONST(116577.31180051794/8.0), + REAL_CONST(116601.94970221359/8.0), + REAL_CONST(116626.5889054704/8.0), + REAL_CONST(116651.22941015086/8.0), + REAL_CONST(116675.87121611751/8.0), + REAL_CONST(116700.51432323294/8.0), + REAL_CONST(116725.15873135976/8.0), + REAL_CONST(116749.8044403606/8.0), + REAL_CONST(116774.45145009817/8.0), + REAL_CONST(116799.0997604352/8.0), + REAL_CONST(116823.74937123443/8.0), + REAL_CONST(116848.40028235866/8.0), + REAL_CONST(116873.05249367072/8.0), + REAL_CONST(116897.70600503348/8.0), + REAL_CONST(116922.36081630984/8.0), + REAL_CONST(116947.01692736275/8.0), + REAL_CONST(116971.67433805518/8.0), + REAL_CONST(116996.33304825013/8.0), + REAL_CONST(117020.99305781067/8.0), + REAL_CONST(117045.65436659988/8.0), + REAL_CONST(117070.31697448085/8.0), + REAL_CONST(117094.98088131678/8.0), + REAL_CONST(117119.64608697082/8.0), + REAL_CONST(117144.31259130624/8.0), + REAL_CONST(117168.98039418629/8.0), + REAL_CONST(117193.64949547425/8.0), + REAL_CONST(117218.31989503348/8.0), + REAL_CONST(117242.99159272734/8.0), + REAL_CONST(117267.66458841923/8.0), + REAL_CONST(117292.33888197262/8.0), + REAL_CONST(117317.01447325097/8.0), + REAL_CONST(117341.6913621178/8.0), + REAL_CONST(117366.36954843666/8.0), + REAL_CONST(117391.04903207115/8.0), + REAL_CONST(117415.72981288488/8.0), + REAL_CONST(117440.41189074152/8.0), + REAL_CONST(117465.09526550474/8.0), + REAL_CONST(117489.77993703831/8.0), + REAL_CONST(117514.46590520597/8.0), + REAL_CONST(117539.15316987153/8.0), + REAL_CONST(117563.84173089883/8.0), + REAL_CONST(117588.53158815173/8.0), + REAL_CONST(117613.22274149416/8.0), + REAL_CONST(117637.91519079007/8.0), + REAL_CONST(117662.60893590341/8.0), + REAL_CONST(117687.30397669821/8.0), + REAL_CONST(117712.00031303853/8.0), + REAL_CONST(117736.69794478847/8.0), + REAL_CONST(117761.39687181212/8.0), + REAL_CONST(117786.09709397367/8.0), + REAL_CONST(117810.7986111373/8.0), + REAL_CONST(117835.50142316725/8.0), + REAL_CONST(117860.20552992777/8.0), + REAL_CONST(117884.91093128319/8.0), + REAL_CONST(117909.6176270978/8.0), + REAL_CONST(117934.32561723603/8.0), + REAL_CONST(117959.03490156225/8.0), + REAL_CONST(117983.74547994092/8.0), + REAL_CONST(118008.45735223651/8.0), + REAL_CONST(118033.17051831353/8.0), + REAL_CONST(118057.88497803656/8.0), + REAL_CONST(118082.60073127014/8.0), + REAL_CONST(118107.31777787894/8.0), + REAL_CONST(118132.03611772758/8.0), + REAL_CONST(118156.75575068076/8.0), + REAL_CONST(118181.47667660323/8.0), + REAL_CONST(118206.19889535972/8.0), + REAL_CONST(118230.92240681504/8.0), + REAL_CONST(118255.64721083404/8.0), + REAL_CONST(118280.37330728157/8.0), + REAL_CONST(118305.10069602253/8.0), + REAL_CONST(118329.82937692189/8.0), + REAL_CONST(118354.55934984458/8.0), + REAL_CONST(118379.29061465565/8.0), + REAL_CONST(118404.02317122012/8.0), + REAL_CONST(118428.75701940308/8.0), + REAL_CONST(118453.49215906965/8.0), + REAL_CONST(118478.22859008498/8.0), + REAL_CONST(118502.96631231424/8.0), + REAL_CONST(118527.70532562268/8.0), + REAL_CONST(118552.44562987552/8.0), + REAL_CONST(118577.18722493808/8.0), + REAL_CONST(118601.93011067568/8.0), + REAL_CONST(118626.67428695368/8.0), + REAL_CONST(118651.41975363747/8.0), + REAL_CONST(118676.16651059251/8.0), + REAL_CONST(118700.91455768423/8.0), + REAL_CONST(118725.66389477813/8.0), + REAL_CONST(118750.41452173979/8.0), + REAL_CONST(118775.16643843475/8.0), + REAL_CONST(118799.91964472862/8.0), + REAL_CONST(118824.67414048707/8.0), + REAL_CONST(118849.42992557574/8.0), + REAL_CONST(118874.18699986035/8.0), + REAL_CONST(118898.94536320666/8.0), + REAL_CONST(118923.70501548045/8.0), + REAL_CONST(118948.46595654752/8.0), + REAL_CONST(118973.22818627374/8.0), + REAL_CONST(118997.99170452499/8.0), + REAL_CONST(119022.7565111672/8.0), + REAL_CONST(119047.52260606633/8.0), + REAL_CONST(119072.28998908834/8.0), + REAL_CONST(119097.0586600993/8.0), + REAL_CONST(119121.82861896523/8.0), + REAL_CONST(119146.59986555226/8.0), + REAL_CONST(119171.3723997265/8.0), + REAL_CONST(119196.14622135412/8.0), + REAL_CONST(119220.92133030134/8.0), + REAL_CONST(119245.69772643436/8.0), + REAL_CONST(119270.47540961947/8.0), + REAL_CONST(119295.25437972297/8.0), + REAL_CONST(119320.03463661121/8.0), + REAL_CONST(119344.81618015055/8.0), + REAL_CONST(119369.5990102074/8.0), + REAL_CONST(119394.38312664822/8.0), + REAL_CONST(119419.16852933947/8.0), + REAL_CONST(119443.95521814766/8.0), + REAL_CONST(119468.74319293935/8.0), + REAL_CONST(119493.53245358112/8.0), + REAL_CONST(119518.32299993958/8.0), + REAL_CONST(119543.11483188139/8.0), + REAL_CONST(119567.90794927324/8.0), + REAL_CONST(119592.70235198183/8.0), + REAL_CONST(119617.49803987393/8.0), + REAL_CONST(119642.29501281632/8.0), + REAL_CONST(119667.09327067583/8.0), + REAL_CONST(119691.89281331931/8.0), + REAL_CONST(119716.69364061367/8.0), + REAL_CONST(119741.49575242582/8.0), + REAL_CONST(119766.29914862274/8.0), + REAL_CONST(119791.10382907141/8.0), + REAL_CONST(119815.90979363887/8.0), + REAL_CONST(119840.71704219218/8.0), + REAL_CONST(119865.52557459843/8.0), + REAL_CONST(119890.33539072477/8.0), + REAL_CONST(119915.14649043836/8.0), + REAL_CONST(119939.95887360642/8.0), + REAL_CONST(119964.77254009615/8.0), + REAL_CONST(119989.58748977486/8.0), + REAL_CONST(120014.40372250983/8.0), + REAL_CONST(120039.22123816841/8.0), + REAL_CONST(120064.04003661797/8.0), + REAL_CONST(120088.86011772591/8.0), + REAL_CONST(120113.6814813597/8.0), + REAL_CONST(120138.5041273868/8.0), + REAL_CONST(120163.3280556747/8.0), + REAL_CONST(120188.15326609099/8.0), + REAL_CONST(120212.97975850321/8.0), + REAL_CONST(120237.807532779/8.0), + REAL_CONST(120262.63658878599/8.0), + REAL_CONST(120287.46692639188/8.0), + REAL_CONST(120312.29854546436/8.0), + REAL_CONST(120337.13144587121/8.0), + REAL_CONST(120361.9656274802/8.0), + REAL_CONST(120386.80109015915/8.0), + REAL_CONST(120411.63783377589/8.0), + REAL_CONST(120436.47585819835/8.0), + REAL_CONST(120461.31516329442/8.0), + REAL_CONST(120486.15574893207/8.0), + REAL_CONST(120510.99761497928/8.0), + REAL_CONST(120535.84076130406/8.0), + REAL_CONST(120560.68518777451/8.0), + REAL_CONST(120585.53089425867/8.0), + REAL_CONST(120610.3778806247/8.0), + REAL_CONST(120635.22614674074/8.0), + REAL_CONST(120660.07569247499/8.0), + REAL_CONST(120684.92651769568/8.0), + REAL_CONST(120709.77862227106/8.0), + REAL_CONST(120734.63200606944/8.0), + REAL_CONST(120759.48666895913/8.0), + REAL_CONST(120784.3426108085/8.0), + REAL_CONST(120809.19983148595/8.0), + REAL_CONST(120834.05833085992/8.0), + REAL_CONST(120858.91810879884/8.0), + REAL_CONST(120883.77916517125/8.0), + REAL_CONST(120908.64149984565/8.0), + REAL_CONST(120933.5051126906/8.0), + REAL_CONST(120958.37000357473/8.0), + REAL_CONST(120983.23617236665/8.0), + REAL_CONST(121008.10361893504/8.0), + REAL_CONST(121032.97234314861/8.0), + REAL_CONST(121057.84234487606/8.0), + REAL_CONST(121082.71362398617/8.0), + REAL_CONST(121107.58618034775/8.0), + REAL_CONST(121132.46001382964/8.0), + REAL_CONST(121157.33512430069/8.0), + REAL_CONST(121182.21151162982/8.0), + REAL_CONST(121207.08917568595/8.0), + REAL_CONST(121231.96811633807/8.0), + REAL_CONST(121256.84833345517/8.0), + REAL_CONST(121281.72982690629/8.0), + REAL_CONST(121306.61259656049/8.0), + REAL_CONST(121331.49664228689/8.0), + REAL_CONST(121356.38196395461/8.0), + REAL_CONST(121381.26856143285/8.0), + REAL_CONST(121406.15643459078/8.0), + REAL_CONST(121431.04558329767/8.0), + REAL_CONST(121455.93600742276/8.0), + REAL_CONST(121480.82770683538/8.0), + REAL_CONST(121505.72068140487/8.0), + REAL_CONST(121530.61493100057/8.0), + REAL_CONST(121555.51045549192/8.0), + REAL_CONST(121580.40725474835/8.0), + REAL_CONST(121605.30532863933/8.0), + REAL_CONST(121630.20467703436/8.0), + REAL_CONST(121655.10529980299/8.0), + REAL_CONST(121680.00719681478/8.0), + REAL_CONST(121704.91036793934/8.0), + REAL_CONST(121729.81481304632/8.0), + REAL_CONST(121754.72053200539/8.0), + REAL_CONST(121779.62752468624/8.0), + REAL_CONST(121804.53579095862/8.0), + REAL_CONST(121829.44533069231/8.0), + REAL_CONST(121854.3561437571/8.0), + REAL_CONST(121879.26823002285/8.0), + REAL_CONST(121904.1815893594/8.0), + REAL_CONST(121929.09622163669/8.0), + REAL_CONST(121954.01212672464/8.0), + REAL_CONST(121978.92930449323/8.0), + REAL_CONST(122003.84775481246/8.0), + REAL_CONST(122028.76747755238/8.0), + REAL_CONST(122053.68847258303/8.0), + REAL_CONST(122078.61073977455/8.0), + REAL_CONST(122103.53427899707/8.0), + REAL_CONST(122128.45909012076/8.0), + REAL_CONST(122153.38517301581/8.0), + REAL_CONST(122178.31252755247/8.0), + REAL_CONST(122203.24115360099/8.0), + REAL_CONST(122228.17105103172/8.0), + REAL_CONST(122253.10221971494/8.0), + REAL_CONST(122278.03465952107/8.0), + REAL_CONST(122302.96837032049/8.0), + REAL_CONST(122327.90335198362/8.0), + REAL_CONST(122352.83960438096/8.0), + REAL_CONST(122377.777127383/8.0), + REAL_CONST(122402.71592086025/8.0), + REAL_CONST(122427.65598468333/8.0), + REAL_CONST(122452.59731872278/8.0), + REAL_CONST(122477.53992284928/8.0), + REAL_CONST(122502.48379693348/8.0), + REAL_CONST(122527.42894084606/8.0), + REAL_CONST(122552.37535445779/8.0), + REAL_CONST(122577.32303763942/8.0), + REAL_CONST(122602.27199026172/8.0), + REAL_CONST(122627.22221219557/8.0), + REAL_CONST(122652.17370331181/8.0), + REAL_CONST(122677.12646348133/8.0), + REAL_CONST(122702.08049257506/8.0), + REAL_CONST(122727.03579046397/8.0), + REAL_CONST(122751.99235701906/8.0), + REAL_CONST(122776.95019211136/8.0), + REAL_CONST(122801.9092956119/8.0), + REAL_CONST(122826.8696673918/8.0), + REAL_CONST(122851.83130732219/8.0), + REAL_CONST(122876.79421527422/8.0), + REAL_CONST(122901.75839111909/8.0), + REAL_CONST(122926.72383472799/8.0), + REAL_CONST(122951.69054597223/8.0), + REAL_CONST(122976.65852472307/8.0), + REAL_CONST(123001.62777085182/8.0), + REAL_CONST(123026.59828422987/8.0), + REAL_CONST(123051.57006472857/8.0), + REAL_CONST(123076.54311221937/8.0), + REAL_CONST(123101.5174265737/8.0), + REAL_CONST(123126.49300766307/8.0), + REAL_CONST(123151.46985535898/8.0), + REAL_CONST(123176.44796953299/8.0), + REAL_CONST(123201.42735005668/8.0), + REAL_CONST(123226.40799680166/8.0), + REAL_CONST(123251.38990963959/8.0), + REAL_CONST(123276.37308844214/8.0), + REAL_CONST(123301.35753308103/8.0), + REAL_CONST(123326.343243428/8.0), + REAL_CONST(123351.33021935483/8.0), + REAL_CONST(123376.31846073334/8.0), + REAL_CONST(123401.30796743535/8.0), + REAL_CONST(123426.29873933276/8.0), + REAL_CONST(123451.29077629748/8.0), + REAL_CONST(123476.28407820144/8.0), + REAL_CONST(123501.2786449166/8.0), + REAL_CONST(123526.27447631498/8.0), + REAL_CONST(123551.27157226863/8.0), + REAL_CONST(123576.26993264959/8.0), + REAL_CONST(123601.26955732999/8.0), + REAL_CONST(123626.27044618195/8.0), + REAL_CONST(123651.27259907764/8.0), + REAL_CONST(123676.27601588926/8.0), + REAL_CONST(123701.28069648903/8.0), + REAL_CONST(123726.28664074924/8.0), + REAL_CONST(123751.29384854218/8.0), + REAL_CONST(123776.30231974016/8.0), + REAL_CONST(123801.31205421555/8.0), + REAL_CONST(123826.32305184075/8.0), + REAL_CONST(123851.33531248817/8.0), + REAL_CONST(123876.34883603029/8.0), + REAL_CONST(123901.36362233957/8.0), + REAL_CONST(123926.37967128855/8.0), + REAL_CONST(123951.39698274979/8.0), + REAL_CONST(123976.41555659588/8.0), + REAL_CONST(124001.43539269941/8.0), + REAL_CONST(124026.45649093305/8.0), + REAL_CONST(124051.47885116948/8.0), + REAL_CONST(124076.50247328142/8.0), + REAL_CONST(124101.5273571416/8.0), + REAL_CONST(124126.55350262282/8.0), + REAL_CONST(124151.58090959788/8.0), + REAL_CONST(124176.60957793961/8.0), + REAL_CONST(124201.63950752091/8.0), + REAL_CONST(124226.67069821467/8.0), + REAL_CONST(124251.70314989384/8.0), + REAL_CONST(124276.73686243138/8.0), + REAL_CONST(124301.7718357003/8.0), + REAL_CONST(124326.80806957364/8.0), + REAL_CONST(124351.84556392446/8.0), + REAL_CONST(124376.88431862585/8.0), + REAL_CONST(124401.92433355095/8.0), + REAL_CONST(124426.96560857294/8.0), + REAL_CONST(124452.00814356498/8.0), + REAL_CONST(124477.05193840031/8.0), + REAL_CONST(124502.0969929522/8.0), + REAL_CONST(124527.14330709392/8.0), + REAL_CONST(124552.19088069882/8.0), + REAL_CONST(124577.23971364023/8.0), + REAL_CONST(124602.28980579154/8.0), + REAL_CONST(124627.34115702618/8.0), + REAL_CONST(124652.3937672176/8.0), + REAL_CONST(124677.44763623926/8.0), + REAL_CONST(124702.50276396469/8.0), + REAL_CONST(124727.55915026742/8.0), + REAL_CONST(124752.61679502104/8.0), + REAL_CONST(124777.67569809916/8.0), + REAL_CONST(124802.73585937542/8.0), + REAL_CONST(124827.79727872348/8.0), + REAL_CONST(124852.85995601704/8.0), + REAL_CONST(124877.92389112986/8.0), + REAL_CONST(124902.98908393568/8.0), + REAL_CONST(124928.05553430831/8.0), + REAL_CONST(124953.1232421216/8.0), + REAL_CONST(124978.19220724938/8.0), + REAL_CONST(125003.26242956554/8.0), + REAL_CONST(125028.33390894404/8.0), + REAL_CONST(125053.40664525882/8.0), + REAL_CONST(125078.48063838384/8.0), + REAL_CONST(125103.55588819318/8.0), + REAL_CONST(125128.63239456083/8.0), + REAL_CONST(125153.71015736091/8.0), + REAL_CONST(125178.78917646752/8.0), + REAL_CONST(125203.86945175481/8.0), + REAL_CONST(125228.95098309696/8.0), + REAL_CONST(125254.03377036817/8.0), + REAL_CONST(125279.1178134427/8.0), + REAL_CONST(125304.20311219479/8.0), + REAL_CONST(125329.28966649878/8.0), + REAL_CONST(125354.37747622898/8.0), + REAL_CONST(125379.46654125977/8.0), + REAL_CONST(125404.55686146552/8.0), + REAL_CONST(125429.6484367207/8.0), + REAL_CONST(125454.74126689974/8.0), + REAL_CONST(125479.83535187715/8.0), + REAL_CONST(125504.93069152744/8.0), + REAL_CONST(125530.02728572517/8.0), + REAL_CONST(125555.12513434493/8.0), + REAL_CONST(125580.22423726133/8.0), + REAL_CONST(125605.32459434902/8.0), + REAL_CONST(125630.4262054827/8.0), + REAL_CONST(125655.52907053704/8.0), + REAL_CONST(125680.63318938682/8.0), + REAL_CONST(125705.73856190679/8.0), + REAL_CONST(125730.84518797178/8.0), + REAL_CONST(125755.9530674566/8.0), + REAL_CONST(125781.06220023613/8.0), + REAL_CONST(125806.17258618528/8.0), + REAL_CONST(125831.28422517896/8.0), + REAL_CONST(125856.39711709213/8.0), + REAL_CONST(125881.51126179981/8.0), + REAL_CONST(125906.62665917698/8.0), + REAL_CONST(125931.74330909875/8.0), + REAL_CONST(125956.86121144016/8.0), + REAL_CONST(125981.98036607634/8.0), + REAL_CONST(126007.10077288245/8.0), + REAL_CONST(126032.22243173365/8.0), + REAL_CONST(126057.34534250517/8.0), + REAL_CONST(126082.46950507225/8.0), + REAL_CONST(126107.59491931014/8.0), + REAL_CONST(126132.72158509417/8.0), + REAL_CONST(126157.84950229966/8.0), + REAL_CONST(126182.97867080198/8.0), + REAL_CONST(126208.10909047653/8.0), + REAL_CONST(126233.24076119871/8.0), + REAL_CONST(126258.37368284403/8.0), + REAL_CONST(126283.50785528794/8.0), + REAL_CONST(126308.64327840599/8.0), + REAL_CONST(126333.77995207369/8.0), + REAL_CONST(126358.91787616667/8.0), + REAL_CONST(126384.0570505605/8.0), + REAL_CONST(126409.19747513086/8.0), + REAL_CONST(126434.3391497534/8.0), + REAL_CONST(126459.48207430386/8.0), + REAL_CONST(126484.62624865794/8.0), + REAL_CONST(126509.77167269142/8.0), + REAL_CONST(126534.9183462801/8.0), + REAL_CONST(126560.06626929982/8.0), + REAL_CONST(126585.21544162642/8.0), + REAL_CONST(126610.36586313581/8.0), + REAL_CONST(126635.51753370393/8.0), + REAL_CONST(126660.67045320668/8.0), + REAL_CONST(126685.82462152008/8.0), + REAL_CONST(126710.98003852014/8.0), + REAL_CONST(126736.13670408291/8.0), + REAL_CONST(126761.29461808444/8.0), + REAL_CONST(126786.45378040087/8.0), + REAL_CONST(126811.61419090834/8.0), + REAL_CONST(126836.77584948298/8.0), + REAL_CONST(126861.93875600102/8.0), + REAL_CONST(126887.10291033868/8.0), + REAL_CONST(126912.26831237224/8.0), + REAL_CONST(126937.43496197795/8.0), + REAL_CONST(126962.60285903217/8.0), + REAL_CONST(126987.77200341123/8.0), + REAL_CONST(127012.94239499152/8.0), + REAL_CONST(127038.11403364947/8.0), + REAL_CONST(127063.2869192615/8.0), + REAL_CONST(127088.46105170409/8.0), + REAL_CONST(127113.63643085376/8.0), + REAL_CONST(127138.81305658702/8.0), + REAL_CONST(127163.99092878048/8.0), + REAL_CONST(127189.17004731069/8.0), + REAL_CONST(127214.35041205429/8.0), + REAL_CONST(127239.53202288797/8.0), + REAL_CONST(127264.71487968838/8.0), + REAL_CONST(127289.89898233226/8.0), + REAL_CONST(127315.08433069635/8.0), + REAL_CONST(127340.27092465744/8.0), + REAL_CONST(127365.45876409234/8.0), + REAL_CONST(127390.64784887788/8.0), + REAL_CONST(127415.83817889093/8.0), + REAL_CONST(127441.02975400841/8.0), + REAL_CONST(127466.22257410725/8.0), + REAL_CONST(127491.41663906439/8.0), + REAL_CONST(127516.61194875685/8.0), + REAL_CONST(127541.80850306165/8.0), + REAL_CONST(127567.00630185583/8.0), + REAL_CONST(127592.20534501647/8.0), + REAL_CONST(127617.4056324207/8.0), + REAL_CONST(127642.60716394568/8.0), + REAL_CONST(127667.80993946856/8.0), + REAL_CONST(127693.01395886653/8.0), + REAL_CONST(127718.21922201688/8.0), + REAL_CONST(127743.42572879682/8.0), + REAL_CONST(127768.63347908368/8.0), + REAL_CONST(127793.84247275478/8.0), + REAL_CONST(127819.05270968749/8.0), + REAL_CONST(127844.26418975917/8.0), + REAL_CONST(127869.47691284724/8.0), + REAL_CONST(127894.69087882918/8.0), + REAL_CONST(127919.90608758242/8.0), + REAL_CONST(127945.12253898452/8.0), + REAL_CONST(127970.34023291297/8.0), + REAL_CONST(127995.55916924537/8.0), + REAL_CONST(128020.77934785932/8.0), + REAL_CONST(128046.00076863244/8.0), + REAL_CONST(128071.22343144237/8.0), + REAL_CONST(128096.44733616684/8.0), + REAL_CONST(128121.67248268353/8.0), + REAL_CONST(128146.89887087021/8.0), + REAL_CONST(128172.12650060465/8.0), + REAL_CONST(128197.35537176467/8.0), + REAL_CONST(128222.5854842281/8.0), + REAL_CONST(128247.81683787282/8.0), + REAL_CONST(128273.04943257671/8.0), + REAL_CONST(128298.28326821771/8.0), + REAL_CONST(128323.51834467379/8.0), + REAL_CONST(128348.75466182294/8.0), + REAL_CONST(128373.99221954317/8.0), + REAL_CONST(128399.23101771252/8.0), + REAL_CONST(128424.47105620909/8.0), + REAL_CONST(128449.71233491098/8.0), + REAL_CONST(128474.95485369631/8.0), + REAL_CONST(128500.19861244329/8.0), + REAL_CONST(128525.44361103009/8.0), + REAL_CONST(128550.68984933494/8.0), + REAL_CONST(128575.93732723613/8.0), + REAL_CONST(128601.18604461191/8.0), + REAL_CONST(128626.43600134061/8.0), + REAL_CONST(128651.68719730059/8.0), + REAL_CONST(128676.93963237021/8.0), + REAL_CONST(128702.1933064279/8.0), + REAL_CONST(128727.44821935208/8.0), + REAL_CONST(128752.70437102125/8.0), + REAL_CONST(128777.96176131385/8.0), + REAL_CONST(128803.22039010846/8.0), + REAL_CONST(128828.48025728362/8.0), + REAL_CONST(128853.74136271792/8.0), + REAL_CONST(128879.00370628996/8.0), + REAL_CONST(128904.26728787841/8.0), + REAL_CONST(128929.53210736193/8.0), + REAL_CONST(128954.79816461923/8.0), + REAL_CONST(128980.06545952905/8.0), + REAL_CONST(129005.33399197015/8.0), + REAL_CONST(129030.60376182134/8.0), + REAL_CONST(129055.87476896142/8.0), + REAL_CONST(129081.14701326926/8.0), + REAL_CONST(129106.42049462376/8.0), + REAL_CONST(129131.6952129038/8.0), + REAL_CONST(129156.97116798835/8.0), + REAL_CONST(129182.24835975636/8.0), + REAL_CONST(129207.52678808685/8.0), + REAL_CONST(129232.80645285884/8.0), + REAL_CONST(129258.08735395141/8.0), + REAL_CONST(129283.36949124365/8.0), + REAL_CONST(129308.65286461466/8.0), + REAL_CONST(129333.9374739436/8.0), + REAL_CONST(129359.22331910966/8.0), + REAL_CONST(129384.51039999202/8.0), + REAL_CONST(129409.79871646997/8.0), + REAL_CONST(129435.08826842274/8.0), + REAL_CONST(129460.37905572963/8.0), + REAL_CONST(129485.67107826998/8.0), + REAL_CONST(129510.96433592314/8.0), + REAL_CONST(129536.25882856851/8.0), + REAL_CONST(129561.55455608548/8.0), + REAL_CONST(129586.85151835352/8.0), + REAL_CONST(129612.14971525209/8.0), + REAL_CONST(129637.4491466607/8.0), + REAL_CONST(129662.74981245887/8.0), + REAL_CONST(129688.0517125262/8.0), + REAL_CONST(129713.35484674224/8.0), + REAL_CONST(129738.65921498663/8.0), + REAL_CONST(129763.96481713903/8.0), + REAL_CONST(129789.27165307909/8.0), + REAL_CONST(129814.57972268655/8.0), + REAL_CONST(129839.88902584116/8.0), + REAL_CONST(129865.19956242264/8.0), + REAL_CONST(129890.51133231082/8.0), + REAL_CONST(129915.82433538554/8.0), + REAL_CONST(129941.13857152662/8.0), + REAL_CONST(129966.45404061397/8.0), + REAL_CONST(129991.7707425275/8.0), + REAL_CONST(130017.08867714716/8.0), + REAL_CONST(130042.4078443529/8.0), + REAL_CONST(130067.72824402474/8.0), + REAL_CONST(130093.04987604271/8.0), + REAL_CONST(130118.37274028687/8.0), + REAL_CONST(130143.69683663732/8.0), + REAL_CONST(130169.02216497416/8.0), + REAL_CONST(130194.34872517755/8.0), + REAL_CONST(130219.67651712766/8.0), + REAL_CONST(130245.0055407047/8.0), + REAL_CONST(130270.33579578891/8.0), + REAL_CONST(130295.66728226055/8.0), + REAL_CONST(130320.99999999991/8.0), + REAL_CONST(130346.33394888733/8.0), + REAL_CONST(130371.66912880314/8.0), + REAL_CONST(130397.00553962773/8.0), + REAL_CONST(130422.34318124152/8.0), + REAL_CONST(130447.68205352494/8.0), + REAL_CONST(130473.02215635845/8.0), + REAL_CONST(130498.36348962256/8.0), + REAL_CONST(130523.70605319779/8.0), + REAL_CONST(130549.0498469647/8.0), + REAL_CONST(130574.39487080388/8.0), + REAL_CONST(130599.74112459592/8.0), + REAL_CONST(130625.08860822149/8.0), + REAL_CONST(130650.43732156123/8.0), + REAL_CONST(130675.78726449587/8.0), + REAL_CONST(130701.13843690613/8.0), + REAL_CONST(130726.49083867275/8.0), + REAL_CONST(130751.84446967654/8.0), + REAL_CONST(130777.19932979831/8.0), + REAL_CONST(130802.5554189189/8.0), + REAL_CONST(130827.91273691918/8.0), + REAL_CONST(130853.27128368006/8.0), + REAL_CONST(130878.63105908247/8.0), + REAL_CONST(130903.99206300738/8.0), + REAL_CONST(130929.35429533575/8.0), + REAL_CONST(130954.71775594862/8.0), + REAL_CONST(130980.08244472703/8.0), + REAL_CONST(131005.44836155206/8.0), + REAL_CONST(131030.81550630482/8.0), + REAL_CONST(131056.18387886642/8.0), + REAL_CONST(131081.55347911804/8.0), + REAL_CONST(131106.92430694087/8.0), + REAL_CONST(131132.29636221612/8.0), + REAL_CONST(131157.66964482504/8.0), + REAL_CONST(131183.0441546489/8.0), + REAL_CONST(131208.41989156904/8.0), + REAL_CONST(131233.79685546676/8.0), + REAL_CONST(131259.17504622342/8.0), + REAL_CONST(131284.55446372041/8.0), + REAL_CONST(131309.93510783918/8.0), + REAL_CONST(131335.31697846117/8.0), + REAL_CONST(131360.70007546784/8.0), + REAL_CONST(131386.0843987407/8.0), + REAL_CONST(131411.46994816128/8.0), + REAL_CONST(131436.85672361116/8.0), + REAL_CONST(131462.24472497194/8.0), + REAL_CONST(131487.63395212521/8.0), + REAL_CONST(131513.02440495262/8.0), + REAL_CONST(131538.41608333588/8.0), + REAL_CONST(131563.80898715663/8.0), + REAL_CONST(131589.2031162967/8.0), + REAL_CONST(131614.59847063778/8.0), + REAL_CONST(131639.9950500617/8.0), + REAL_CONST(131665.39285445024/8.0), + REAL_CONST(131690.79188368531/8.0), + REAL_CONST(131716.19213764873/8.0), + REAL_CONST(131741.59361622241/8.0), + REAL_CONST(131766.99631928833/8.0), + REAL_CONST(131792.40024672839/8.0), + REAL_CONST(131817.80539842462/8.0), + REAL_CONST(131843.21177425905/8.0), + REAL_CONST(131868.61937411371/8.0), + REAL_CONST(131894.02819787065/8.0), + REAL_CONST(131919.43824541202/8.0), + REAL_CONST(131944.84951661993/8.0), + REAL_CONST(131970.26201137656/8.0), + REAL_CONST(131995.67572956407/8.0), + REAL_CONST(132021.09067106468/8.0), + REAL_CONST(132046.50683576067/8.0), + REAL_CONST(132071.9242235343/8.0), + REAL_CONST(132097.34283426782/8.0), + REAL_CONST(132122.76266784366/8.0), + REAL_CONST(132148.1837241441/8.0), + REAL_CONST(132173.60600305157/8.0), + REAL_CONST(132199.02950444847/8.0), + REAL_CONST(132224.45422821722/8.0), + REAL_CONST(132249.88017424036/8.0), + REAL_CONST(132275.30734240031/8.0), + REAL_CONST(132300.73573257966/8.0), + REAL_CONST(132326.16534466096/8.0), + REAL_CONST(132351.59617852676/8.0), + REAL_CONST(132377.02823405969/8.0), + REAL_CONST(132402.46151114244/8.0), + REAL_CONST(132427.89600965759/8.0), + REAL_CONST(132453.33172948789/8.0), + REAL_CONST(132478.76867051609/8.0), + REAL_CONST(132504.20683262491/8.0), + REAL_CONST(132529.64621569714/8.0), + REAL_CONST(132555.08681961559/8.0), + REAL_CONST(132580.5286442631/8.0), + REAL_CONST(132605.97168952253/8.0), + REAL_CONST(132631.41595527678/8.0), + REAL_CONST(132656.86144140881/8.0), + REAL_CONST(132682.30814780149/8.0), + REAL_CONST(132707.75607433787/8.0), + REAL_CONST(132733.20522090094/8.0), + REAL_CONST(132758.65558737374/8.0), + REAL_CONST(132784.10717363929/8.0), + REAL_CONST(132809.55997958075/8.0), + REAL_CONST(132835.01400508118/8.0), + REAL_CONST(132860.46925002377/8.0), + REAL_CONST(132885.92571429166/8.0), + REAL_CONST(132911.38339776811/8.0), + REAL_CONST(132936.84230033628/8.0), + REAL_CONST(132962.30242187946/8.0), + REAL_CONST(132987.76376228096/8.0), + REAL_CONST(133013.22632142407/8.0), + REAL_CONST(133038.69009919214/8.0), + REAL_CONST(133064.15509546854/8.0), + REAL_CONST(133089.62131013666/8.0), + REAL_CONST(133115.08874307995/8.0), + REAL_CONST(133140.55739418184/8.0), + REAL_CONST(133166.02726332581/8.0), + REAL_CONST(133191.49835039541/8.0), + REAL_CONST(133216.97065527414/8.0), + REAL_CONST(133242.44417784561/8.0), + REAL_CONST(133267.91891799335/8.0), + REAL_CONST(133293.39487560102/8.0), + REAL_CONST(133318.87205055228/8.0), + REAL_CONST(133344.35044273079/8.0), + REAL_CONST(133369.83005202023/8.0), + REAL_CONST(133395.31087830439/8.0), + REAL_CONST(133420.79292146701/8.0), + REAL_CONST(133446.27618139185/8.0), + REAL_CONST(133471.76065796276/8.0), + REAL_CONST(133497.24635106357/8.0), + REAL_CONST(133522.73326057816/8.0), + REAL_CONST(133548.22138639039/8.0), + REAL_CONST(133573.71072838426/8.0), + REAL_CONST(133599.20128644365/8.0), + REAL_CONST(133624.69306045261/8.0), + REAL_CONST(133650.1860502951/8.0), + REAL_CONST(133675.68025585517/8.0), + REAL_CONST(133701.1756770169/8.0), + REAL_CONST(133726.67231366437/8.0), + REAL_CONST(133752.17016568172/8.0), + REAL_CONST(133777.66923295305/8.0), + REAL_CONST(133803.16951536259/8.0), + REAL_CONST(133828.67101279454/8.0), + REAL_CONST(133854.17372513309/8.0), + REAL_CONST(133879.67765226253/8.0), + REAL_CONST(133905.18279406714/8.0), + REAL_CONST(133930.68915043125/8.0), + REAL_CONST(133956.19672123916/8.0), + REAL_CONST(133981.70550637526/8.0), + REAL_CONST(134007.21550572399/8.0), + REAL_CONST(134032.7267191697/8.0), + REAL_CONST(134058.23914659687/8.0), + REAL_CONST(134083.75278789/8.0), + REAL_CONST(134109.26764293358/8.0), + REAL_CONST(134134.78371161217/8.0), + REAL_CONST(134160.30099381026/8.0), + REAL_CONST(134185.8194894125/8.0), + REAL_CONST(134211.33919830353/8.0), + REAL_CONST(134236.8601203679/8.0), + REAL_CONST(134262.38225549037/8.0), + REAL_CONST(134287.90560355558/8.0), + REAL_CONST(134313.43016444831/8.0), + REAL_CONST(134338.95593805326/8.0), + REAL_CONST(134364.48292425525/8.0), + REAL_CONST(134390.01112293909/8.0), + REAL_CONST(134415.54053398955/8.0), + REAL_CONST(134441.07115729159/8.0), + REAL_CONST(134466.60299273001/8.0), + REAL_CONST(134492.1360401898/8.0), + REAL_CONST(134517.67029955584/8.0), + REAL_CONST(134543.20577071316/8.0), + REAL_CONST(134568.74245354676/8.0), + REAL_CONST(134594.28034794159/8.0), + REAL_CONST(134619.81945378278/8.0), + REAL_CONST(134645.35977095537/8.0), + REAL_CONST(134670.90129934452/8.0), + REAL_CONST(134696.4440388353/8.0), + REAL_CONST(134721.98798931291/8.0), + REAL_CONST(134747.53315066252/8.0), + REAL_CONST(134773.07952276937/8.0), + REAL_CONST(134798.62710551871/8.0), + REAL_CONST(134824.17589879577/8.0), + REAL_CONST(134849.72590248589/8.0), + REAL_CONST(134875.27711647438/8.0), + REAL_CONST(134900.82954064661/8.0), + REAL_CONST(134926.38317488792/8.0), + REAL_CONST(134951.93801908373/8.0), + REAL_CONST(134977.49407311951/8.0), + REAL_CONST(135003.05133688069/8.0), + REAL_CONST(135028.60981025276/8.0), + REAL_CONST(135054.16949312127/8.0), + REAL_CONST(135079.73038537172/8.0), + REAL_CONST(135105.29248688967/8.0), + REAL_CONST(135130.85579756077/8.0), + REAL_CONST(135156.42031727062/8.0), + REAL_CONST(135181.98604590484/8.0), + REAL_CONST(135207.55298334916/8.0), + REAL_CONST(135233.12112948924/8.0), + REAL_CONST(135258.69048421088/8.0), + REAL_CONST(135284.26104739975/8.0), + REAL_CONST(135309.83281894168/8.0), + REAL_CONST(135335.4057987225/8.0), + REAL_CONST(135360.97998662802/8.0), + REAL_CONST(135386.55538254412/8.0), + REAL_CONST(135412.13198635669/8.0), + REAL_CONST(135437.70979795168/8.0), + REAL_CONST(135463.28881721498/8.0), + REAL_CONST(135488.86904403262/8.0), + REAL_CONST(135514.45047829056/8.0), + REAL_CONST(135540.03311987486/8.0), + REAL_CONST(135565.61696867159/8.0), + REAL_CONST(135591.20202456677/8.0), + REAL_CONST(135616.78828744654/8.0), + REAL_CONST(135642.37575719706/8.0), + REAL_CONST(135667.96443370447/8.0), + REAL_CONST(135693.55431685498/8.0), + REAL_CONST(135719.14540653475/8.0), + REAL_CONST(135744.73770263011/8.0), + REAL_CONST(135770.33120502727/8.0), + REAL_CONST(135795.92591361253/8.0), + REAL_CONST(135821.52182827223/8.0), + REAL_CONST(135847.11894889272/8.0), + REAL_CONST(135872.7172753604/8.0), + REAL_CONST(135898.31680756161/8.0), + REAL_CONST(135923.91754538284/8.0), + REAL_CONST(135949.51948871053/8.0), + REAL_CONST(135975.12263743114/8.0), + REAL_CONST(136000.72699143123/8.0), + REAL_CONST(136026.33255059729/8.0), + REAL_CONST(136051.93931481591/8.0), + REAL_CONST(136077.54728397369/8.0), + REAL_CONST(136103.15645795723/8.0), + REAL_CONST(136128.76683665317/8.0), + REAL_CONST(136154.37841994822/8.0), + REAL_CONST(136179.99120772901/8.0), + REAL_CONST(136205.60519988232/8.0), + REAL_CONST(136231.2203962949/8.0), + REAL_CONST(136256.83679685349/8.0), + REAL_CONST(136282.45440144493/8.0), + REAL_CONST(136308.07320995603/8.0), + REAL_CONST(136333.69322227367/8.0), + REAL_CONST(136359.31443828469/8.0), + REAL_CONST(136384.93685787608/8.0), + REAL_CONST(136410.56048093468/8.0), + REAL_CONST(136436.18530734754/8.0), + REAL_CONST(136461.81133700156/8.0), + REAL_CONST(136487.43856978384/8.0), + REAL_CONST(136513.06700558143/8.0), + REAL_CONST(136538.6966442813/8.0), + REAL_CONST(136564.32748577066/8.0), + REAL_CONST(136589.95952993655/8.0), + REAL_CONST(136615.59277666616/8.0), + REAL_CONST(136641.22722584667/8.0), + REAL_CONST(136666.86287736523/8.0), + REAL_CONST(136692.49973110916/8.0), + REAL_CONST(136718.13778696564/8.0), + REAL_CONST(136743.77704482197/8.0), + REAL_CONST(136769.41750456547/8.0), + REAL_CONST(136795.05916608346/8.0), + REAL_CONST(136820.70202926331/8.0), + REAL_CONST(136846.34609399244/8.0), + REAL_CONST(136871.99136015819/8.0), + REAL_CONST(136897.63782764805/8.0), + REAL_CONST(136923.28549634948/8.0), + REAL_CONST(136948.93436614997/8.0), + REAL_CONST(136974.58443693706/8.0), + REAL_CONST(137000.23570859825/8.0), + REAL_CONST(137025.88818102115/8.0), + REAL_CONST(137051.54185409332/8.0), + REAL_CONST(137077.19672770242/8.0), + REAL_CONST(137102.85280173609/8.0), + REAL_CONST(137128.51007608202/8.0), + REAL_CONST(137154.16855062786/8.0), + REAL_CONST(137179.82822526142/8.0), + REAL_CONST(137205.48909987041/8.0), + REAL_CONST(137231.15117434258/8.0), + REAL_CONST(137256.8144485658/8.0), + REAL_CONST(137282.47892242789/8.0), + REAL_CONST(137308.14459581667/8.0), + REAL_CONST(137333.81146862009/8.0), + REAL_CONST(137359.47954072602/8.0), + REAL_CONST(137385.14881202241/8.0), + REAL_CONST(137410.81928239719/8.0), + REAL_CONST(137436.49095173844/8.0), + REAL_CONST(137462.16381993407/8.0), + REAL_CONST(137487.83788687221/8.0), + REAL_CONST(137513.51315244089/8.0), + REAL_CONST(137539.18961652822/8.0), + REAL_CONST(137564.86727902229/8.0), + REAL_CONST(137590.54613981131/8.0), + REAL_CONST(137616.22619878338/8.0), + REAL_CONST(137641.90745582676/8.0), + REAL_CONST(137667.58991082967/8.0), + REAL_CONST(137693.27356368033/8.0), + REAL_CONST(137718.95841426702/8.0), + REAL_CONST(137744.64446247809/8.0), + REAL_CONST(137770.33170820182/8.0), + REAL_CONST(137796.02015132661/8.0), + REAL_CONST(137821.70979174081/8.0), + REAL_CONST(137847.40062933284/8.0), + REAL_CONST(137873.09266399115/8.0), + REAL_CONST(137898.78589560417/8.0), + REAL_CONST(137924.48032406042/8.0), + REAL_CONST(137950.17594924837/8.0), + REAL_CONST(137975.8727710566/8.0), + REAL_CONST(138001.57078937365/8.0), + REAL_CONST(138027.27000408815/8.0), + REAL_CONST(138052.97041508864/8.0), + REAL_CONST(138078.67202226384/8.0), + REAL_CONST(138104.3748255024/8.0), + REAL_CONST(138130.07882469296/8.0), + REAL_CONST(138155.78401972432/8.0), + REAL_CONST(138181.49041048516/8.0), + REAL_CONST(138207.1979968643/8.0), + REAL_CONST(138232.9067787505/8.0), + REAL_CONST(138258.61675603263/8.0), + REAL_CONST(138284.32792859949/8.0), + REAL_CONST(138310.04029633995/8.0), + REAL_CONST(138335.75385914298/8.0), + REAL_CONST(138361.46861689744/8.0), + REAL_CONST(138387.18456949232/8.0), + REAL_CONST(138412.90171681659/8.0), + REAL_CONST(138438.62005875923/8.0), + REAL_CONST(138464.33959520931/8.0), + REAL_CONST(138490.06032605586/8.0), + REAL_CONST(138515.78225118798/8.0), + REAL_CONST(138541.50537049473/8.0), + REAL_CONST(138567.2296838653/8.0), + REAL_CONST(138592.95519118884/8.0), + REAL_CONST(138618.68189235451/8.0), + REAL_CONST(138644.40978725153/8.0), + REAL_CONST(138670.13887576913/8.0), + REAL_CONST(138695.86915779658/8.0), + REAL_CONST(138721.60063322316/8.0), + REAL_CONST(138747.33330193823/8.0), + REAL_CONST(138773.06716383106/8.0), + REAL_CONST(138798.80221879104/8.0), + REAL_CONST(138824.53846670757/8.0), + REAL_CONST(138850.27590747006/8.0), + REAL_CONST(138876.01454096794/8.0), + REAL_CONST(138901.7543670907/8.0), + REAL_CONST(138927.49538572782/8.0), + REAL_CONST(138953.2375967688/8.0), + REAL_CONST(138978.9810001032/8.0), + REAL_CONST(139004.72559562061/8.0), + REAL_CONST(139030.47138321059/8.0), + REAL_CONST(139056.2183627628/8.0), + REAL_CONST(139081.96653416683/8.0), + REAL_CONST(139107.71589731239/8.0), + REAL_CONST(139133.46645208917/8.0), + REAL_CONST(139159.21819838689/8.0), + REAL_CONST(139184.97113609532/8.0), + REAL_CONST(139210.72526510421/8.0), + REAL_CONST(139236.48058530336/8.0), + REAL_CONST(139262.23709658257/8.0), + REAL_CONST(139287.99479883176/8.0), + REAL_CONST(139313.75369194071/8.0), + REAL_CONST(139339.51377579942/8.0), + REAL_CONST(139365.27505029776/8.0), + REAL_CONST(139391.03751532568/8.0), + REAL_CONST(139416.80117077316/8.0), + REAL_CONST(139442.56601653024/8.0), + REAL_CONST(139468.33205248689/8.0), + REAL_CONST(139494.09927853322/8.0), + REAL_CONST(139519.86769455927/8.0), + REAL_CONST(139545.63730045516/8.0), + REAL_CONST(139571.408096111/8.0), + REAL_CONST(139597.18008141697/8.0), + REAL_CONST(139622.95325626322/8.0), + REAL_CONST(139648.72762054001/8.0), + REAL_CONST(139674.5031741375/8.0), + REAL_CONST(139700.27991694602/8.0), + REAL_CONST(139726.05784885579/8.0), + REAL_CONST(139751.83696975713/8.0), + REAL_CONST(139777.61727954043/8.0), + REAL_CONST(139803.39877809596/8.0), + REAL_CONST(139829.18146531415/8.0), + REAL_CONST(139854.96534108539/8.0), + REAL_CONST(139880.75040530015/8.0), + REAL_CONST(139906.53665784886/8.0), + REAL_CONST(139932.32409862199/8.0), + REAL_CONST(139958.11272751007/8.0), + REAL_CONST(139983.90254440365/8.0), + REAL_CONST(140009.69354919327/8.0), + REAL_CONST(140035.48574176949/8.0), + REAL_CONST(140061.27912202294/8.0), + REAL_CONST(140087.07368984428/8.0), + REAL_CONST(140112.86944512415/8.0), + REAL_CONST(140138.66638775321/8.0), + REAL_CONST(140164.4645176222/8.0), + REAL_CONST(140190.26383462184/8.0), + REAL_CONST(140216.06433864293/8.0), + REAL_CONST(140241.86602957622/8.0), + REAL_CONST(140267.66890731253/8.0), + REAL_CONST(140293.47297174268/8.0), + REAL_CONST(140319.27822275754/8.0), + REAL_CONST(140345.08466024802/8.0), + REAL_CONST(140370.89228410498/8.0), + REAL_CONST(140396.70109421943/8.0), + REAL_CONST(140422.51109048226/8.0), + REAL_CONST(140448.32227278448/8.0), + REAL_CONST(140474.13464101712/8.0), + REAL_CONST(140499.94819507122/8.0), + REAL_CONST(140525.76293483781/8.0), + REAL_CONST(140551.57886020801/8.0), + REAL_CONST(140577.3959710729/8.0), + REAL_CONST(140603.21426732364/8.0), + REAL_CONST(140629.03374885136/8.0), + REAL_CONST(140654.85441554731/8.0), + REAL_CONST(140680.67626730262/8.0), + REAL_CONST(140706.49930400858/8.0), + REAL_CONST(140732.32352555645/8.0), + REAL_CONST(140758.1489318375/8.0), + REAL_CONST(140783.97552274304/8.0), + REAL_CONST(140809.80329816442/8.0), + REAL_CONST(140835.63225799298/8.0), + REAL_CONST(140861.46240212015/8.0), + REAL_CONST(140887.29373043729/8.0), + REAL_CONST(140913.12624283586/8.0), + REAL_CONST(140938.95993920733/8.0), + REAL_CONST(140964.79481944317/8.0), + REAL_CONST(140990.63088343487/8.0), + REAL_CONST(141016.46813107401/8.0), + REAL_CONST(141042.30656225214/8.0), + REAL_CONST(141068.14617686081/8.0), + REAL_CONST(141093.98697479168/8.0), + REAL_CONST(141119.82895593636/8.0), + REAL_CONST(141145.6721201865/8.0), + REAL_CONST(141171.51646743377/8.0), + REAL_CONST(141197.36199756994/8.0), + REAL_CONST(141223.20871048668/8.0), + REAL_CONST(141249.05660607578/8.0), + REAL_CONST(141274.90568422904/8.0), + REAL_CONST(141300.75594483822/8.0), + REAL_CONST(141326.6073877952/8.0), + REAL_CONST(141352.4600129918/8.0), + REAL_CONST(141378.31382031992/8.0), + REAL_CONST(141404.16880967148/8.0), + REAL_CONST(141430.02498093838/8.0), + REAL_CONST(141455.8823340126/8.0), + REAL_CONST(141481.74086878612/8.0), + REAL_CONST(141507.60058515094/8.0), + REAL_CONST(141533.46148299909/8.0), + REAL_CONST(141559.32356222265/8.0), + REAL_CONST(141585.18682271364/8.0), + REAL_CONST(141611.05126436421/8.0), + REAL_CONST(141636.9168870665/8.0), + REAL_CONST(141662.78369071262/8.0), + REAL_CONST(141688.65167519479/8.0), + REAL_CONST(141714.5208404052/8.0), + REAL_CONST(141740.39118623605/8.0), + REAL_CONST(141766.26271257963/8.0), + REAL_CONST(141792.1354193282/8.0), + REAL_CONST(141818.00930637406/8.0), + REAL_CONST(141843.88437360956/8.0), + REAL_CONST(141869.760620927/8.0), + REAL_CONST(141895.6380482188/8.0), + REAL_CONST(141921.51665537735/8.0), + REAL_CONST(141947.39644229505/8.0), + REAL_CONST(141973.27740886438/8.0), + REAL_CONST(141999.15955497778/8.0), + REAL_CONST(142025.04288052776/8.0), + REAL_CONST(142050.92738540689/8.0), + REAL_CONST(142076.81306950765/8.0), + REAL_CONST(142102.69993272264/8.0), + REAL_CONST(142128.58797494444/8.0), + REAL_CONST(142154.47719606571/8.0), + REAL_CONST(142180.36759597904/8.0), + REAL_CONST(142206.25917457714/8.0), + REAL_CONST(142232.15193175265/8.0), + REAL_CONST(142258.04586739838/8.0), + REAL_CONST(142283.94098140698/8.0), + REAL_CONST(142309.83727367126/8.0), + REAL_CONST(142335.73474408401/8.0), + REAL_CONST(142361.63339253806/8.0), + REAL_CONST(142387.5332189262/8.0), + REAL_CONST(142413.43422314132/8.0), + REAL_CONST(142439.33640507635/8.0), + REAL_CONST(142465.23976462413/8.0), + REAL_CONST(142491.14430167765/8.0), + REAL_CONST(142517.05001612983/8.0), + REAL_CONST(142542.95690787368/8.0), + REAL_CONST(142568.86497680223/8.0), + REAL_CONST(142594.77422280848/8.0), + REAL_CONST(142620.68464578551/8.0), + REAL_CONST(142646.5962456264/8.0), + REAL_CONST(142672.50902222423/8.0), + REAL_CONST(142698.42297547215/8.0), + REAL_CONST(142724.33810526333/8.0), + REAL_CONST(142750.25441149093/8.0), + REAL_CONST(142776.17189404817/8.0), + REAL_CONST(142802.09055282827/8.0), + REAL_CONST(142828.01038772447/8.0), + REAL_CONST(142853.93139863008/8.0), + REAL_CONST(142879.85358543837/8.0), + REAL_CONST(142905.77694804268/8.0), + REAL_CONST(142931.70148633636/8.0), + REAL_CONST(142957.62720021277/8.0), + REAL_CONST(142983.55408956532/8.0), + REAL_CONST(143009.48215428743/8.0), + REAL_CONST(143035.41139427255/8.0), + REAL_CONST(143061.34180941415/8.0), + REAL_CONST(143087.27339960571/8.0), + REAL_CONST(143113.20616474075/8.0), + REAL_CONST(143139.14010471283/8.0), + REAL_CONST(143165.07521941551/8.0), + REAL_CONST(143191.01150874238/8.0), + REAL_CONST(143216.94897258704/8.0), + REAL_CONST(143242.88761084314/8.0), + REAL_CONST(143268.82742340435/8.0), + REAL_CONST(143294.76841016437/8.0), + REAL_CONST(143320.71057101688/8.0), + REAL_CONST(143346.65390585564/8.0), + REAL_CONST(143372.59841457437/8.0), + REAL_CONST(143398.54409706692/8.0), + REAL_CONST(143424.49095322701/8.0), + REAL_CONST(143450.43898294857/8.0), + REAL_CONST(143476.38818612538/8.0), + REAL_CONST(143502.33856265133/8.0), + REAL_CONST(143528.29011242036/8.0), + REAL_CONST(143554.24283532638/8.0), + REAL_CONST(143580.19673126334/8.0), + REAL_CONST(143606.1518001252/8.0), + REAL_CONST(143632.10804180597/8.0), + REAL_CONST(143658.06545619969/8.0), + REAL_CONST(143684.02404320039/8.0), + REAL_CONST(143709.98380270213/8.0), + REAL_CONST(143735.944734599/8.0), + REAL_CONST(143761.90683878519/8.0), + REAL_CONST(143787.87011515474/8.0), + REAL_CONST(143813.83456360188/8.0), + REAL_CONST(143839.8001840208/8.0), + REAL_CONST(143865.76697630569/8.0), + REAL_CONST(143891.73494035081/8.0), + REAL_CONST(143917.7040760504/8.0), + REAL_CONST(143943.67438329876/8.0), + REAL_CONST(143969.6458619902/8.0), + REAL_CONST(143995.61851201905/8.0), + REAL_CONST(144021.59233327967/8.0), + REAL_CONST(144047.56732566646/8.0), + REAL_CONST(144073.54348907378/8.0), + REAL_CONST(144099.52082339607/8.0), + REAL_CONST(144125.49932852783/8.0), + REAL_CONST(144151.4790043635/8.0), + REAL_CONST(144177.45985079758/8.0), + REAL_CONST(144203.44186772458/8.0), + REAL_CONST(144229.42505503909/8.0), + REAL_CONST(144255.40941263564/8.0), + REAL_CONST(144281.39494040885/8.0), + REAL_CONST(144307.38163825331/8.0), + REAL_CONST(144333.36950606373/8.0), + REAL_CONST(144359.35854373468/8.0), + REAL_CONST(144385.34875116093/8.0), + REAL_CONST(144411.34012823718/8.0), + REAL_CONST(144437.33267485813/8.0), + REAL_CONST(144463.32639091855/8.0), + REAL_CONST(144489.32127631325/8.0), + REAL_CONST(144515.31733093705/8.0), + REAL_CONST(144541.31455468474/8.0), + REAL_CONST(144567.3129474512/8.0), + REAL_CONST(144593.3125091313/8.0), + REAL_CONST(144619.31323961995/8.0), + REAL_CONST(144645.31513881206/8.0), + REAL_CONST(144671.31820660262/8.0), + REAL_CONST(144697.32244288657/8.0), + REAL_CONST(144723.32784755889/8.0), + REAL_CONST(144749.33442051467/8.0), + REAL_CONST(144775.34216164888/8.0), + REAL_CONST(144801.35107085665/8.0), + REAL_CONST(144827.36114803303/8.0), + REAL_CONST(144853.37239307314/8.0), + REAL_CONST(144879.38480587213/8.0), + REAL_CONST(144905.39838632516/8.0), + REAL_CONST(144931.41313432742/8.0), + REAL_CONST(144957.4290497741/8.0), + REAL_CONST(144983.44613256046/8.0), + REAL_CONST(145009.46438258173/8.0), + REAL_CONST(145035.48379973322/8.0), + REAL_CONST(145061.50438391021/8.0), + REAL_CONST(145087.52613500805/8.0), + REAL_CONST(145113.54905292206/8.0), + REAL_CONST(145139.57313754765/8.0), + REAL_CONST(145165.59838878017/8.0), + REAL_CONST(145191.62480651509/8.0), + REAL_CONST(145217.65239064783/8.0), + REAL_CONST(145243.68114107384/8.0), + REAL_CONST(145269.71105768863/8.0), + REAL_CONST(145295.74214038774/8.0), + REAL_CONST(145321.77438906668/8.0), + REAL_CONST(145347.80780362099/8.0), + REAL_CONST(145373.84238394629/8.0), + REAL_CONST(145399.87812993818/8.0), + REAL_CONST(145425.91504149229/8.0), + REAL_CONST(145451.95311850426/8.0), + REAL_CONST(145477.9923608698/8.0), + REAL_CONST(145504.03276848458/8.0), + REAL_CONST(145530.07434124436/8.0), + REAL_CONST(145556.11707904484/8.0), + REAL_CONST(145582.16098178181/8.0), + REAL_CONST(145608.20604935108/8.0), + REAL_CONST(145634.25228164849/8.0), + REAL_CONST(145660.29967856981/8.0), + REAL_CONST(145686.34824001096/8.0), + REAL_CONST(145712.39796586783/8.0), + REAL_CONST(145738.4488560363/8.0), + REAL_CONST(145764.50091041232/8.0), + REAL_CONST(145790.55412889185/8.0), + REAL_CONST(145816.60851137087/8.0), + REAL_CONST(145842.66405774537/8.0), + REAL_CONST(145868.72076791141/8.0), + REAL_CONST(145894.77864176501/8.0), + REAL_CONST(145920.83767920226/8.0), + REAL_CONST(145946.89788011924/8.0), + REAL_CONST(145972.95924441208/8.0), + REAL_CONST(145999.02177197693/8.0), + REAL_CONST(146025.08546270995/8.0), + REAL_CONST(146051.15031650732/8.0), + REAL_CONST(146077.21633326527/8.0), + REAL_CONST(146103.28351288004/8.0), + REAL_CONST(146129.35185524789/8.0), + REAL_CONST(146155.42136026506/8.0), + REAL_CONST(146181.49202782792/8.0), + REAL_CONST(146207.56385783272/8.0), + REAL_CONST(146233.63685017588/8.0), + REAL_CONST(146259.71100475377/8.0), + REAL_CONST(146285.78632146274/8.0), + REAL_CONST(146311.86280019928/8.0), + REAL_CONST(146337.94044085976/8.0), + REAL_CONST(146364.01924334071/8.0), + REAL_CONST(146390.09920753856/8.0), + REAL_CONST(146416.18033334985/8.0), + REAL_CONST(146442.26262067116/8.0), + REAL_CONST(146468.34606939898/8.0), + REAL_CONST(146494.43067942993/8.0), + REAL_CONST(146520.51645066062/8.0), + REAL_CONST(146546.60338298764/8.0), + REAL_CONST(146572.69147630769/8.0), + REAL_CONST(146598.78073051744/8.0), + REAL_CONST(146624.87114551352/8.0), + REAL_CONST(146650.96272119274/8.0), + REAL_CONST(146677.05545745179/8.0), + REAL_CONST(146703.14935418745/8.0), + REAL_CONST(146729.2444112965/8.0), + REAL_CONST(146755.34062867577/8.0), + REAL_CONST(146781.43800622207/8.0), + REAL_CONST(146807.53654383228/8.0), + REAL_CONST(146833.63624140329/8.0), + REAL_CONST(146859.73709883197/8.0), + REAL_CONST(146885.83911601527/8.0), + REAL_CONST(146911.94229285014/8.0), + REAL_CONST(146938.04662923355/8.0), + REAL_CONST(146964.15212506248/8.0), + REAL_CONST(146990.25878023397/8.0), + REAL_CONST(147016.36659464505/8.0), + REAL_CONST(147042.47556819281/8.0), + REAL_CONST(147068.58570077427/8.0), + REAL_CONST(147094.6969922866/8.0), + REAL_CONST(147120.80944262692/8.0), + REAL_CONST(147146.92305169237/8.0), + REAL_CONST(147173.03781938017/8.0), + REAL_CONST(147199.15374558745/8.0), + REAL_CONST(147225.27083021149/8.0), + REAL_CONST(147251.38907314953/8.0), + REAL_CONST(147277.50847429881/8.0), + REAL_CONST(147303.62903355664/8.0), + REAL_CONST(147329.75075082036/8.0), + REAL_CONST(147355.87362598727/8.0), + REAL_CONST(147381.99765895473/8.0), + REAL_CONST(147408.12284962015/8.0), + REAL_CONST(147434.24919788091/8.0), + REAL_CONST(147460.37670363448/8.0), + REAL_CONST(147486.50536677826/8.0), + REAL_CONST(147512.63518720976/8.0), + REAL_CONST(147538.76616482646/8.0), + REAL_CONST(147564.89829952587/8.0), + REAL_CONST(147591.03159120557/8.0), + REAL_CONST(147617.16603976308/8.0), + REAL_CONST(147643.30164509601/8.0), + REAL_CONST(147669.43840710199/8.0), + REAL_CONST(147695.57632567859/8.0), + REAL_CONST(147721.71540072354/8.0), + REAL_CONST(147747.85563213445/8.0), + REAL_CONST(147773.99701980909/8.0), + REAL_CONST(147800.13956364512/8.0), + REAL_CONST(147826.28326354033/8.0), + REAL_CONST(147852.42811939248/8.0), + REAL_CONST(147878.57413109933/8.0), + REAL_CONST(147904.72129855872/8.0), + REAL_CONST(147930.86962166851/8.0), + REAL_CONST(147957.01910032652/8.0), + REAL_CONST(147983.16973443062/8.0), + REAL_CONST(148009.32152387875/8.0), + REAL_CONST(148035.47446856883/8.0), + REAL_CONST(148061.62856839882/8.0), + REAL_CONST(148087.78382326665/8.0), + REAL_CONST(148113.94023307035/8.0), + REAL_CONST(148140.09779770792/8.0), + REAL_CONST(148166.25651707739/8.0), + REAL_CONST(148192.41639107687/8.0), + REAL_CONST(148218.57741960438/8.0), + REAL_CONST(148244.73960255808/8.0), + REAL_CONST(148270.90293983606/8.0), + REAL_CONST(148297.0674313365/8.0), + REAL_CONST(148323.23307695755/8.0), + REAL_CONST(148349.39987659742/8.0), + REAL_CONST(148375.56783015432/8.0), + REAL_CONST(148401.73693752653/8.0), + REAL_CONST(148427.90719861226/8.0), + REAL_CONST(148454.07861330983/8.0), + REAL_CONST(148480.25118151752/8.0), + REAL_CONST(148506.42490313368/8.0), + REAL_CONST(148532.59977805667/8.0), + REAL_CONST(148558.77580618486/8.0), + REAL_CONST(148584.95298741665/8.0), + REAL_CONST(148611.13132165043/8.0), + REAL_CONST(148637.31080878471/8.0), + REAL_CONST(148663.49144871789/8.0), + REAL_CONST(148689.6732413485/8.0), + REAL_CONST(148715.85618657502/8.0), + REAL_CONST(148742.040284296/8.0), + REAL_CONST(148768.22553440998/8.0), + REAL_CONST(148794.41193681557/8.0), + REAL_CONST(148820.59949141133/8.0), + REAL_CONST(148846.78819809589/8.0), + REAL_CONST(148872.97805676793/8.0), + REAL_CONST(148899.16906732606/8.0), + REAL_CONST(148925.36122966901/8.0), + REAL_CONST(148951.55454369547/8.0), + REAL_CONST(148977.74900930419/8.0), + REAL_CONST(149003.9446263939/8.0), + REAL_CONST(149030.1413948634/8.0), + REAL_CONST(149056.33931461151/8.0), + REAL_CONST(149082.53838553699/8.0), + REAL_CONST(149108.73860753875/8.0), + REAL_CONST(149134.9399805156/8.0), + REAL_CONST(149161.14250436646/8.0), + REAL_CONST(149187.34617899026/8.0), + REAL_CONST(149213.5510042859/8.0), + REAL_CONST(149239.75698015234/8.0), + REAL_CONST(149265.96410648854/8.0), + REAL_CONST(149292.17238319354/8.0), + REAL_CONST(149318.38181016635/8.0), + REAL_CONST(149344.59238730598/8.0), + REAL_CONST(149370.80411451156/8.0), + REAL_CONST(149397.01699168212/8.0), + REAL_CONST(149423.23101871679/8.0), + REAL_CONST(149449.44619551473/8.0), + REAL_CONST(149475.66252197503/8.0), + REAL_CONST(149501.87999799693/8.0), + REAL_CONST(149528.0986234796/8.0), + REAL_CONST(149554.31839832227/8.0), + REAL_CONST(149580.53932242419/8.0), + REAL_CONST(149606.76139568459/8.0), + REAL_CONST(149632.98461800278/8.0), + REAL_CONST(149659.20898927809/8.0), + REAL_CONST(149685.43450940982/8.0), + REAL_CONST(149711.66117829733/8.0), + REAL_CONST(149737.88899584001/8.0), + REAL_CONST(149764.11796193724/8.0), + REAL_CONST(149790.34807648844/8.0), + REAL_CONST(149816.57933939309/8.0), + REAL_CONST(149842.81175055061/8.0), + REAL_CONST(149869.04530986046/8.0), + REAL_CONST(149895.28001722222/8.0), + REAL_CONST(149921.51587253538/8.0), + REAL_CONST(149947.75287569952/8.0), + REAL_CONST(149973.99102661415/8.0), + REAL_CONST(150000.23032517891/8.0), + REAL_CONST(150026.47077129342/8.0), + REAL_CONST(150052.71236485732/8.0), + REAL_CONST(150078.95510577026/8.0), + REAL_CONST(150105.1989939319/8.0), + REAL_CONST(150131.444029242/8.0), + REAL_CONST(150157.69021160025/8.0), + REAL_CONST(150183.93754090639/8.0), + REAL_CONST(150210.18601706024/8.0), + REAL_CONST(150236.43563996154/8.0), + REAL_CONST(150262.68640951012/8.0), + REAL_CONST(150288.93832560582/8.0), + REAL_CONST(150315.19138814852/8.0), + REAL_CONST(150341.44559703805/8.0), + REAL_CONST(150367.70095217437/8.0), + REAL_CONST(150393.95745345735/8.0), + REAL_CONST(150420.21510078697/8.0), + REAL_CONST(150446.47389406321/8.0), + REAL_CONST(150472.73383318601/8.0), + REAL_CONST(150498.99491805542/8.0), + REAL_CONST(150525.25714857146/8.0), + REAL_CONST(150551.52052463419/8.0), + REAL_CONST(150577.78504614369/8.0), + REAL_CONST(150604.05071300003/8.0), + REAL_CONST(150630.31752510337/8.0), + REAL_CONST(150656.58548235384/8.0), + REAL_CONST(150682.85458465159/8.0), + REAL_CONST(150709.1248318968/8.0), + REAL_CONST(150735.39622398972/8.0), + REAL_CONST(150761.66876083051/8.0), + REAL_CONST(150787.9424423195/8.0), + REAL_CONST(150814.21726835691/8.0), + REAL_CONST(150840.49323884305/8.0), + REAL_CONST(150866.77035367821/8.0), + REAL_CONST(150893.04861276277/8.0), + REAL_CONST(150919.32801599705/8.0), + REAL_CONST(150945.60856328148/8.0), + REAL_CONST(150971.89025451642/8.0), + REAL_CONST(150998.17308960229/8.0), + REAL_CONST(151024.45706843957/8.0), + REAL_CONST(151050.74219092872/8.0), + REAL_CONST(151077.02845697021/8.0), + REAL_CONST(151103.31586646455/8.0), + REAL_CONST(151129.60441931229/8.0), + REAL_CONST(151155.894115414/8.0), + REAL_CONST(151182.1849546702/8.0), + REAL_CONST(151208.47693698155/8.0), + REAL_CONST(151234.77006224863/8.0), + REAL_CONST(151261.06433037209/8.0), + REAL_CONST(151287.35974125259/8.0), + REAL_CONST(151313.65629479082/8.0), + REAL_CONST(151339.95399088747/8.0), + REAL_CONST(151366.25282944329/8.0), + REAL_CONST(151392.55281035902/8.0), + REAL_CONST(151418.85393353543/8.0), + REAL_CONST(151445.1561988733/8.0), + REAL_CONST(151471.45960627345/8.0), + REAL_CONST(151497.76415563675/8.0), + REAL_CONST(151524.06984686397/8.0), + REAL_CONST(151550.37667985607/8.0), + REAL_CONST(151576.68465451393/8.0), + REAL_CONST(151602.99377073845/8.0), + REAL_CONST(151629.30402843058/8.0), + REAL_CONST(151655.61542749128/8.0), + REAL_CONST(151681.92796782157/8.0), + REAL_CONST(151708.24164932242/8.0), + REAL_CONST(151734.55647189484/8.0), + REAL_CONST(151760.87243543993/8.0), + REAL_CONST(151787.18953985872/8.0), + REAL_CONST(151813.50778505235/8.0), + REAL_CONST(151839.82717092187/8.0), + REAL_CONST(151866.14769736846/8.0), + REAL_CONST(151892.46936429327/8.0), + REAL_CONST(151918.79217159748/8.0), + REAL_CONST(151945.11611918229/8.0), + REAL_CONST(151971.44120694889/8.0), + REAL_CONST(151997.76743479856/8.0), + REAL_CONST(152024.09480263255/8.0), + REAL_CONST(152050.42331035214/8.0), + REAL_CONST(152076.75295785864/8.0), + REAL_CONST(152103.08374505339/8.0), + REAL_CONST(152129.41567183775/8.0), + REAL_CONST(152155.74873811303/8.0), + REAL_CONST(152182.08294378067/8.0), + REAL_CONST(152208.41828874208/8.0), + REAL_CONST(152234.75477289871/8.0), + REAL_CONST(152261.09239615197/8.0), + REAL_CONST(152287.43115840337/8.0), + REAL_CONST(152313.77105955439/8.0), + REAL_CONST(152340.11209950657/8.0), + REAL_CONST(152366.45427816146/8.0), + REAL_CONST(152392.79759542056/8.0), + REAL_CONST(152419.14205118554/8.0), + REAL_CONST(152445.48764535793/8.0), + REAL_CONST(152471.8343778394/8.0), + REAL_CONST(152498.18224853161/8.0), + REAL_CONST(152524.53125733617/8.0), + REAL_CONST(152550.88140415482/8.0), + REAL_CONST(152577.23268888926/8.0), + REAL_CONST(152603.58511144121/8.0), + REAL_CONST(152629.93867171241/8.0), + REAL_CONST(152656.29336960468/8.0), + REAL_CONST(152682.64920501978/8.0), + REAL_CONST(152709.00617785956/8.0), + REAL_CONST(152735.36428802583/8.0), + REAL_CONST(152761.72353542043/8.0), + REAL_CONST(152788.08391994529/8.0), + REAL_CONST(152814.44544150229/8.0), + REAL_CONST(152840.80809999333/8.0), + REAL_CONST(152867.17189532038/8.0), + REAL_CONST(152893.53682738543/8.0), + REAL_CONST(152919.90289609041/8.0), + REAL_CONST(152946.27010133737/8.0), + REAL_CONST(152972.63844302832/8.0), + REAL_CONST(152999.00792106529/8.0), + REAL_CONST(153025.37853535041/8.0), + REAL_CONST(153051.7502857857/8.0), + REAL_CONST(153078.12317227334/8.0), + REAL_CONST(153104.4971947154/8.0), + REAL_CONST(153130.8723530141/8.0), + REAL_CONST(153157.24864707157/8.0), + REAL_CONST(153183.62607679001/8.0), + REAL_CONST(153210.00464207167/8.0), + REAL_CONST(153236.38434281875/8.0), + REAL_CONST(153262.76517893354/8.0), + REAL_CONST(153289.14715031831/8.0), + REAL_CONST(153315.53025687535/8.0), + REAL_CONST(153341.91449850702/8.0), + REAL_CONST(153368.2998751156/8.0), + REAL_CONST(153394.68638660354/8.0), + REAL_CONST(153421.07403287315/8.0), + REAL_CONST(153447.46281382689/8.0), + REAL_CONST(153473.85272936718/8.0), + REAL_CONST(153500.24377939643/8.0), + REAL_CONST(153526.63596381716/8.0), + REAL_CONST(153553.02928253182/8.0), + REAL_CONST(153579.42373544298/8.0), + REAL_CONST(153605.81932245308/8.0), + REAL_CONST(153632.21604346478/8.0), + REAL_CONST(153658.61389838057/8.0), + REAL_CONST(153685.0128871031/8.0), + REAL_CONST(153711.41300953497/8.0), + REAL_CONST(153737.81426557881/8.0), + REAL_CONST(153764.21665513728/8.0), + REAL_CONST(153790.62017811305/8.0), + REAL_CONST(153817.02483440886/8.0), + REAL_CONST(153843.43062392739/8.0), + REAL_CONST(153869.83754657139/8.0), + REAL_CONST(153896.24560224367/8.0), + REAL_CONST(153922.65479084692/8.0), + REAL_CONST(153949.06511228404/8.0), + REAL_CONST(153975.4765664578/8.0), + REAL_CONST(154001.88915327107/8.0), + REAL_CONST(154028.30287262669/8.0), + REAL_CONST(154054.71772442761/8.0), + REAL_CONST(154081.13370857667/8.0), + REAL_CONST(154107.55082497682/8.0), + REAL_CONST(154133.96907353101/8.0), + REAL_CONST(154160.38845414223/8.0), + REAL_CONST(154186.80896671346/8.0), + REAL_CONST(154213.23061114774/8.0), + REAL_CONST(154239.65338734805/8.0), + REAL_CONST(154266.07729521746/8.0), + REAL_CONST(154292.50233465908/8.0), + REAL_CONST(154318.92850557598/8.0), + REAL_CONST(154345.35580787127/8.0), + REAL_CONST(154371.7842414481/8.0), + REAL_CONST(154398.21380620965/8.0), + REAL_CONST(154424.64450205903/8.0), + REAL_CONST(154451.07632889951/8.0), + REAL_CONST(154477.50928663427/8.0), + REAL_CONST(154503.94337516659/8.0), + REAL_CONST(154530.37859439969/8.0), + REAL_CONST(154556.81494423689/8.0), + REAL_CONST(154583.25242458144/8.0), + REAL_CONST(154609.69103533673/8.0), + REAL_CONST(154636.13077640603/8.0), + REAL_CONST(154662.57164769279/8.0), + REAL_CONST(154689.01364910032/8.0), + REAL_CONST(154715.45678053208/8.0), + REAL_CONST(154741.90104189145/8.0), + REAL_CONST(154768.34643308193/8.0), + REAL_CONST(154794.79295400696/8.0), + REAL_CONST(154821.24060457002/8.0), + REAL_CONST(154847.68938467462/8.0), + REAL_CONST(154874.13929422433/8.0), + REAL_CONST(154900.59033312264/8.0), + REAL_CONST(154927.04250127316/8.0), + REAL_CONST(154953.49579857948/8.0), + REAL_CONST(154979.95022494521/8.0), + REAL_CONST(155006.40578027396/8.0), + REAL_CONST(155032.86246446942/8.0), + REAL_CONST(155059.32027743524/8.0), + REAL_CONST(155085.77921907514/8.0), + REAL_CONST(155112.2392892928/8.0), + REAL_CONST(155138.70048799197/8.0), + REAL_CONST(155165.16281507642/8.0), + REAL_CONST(155191.62627044989/8.0), + REAL_CONST(155218.09085401625/8.0), + REAL_CONST(155244.55656567923/8.0), + REAL_CONST(155271.02340534274/8.0), + REAL_CONST(155297.49137291059/8.0), + REAL_CONST(155323.96046828668/8.0), + REAL_CONST(155350.4306913749/8.0), + REAL_CONST(155376.90204207919/8.0), + REAL_CONST(155403.37452030348/8.0), + REAL_CONST(155429.84812595171/8.0), + REAL_CONST(155456.32285892789/8.0), + REAL_CONST(155482.79871913602/8.0), + REAL_CONST(155509.27570648011/8.0), + REAL_CONST(155535.75382086422/8.0), + REAL_CONST(155562.23306219239/8.0), + REAL_CONST(155588.71343036872/8.0), + REAL_CONST(155615.19492529731/8.0), + REAL_CONST(155641.67754688227/8.0), + REAL_CONST(155668.16129502779/8.0), + REAL_CONST(155694.64616963797/8.0), + REAL_CONST(155721.13217061706/8.0), + REAL_CONST(155747.61929786921/8.0), + REAL_CONST(155774.10755129869/8.0), + REAL_CONST(155800.59693080973/8.0), + REAL_CONST(155827.08743630661/8.0), + REAL_CONST(155853.57906769359/8.0), + REAL_CONST(155880.07182487496/8.0), + REAL_CONST(155906.56570775513/8.0), + REAL_CONST(155933.06071623837/8.0), + REAL_CONST(155959.55685022907/8.0), + REAL_CONST(155986.05410963166/8.0), + REAL_CONST(156012.5524943505/8.0), + REAL_CONST(156039.05200429002/8.0), + REAL_CONST(156065.55263935472/8.0), + REAL_CONST(156092.054399449/8.0), + REAL_CONST(156118.5572844774/8.0), + REAL_CONST(156145.06129434443/8.0), + REAL_CONST(156171.5664289546/8.0), + REAL_CONST(156198.07268821247/8.0), + REAL_CONST(156224.5800720226/8.0), + REAL_CONST(156251.08858028959/8.0), + REAL_CONST(156277.59821291809/8.0), + REAL_CONST(156304.10896981266/8.0), + REAL_CONST(156330.62085087801/8.0), + REAL_CONST(156357.1338560188/8.0), + REAL_CONST(156383.64798513969/8.0), + REAL_CONST(156410.16323814544/8.0), + REAL_CONST(156436.67961494075/8.0), + REAL_CONST(156463.1971154304/8.0), + REAL_CONST(156489.71573951913/8.0), + REAL_CONST(156516.23548711176/8.0), + REAL_CONST(156542.75635811311/8.0), + REAL_CONST(156569.27835242799/8.0), + REAL_CONST(156595.80146996127/8.0), + REAL_CONST(156622.32571061782/8.0), + REAL_CONST(156648.85107430254/8.0), + REAL_CONST(156675.37756092031/8.0), + REAL_CONST(156701.90517037612/8.0), + REAL_CONST(156728.43390257491/8.0), + REAL_CONST(156754.96375742162/8.0), + REAL_CONST(156781.49473482129/8.0), + REAL_CONST(156808.02683467892/8.0), + REAL_CONST(156834.5600568995/8.0), + REAL_CONST(156861.09440138817/8.0), + REAL_CONST(156887.62986804993/8.0), + REAL_CONST(156914.16645678994/8.0), + REAL_CONST(156940.70416751326/8.0), + REAL_CONST(156967.24300012505/8.0), + REAL_CONST(156993.78295453047/8.0), + REAL_CONST(157020.32403063469/8.0), + REAL_CONST(157046.8662283429/8.0), + REAL_CONST(157073.40954756032/8.0), + REAL_CONST(157099.9539881922/8.0), + REAL_CONST(157126.49955014378/8.0), + REAL_CONST(157153.04623332032/8.0), + REAL_CONST(157179.59403762716/8.0), + REAL_CONST(157206.14296296958/8.0), + REAL_CONST(157232.69300925292/8.0), + REAL_CONST(157259.24417638258/8.0), + REAL_CONST(157285.79646426387/8.0), + REAL_CONST(157312.34987280221/8.0), + REAL_CONST(157338.90440190304/8.0), + REAL_CONST(157365.46005147175/8.0), + REAL_CONST(157392.01682141385/8.0), + REAL_CONST(157418.57471163478/8.0), + REAL_CONST(157445.13372204005/8.0), + REAL_CONST(157471.69385253513/8.0), + REAL_CONST(157498.25510302564/8.0), + REAL_CONST(157524.81747341706/8.0), + REAL_CONST(157551.38096361503/8.0), + REAL_CONST(157577.9455735251/8.0), + REAL_CONST(157604.51130305286/8.0), + REAL_CONST(157631.07815210402/8.0), + REAL_CONST(157657.64612058419/8.0), + REAL_CONST(157684.21520839902/8.0), + REAL_CONST(157710.78541545427/8.0), + REAL_CONST(157737.35674165559/8.0), + REAL_CONST(157763.92918690876/8.0), + REAL_CONST(157790.50275111952/8.0), + REAL_CONST(157817.07743419363/8.0), + REAL_CONST(157843.65323603692/8.0), + REAL_CONST(157870.23015655516/8.0), + REAL_CONST(157896.80819565422/8.0), + REAL_CONST(157923.3873532399/8.0), + REAL_CONST(157949.96762921812/8.0), + REAL_CONST(157976.54902349479/8.0), + REAL_CONST(158003.13153597576/8.0), + REAL_CONST(158029.71516656701/8.0), + REAL_CONST(158056.29991517449/8.0), + REAL_CONST(158082.88578170416/8.0), + REAL_CONST(158109.47276606198/8.0), + REAL_CONST(158136.06086815402/8.0), + REAL_CONST(158162.65008788629/8.0), + REAL_CONST(158189.24042516484/8.0), + REAL_CONST(158215.83187989573/8.0), + REAL_CONST(158242.42445198505/8.0), + REAL_CONST(158269.01814133892/8.0), + REAL_CONST(158295.61294786347/8.0), + REAL_CONST(158322.20887146486/8.0), + REAL_CONST(158348.80591204923/8.0), + REAL_CONST(158375.4040695228/8.0), + REAL_CONST(158402.00334379176/8.0), + REAL_CONST(158428.60373476235/8.0), + REAL_CONST(158455.2052423408/8.0), + REAL_CONST(158481.80786643337/8.0), + REAL_CONST(158508.41160694641/8.0), + REAL_CONST(158535.01646378616/8.0), + REAL_CONST(158561.62243685898/8.0), + REAL_CONST(158588.2295260712/8.0), + REAL_CONST(158614.8377313292/8.0), + REAL_CONST(158641.44705253936/8.0), + REAL_CONST(158668.05748960807/8.0), + REAL_CONST(158694.66904244179/8.0), + REAL_CONST(158721.28171094693/8.0), + REAL_CONST(158747.89549502998/8.0), + REAL_CONST(158774.5103945974/8.0), + REAL_CONST(158801.12640955573/8.0), + REAL_CONST(158827.74353981143/8.0), + REAL_CONST(158854.36178527112/8.0), + REAL_CONST(158880.9811458413/8.0), + REAL_CONST(158907.60162142856/8.0), + REAL_CONST(158934.22321193956/8.0), + REAL_CONST(158960.84591728085/8.0), + REAL_CONST(158987.46973735912/8.0), + REAL_CONST(159014.09467208097/8.0), + REAL_CONST(159040.72072135314/8.0), + REAL_CONST(159067.3478850823/8.0), + REAL_CONST(159093.97616317519/8.0), + REAL_CONST(159120.60555553852/8.0), + REAL_CONST(159147.23606207906/8.0), + REAL_CONST(159173.8676827036/8.0), + REAL_CONST(159200.50041731889/8.0), + REAL_CONST(159227.13426583182/8.0), + REAL_CONST(159253.76922814918/8.0), + REAL_CONST(159280.40530417781/8.0), + REAL_CONST(159307.04249382461/8.0), + REAL_CONST(159333.68079699649/8.0), + REAL_CONST(159360.32021360032/8.0), + REAL_CONST(159386.96074354305/8.0), + REAL_CONST(159413.60238673165/8.0), + REAL_CONST(159440.24514307309/8.0), + REAL_CONST(159466.88901247433/8.0), + REAL_CONST(159493.53399484244/8.0), + REAL_CONST(159520.18009008438/8.0), + REAL_CONST(159546.82729810724/8.0), + REAL_CONST(159573.47561881805/8.0), + REAL_CONST(159600.12505212394/8.0), + REAL_CONST(159626.77559793202/8.0), + REAL_CONST(159653.42725614941/8.0), + REAL_CONST(159680.08002668325/8.0), + REAL_CONST(159706.73390944069/8.0), + REAL_CONST(159733.38890432892/8.0), + REAL_CONST(159760.04501125516/8.0), + REAL_CONST(159786.70223012666/8.0), + REAL_CONST(159813.36056085059/8.0), + REAL_CONST(159840.02000333427/8.0), + REAL_CONST(159866.68055748497/8.0), + REAL_CONST(159893.34222320997/8.0), + REAL_CONST(159920.00500041663/8.0), + REAL_CONST(159946.66888901225/8.0), + REAL_CONST(159973.33388890422/8.0), + REAL_CONST(159999.99999999988/8.0), + REAL_CONST(160026.66722220668/8.0), + REAL_CONST(160053.33555543202/8.0), + REAL_CONST(160080.0049995833/8.0), + REAL_CONST(160106.67555456801/8.0), + REAL_CONST(160133.3472202936/8.0), + REAL_CONST(160160.0199966676/8.0), + REAL_CONST(160186.6938835975/8.0), + REAL_CONST(160213.36888099083/8.0), + REAL_CONST(160240.04498875517/8.0), + REAL_CONST(160266.72220679806/8.0), + REAL_CONST(160293.40053502709/8.0), + REAL_CONST(160320.07997334987/8.0), + REAL_CONST(160346.76052167406/8.0), + REAL_CONST(160373.44217990729/8.0), + REAL_CONST(160400.1249479572/8.0), + REAL_CONST(160426.80882573154/8.0), + REAL_CONST(160453.49381313793/8.0), + REAL_CONST(160480.17991008417/8.0), + REAL_CONST(160506.86711647795/8.0), + REAL_CONST(160533.55543222709/8.0), + REAL_CONST(160560.24485723933/8.0), + REAL_CONST(160586.93539142248/8.0), + REAL_CONST(160613.62703468435/8.0), + REAL_CONST(160640.31978693281/8.0), + REAL_CONST(160667.01364807569/8.0), + REAL_CONST(160693.70861802087/8.0), + REAL_CONST(160720.40469667627/8.0), + REAL_CONST(160747.1018839498/8.0), + REAL_CONST(160773.80017974938/8.0), + REAL_CONST(160800.49958398298/8.0), + REAL_CONST(160827.20009655855/8.0), + REAL_CONST(160853.90171738411/8.0), + REAL_CONST(160880.60444636765/8.0), + REAL_CONST(160907.30828341722/8.0), + REAL_CONST(160934.01322844089/8.0), + REAL_CONST(160960.71928134665/8.0), + REAL_CONST(160987.42644204266/8.0), + REAL_CONST(161014.13471043704/8.0), + REAL_CONST(161040.84408643784/8.0), + REAL_CONST(161067.55456995327/8.0), + REAL_CONST(161094.26616089148/8.0), + REAL_CONST(161120.97885916062/8.0), + REAL_CONST(161147.69266466892/8.0), + REAL_CONST(161174.40757732463/8.0), + REAL_CONST(161201.12359703594/8.0), + REAL_CONST(161227.84072371112/8.0), + REAL_CONST(161254.55895725847/8.0), + REAL_CONST(161281.27829758628/8.0), + REAL_CONST(161307.99874460287/8.0), + REAL_CONST(161334.72029821656/8.0), + REAL_CONST(161361.44295833571/8.0), + REAL_CONST(161388.1667248687/8.0), + REAL_CONST(161414.89159772391/8.0), + REAL_CONST(161441.61757680977/8.0), + REAL_CONST(161468.34466203468/8.0), + REAL_CONST(161495.07285330712/8.0), + REAL_CONST(161521.80215053557/8.0), + REAL_CONST(161548.53255362847/8.0), + REAL_CONST(161575.26406249436/8.0), + REAL_CONST(161601.99667704175/8.0), + REAL_CONST(161628.7303971792/8.0), + REAL_CONST(161655.46522281526/8.0), + REAL_CONST(161682.20115385848/8.0), + REAL_CONST(161708.93819021754/8.0), + REAL_CONST(161735.67633180099/8.0), + REAL_CONST(161762.41557851751/8.0), + REAL_CONST(161789.15593027571/8.0), + REAL_CONST(161815.89738698432/8.0), + REAL_CONST(161842.63994855201/8.0), + REAL_CONST(161869.38361488748/8.0), + REAL_CONST(161896.1283858995/8.0), + REAL_CONST(161922.87426149679/8.0), + REAL_CONST(161949.62124158812/8.0), + REAL_CONST(161976.36932608229/8.0), + REAL_CONST(162003.1185148881/8.0), + REAL_CONST(162029.8688079144/8.0), + REAL_CONST(162056.62020507001/8.0), + REAL_CONST(162083.37270626382/8.0), + REAL_CONST(162110.12631140469/8.0), + REAL_CONST(162136.88102040152/8.0), + REAL_CONST(162163.63683316324/8.0), + REAL_CONST(162190.39374959879/8.0), + REAL_CONST(162217.15176961714/8.0), + REAL_CONST(162243.91089312723/8.0), + REAL_CONST(162270.67112003808/8.0), + REAL_CONST(162297.43245025873/8.0), + REAL_CONST(162324.19488369819/8.0), + REAL_CONST(162350.9584202655/8.0), + REAL_CONST(162377.72305986975/8.0), + REAL_CONST(162404.48880242003/8.0), + REAL_CONST(162431.25564782543/8.0), + REAL_CONST(162458.02359599507/8.0), + REAL_CONST(162484.79264683815/8.0), + REAL_CONST(162511.56280026378/8.0), + REAL_CONST(162538.33405618116/8.0), + REAL_CONST(162565.10641449949/8.0), + REAL_CONST(162591.87987512801/8.0), + REAL_CONST(162618.65443797593/8.0), + REAL_CONST(162645.43010295252/8.0), + REAL_CONST(162672.20686996708/8.0), + REAL_CONST(162698.98473892888/8.0), + REAL_CONST(162725.76370974723/8.0), + REAL_CONST(162752.54378233149/8.0), + REAL_CONST(162779.32495659095/8.0), + REAL_CONST(162806.10723243505/8.0), + REAL_CONST(162832.89060977317/8.0), + REAL_CONST(162859.67508851466/8.0), + REAL_CONST(162886.46066856899/8.0), + REAL_CONST(162913.24734984562/8.0), + REAL_CONST(162940.03513225398/8.0), + REAL_CONST(162966.82401570358/8.0), + REAL_CONST(162993.6140001039/8.0), + REAL_CONST(163020.40508536444/8.0), + REAL_CONST(163047.19727139481/8.0), + REAL_CONST(163073.99055810447/8.0), + REAL_CONST(163100.78494540305/8.0), + REAL_CONST(163127.58043320014/8.0), + REAL_CONST(163154.37702140535/8.0), + REAL_CONST(163181.17470992831/8.0), + REAL_CONST(163207.97349867865/8.0), + REAL_CONST(163234.77338756606/8.0), + REAL_CONST(163261.57437650024/8.0), + REAL_CONST(163288.37646539087/8.0), + REAL_CONST(163315.17965414765/8.0), + REAL_CONST(163341.98394268038/8.0), + REAL_CONST(163368.78933089875/8.0), + REAL_CONST(163395.59581871261/8.0), + REAL_CONST(163422.40340603172/8.0), + REAL_CONST(163449.2120927659/8.0), + REAL_CONST(163476.02187882498/8.0), + REAL_CONST(163502.83276411882/8.0), + REAL_CONST(163529.6447485573/8.0), + REAL_CONST(163556.45783205028/8.0), + REAL_CONST(163583.2720145077/8.0), + REAL_CONST(163610.08729583945/8.0), + REAL_CONST(163636.90367595552/8.0), + REAL_CONST(163663.72115476584/8.0), + REAL_CONST(163690.53973218042/8.0), + REAL_CONST(163717.35940810922/8.0), + REAL_CONST(163744.18018246227/8.0), + REAL_CONST(163771.00205514964/8.0), + REAL_CONST(163797.82502608138/8.0), + REAL_CONST(163824.64909516752/8.0), + REAL_CONST(163851.4742623182/8.0), + REAL_CONST(163878.3005274435/8.0), + REAL_CONST(163905.12789045356/8.0), + REAL_CONST(163931.95635125853/8.0), + REAL_CONST(163958.78590976857/8.0), + REAL_CONST(163985.61656589387/8.0), + REAL_CONST(164012.44831954464/8.0), + REAL_CONST(164039.28117063109/8.0), + REAL_CONST(164066.11511906344/8.0), + REAL_CONST(164092.95016475199/8.0), + REAL_CONST(164119.78630760699/8.0), + REAL_CONST(164146.62354753874/8.0), + REAL_CONST(164173.46188445756/8.0), + REAL_CONST(164200.30131827376/8.0), + REAL_CONST(164227.14184889771/8.0), + REAL_CONST(164253.98347623978/8.0), + REAL_CONST(164280.82620021031/8.0), + REAL_CONST(164307.67002071979/8.0), + REAL_CONST(164334.51493767856/8.0), + REAL_CONST(164361.3609509971/8.0), + REAL_CONST(164388.20806058586/8.0), + REAL_CONST(164415.05626635533/8.0), + REAL_CONST(164441.905568216/8.0), + REAL_CONST(164468.75596607837/8.0), + REAL_CONST(164495.607459853/8.0), + REAL_CONST(164522.4600494504/8.0), + REAL_CONST(164549.31373478117/8.0), + REAL_CONST(164576.16851575591/8.0), + REAL_CONST(164603.02439228518/8.0), + REAL_CONST(164629.88136427966/8.0), + REAL_CONST(164656.73943164994/8.0), + REAL_CONST(164683.59859430668/8.0), + REAL_CONST(164710.45885216061/8.0), + REAL_CONST(164737.32020512238/8.0), + REAL_CONST(164764.1826531027/8.0), + REAL_CONST(164791.04619601235/8.0), + REAL_CONST(164817.91083376206/8.0), + REAL_CONST(164844.77656626256/8.0), + REAL_CONST(164871.64339342469/8.0), + REAL_CONST(164898.51131515924/8.0), + REAL_CONST(164925.38033137703/8.0), + REAL_CONST(164952.25044198887/8.0), + REAL_CONST(164979.1216469057/8.0), + REAL_CONST(165005.9939460383/8.0), + REAL_CONST(165032.86733929763/8.0), + REAL_CONST(165059.7418265946/8.0), + REAL_CONST(165086.61740784015/8.0), + REAL_CONST(165113.4940829452/8.0) +#endif +}; + +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/is.c b/third_party/faad_hdc/upstream/libfaad/is.c new file mode 100644 index 000000000..b9945c720 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/is.c @@ -0,0 +1,106 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: is.c,v 1.28 2007/11/01 12:33:31 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include "syntax.h" +#include "is.h" + +#ifdef FIXED_POINT +static real_t pow05_table[] = { + COEF_CONST(1.0), /* 0.5^( 0/4) */ + COEF_CONST(0.84089641525371), /* 0.5^(+1/4) */ + COEF_CONST(0.70710678118655), /* 0.5^(+2/4) */ + COEF_CONST(0.59460355750136) /* 0.5^(+3/4) */ +}; +#endif + +void is_decode(ic_stream *ics, ic_stream *icsr, real_t *l_spec, real_t *r_spec, + uint16_t frame_len) +{ + uint8_t g, sfb, b; + uint16_t i; + real_t scale; +#ifdef FIXED_POINT + int32_t exp, frac; +#endif + + uint16_t nshort = frame_len/8; + uint8_t group = 0; + + for (g = 0; g < icsr->num_window_groups; g++) + { + /* Do intensity stereo decoding */ + for (b = 0; b < icsr->window_group_length[g]; b++) + { + for (sfb = 0; sfb < icsr->max_sfb; sfb++) + { + if (is_intensity(icsr, g, sfb)) + { + int16_t scale_factor = icsr->scale_factors[g][sfb]; +#ifdef MAIN_DEC + /* For scalefactor bands coded in intensity stereo the + corresponding predictors in the right channel are + switched to "off". + */ + ics->pred.prediction_used[sfb] = 0; + icsr->pred.prediction_used[sfb] = 0; +#endif + +#ifndef FIXED_POINT + scale_factor = min(max(scale_factor, -120), 120); + scale = (real_t)pow(0.5, (0.25*scale_factor)); +#else + scale_factor = min(max(scale_factor, -60), 60); + exp = scale_factor >> 2; /* exp is -15..15 */ + frac = scale_factor & 3; + scale = pow05_table[frac]; + exp += COEF_BITS - REAL_BITS; /* exp is -1..29 */ + if (exp < 0) + scale <<= -exp; + else + scale >>= exp; +#endif + + /* Scale from left to right channel, + do not touch left channel */ + for (i = icsr->swb_offset[sfb]; i < min(icsr->swb_offset[sfb+1], ics->swb_offset_max); i++) + { + r_spec[(group*nshort)+i] = MUL_R(l_spec[(group*nshort)+i], scale); + if (is_intensity(icsr, g, sfb) != invert_intensity(ics, g, sfb)) + r_spec[(group*nshort)+i] = -r_spec[(group*nshort)+i]; + } + } + } + group++; + } + } +} diff --git a/third_party/faad_hdc/upstream/libfaad/is.h b/third_party/faad_hdc/upstream/libfaad/is.h new file mode 100644 index 000000000..6a8d2890d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/is.h @@ -0,0 +1,67 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: is.h,v 1.20 2007/11/01 12:33:31 menno Exp $ +**/ + +#ifndef __IS_H__ +#define __IS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "syntax.h" + +void is_decode(ic_stream *ics, ic_stream *icsr, real_t *l_spec, real_t *r_spec, + uint16_t frame_len); + +static INLINE int8_t is_intensity(ic_stream *ics, uint8_t group, uint8_t sfb) +{ + switch (ics->sfb_cb[group][sfb]) + { + case INTENSITY_HCB: + return 1; + case INTENSITY_HCB2: + return -1; + default: + return 0; + } +} + +static INLINE int8_t invert_intensity(ic_stream *ics, uint8_t group, uint8_t sfb) +{ + if (ics->ms_mask_present == 1) + return (1-2*ics->ms_used[group][sfb]); + return 1; +} + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/kbd_win.h b/third_party/faad_hdc/upstream/libfaad/kbd_win.h new file mode 100644 index 000000000..5f3a84bac --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/kbd_win.h @@ -0,0 +1,2297 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: kbd_win.h,v 1.21 2007/11/01 12:33:31 menno Exp $ +**/ + +#ifndef __KBD_WIN_H__ +#define __KBD_WIN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4305) +#pragma warning(disable:4244) +#endif + +ALIGN static const real_t kbd_long_1024[] = +{ + FRAC_CONST(0.00029256153896361), + FRAC_CONST(0.00042998567353047), + FRAC_CONST(0.00054674074589540), + FRAC_CONST(0.00065482304299792), + FRAC_CONST(0.00075870195068747), + FRAC_CONST(0.00086059331713336), + FRAC_CONST(0.00096177541439010), + FRAC_CONST(0.0010630609410878), + FRAC_CONST(0.0011650036308132), + FRAC_CONST(0.0012680012194148), + FRAC_CONST(0.0013723517232956), + FRAC_CONST(0.0014782864109136), + FRAC_CONST(0.0015859901976719), + FRAC_CONST(0.0016956148252373), + FRAC_CONST(0.0018072876903517), + FRAC_CONST(0.0019211179405514), + FRAC_CONST(0.0020372007924215), + FRAC_CONST(0.0021556206591754), + FRAC_CONST(0.0022764534599614), + FRAC_CONST(0.0023997683540995), + FRAC_CONST(0.0025256290631156), + FRAC_CONST(0.0026540948920831), + FRAC_CONST(0.0027852215281403), + FRAC_CONST(0.0029190616715331), + FRAC_CONST(0.0030556655443223), + FRAC_CONST(0.0031950812943391), + FRAC_CONST(0.0033373553240392), + FRAC_CONST(0.0034825325586930), + FRAC_CONST(0.0036306566699199), + FRAC_CONST(0.0037817702604646), + FRAC_CONST(0.0039359150179719), + FRAC_CONST(0.0040931318437260), + FRAC_CONST(0.0042534609610026), + FRAC_CONST(0.0044169420066964), + FRAC_CONST(0.0045836141091341), + FRAC_CONST(0.0047535159544086), + FRAC_CONST(0.0049266858431214), + FRAC_CONST(0.0051031617390698), + FRAC_CONST(0.0052829813111335), + FRAC_CONST(0.0054661819693975), + FRAC_CONST(0.0056528008963682), + FRAC_CONST(0.0058428750739943), + FRAC_CONST(0.0060364413070882), + FRAC_CONST(0.0062335362436492), + FRAC_CONST(0.0064341963925079), + FRAC_CONST(0.0066384581386503), + FRAC_CONST(0.0068463577565218), + FRAC_CONST(0.0070579314215715), + FRAC_CONST(0.0072732152202559), + FRAC_CONST(0.0074922451586909), + FRAC_CONST(0.0077150571701162), + FRAC_CONST(0.0079416871213115), + FRAC_CONST(0.0081721708180857), + FRAC_CONST(0.0084065440099458), + FRAC_CONST(0.0086448423940363), + FRAC_CONST(0.0088871016184291), + FRAC_CONST(0.0091333572848345), + FRAC_CONST(0.0093836449507939), + FRAC_CONST(0.0096380001314086), + FRAC_CONST(0.0098964583006517), + FRAC_CONST(0.010159054892306), + FRAC_CONST(0.010425825300561), + FRAC_CONST(0.010696804880310), + FRAC_CONST(0.010972028947167), + FRAC_CONST(0.011251532777236), + FRAC_CONST(0.011535351606646), + FRAC_CONST(0.011823520630897), + FRAC_CONST(0.012116075003993), + FRAC_CONST(0.012413049837429), + FRAC_CONST(0.012714480198999), + FRAC_CONST(0.013020401111478), + FRAC_CONST(0.013330847551161), + FRAC_CONST(0.013645854446288), + FRAC_CONST(0.013965456675352), + FRAC_CONST(0.014289689065314), + FRAC_CONST(0.014618586389712), + FRAC_CONST(0.014952183366697), + FRAC_CONST(0.015290514656976), + FRAC_CONST(0.015633614861688), + FRAC_CONST(0.015981518520214), + FRAC_CONST(0.016334260107915), + FRAC_CONST(0.016691874033817), + FRAC_CONST(0.017054394638241), + FRAC_CONST(0.017421856190380), + FRAC_CONST(0.017794292885832), + FRAC_CONST(0.018171738844085), + FRAC_CONST(0.018554228105962), + FRAC_CONST(0.018941794631032), + FRAC_CONST(0.019334472294980), + FRAC_CONST(0.019732294886947), + FRAC_CONST(0.020135296106839), + FRAC_CONST(0.020543509562604), + FRAC_CONST(0.020956968767488), + FRAC_CONST(0.021375707137257), + FRAC_CONST(0.021799757987407), + FRAC_CONST(0.022229154530343), + FRAC_CONST(0.022663929872540), + FRAC_CONST(0.023104117011689), + FRAC_CONST(0.023549748833816), + FRAC_CONST(0.024000858110398), + FRAC_CONST(0.024457477495451), + FRAC_CONST(0.024919639522613), + FRAC_CONST(0.025387376602207), + FRAC_CONST(0.025860721018295), + FRAC_CONST(0.026339704925726), + FRAC_CONST(0.026824360347160), + FRAC_CONST(0.027314719170100), + FRAC_CONST(0.027810813143900), + FRAC_CONST(0.028312673876775), + FRAC_CONST(0.028820332832801), + FRAC_CONST(0.029333821328905), + FRAC_CONST(0.029853170531859), + FRAC_CONST(0.030378411455255), + FRAC_CONST(0.030909574956490), + FRAC_CONST(0.031446691733739), + FRAC_CONST(0.031989792322926), + FRAC_CONST(0.032538907094693), + FRAC_CONST(0.033094066251369), + FRAC_CONST(0.033655299823935), + FRAC_CONST(0.034222637668991), + FRAC_CONST(0.034796109465717), + FRAC_CONST(0.035375744712844), + FRAC_CONST(0.035961572725616), + FRAC_CONST(0.036553622632758), + FRAC_CONST(0.037151923373446), + FRAC_CONST(0.037756503694277), + FRAC_CONST(0.038367392146243), + FRAC_CONST(0.038984617081711), + FRAC_CONST(0.039608206651398), + FRAC_CONST(0.040238188801359), + FRAC_CONST(0.040874591269976), + FRAC_CONST(0.041517441584950), + FRAC_CONST(0.042166767060301), + FRAC_CONST(0.042822594793376), + FRAC_CONST(0.043484951661852), + FRAC_CONST(0.044153864320760), + FRAC_CONST(0.044829359199509), + FRAC_CONST(0.045511462498913), + FRAC_CONST(0.046200200188234), + FRAC_CONST(0.046895598002228), + FRAC_CONST(0.047597681438201), + FRAC_CONST(0.048306475753074), + FRAC_CONST(0.049022005960455), + FRAC_CONST(0.049744296827725), + FRAC_CONST(0.050473372873129), + FRAC_CONST(0.051209258362879), + FRAC_CONST(0.051951977308273), + FRAC_CONST(0.052701553462813), + FRAC_CONST(0.053458010319350), + FRAC_CONST(0.054221371107223), + FRAC_CONST(0.054991658789428), + FRAC_CONST(0.055768896059787), + FRAC_CONST(0.056553105340134), + FRAC_CONST(0.057344308777513), + FRAC_CONST(0.058142528241393), + FRAC_CONST(0.058947785320893), + FRAC_CONST(0.059760101322019), + FRAC_CONST(0.060579497264926), + FRAC_CONST(0.061405993881180), + FRAC_CONST(0.062239611611049), + FRAC_CONST(0.063080370600799), + FRAC_CONST(0.063928290700012), + FRAC_CONST(0.064783391458919), + FRAC_CONST(0.065645692125747), + FRAC_CONST(0.066515211644086), + FRAC_CONST(0.067391968650269), + FRAC_CONST(0.068275981470777), + FRAC_CONST(0.069167268119652), + FRAC_CONST(0.070065846295935), + FRAC_CONST(0.070971733381121), + FRAC_CONST(0.071884946436630), + FRAC_CONST(0.072805502201299), + FRAC_CONST(0.073733417088896), + FRAC_CONST(0.074668707185649), + FRAC_CONST(0.075611388247794), + FRAC_CONST(0.076561475699152), + FRAC_CONST(0.077518984628715), + FRAC_CONST(0.078483929788261), + FRAC_CONST(0.079456325589986), + FRAC_CONST(0.080436186104162), + FRAC_CONST(0.081423525056808), + FRAC_CONST(0.082418355827392), + FRAC_CONST(0.083420691446553), + FRAC_CONST(0.084430544593841), + FRAC_CONST(0.085447927595483), + FRAC_CONST(0.086472852422178), + FRAC_CONST(0.087505330686900), + FRAC_CONST(0.088545373642744), + FRAC_CONST(0.089592992180780), + FRAC_CONST(0.090648196827937), + FRAC_CONST(0.091710997744919), + FRAC_CONST(0.092781404724131), + FRAC_CONST(0.093859427187640), + FRAC_CONST(0.094945074185163), + FRAC_CONST(0.096038354392069), + FRAC_CONST(0.097139276107423), + FRAC_CONST(0.098247847252041), + FRAC_CONST(0.099364075366580), + FRAC_CONST(0.10048796760965), + FRAC_CONST(0.10161953075597), + FRAC_CONST(0.10275877119451), + FRAC_CONST(0.10390569492671), + FRAC_CONST(0.10506030756469), + FRAC_CONST(0.10622261432949), + FRAC_CONST(0.10739262004941), + FRAC_CONST(0.10857032915821), + FRAC_CONST(0.10975574569357), + FRAC_CONST(0.11094887329534), + FRAC_CONST(0.11214971520402), + FRAC_CONST(0.11335827425914), + FRAC_CONST(0.11457455289772), + FRAC_CONST(0.11579855315274), + FRAC_CONST(0.11703027665170), + FRAC_CONST(0.11826972461510), + FRAC_CONST(0.11951689785504), + FRAC_CONST(0.12077179677383), + FRAC_CONST(0.12203442136263), + FRAC_CONST(0.12330477120008), + FRAC_CONST(0.12458284545102), + FRAC_CONST(0.12586864286523), + FRAC_CONST(0.12716216177615), + FRAC_CONST(0.12846340009971), + FRAC_CONST(0.12977235533312), + FRAC_CONST(0.13108902455375), + FRAC_CONST(0.13241340441801), + FRAC_CONST(0.13374549116025), + FRAC_CONST(0.13508528059173), + FRAC_CONST(0.13643276809961), + FRAC_CONST(0.13778794864595), + FRAC_CONST(0.13915081676677), + FRAC_CONST(0.14052136657114), + FRAC_CONST(0.14189959174027), + FRAC_CONST(0.14328548552671), + FRAC_CONST(0.14467904075349), + FRAC_CONST(0.14608024981336), + FRAC_CONST(0.14748910466804), + FRAC_CONST(0.14890559684750), + FRAC_CONST(0.15032971744929), + FRAC_CONST(0.15176145713790), + FRAC_CONST(0.15320080614414), + FRAC_CONST(0.15464775426459), + FRAC_CONST(0.15610229086100), + FRAC_CONST(0.15756440485987), + FRAC_CONST(0.15903408475193), + FRAC_CONST(0.16051131859170), + FRAC_CONST(0.16199609399712), + FRAC_CONST(0.16348839814917), + FRAC_CONST(0.16498821779156), + FRAC_CONST(0.16649553923042), + FRAC_CONST(0.16801034833404), + FRAC_CONST(0.16953263053270), + FRAC_CONST(0.17106237081842), + FRAC_CONST(0.17259955374484), + FRAC_CONST(0.17414416342714), + FRAC_CONST(0.17569618354193), + FRAC_CONST(0.17725559732720), + FRAC_CONST(0.17882238758238), + FRAC_CONST(0.18039653666830), + FRAC_CONST(0.18197802650733), + FRAC_CONST(0.18356683858343), + FRAC_CONST(0.18516295394233), + FRAC_CONST(0.18676635319174), + FRAC_CONST(0.18837701650148), + FRAC_CONST(0.18999492360384), + FRAC_CONST(0.19162005379380), + FRAC_CONST(0.19325238592940), + FRAC_CONST(0.19489189843209), + FRAC_CONST(0.19653856928714), + FRAC_CONST(0.19819237604409), + FRAC_CONST(0.19985329581721), + FRAC_CONST(0.20152130528605), + FRAC_CONST(0.20319638069594), + FRAC_CONST(0.20487849785865), + FRAC_CONST(0.20656763215298), + FRAC_CONST(0.20826375852540), + FRAC_CONST(0.20996685149083), + FRAC_CONST(0.21167688513330), + FRAC_CONST(0.21339383310678), + FRAC_CONST(0.21511766863598), + FRAC_CONST(0.21684836451719), + FRAC_CONST(0.21858589311922), + FRAC_CONST(0.22033022638425), + FRAC_CONST(0.22208133582887), + FRAC_CONST(0.22383919254503), + FRAC_CONST(0.22560376720111), + FRAC_CONST(0.22737503004300), + FRAC_CONST(0.22915295089517), + FRAC_CONST(0.23093749916189), + FRAC_CONST(0.23272864382838), + FRAC_CONST(0.23452635346201), + FRAC_CONST(0.23633059621364), + FRAC_CONST(0.23814133981883), + FRAC_CONST(0.23995855159925), + FRAC_CONST(0.24178219846403), + FRAC_CONST(0.24361224691114), + FRAC_CONST(0.24544866302890), + FRAC_CONST(0.24729141249740), + FRAC_CONST(0.24914046059007), + FRAC_CONST(0.25099577217522), + FRAC_CONST(0.25285731171763), + FRAC_CONST(0.25472504328019), + FRAC_CONST(0.25659893052556), + FRAC_CONST(0.25847893671788), + FRAC_CONST(0.26036502472451), + FRAC_CONST(0.26225715701781), + FRAC_CONST(0.26415529567692), + FRAC_CONST(0.26605940238966), + FRAC_CONST(0.26796943845439), + FRAC_CONST(0.26988536478190), + FRAC_CONST(0.27180714189742), + FRAC_CONST(0.27373472994256), + FRAC_CONST(0.27566808867736), + FRAC_CONST(0.27760717748238), + FRAC_CONST(0.27955195536071), + FRAC_CONST(0.28150238094021), + FRAC_CONST(0.28345841247557), + FRAC_CONST(0.28542000785059), + FRAC_CONST(0.28738712458038), + FRAC_CONST(0.28935971981364), + FRAC_CONST(0.29133775033492), + FRAC_CONST(0.29332117256704), + FRAC_CONST(0.29530994257338), + FRAC_CONST(0.29730401606034), + FRAC_CONST(0.29930334837974), + FRAC_CONST(0.30130789453132), + FRAC_CONST(0.30331760916521), + FRAC_CONST(0.30533244658452), + FRAC_CONST(0.30735236074785), + FRAC_CONST(0.30937730527195), + FRAC_CONST(0.31140723343430), + FRAC_CONST(0.31344209817583), + FRAC_CONST(0.31548185210356), + FRAC_CONST(0.31752644749341), + FRAC_CONST(0.31957583629288), + FRAC_CONST(0.32162997012390), + FRAC_CONST(0.32368880028565), + FRAC_CONST(0.32575227775738), + FRAC_CONST(0.32782035320134), + FRAC_CONST(0.32989297696566), + FRAC_CONST(0.33197009908736), + FRAC_CONST(0.33405166929523), + FRAC_CONST(0.33613763701295), + FRAC_CONST(0.33822795136203), + FRAC_CONST(0.34032256116495), + FRAC_CONST(0.34242141494820), + FRAC_CONST(0.34452446094547), + FRAC_CONST(0.34663164710072), + FRAC_CONST(0.34874292107143), + FRAC_CONST(0.35085823023181), + FRAC_CONST(0.35297752167598), + FRAC_CONST(0.35510074222129), + FRAC_CONST(0.35722783841160), + FRAC_CONST(0.35935875652060), + FRAC_CONST(0.36149344255514), + FRAC_CONST(0.36363184225864), + FRAC_CONST(0.36577390111444), + FRAC_CONST(0.36791956434930), + FRAC_CONST(0.37006877693676), + FRAC_CONST(0.37222148360070), + FRAC_CONST(0.37437762881878), + FRAC_CONST(0.37653715682603), + FRAC_CONST(0.37870001161834), + FRAC_CONST(0.38086613695607), + FRAC_CONST(0.38303547636766), + FRAC_CONST(0.38520797315322), + FRAC_CONST(0.38738357038821), + FRAC_CONST(0.38956221092708), + FRAC_CONST(0.39174383740701), + FRAC_CONST(0.39392839225157), + FRAC_CONST(0.39611581767449), + FRAC_CONST(0.39830605568342), + FRAC_CONST(0.40049904808370), + FRAC_CONST(0.40269473648218), + FRAC_CONST(0.40489306229101), + FRAC_CONST(0.40709396673153), + FRAC_CONST(0.40929739083810), + FRAC_CONST(0.41150327546197), + FRAC_CONST(0.41371156127524), + FRAC_CONST(0.41592218877472), + FRAC_CONST(0.41813509828594), + FRAC_CONST(0.42035022996702), + FRAC_CONST(0.42256752381274), + FRAC_CONST(0.42478691965848), + FRAC_CONST(0.42700835718423), + FRAC_CONST(0.42923177591866), + FRAC_CONST(0.43145711524314), + FRAC_CONST(0.43368431439580), + FRAC_CONST(0.43591331247564), + FRAC_CONST(0.43814404844658), + FRAC_CONST(0.44037646114161), + FRAC_CONST(0.44261048926688), + FRAC_CONST(0.44484607140589), + FRAC_CONST(0.44708314602359), + FRAC_CONST(0.44932165147057), + FRAC_CONST(0.45156152598727), + FRAC_CONST(0.45380270770813), + FRAC_CONST(0.45604513466581), + FRAC_CONST(0.45828874479543), + FRAC_CONST(0.46053347593880), + FRAC_CONST(0.46277926584861), + FRAC_CONST(0.46502605219277), + FRAC_CONST(0.46727377255861), + FRAC_CONST(0.46952236445718), + FRAC_CONST(0.47177176532752), + FRAC_CONST(0.47402191254100), + FRAC_CONST(0.47627274340557), + FRAC_CONST(0.47852419517009), + FRAC_CONST(0.48077620502869), + FRAC_CONST(0.48302871012505), + FRAC_CONST(0.48528164755674), + FRAC_CONST(0.48753495437962), + FRAC_CONST(0.48978856761212), + FRAC_CONST(0.49204242423966), + FRAC_CONST(0.49429646121898), + FRAC_CONST(0.49655061548250), + FRAC_CONST(0.49880482394273), + FRAC_CONST(0.50105902349665), + FRAC_CONST(0.50331315103004), + FRAC_CONST(0.50556714342194), + FRAC_CONST(0.50782093754901), + FRAC_CONST(0.51007447028990), + FRAC_CONST(0.51232767852971), + FRAC_CONST(0.51458049916433), + FRAC_CONST(0.51683286910489), + FRAC_CONST(0.51908472528213), + FRAC_CONST(0.52133600465083), + FRAC_CONST(0.52358664419420), + FRAC_CONST(0.52583658092832), + FRAC_CONST(0.52808575190648), + FRAC_CONST(0.53033409422367), + FRAC_CONST(0.53258154502092), + FRAC_CONST(0.53482804148974), + FRAC_CONST(0.53707352087652), + FRAC_CONST(0.53931792048690), + FRAC_CONST(0.54156117769021), + FRAC_CONST(0.54380322992385), + FRAC_CONST(0.54604401469766), + FRAC_CONST(0.54828346959835), + FRAC_CONST(0.55052153229384), + FRAC_CONST(0.55275814053768), + FRAC_CONST(0.55499323217338), + FRAC_CONST(0.55722674513883), + FRAC_CONST(0.55945861747062), + FRAC_CONST(0.56168878730842), + FRAC_CONST(0.56391719289930), + FRAC_CONST(0.56614377260214), + FRAC_CONST(0.56836846489188), + FRAC_CONST(0.57059120836390), + FRAC_CONST(0.57281194173835), + FRAC_CONST(0.57503060386439), + FRAC_CONST(0.57724713372458), + FRAC_CONST(0.57946147043912), + FRAC_CONST(0.58167355327012), + FRAC_CONST(0.58388332162591), + FRAC_CONST(0.58609071506528), + FRAC_CONST(0.58829567330173), + FRAC_CONST(0.59049813620770), + FRAC_CONST(0.59269804381879), + FRAC_CONST(0.59489533633802), + FRAC_CONST(0.59708995413996), + FRAC_CONST(0.59928183777495), + FRAC_CONST(0.60147092797329), + FRAC_CONST(0.60365716564937), + FRAC_CONST(0.60584049190582), + FRAC_CONST(0.60802084803764), + FRAC_CONST(0.61019817553632), + FRAC_CONST(0.61237241609393), + FRAC_CONST(0.61454351160718), + FRAC_CONST(0.61671140418155), + FRAC_CONST(0.61887603613527), + FRAC_CONST(0.62103735000336), + FRAC_CONST(0.62319528854167), + FRAC_CONST(0.62534979473088), + FRAC_CONST(0.62750081178042), + FRAC_CONST(0.62964828313250), + FRAC_CONST(0.63179215246597), + FRAC_CONST(0.63393236370030), + FRAC_CONST(0.63606886099946), + FRAC_CONST(0.63820158877577), + FRAC_CONST(0.64033049169379), + FRAC_CONST(0.64245551467413), + FRAC_CONST(0.64457660289729), + FRAC_CONST(0.64669370180740), + FRAC_CONST(0.64880675711607), + FRAC_CONST(0.65091571480603), + FRAC_CONST(0.65302052113494), + FRAC_CONST(0.65512112263906), + FRAC_CONST(0.65721746613689), + FRAC_CONST(0.65930949873289), + FRAC_CONST(0.66139716782102), + FRAC_CONST(0.66348042108842), + FRAC_CONST(0.66555920651892), + FRAC_CONST(0.66763347239664), + FRAC_CONST(0.66970316730947), + FRAC_CONST(0.67176824015260), + FRAC_CONST(0.67382864013196), + FRAC_CONST(0.67588431676768), + FRAC_CONST(0.67793521989751), + FRAC_CONST(0.67998129968017), + FRAC_CONST(0.68202250659876), + FRAC_CONST(0.68405879146403), + FRAC_CONST(0.68609010541774), + FRAC_CONST(0.68811639993588), + FRAC_CONST(0.69013762683195), + FRAC_CONST(0.69215373826012), + FRAC_CONST(0.69416468671849), + FRAC_CONST(0.69617042505214), + FRAC_CONST(0.69817090645634), + FRAC_CONST(0.70016608447958), + FRAC_CONST(0.70215591302664), + FRAC_CONST(0.70414034636163), + FRAC_CONST(0.70611933911096), + FRAC_CONST(0.70809284626630), + FRAC_CONST(0.71006082318751), + FRAC_CONST(0.71202322560554), + FRAC_CONST(0.71398000962530), + FRAC_CONST(0.71593113172842), + FRAC_CONST(0.71787654877613), + FRAC_CONST(0.71981621801195), + FRAC_CONST(0.72175009706445), + FRAC_CONST(0.72367814394990), + FRAC_CONST(0.72560031707496), + FRAC_CONST(0.72751657523927), + FRAC_CONST(0.72942687763803), + FRAC_CONST(0.73133118386457), + FRAC_CONST(0.73322945391280), + FRAC_CONST(0.73512164817975), + FRAC_CONST(0.73700772746796), + FRAC_CONST(0.73888765298787), + FRAC_CONST(0.74076138636020), + FRAC_CONST(0.74262888961827), + FRAC_CONST(0.74449012521027), + FRAC_CONST(0.74634505600152), + FRAC_CONST(0.74819364527663), + FRAC_CONST(0.75003585674175), + FRAC_CONST(0.75187165452661), + FRAC_CONST(0.75370100318668), + FRAC_CONST(0.75552386770515), + FRAC_CONST(0.75734021349500), + FRAC_CONST(0.75915000640095), + FRAC_CONST(0.76095321270137), + FRAC_CONST(0.76274979911019), + FRAC_CONST(0.76453973277875), + FRAC_CONST(0.76632298129757), + FRAC_CONST(0.76809951269819), + FRAC_CONST(0.76986929545481), + FRAC_CONST(0.77163229848604), + FRAC_CONST(0.77338849115651), + FRAC_CONST(0.77513784327849), + FRAC_CONST(0.77688032511340), + FRAC_CONST(0.77861590737340), + FRAC_CONST(0.78034456122283), + FRAC_CONST(0.78206625827961), + FRAC_CONST(0.78378097061667), + FRAC_CONST(0.78548867076330), + FRAC_CONST(0.78718933170643), + FRAC_CONST(0.78888292689189), + FRAC_CONST(0.79056943022564), + FRAC_CONST(0.79224881607494), + FRAC_CONST(0.79392105926949), + FRAC_CONST(0.79558613510249), + FRAC_CONST(0.79724401933170), + FRAC_CONST(0.79889468818046), + FRAC_CONST(0.80053811833858), + FRAC_CONST(0.80217428696334), + FRAC_CONST(0.80380317168028), + FRAC_CONST(0.80542475058405), + FRAC_CONST(0.80703900223920), + FRAC_CONST(0.80864590568089), + FRAC_CONST(0.81024544041560), + FRAC_CONST(0.81183758642175), + FRAC_CONST(0.81342232415032), + FRAC_CONST(0.81499963452540), + FRAC_CONST(0.81656949894467), + FRAC_CONST(0.81813189927991), + FRAC_CONST(0.81968681787738), + FRAC_CONST(0.82123423755821), + FRAC_CONST(0.82277414161874), + FRAC_CONST(0.82430651383076), + FRAC_CONST(0.82583133844180), + FRAC_CONST(0.82734860017528), + FRAC_CONST(0.82885828423070), + FRAC_CONST(0.83036037628369), + FRAC_CONST(0.83185486248609), + FRAC_CONST(0.83334172946597), + FRAC_CONST(0.83482096432759), + FRAC_CONST(0.83629255465130), + FRAC_CONST(0.83775648849344), + FRAC_CONST(0.83921275438615), + FRAC_CONST(0.84066134133716), + FRAC_CONST(0.84210223882952), + FRAC_CONST(0.84353543682130), + FRAC_CONST(0.84496092574524), + FRAC_CONST(0.84637869650833), + FRAC_CONST(0.84778874049138), + FRAC_CONST(0.84919104954855), + FRAC_CONST(0.85058561600677), + FRAC_CONST(0.85197243266520), + FRAC_CONST(0.85335149279457), + FRAC_CONST(0.85472279013653), + FRAC_CONST(0.85608631890295), + FRAC_CONST(0.85744207377513), + FRAC_CONST(0.85879004990298), + FRAC_CONST(0.86013024290422), + FRAC_CONST(0.86146264886346), + FRAC_CONST(0.86278726433124), + FRAC_CONST(0.86410408632306), + FRAC_CONST(0.86541311231838), + FRAC_CONST(0.86671434025950), + FRAC_CONST(0.86800776855046), + FRAC_CONST(0.86929339605590), + FRAC_CONST(0.87057122209981), + FRAC_CONST(0.87184124646433), + FRAC_CONST(0.87310346938840), + FRAC_CONST(0.87435789156650), + FRAC_CONST(0.87560451414719), + FRAC_CONST(0.87684333873173), + FRAC_CONST(0.87807436737261), + FRAC_CONST(0.87929760257204), + FRAC_CONST(0.88051304728038), + FRAC_CONST(0.88172070489456), + FRAC_CONST(0.88292057925645), + FRAC_CONST(0.88411267465117), + FRAC_CONST(0.88529699580537), + FRAC_CONST(0.88647354788545), + FRAC_CONST(0.88764233649580), + FRAC_CONST(0.88880336767692), + FRAC_CONST(0.88995664790351), + FRAC_CONST(0.89110218408260), + FRAC_CONST(0.89223998355154), + FRAC_CONST(0.89337005407600), + FRAC_CONST(0.89449240384793), + FRAC_CONST(0.89560704148345), + FRAC_CONST(0.89671397602074), + FRAC_CONST(0.89781321691786), + FRAC_CONST(0.89890477405053), + FRAC_CONST(0.89998865770993), + FRAC_CONST(0.90106487860034), + FRAC_CONST(0.90213344783689), + FRAC_CONST(0.90319437694315), + FRAC_CONST(0.90424767784873), + FRAC_CONST(0.90529336288690), + FRAC_CONST(0.90633144479201), + FRAC_CONST(0.90736193669708), + FRAC_CONST(0.90838485213119), + FRAC_CONST(0.90940020501694), + FRAC_CONST(0.91040800966776), + FRAC_CONST(0.91140828078533), + FRAC_CONST(0.91240103345685), + FRAC_CONST(0.91338628315231), + FRAC_CONST(0.91436404572173), + FRAC_CONST(0.91533433739238), + FRAC_CONST(0.91629717476594), + FRAC_CONST(0.91725257481564), + FRAC_CONST(0.91820055488334), + FRAC_CONST(0.91914113267664), + FRAC_CONST(0.92007432626589), + FRAC_CONST(0.92100015408120), + FRAC_CONST(0.92191863490944), + FRAC_CONST(0.92282978789113), + FRAC_CONST(0.92373363251740), + FRAC_CONST(0.92463018862687), + FRAC_CONST(0.92551947640245), + FRAC_CONST(0.92640151636824), + FRAC_CONST(0.92727632938624), + FRAC_CONST(0.92814393665320), + FRAC_CONST(0.92900435969727), + FRAC_CONST(0.92985762037477), + FRAC_CONST(0.93070374086684), + FRAC_CONST(0.93154274367610), + FRAC_CONST(0.93237465162328), + FRAC_CONST(0.93319948784382), + FRAC_CONST(0.93401727578443), + FRAC_CONST(0.93482803919967), + FRAC_CONST(0.93563180214841), + FRAC_CONST(0.93642858899043), + FRAC_CONST(0.93721842438279), + FRAC_CONST(0.93800133327637), + FRAC_CONST(0.93877734091223), + FRAC_CONST(0.93954647281807), + FRAC_CONST(0.94030875480458), + FRAC_CONST(0.94106421296182), + FRAC_CONST(0.94181287365556), + FRAC_CONST(0.94255476352362), + FRAC_CONST(0.94328990947213), + FRAC_CONST(0.94401833867184), + FRAC_CONST(0.94474007855439), + FRAC_CONST(0.94545515680855), + FRAC_CONST(0.94616360137644), + FRAC_CONST(0.94686544044975), + FRAC_CONST(0.94756070246592), + FRAC_CONST(0.94824941610434), + FRAC_CONST(0.94893161028248), + FRAC_CONST(0.94960731415209), + FRAC_CONST(0.95027655709525), + FRAC_CONST(0.95093936872056), + FRAC_CONST(0.95159577885924), + FRAC_CONST(0.95224581756115), + FRAC_CONST(0.95288951509097), + FRAC_CONST(0.95352690192417), + FRAC_CONST(0.95415800874314), + FRAC_CONST(0.95478286643320), + FRAC_CONST(0.95540150607863), + FRAC_CONST(0.95601395895871), + FRAC_CONST(0.95662025654373), + FRAC_CONST(0.95722043049100), + FRAC_CONST(0.95781451264084), + FRAC_CONST(0.95840253501260), + FRAC_CONST(0.95898452980058), + FRAC_CONST(0.95956052937008), + FRAC_CONST(0.96013056625336), + FRAC_CONST(0.96069467314557), + FRAC_CONST(0.96125288290073), + FRAC_CONST(0.96180522852773), + FRAC_CONST(0.96235174318622), + FRAC_CONST(0.96289246018262), + FRAC_CONST(0.96342741296604), + FRAC_CONST(0.96395663512424), + FRAC_CONST(0.96448016037959), + FRAC_CONST(0.96499802258499), + FRAC_CONST(0.96551025571985), + FRAC_CONST(0.96601689388602), + FRAC_CONST(0.96651797130376), + FRAC_CONST(0.96701352230768), + FRAC_CONST(0.96750358134269), + FRAC_CONST(0.96798818295998), + FRAC_CONST(0.96846736181297), + FRAC_CONST(0.96894115265327), + FRAC_CONST(0.96940959032667), + FRAC_CONST(0.96987270976912), + FRAC_CONST(0.97033054600270), + FRAC_CONST(0.97078313413161), + FRAC_CONST(0.97123050933818), + FRAC_CONST(0.97167270687887), + FRAC_CONST(0.97210976208030), + FRAC_CONST(0.97254171033525), + FRAC_CONST(0.97296858709871), + FRAC_CONST(0.97339042788392), + FRAC_CONST(0.97380726825843), + FRAC_CONST(0.97421914384017), + FRAC_CONST(0.97462609029350), + FRAC_CONST(0.97502814332534), + FRAC_CONST(0.97542533868127), + FRAC_CONST(0.97581771214160), + FRAC_CONST(0.97620529951759), + FRAC_CONST(0.97658813664749), + FRAC_CONST(0.97696625939282), + FRAC_CONST(0.97733970363445), + FRAC_CONST(0.97770850526884), + FRAC_CONST(0.97807270020427), + FRAC_CONST(0.97843232435704), + FRAC_CONST(0.97878741364771), + FRAC_CONST(0.97913800399743), + FRAC_CONST(0.97948413132414), + FRAC_CONST(0.97982583153895), + FRAC_CONST(0.98016314054243), + FRAC_CONST(0.98049609422096), + FRAC_CONST(0.98082472844313), + FRAC_CONST(0.98114907905608), + FRAC_CONST(0.98146918188197), + FRAC_CONST(0.98178507271438), + FRAC_CONST(0.98209678731477), + FRAC_CONST(0.98240436140902), + FRAC_CONST(0.98270783068385), + FRAC_CONST(0.98300723078342), + FRAC_CONST(0.98330259730589), + FRAC_CONST(0.98359396579995), + FRAC_CONST(0.98388137176152), + FRAC_CONST(0.98416485063031), + FRAC_CONST(0.98444443778651), + FRAC_CONST(0.98472016854752), + FRAC_CONST(0.98499207816463), + FRAC_CONST(0.98526020181980), + FRAC_CONST(0.98552457462240), + FRAC_CONST(0.98578523160609), + FRAC_CONST(0.98604220772560), + FRAC_CONST(0.98629553785362), + FRAC_CONST(0.98654525677772), + FRAC_CONST(0.98679139919726), + FRAC_CONST(0.98703399972035), + FRAC_CONST(0.98727309286089), + FRAC_CONST(0.98750871303556), + FRAC_CONST(0.98774089456089), + FRAC_CONST(0.98796967165036), + FRAC_CONST(0.98819507841154), + FRAC_CONST(0.98841714884323), + FRAC_CONST(0.98863591683269), + FRAC_CONST(0.98885141615285), + FRAC_CONST(0.98906368045957), + FRAC_CONST(0.98927274328896), + FRAC_CONST(0.98947863805473), + FRAC_CONST(0.98968139804554), + FRAC_CONST(0.98988105642241), + FRAC_CONST(0.99007764621618), + FRAC_CONST(0.99027120032501), + FRAC_CONST(0.99046175151186), + FRAC_CONST(0.99064933240208), + FRAC_CONST(0.99083397548099), + FRAC_CONST(0.99101571309153), + FRAC_CONST(0.99119457743191), + FRAC_CONST(0.99137060055337), + FRAC_CONST(0.99154381435784), + FRAC_CONST(0.99171425059582), + FRAC_CONST(0.99188194086414), + FRAC_CONST(0.99204691660388), + FRAC_CONST(0.99220920909823), + FRAC_CONST(0.99236884947045), + FRAC_CONST(0.99252586868186), + FRAC_CONST(0.99268029752989), + FRAC_CONST(0.99283216664606), + FRAC_CONST(0.99298150649419), + FRAC_CONST(0.99312834736847), + FRAC_CONST(0.99327271939167), + FRAC_CONST(0.99341465251338), + FRAC_CONST(0.99355417650825), + FRAC_CONST(0.99369132097430), + FRAC_CONST(0.99382611533130), + FRAC_CONST(0.99395858881910), + FRAC_CONST(0.99408877049612), + FRAC_CONST(0.99421668923778), + FRAC_CONST(0.99434237373503), + FRAC_CONST(0.99446585249289), + FRAC_CONST(0.99458715382906), + FRAC_CONST(0.99470630587254), + FRAC_CONST(0.99482333656229), + FRAC_CONST(0.99493827364600), + FRAC_CONST(0.99505114467878), + FRAC_CONST(0.99516197702200), + FRAC_CONST(0.99527079784214), + FRAC_CONST(0.99537763410962), + FRAC_CONST(0.99548251259777), + FRAC_CONST(0.99558545988178), + FRAC_CONST(0.99568650233767), + FRAC_CONST(0.99578566614138), + FRAC_CONST(0.99588297726783), + FRAC_CONST(0.99597846149005), + FRAC_CONST(0.99607214437834), + FRAC_CONST(0.99616405129947), + FRAC_CONST(0.99625420741595), + FRAC_CONST(0.99634263768527), + FRAC_CONST(0.99642936685928), + FRAC_CONST(0.99651441948352), + FRAC_CONST(0.99659781989663), + FRAC_CONST(0.99667959222978), + FRAC_CONST(0.99675976040620), + FRAC_CONST(0.99683834814063), + FRAC_CONST(0.99691537893895), + FRAC_CONST(0.99699087609774), + FRAC_CONST(0.99706486270391), + FRAC_CONST(0.99713736163442), + FRAC_CONST(0.99720839555593), + FRAC_CONST(0.99727798692461), + FRAC_CONST(0.99734615798589), + FRAC_CONST(0.99741293077431), + FRAC_CONST(0.99747832711337), + FRAC_CONST(0.99754236861541), + FRAC_CONST(0.99760507668158), + FRAC_CONST(0.99766647250181), + FRAC_CONST(0.99772657705478), + FRAC_CONST(0.99778541110799), + FRAC_CONST(0.99784299521785), + FRAC_CONST(0.99789934972976), + FRAC_CONST(0.99795449477828), + FRAC_CONST(0.99800845028730), + FRAC_CONST(0.99806123597027), + FRAC_CONST(0.99811287133042), + FRAC_CONST(0.99816337566108), + FRAC_CONST(0.99821276804596), + FRAC_CONST(0.99826106735952), + FRAC_CONST(0.99830829226732), + FRAC_CONST(0.99835446122649), + FRAC_CONST(0.99839959248609), + FRAC_CONST(0.99844370408765), + FRAC_CONST(0.99848681386566), + FRAC_CONST(0.99852893944805), + FRAC_CONST(0.99857009825685), + FRAC_CONST(0.99861030750869), + FRAC_CONST(0.99864958421549), + FRAC_CONST(0.99868794518504), + FRAC_CONST(0.99872540702178), + FRAC_CONST(0.99876198612738), + FRAC_CONST(0.99879769870160), + FRAC_CONST(0.99883256074295), + FRAC_CONST(0.99886658804953), + FRAC_CONST(0.99889979621983), + FRAC_CONST(0.99893220065356), + FRAC_CONST(0.99896381655254), + FRAC_CONST(0.99899465892154), + FRAC_CONST(0.99902474256924), + FRAC_CONST(0.99905408210916), + FRAC_CONST(0.99908269196056), + FRAC_CONST(0.99911058634952), + FRAC_CONST(0.99913777930986), + FRAC_CONST(0.99916428468421), + FRAC_CONST(0.99919011612505), + FRAC_CONST(0.99921528709576), + FRAC_CONST(0.99923981087174), + FRAC_CONST(0.99926370054150), + FRAC_CONST(0.99928696900779), + FRAC_CONST(0.99930962898876), + FRAC_CONST(0.99933169301910), + FRAC_CONST(0.99935317345126), + FRAC_CONST(0.99937408245662), + FRAC_CONST(0.99939443202674), + FRAC_CONST(0.99941423397457), + FRAC_CONST(0.99943349993572), + FRAC_CONST(0.99945224136972), + FRAC_CONST(0.99947046956130), + FRAC_CONST(0.99948819562171), + FRAC_CONST(0.99950543049000), + FRAC_CONST(0.99952218493439), + FRAC_CONST(0.99953846955355), + FRAC_CONST(0.99955429477803), + FRAC_CONST(0.99956967087154), + FRAC_CONST(0.99958460793242), + FRAC_CONST(0.99959911589494), + FRAC_CONST(0.99961320453077), + FRAC_CONST(0.99962688345035), + FRAC_CONST(0.99964016210433), + FRAC_CONST(0.99965304978499), + FRAC_CONST(0.99966555562769), + FRAC_CONST(0.99967768861231), + FRAC_CONST(0.99968945756473), + FRAC_CONST(0.99970087115825), + FRAC_CONST(0.99971193791510), + FRAC_CONST(0.99972266620792), + FRAC_CONST(0.99973306426121), + FRAC_CONST(0.99974314015288), + FRAC_CONST(0.99975290181568), + FRAC_CONST(0.99976235703876), + FRAC_CONST(0.99977151346914), + FRAC_CONST(0.99978037861326), + FRAC_CONST(0.99978895983845), + FRAC_CONST(0.99979726437448), + FRAC_CONST(0.99980529931507), + FRAC_CONST(0.99981307161943), + FRAC_CONST(0.99982058811377), + FRAC_CONST(0.99982785549283), + FRAC_CONST(0.99983488032144), + FRAC_CONST(0.99984166903600), + FRAC_CONST(0.99984822794606), + FRAC_CONST(0.99985456323584), + FRAC_CONST(0.99986068096572), + FRAC_CONST(0.99986658707386), + FRAC_CONST(0.99987228737764), + FRAC_CONST(0.99987778757524), + FRAC_CONST(0.99988309324717), + FRAC_CONST(0.99988820985777), + FRAC_CONST(0.99989314275675), + FRAC_CONST(0.99989789718072), + FRAC_CONST(0.99990247825468), + FRAC_CONST(0.99990689099357), + FRAC_CONST(0.99991114030376), + FRAC_CONST(0.99991523098456), + FRAC_CONST(0.99991916772971), + FRAC_CONST(0.99992295512891), + FRAC_CONST(0.99992659766930), + FRAC_CONST(0.99993009973692), + FRAC_CONST(0.99993346561824), + FRAC_CONST(0.99993669950161), + FRAC_CONST(0.99993980547870), + FRAC_CONST(0.99994278754604), + FRAC_CONST(0.99994564960642), + FRAC_CONST(0.99994839547033), + FRAC_CONST(0.99995102885747), + FRAC_CONST(0.99995355339809), + FRAC_CONST(0.99995597263451), + FRAC_CONST(0.99995829002249), + FRAC_CONST(0.99996050893264), + FRAC_CONST(0.99996263265183), + FRAC_CONST(0.99996466438460), + FRAC_CONST(0.99996660725452), + FRAC_CONST(0.99996846430558), + FRAC_CONST(0.99997023850356), + FRAC_CONST(0.99997193273736), + FRAC_CONST(0.99997354982037), + FRAC_CONST(0.99997509249183), + FRAC_CONST(0.99997656341810), + FRAC_CONST(0.99997796519400), + FRAC_CONST(0.99997930034415), + FRAC_CONST(0.99998057132421), + FRAC_CONST(0.99998178052220), + FRAC_CONST(0.99998293025975), + FRAC_CONST(0.99998402279338), + FRAC_CONST(0.99998506031574), + FRAC_CONST(0.99998604495686), + FRAC_CONST(0.99998697878536), + FRAC_CONST(0.99998786380966), + FRAC_CONST(0.99998870197921), + FRAC_CONST(0.99998949518567), + FRAC_CONST(0.99999024526408), + FRAC_CONST(0.99999095399401), + FRAC_CONST(0.99999162310077), + FRAC_CONST(0.99999225425649), + FRAC_CONST(0.99999284908128), + FRAC_CONST(0.99999340914435), + FRAC_CONST(0.99999393596510), + FRAC_CONST(0.99999443101421), + FRAC_CONST(0.99999489571473), + FRAC_CONST(0.99999533144314), + FRAC_CONST(0.99999573953040), + FRAC_CONST(0.99999612126300), + FRAC_CONST(0.99999647788395), + FRAC_CONST(0.99999681059383), + FRAC_CONST(0.99999712055178), + FRAC_CONST(0.99999740887647), + FRAC_CONST(0.99999767664709), + FRAC_CONST(0.99999792490431), + FRAC_CONST(0.99999815465123), + FRAC_CONST(0.99999836685427), + FRAC_CONST(0.99999856244415), + FRAC_CONST(0.99999874231676), + FRAC_CONST(0.99999890733405), + FRAC_CONST(0.99999905832493), + FRAC_CONST(0.99999919608613), + FRAC_CONST(0.99999932138304), + FRAC_CONST(0.99999943495056), + FRAC_CONST(0.99999953749392), + FRAC_CONST(0.99999962968950), + FRAC_CONST(0.99999971218563), + FRAC_CONST(0.99999978560337), + FRAC_CONST(0.99999985053727), + FRAC_CONST(0.99999990755616), + FRAC_CONST(0.99999995720387) +}; + +#ifdef ALLOW_SMALL_FRAMELENGTH +ALIGN static const real_t kbd_long_960[] = { + FRAC_CONST(0.0003021562530949), + FRAC_CONST(0.0004452267024786), + FRAC_CONST(0.0005674947527496), + FRAC_CONST(0.0006812465553466), + FRAC_CONST(0.0007910496776387), + FRAC_CONST(0.0008991655033895), + FRAC_CONST(0.0010068978259384), + FRAC_CONST(0.0011150758515751), + FRAC_CONST(0.0012242653193642), + FRAC_CONST(0.0013348735658205), + FRAC_CONST(0.0014472068670273), + FRAC_CONST(0.0015615039850448), + FRAC_CONST(0.0016779568885263), + FRAC_CONST(0.0017967241232412), + FRAC_CONST(0.0019179397560955), + FRAC_CONST(0.0020417195415393), + FRAC_CONST(0.0021681652836642), + FRAC_CONST(0.0022973679910599), + FRAC_CONST(0.0024294102029937), + FRAC_CONST(0.0025643677339078), + FRAC_CONST(0.0027023110014772), + FRAC_CONST(0.0028433060512612), + FRAC_CONST(0.0029874153568025), + FRAC_CONST(0.0031346984511728), + FRAC_CONST(0.0032852124303662), + FRAC_CONST(0.0034390123581190), + FRAC_CONST(0.0035961515940931), + FRAC_CONST(0.0037566820618961), + FRAC_CONST(0.0039206544694386), + FRAC_CONST(0.0040881184912194), + FRAC_CONST(0.0042591229199617), + FRAC_CONST(0.0044337157933972), + FRAC_CONST(0.0046119445007641), + FRAC_CONST(0.0047938558726415), + FRAC_CONST(0.0049794962570131), + FRAC_CONST(0.0051689115838900), + FRAC_CONST(0.0053621474203763), + FRAC_CONST(0.0055592490177131), + FRAC_CONST(0.0057602613515573), + FRAC_CONST(0.0059652291565289), + FRAC_CONST(0.0061741969558843), + FRAC_CONST(0.0063872090870253), + FRAC_CONST(0.0066043097234387), + FRAC_CONST(0.0068255428935640), + FRAC_CONST(0.0070509524970088), + FRAC_CONST(0.0072805823184660), + FRAC_CONST(0.0075144760396340), + FRAC_CONST(0.0077526772493942), + FRAC_CONST(0.0079952294524673), + FRAC_CONST(0.0082421760767325), + FRAC_CONST(0.0084935604793733), + FRAC_CONST(0.0087494259519870), + FRAC_CONST(0.0090098157247792), + FRAC_CONST(0.0092747729699467), + FRAC_CONST(0.0095443408043399), + FRAC_CONST(0.0098185622914832), + FRAC_CONST(0.0100974804430226), + FRAC_CONST(0.0103811382196612), + FRAC_CONST(0.0106695785316351), + FRAC_CONST(0.0109628442387771), + FRAC_CONST(0.0112609781502091), + FRAC_CONST(0.0115640230236993), + FRAC_CONST(0.0118720215647169), + FRAC_CONST(0.0121850164252137), + FRAC_CONST(0.0125030502021561), + FRAC_CONST(0.0128261654358321), + FRAC_CONST(0.0131544046079532), + FRAC_CONST(0.0134878101395681), + FRAC_CONST(0.0138264243888068), + FRAC_CONST(0.0141702896484671), + FRAC_CONST(0.0145194481434592), + FRAC_CONST(0.0148739420281182), + FRAC_CONST(0.0152338133833959), + FRAC_CONST(0.0155991042139432), + FRAC_CONST(0.0159698564450882), + FRAC_CONST(0.0163461119197227), + FRAC_CONST(0.0167279123950996), + FRAC_CONST(0.0171152995395520), + FRAC_CONST(0.0175083149291368), + FRAC_CONST(0.0179070000442104), + FRAC_CONST(0.0183113962659409), + FRAC_CONST(0.0187215448727609), + FRAC_CONST(0.0191374870367659), + FRAC_CONST(0.0195592638200623), + FRAC_CONST(0.0199869161710679), + FRAC_CONST(0.0204204849207691), + FRAC_CONST(0.0208600107789370), + FRAC_CONST(0.0213055343303066), + FRAC_CONST(0.0217570960307201), + FRAC_CONST(0.0222147362032386), + FRAC_CONST(0.0226784950342228), + FRAC_CONST(0.0231484125693867), + FRAC_CONST(0.0236245287098244), + FRAC_CONST(0.0241068832080138), + FRAC_CONST(0.0245955156637973), + FRAC_CONST(0.0250904655203431), + FRAC_CONST(0.0255917720600868), + FRAC_CONST(0.0260994744006559), + FRAC_CONST(0.0266136114907790), + FRAC_CONST(0.0271342221061795), + FRAC_CONST(0.0276613448454576), + FRAC_CONST(0.0281950181259587), + FRAC_CONST(0.0287352801796329), + FRAC_CONST(0.0292821690488833), + FRAC_CONST(0.0298357225824074), + FRAC_CONST(0.0303959784310299), + FRAC_CONST(0.0309629740435296), + FRAC_CONST(0.0315367466624615), + FRAC_CONST(0.0321173333199732), + FRAC_CONST(0.0327047708336193), + FRAC_CONST(0.0332990958021720), + FRAC_CONST(0.0339003446014307), + FRAC_CONST(0.0345085533800302), + FRAC_CONST(0.0351237580552491), + FRAC_CONST(0.0357459943088193), + FRAC_CONST(0.0363752975827358), + FRAC_CONST(0.0370117030750704), + FRAC_CONST(0.0376552457357870), + FRAC_CONST(0.0383059602625614), + FRAC_CONST(0.0389638810966056), + FRAC_CONST(0.0396290424184964), + FRAC_CONST(0.0403014781440112), + FRAC_CONST(0.0409812219199691), + FRAC_CONST(0.0416683071200799), + FRAC_CONST(0.0423627668408009), + FRAC_CONST(0.0430646338972016), + FRAC_CONST(0.0437739408188385), + FRAC_CONST(0.0444907198456388), + FRAC_CONST(0.0452150029237951), + FRAC_CONST(0.0459468217016708), + FRAC_CONST(0.0466862075257170), + FRAC_CONST(0.0474331914364021), + FRAC_CONST(0.0481878041641539), + FRAC_CONST(0.0489500761253148), + FRAC_CONST(0.0497200374181119), + FRAC_CONST(0.0504977178186404), + FRAC_CONST(0.0512831467768636), + FRAC_CONST(0.0520763534126273), + FRAC_CONST(0.0528773665116913), + FRAC_CONST(0.0536862145217772), + FRAC_CONST(0.0545029255486345), + FRAC_CONST(0.0553275273521232), + FRAC_CONST(0.0561600473423164), + FRAC_CONST(0.0570005125756209), + FRAC_CONST(0.0578489497509179), + FRAC_CONST(0.0587053852057233), + FRAC_CONST(0.0595698449123695), + FRAC_CONST(0.0604423544742077), + FRAC_CONST(0.0613229391218317), + FRAC_CONST(0.0622116237093247), + FRAC_CONST(0.0631084327105284), + FRAC_CONST(0.0640133902153352), + FRAC_CONST(0.0649265199260043), + FRAC_CONST(0.0658478451535027), + FRAC_CONST(0.0667773888138695), + FRAC_CONST(0.0677151734246072), + FRAC_CONST(0.0686612211010977), + FRAC_CONST(0.0696155535530446), + FRAC_CONST(0.0705781920809429), + FRAC_CONST(0.0715491575725758), + FRAC_CONST(0.0725284704995383), + FRAC_CONST(0.0735161509137906), + FRAC_CONST(0.0745122184442388), + FRAC_CONST(0.0755166922933461), + FRAC_CONST(0.0765295912337720), + FRAC_CONST(0.0775509336050437), + FRAC_CONST(0.0785807373102561), + FRAC_CONST(0.0796190198128044), + FRAC_CONST(0.0806657981331473), + FRAC_CONST(0.0817210888456026), + FRAC_CONST(0.0827849080751753), + FRAC_CONST(0.0838572714944183), + FRAC_CONST(0.0849381943203265), + FRAC_CONST(0.0860276913112652), + FRAC_CONST(0.0871257767639319), + FRAC_CONST(0.0882324645103534), + FRAC_CONST(0.0893477679149177), + FRAC_CONST(0.0904716998714418), + FRAC_CONST(0.0916042728002747), + FRAC_CONST(0.0927454986454381), + FRAC_CONST(0.0938953888718020), + FRAC_CONST(0.0950539544622996), + FRAC_CONST(0.0962212059151784), + FRAC_CONST(0.0973971532412897), + FRAC_CONST(0.0985818059614169), + FRAC_CONST(0.0997751731036425), + FRAC_CONST(0.1009772632007537), + FRAC_CONST(0.1021880842876888), + FRAC_CONST(0.1034076438990227), + FRAC_CONST(0.1046359490664932), + FRAC_CONST(0.1058730063165681), + FRAC_CONST(0.1071188216680533), + FRAC_CONST(0.1083734006297428), + FRAC_CONST(0.1096367481981100), + FRAC_CONST(0.1109088688550422), + FRAC_CONST(0.1121897665656167), + FRAC_CONST(0.1134794447759207), + FRAC_CONST(0.1147779064109143), + FRAC_CONST(0.1160851538723372), + FRAC_CONST(0.1174011890366591), + FRAC_CONST(0.1187260132530751), + FRAC_CONST(0.1200596273415457), + FRAC_CONST(0.1214020315908810), + FRAC_CONST(0.1227532257568719), + FRAC_CONST(0.1241132090604651), + FRAC_CONST(0.1254819801859856), + FRAC_CONST(0.1268595372794049), + FRAC_CONST(0.1282458779466558), + FRAC_CONST(0.1296409992519942), + FRAC_CONST(0.1310448977164081), + FRAC_CONST(0.1324575693160745), + FRAC_CONST(0.1338790094808633), + FRAC_CONST(0.1353092130928902), + FRAC_CONST(0.1367481744851168), + FRAC_CONST(0.1381958874400010), + FRAC_CONST(0.1396523451881945), + FRAC_CONST(0.1411175404072910), + FRAC_CONST(0.1425914652206223), + FRAC_CONST(0.1440741111961058), + FRAC_CONST(0.1455654693451402), + FRAC_CONST(0.1470655301215526), + FRAC_CONST(0.1485742834205956), + FRAC_CONST(0.1500917185779945), + FRAC_CONST(0.1516178243690463), + FRAC_CONST(0.1531525890077689), + FRAC_CONST(0.1546960001461024), + FRAC_CONST(0.1562480448731608), + FRAC_CONST(0.1578087097145364), + FRAC_CONST(0.1593779806316558), + FRAC_CONST(0.1609558430211876), + FRAC_CONST(0.1625422817145027), + FRAC_CONST(0.1641372809771871), + FRAC_CONST(0.1657408245086070), + FRAC_CONST(0.1673528954415270), + FRAC_CONST(0.1689734763417811), + FRAC_CONST(0.1706025492079969), + FRAC_CONST(0.1722400954713725), + FRAC_CONST(0.1738860959955082), + FRAC_CONST(0.1755405310762898), + FRAC_CONST(0.1772033804418275), + FRAC_CONST(0.1788746232524467), + FRAC_CONST(0.1805542381007349), + FRAC_CONST(0.1822422030116404), + FRAC_CONST(0.1839384954426268), + FRAC_CONST(0.1856430922838810), + FRAC_CONST(0.1873559698585756), + FRAC_CONST(0.1890771039231862), + FRAC_CONST(0.1908064696678625), + FRAC_CONST(0.1925440417168546), + FRAC_CONST(0.1942897941289937), + FRAC_CONST(0.1960437003982277), + FRAC_CONST(0.1978057334542116), + FRAC_CONST(0.1995758656629525), + FRAC_CONST(0.2013540688275098), + FRAC_CONST(0.2031403141887507), + FRAC_CONST(0.2049345724261595), + FRAC_CONST(0.2067368136587033), + FRAC_CONST(0.2085470074457521), + FRAC_CONST(0.2103651227880538), + FRAC_CONST(0.2121911281287646), + FRAC_CONST(0.2140249913545346), + FRAC_CONST(0.2158666797966480), + FRAC_CONST(0.2177161602322188), + FRAC_CONST(0.2195733988854414), + FRAC_CONST(0.2214383614288963), + FRAC_CONST(0.2233110129849106), + FRAC_CONST(0.2251913181269740), + FRAC_CONST(0.2270792408812093), + FRAC_CONST(0.2289747447278976), + FRAC_CONST(0.2308777926030592), + FRAC_CONST(0.2327883469000885), + FRAC_CONST(0.2347063694714437), + FRAC_CONST(0.2366318216303919), + FRAC_CONST(0.2385646641528076), + FRAC_CONST(0.2405048572790267), + FRAC_CONST(0.2424523607157545), + FRAC_CONST(0.2444071336380283), + FRAC_CONST(0.2463691346912334), + FRAC_CONST(0.2483383219931741), + FRAC_CONST(0.2503146531361985), + FRAC_CONST(0.2522980851893767), + FRAC_CONST(0.2542885747007335), + FRAC_CONST(0.2562860776995335), + FRAC_CONST(0.2582905496986215), + FRAC_CONST(0.2603019456968142), + FRAC_CONST(0.2623202201813464), + FRAC_CONST(0.2643453271303700), + FRAC_CONST(0.2663772200155053), + FRAC_CONST(0.2684158518044454), + FRAC_CONST(0.2704611749636135), + FRAC_CONST(0.2725131414608710), + FRAC_CONST(0.2745717027682799), + FRAC_CONST(0.2766368098649151), + FRAC_CONST(0.2787084132397296), + FRAC_CONST(0.2807864628944707), + FRAC_CONST(0.2828709083466482), + FRAC_CONST(0.2849616986325523), + FRAC_CONST(0.2870587823103237), + FRAC_CONST(0.2891621074630737), + FRAC_CONST(0.2912716217020546), + FRAC_CONST(0.2933872721698803), + FRAC_CONST(0.2955090055437973), + FRAC_CONST(0.2976367680390041), + FRAC_CONST(0.2997705054120213), + FRAC_CONST(0.3019101629641097), + FRAC_CONST(0.3040556855447379), + FRAC_CONST(0.3062070175550981), + FRAC_CONST(0.3083641029516701), + FRAC_CONST(0.3105268852498334), + FRAC_CONST(0.3126953075275265), + FRAC_CONST(0.3148693124289546), + FRAC_CONST(0.3170488421683428), + FRAC_CONST(0.3192338385337370), + FRAC_CONST(0.3214242428908514), + FRAC_CONST(0.3236199961869606), + FRAC_CONST(0.3258210389548392), + FRAC_CONST(0.3280273113167459), + FRAC_CONST(0.3302387529884521), + FRAC_CONST(0.3324553032833160), + FRAC_CONST(0.3346769011164010), + FRAC_CONST(0.3369034850086373), + FRAC_CONST(0.3391349930910280), + FRAC_CONST(0.3413713631088974), + FRAC_CONST(0.3436125324261830), + FRAC_CONST(0.3458584380297697), + FRAC_CONST(0.3481090165338656), + FRAC_CONST(0.3503642041844199), + FRAC_CONST(0.3526239368635820), + FRAC_CONST(0.3548881500942010), + FRAC_CONST(0.3571567790443668), + FRAC_CONST(0.3594297585319891), + FRAC_CONST(0.3617070230294185), + FRAC_CONST(0.3639885066681048), + FRAC_CONST(0.3662741432432950), + FRAC_CONST(0.3685638662187693), + FRAC_CONST(0.3708576087316147), + FRAC_CONST(0.3731553035970366), + FRAC_CONST(0.3754568833132069), + FRAC_CONST(0.3777622800661488), + FRAC_CONST(0.3800714257346570), + FRAC_CONST(0.3823842518952546), + FRAC_CONST(0.3847006898271841), + FRAC_CONST(0.3870206705174334), + FRAC_CONST(0.3893441246657958), + FRAC_CONST(0.3916709826899639), + FRAC_CONST(0.3940011747306560), + FRAC_CONST(0.3963346306567764), + FRAC_CONST(0.3986712800706062), + FRAC_CONST(0.4010110523130271), + FRAC_CONST(0.4033538764687756), + FRAC_CONST(0.4056996813717284), + FRAC_CONST(0.4080483956102172), + FRAC_CONST(0.4103999475323736), + FRAC_CONST(0.4127542652515031), + FRAC_CONST(0.4151112766514873), + FRAC_CONST(0.4174709093922143), + FRAC_CONST(0.4198330909150365), + FRAC_CONST(0.4221977484482556), + FRAC_CONST(0.4245648090126334), + FRAC_CONST(0.4269341994269293), + FRAC_CONST(0.4293058463134616), + FRAC_CONST(0.4316796761036958), + FRAC_CONST(0.4340556150438547), + FRAC_CONST(0.4364335892005536), + FRAC_CONST(0.4388135244664580), + FRAC_CONST(0.4411953465659639), + FRAC_CONST(0.4435789810609000), + FRAC_CONST(0.4459643533562509), + FRAC_CONST(0.4483513887059016), + FRAC_CONST(0.4507400122184019), + FRAC_CONST(0.4531301488627497), + FRAC_CONST(0.4555217234741947), + FRAC_CONST(0.4579146607600593), + FRAC_CONST(0.4603088853055777), + FRAC_CONST(0.4627043215797521), + FRAC_CONST(0.4651008939412254), + FRAC_CONST(0.4674985266441709), + FRAC_CONST(0.4698971438441951), + FRAC_CONST(0.4722966696042580), + FRAC_CONST(0.4746970279006055), + FRAC_CONST(0.4770981426287164), + FRAC_CONST(0.4794999376092619), + FRAC_CONST(0.4819023365940778), + FRAC_CONST(0.4843052632721476), + FRAC_CONST(0.4867086412755978), + FRAC_CONST(0.4891123941857028), + FRAC_CONST(0.4915164455388997), + FRAC_CONST(0.4939207188328126), + FRAC_CONST(0.4963251375322855), + FRAC_CONST(0.4987296250754225), + FRAC_CONST(0.5011341048796359), + FRAC_CONST(0.5035385003477012), + FRAC_CONST(0.5059427348738168), + FRAC_CONST(0.5083467318496706), + FRAC_CONST(0.5107504146705106), + FRAC_CONST(0.5131537067412193), + FRAC_CONST(0.5155565314823923), + FRAC_CONST(0.5179588123364193), + FRAC_CONST(0.5203604727735667), + FRAC_CONST(0.5227614362980630), + FRAC_CONST(0.5251616264541841), + FRAC_CONST(0.5275609668323384), + FRAC_CONST(0.5299593810751532), + FRAC_CONST(0.5323567928835578), + FRAC_CONST(0.5347531260228663), + FRAC_CONST(0.5371483043288580), + FRAC_CONST(0.5395422517138538), + FRAC_CONST(0.5419348921727899), + FRAC_CONST(0.5443261497892862), + FRAC_CONST(0.5467159487417104), + FRAC_CONST(0.5491042133092364), + FRAC_CONST(0.5514908678778958), + FRAC_CONST(0.5538758369466227), + FRAC_CONST(0.5562590451332913), + FRAC_CONST(0.5586404171807443), + FRAC_CONST(0.5610198779628133), + FRAC_CONST(0.5633973524903286), + FRAC_CONST(0.5657727659171199), + FRAC_CONST(0.5681460435460047), + FRAC_CONST(0.5705171108347663), + FRAC_CONST(0.5728858934021188), + FRAC_CONST(0.5752523170336598), + FRAC_CONST(0.5776163076878088), + FRAC_CONST(0.5799777915017323), + FRAC_CONST(0.5823366947972535), + FRAC_CONST(0.5846929440867458), + FRAC_CONST(0.5870464660790119), + FRAC_CONST(0.5893971876851449), + FRAC_CONST(0.5917450360243719), + FRAC_CONST(0.5940899384298793), + FRAC_CONST(0.5964318224546208), + FRAC_CONST(0.5987706158771039), + FRAC_CONST(0.6011062467071583), + FRAC_CONST(0.6034386431916822), + FRAC_CONST(0.6057677338203681), + FRAC_CONST(0.6080934473314057), + FRAC_CONST(0.6104157127171639), + FRAC_CONST(0.6127344592298474), + FRAC_CONST(0.6150496163871310), + FRAC_CONST(0.6173611139777690), + FRAC_CONST(0.6196688820671789), + FRAC_CONST(0.6219728510029997), + FRAC_CONST(0.6242729514206247), + FRAC_CONST(0.6265691142487051), + FRAC_CONST(0.6288612707146283), + FRAC_CONST(0.6311493523499663), + FRAC_CONST(0.6334332909958958), + FRAC_CONST(0.6357130188085891), + FRAC_CONST(0.6379884682645743), + FRAC_CONST(0.6402595721660647), + FRAC_CONST(0.6425262636462578), + FRAC_CONST(0.6447884761746012), + FRAC_CONST(0.6470461435620266), + FRAC_CONST(0.6492991999661505), + FRAC_CONST(0.6515475798964411), + FRAC_CONST(0.6537912182193508), + FRAC_CONST(0.6560300501634142), + FRAC_CONST(0.6582640113243098), + FRAC_CONST(0.6604930376698862), + FRAC_CONST(0.6627170655451516), + FRAC_CONST(0.6649360316772256), + FRAC_CONST(0.6671498731802533), + FRAC_CONST(0.6693585275602818), + FRAC_CONST(0.6715619327200959), + FRAC_CONST(0.6737600269640164), + FRAC_CONST(0.6759527490026566), + FRAC_CONST(0.6781400379576392), + FRAC_CONST(0.6803218333662715), + FRAC_CONST(0.6824980751861787), + FRAC_CONST(0.6846687037998949), + FRAC_CONST(0.6868336600194123), + FRAC_CONST(0.6889928850906855), + FRAC_CONST(0.6911463206980928), + FRAC_CONST(0.6932939089688525), + FRAC_CONST(0.6954355924773949), + FRAC_CONST(0.6975713142496884), + FRAC_CONST(0.6997010177675195), + FRAC_CONST(0.7018246469727265), + FRAC_CONST(0.7039421462713862), + FRAC_CONST(0.7060534605379528), + FRAC_CONST(0.7081585351193496), + FRAC_CONST(0.7102573158390105), + FRAC_CONST(0.7123497490008750), + FRAC_CONST(0.7144357813933307), + FRAC_CONST(0.7165153602931092), + FRAC_CONST(0.7185884334691287), + FRAC_CONST(0.7206549491862871), + FRAC_CONST(0.7227148562092042), + FRAC_CONST(0.7247681038059106), + FRAC_CONST(0.7268146417514855), + FRAC_CONST(0.7288544203316418), + FRAC_CONST(0.7308873903462577), + FRAC_CONST(0.7329135031128549), + FRAC_CONST(0.7349327104700221), + FRAC_CONST(0.7369449647807855), + FRAC_CONST(0.7389502189359237), + FRAC_CONST(0.7409484263572271), + FRAC_CONST(0.7429395410007016), + FRAC_CONST(0.7449235173597176), + FRAC_CONST(0.7469003104681008), + FRAC_CONST(0.7488698759031670), + FRAC_CONST(0.7508321697887005), + FRAC_CONST(0.7527871487978728), + FRAC_CONST(0.7547347701561059), + FRAC_CONST(0.7566749916438754), + FRAC_CONST(0.7586077715994560), + FRAC_CONST(0.7605330689216074), + FRAC_CONST(0.7624508430722016), + FRAC_CONST(0.7643610540787891), + FRAC_CONST(0.7662636625371070), + FRAC_CONST(0.7681586296135255), + FRAC_CONST(0.7700459170474343), + FRAC_CONST(0.7719254871535672), + FRAC_CONST(0.7737973028242671), + FRAC_CONST(0.7756613275316875), + FRAC_CONST(0.7775175253299340), + FRAC_CONST(0.7793658608571425), + FRAC_CONST(0.7812062993374951), + FRAC_CONST(0.7830388065831744), + FRAC_CONST(0.7848633489962533), + FRAC_CONST(0.7866798935705233), + FRAC_CONST(0.7884884078932579), + FRAC_CONST(0.7902888601469138), + FRAC_CONST(0.7920812191107668), + FRAC_CONST(0.7938654541624850), + FRAC_CONST(0.7956415352796368), + FRAC_CONST(0.7974094330411343), + FRAC_CONST(0.7991691186286133), + FRAC_CONST(0.8009205638277465), + FRAC_CONST(0.8026637410294932), + FRAC_CONST(0.8043986232312831), + FRAC_CONST(0.8061251840381346), + FRAC_CONST(0.8078433976637077), + FRAC_CONST(0.8095532389312917), + FRAC_CONST(0.8112546832747255), + FRAC_CONST(0.8129477067392539), + FRAC_CONST(0.8146322859823164), + FRAC_CONST(0.8163083982742698), + FRAC_CONST(0.8179760214990457), + FRAC_CONST(0.8196351341547393), + FRAC_CONST(0.8212857153541345), + FRAC_CONST(0.8229277448251595), + FRAC_CONST(0.8245612029112778), + FRAC_CONST(0.8261860705718113), + FRAC_CONST(0.8278023293821971), + FRAC_CONST(0.8294099615341773), + FRAC_CONST(0.8310089498359212), + FRAC_CONST(0.8325992777120815), + FRAC_CONST(0.8341809292037831), + FRAC_CONST(0.8357538889685445), + FRAC_CONST(0.8373181422801330), + FRAC_CONST(0.8388736750283521), + FRAC_CONST(0.8404204737187619), + FRAC_CONST(0.8419585254723335), + FRAC_CONST(0.8434878180250348), + FRAC_CONST(0.8450083397273509), + FRAC_CONST(0.8465200795437368), + FRAC_CONST(0.8480230270520029), + FRAC_CONST(0.8495171724426350), + FRAC_CONST(0.8510025065180464), + FRAC_CONST(0.8524790206917633), + FRAC_CONST(0.8539467069875448), + FRAC_CONST(0.8554055580384357), + FRAC_CONST(0.8568555670857525), + FRAC_CONST(0.8582967279780043), + FRAC_CONST(0.8597290351697464), + FRAC_CONST(0.8611524837203691), + FRAC_CONST(0.8625670692928198), + FRAC_CONST(0.8639727881522599), + FRAC_CONST(0.8653696371646555), + FRAC_CONST(0.8667576137953045), + FRAC_CONST(0.8681367161072958), + FRAC_CONST(0.8695069427599065), + FRAC_CONST(0.8708682930069319), + FRAC_CONST(0.8722207666949527), + FRAC_CONST(0.8735643642615368), + FRAC_CONST(0.8748990867333771), + FRAC_CONST(0.8762249357243662), + FRAC_CONST(0.8775419134336067), + FRAC_CONST(0.8788500226433579), + FRAC_CONST(0.8801492667169208), + FRAC_CONST(0.8814396495964587), + FRAC_CONST(0.8827211758007560), + FRAC_CONST(0.8839938504229149), + FRAC_CONST(0.8852576791279895), + FRAC_CONST(0.8865126681505587), + FRAC_CONST(0.8877588242922386), + FRAC_CONST(0.8889961549191320), + FRAC_CONST(0.8902246679592184), + FRAC_CONST(0.8914443718996848), + FRAC_CONST(0.8926552757841945), + FRAC_CONST(0.8938573892100969), + FRAC_CONST(0.8950507223255798), + FRAC_CONST(0.8962352858267605), + FRAC_CONST(0.8974110909547198), + FRAC_CONST(0.8985781494924783), + FRAC_CONST(0.8997364737619142), + FRAC_CONST(0.9008860766206249), + FRAC_CONST(0.9020269714587307), + FRAC_CONST(0.9031591721956235), + FRAC_CONST(0.9042826932766591), + FRAC_CONST(0.9053975496697941), + FRAC_CONST(0.9065037568621681), + FRAC_CONST(0.9076013308566311), + FRAC_CONST(0.9086902881682180), + FRAC_CONST(0.9097706458205682), + FRAC_CONST(0.9108424213422940), + FRAC_CONST(0.9119056327632955), + FRAC_CONST(0.9129602986110235), + FRAC_CONST(0.9140064379066919), + FRAC_CONST(0.9150440701614393), + FRAC_CONST(0.9160732153724396), + FRAC_CONST(0.9170938940189634), + FRAC_CONST(0.9181061270583908), + FRAC_CONST(0.9191099359221748), + FRAC_CONST(0.9201053425117579), + FRAC_CONST(0.9210923691944400), + FRAC_CONST(0.9220710387992010), + FRAC_CONST(0.9230413746124764), + FRAC_CONST(0.9240034003738882), + FRAC_CONST(0.9249571402719298), + FRAC_CONST(0.9259026189396085), + FRAC_CONST(0.9268398614500427), + FRAC_CONST(0.9277688933120170), + FRAC_CONST(0.9286897404654957), + FRAC_CONST(0.9296024292770939), + FRAC_CONST(0.9305069865355076), + FRAC_CONST(0.9314034394469048), + FRAC_CONST(0.9322918156302762), + FRAC_CONST(0.9331721431127471), + FRAC_CONST(0.9340444503248519), + FRAC_CONST(0.9349087660957711), + FRAC_CONST(0.9357651196485313), + FRAC_CONST(0.9366135405951697), + FRAC_CONST(0.9374540589318637), + FRAC_CONST(0.9382867050340261), + FRAC_CONST(0.9391115096513655), + FRAC_CONST(0.9399285039029165), + FRAC_CONST(0.9407377192720349), + FRAC_CONST(0.9415391876013639), + FRAC_CONST(0.9423329410877687), + FRAC_CONST(0.9431190122772415), + FRAC_CONST(0.9438974340597782), + FRAC_CONST(0.9446682396642262), + FRAC_CONST(0.9454314626531054), + FRAC_CONST(0.9461871369174033), + FRAC_CONST(0.9469352966713429), + FRAC_CONST(0.9476759764471278), + FRAC_CONST(0.9484092110896616), + FRAC_CONST(0.9491350357512457), + FRAC_CONST(0.9498534858862532), + FRAC_CONST(0.9505645972457831), + FRAC_CONST(0.9512684058722927), + FRAC_CONST(0.9519649480942105), + FRAC_CONST(0.9526542605205314), + FRAC_CONST(0.9533363800353921), + FRAC_CONST(0.9540113437926313), + FRAC_CONST(0.9546791892103320), + FRAC_CONST(0.9553399539653500), + FRAC_CONST(0.9559936759878265), + FRAC_CONST(0.9566403934556893), + FRAC_CONST(0.9572801447891388), + FRAC_CONST(0.9579129686451244), + FRAC_CONST(0.9585389039118085), + FRAC_CONST(0.9591579897030224), + FRAC_CONST(0.9597702653527108), + FRAC_CONST(0.9603757704093711), + FRAC_CONST(0.9609745446304828), + FRAC_CONST(0.9615666279769324), + FRAC_CONST(0.9621520606074324), + FRAC_CONST(0.9627308828729358), + FRAC_CONST(0.9633031353110477), + FRAC_CONST(0.9638688586404335), + FRAC_CONST(0.9644280937552258), + FRAC_CONST(0.9649808817194311), + FRAC_CONST(0.9655272637613366), + FRAC_CONST(0.9660672812679171), + FRAC_CONST(0.9666009757792454), + FRAC_CONST(0.9671283889829055), + FRAC_CONST(0.9676495627084089), + FRAC_CONST(0.9681645389216160), + FRAC_CONST(0.9686733597191652), + FRAC_CONST(0.9691760673229058), + FRAC_CONST(0.9696727040743406), + FRAC_CONST(0.9701633124290767), + FRAC_CONST(0.9706479349512860), + FRAC_CONST(0.9711266143081750), + FRAC_CONST(0.9715993932644684), + FRAC_CONST(0.9720663146769026), + FRAC_CONST(0.9725274214887337), + FRAC_CONST(0.9729827567242596), + FRAC_CONST(0.9734323634833574), + FRAC_CONST(0.9738762849360358), + FRAC_CONST(0.9743145643170059), + FRAC_CONST(0.9747472449202687), + FRAC_CONST(0.9751743700937215), + FRAC_CONST(0.9755959832337850), + FRAC_CONST(0.9760121277800496), + FRAC_CONST(0.9764228472099433), + FRAC_CONST(0.9768281850334235), + FRAC_CONST(0.9772281847876897), + FRAC_CONST(0.9776228900319223), + FRAC_CONST(0.9780123443420448), + FRAC_CONST(0.9783965913055132), + FRAC_CONST(0.9787756745161313), + FRAC_CONST(0.9791496375688939), + FRAC_CONST(0.9795185240548578), + FRAC_CONST(0.9798823775560431), + FRAC_CONST(0.9802412416403639), + FRAC_CONST(0.9805951598565897), + FRAC_CONST(0.9809441757293399), + FRAC_CONST(0.9812883327541090), + FRAC_CONST(0.9816276743923267), + FRAC_CONST(0.9819622440664515), + FRAC_CONST(0.9822920851550995), + FRAC_CONST(0.9826172409882086), + FRAC_CONST(0.9829377548422400), + FRAC_CONST(0.9832536699354163), + FRAC_CONST(0.9835650294229984), + FRAC_CONST(0.9838718763926001), + FRAC_CONST(0.9841742538595437), + FRAC_CONST(0.9844722047622547), + FRAC_CONST(0.9847657719576983), + FRAC_CONST(0.9850549982168574), + FRAC_CONST(0.9853399262202529), + FRAC_CONST(0.9856205985535073), + FRAC_CONST(0.9858970577029519), + FRAC_CONST(0.9861693460512790), + FRAC_CONST(0.9864375058732389), + FRAC_CONST(0.9867015793313820), + FRAC_CONST(0.9869616084718489), + FRAC_CONST(0.9872176352202061), + FRAC_CONST(0.9874697013773301), + FRAC_CONST(0.9877178486153397), + FRAC_CONST(0.9879621184735767), + FRAC_CONST(0.9882025523546365), + FRAC_CONST(0.9884391915204485), + FRAC_CONST(0.9886720770884069), + FRAC_CONST(0.9889012500275530), + FRAC_CONST(0.9891267511548089), + FRAC_CONST(0.9893486211312621), + FRAC_CONST(0.9895669004585049), + FRAC_CONST(0.9897816294750255), + FRAC_CONST(0.9899928483526520), + FRAC_CONST(0.9902005970930525), + FRAC_CONST(0.9904049155242876), + FRAC_CONST(0.9906058432974180), + FRAC_CONST(0.9908034198831690), + FRAC_CONST(0.9909976845686489), + FRAC_CONST(0.9911886764541239), + FRAC_CONST(0.9913764344498495), + FRAC_CONST(0.9915609972729590), + FRAC_CONST(0.9917424034444086), + FRAC_CONST(0.9919206912859797), + FRAC_CONST(0.9920958989173397), + FRAC_CONST(0.9922680642531603), + FRAC_CONST(0.9924372250002933), + FRAC_CONST(0.9926034186550070), + FRAC_CONST(0.9927666825002789), + FRAC_CONST(0.9929270536031491), + FRAC_CONST(0.9930845688121325), + FRAC_CONST(0.9932392647546895), + FRAC_CONST(0.9933911778347579), + FRAC_CONST(0.9935403442303433), + FRAC_CONST(0.9936867998911693), + FRAC_CONST(0.9938305805363887), + FRAC_CONST(0.9939717216523539), + FRAC_CONST(0.9941102584904481), + FRAC_CONST(0.9942462260649764), + FRAC_CONST(0.9943796591511174), + FRAC_CONST(0.9945105922829353), + FRAC_CONST(0.9946390597514524), + FRAC_CONST(0.9947650956027824), + FRAC_CONST(0.9948887336363228), + FRAC_CONST(0.9950100074030103), + FRAC_CONST(0.9951289502036336), + FRAC_CONST(0.9952455950872091), + FRAC_CONST(0.9953599748494155), + FRAC_CONST(0.9954721220310890), + FRAC_CONST(0.9955820689167787), + FRAC_CONST(0.9956898475333619), + FRAC_CONST(0.9957954896487196), + FRAC_CONST(0.9958990267704713), + FRAC_CONST(0.9960004901447701), + FRAC_CONST(0.9960999107551559), + FRAC_CONST(0.9961973193214694), + FRAC_CONST(0.9962927462988245), + FRAC_CONST(0.9963862218766388), + FRAC_CONST(0.9964777759777242), + FRAC_CONST(0.9965674382574342), + FRAC_CONST(0.9966552381028704), + FRAC_CONST(0.9967412046321465), + FRAC_CONST(0.9968253666937095), + FRAC_CONST(0.9969077528657186), + FRAC_CONST(0.9969883914554805), + FRAC_CONST(0.9970673104989413), + FRAC_CONST(0.9971445377602348), + FRAC_CONST(0.9972201007312871), + FRAC_CONST(0.9972940266314749), + FRAC_CONST(0.9973663424073412), + FRAC_CONST(0.9974370747323638), + FRAC_CONST(0.9975062500067785), + FRAC_CONST(0.9975738943574574), + FRAC_CONST(0.9976400336378379), + FRAC_CONST(0.9977046934279079), + FRAC_CONST(0.9977678990342401), + FRAC_CONST(0.9978296754900812), + FRAC_CONST(0.9978900475554902), + FRAC_CONST(0.9979490397175296), + FRAC_CONST(0.9980066761905056), + FRAC_CONST(0.9980629809162593), + FRAC_CONST(0.9981179775645063), + FRAC_CONST(0.9981716895332257), + FRAC_CONST(0.9982241399490979), + FRAC_CONST(0.9982753516679893), + FRAC_CONST(0.9983253472754841), + FRAC_CONST(0.9983741490874634), + FRAC_CONST(0.9984217791507299), + FRAC_CONST(0.9984682592436778), + FRAC_CONST(0.9985136108770075), + FRAC_CONST(0.9985578552944850), + FRAC_CONST(0.9986010134737439), + FRAC_CONST(0.9986431061271304), + FRAC_CONST(0.9986841537025921), + FRAC_CONST(0.9987241763846056), + FRAC_CONST(0.9987631940951476), + FRAC_CONST(0.9988012264947044), + FRAC_CONST(0.9988382929833222), + FRAC_CONST(0.9988744127016956), + FRAC_CONST(0.9989096045322947), + FRAC_CONST(0.9989438871005292), + FRAC_CONST(0.9989772787759494), + FRAC_CONST(0.9990097976734847), + FRAC_CONST(0.9990414616547146), + FRAC_CONST(0.9990722883291779), + FRAC_CONST(0.9991022950557125), + FRAC_CONST(0.9991314989438310), + FRAC_CONST(0.9991599168551279), + FRAC_CONST(0.9991875654047181), + FRAC_CONST(0.9992144609627068), + FRAC_CONST(0.9992406196556911), + FRAC_CONST(0.9992660573682882), + FRAC_CONST(0.9992907897446957), + FRAC_CONST(0.9993148321902777), + FRAC_CONST(0.9993381998731797), + FRAC_CONST(0.9993609077259696), + FRAC_CONST(0.9993829704473038), + FRAC_CONST(0.9994044025036201), + FRAC_CONST(0.9994252181308537), + FRAC_CONST(0.9994454313361775), + FRAC_CONST(0.9994650558997651), + FRAC_CONST(0.9994841053765757), + FRAC_CONST(0.9995025930981609), + FRAC_CONST(0.9995205321744921), + FRAC_CONST(0.9995379354958073), + FRAC_CONST(0.9995548157344778), + FRAC_CONST(0.9995711853468930), + FRAC_CONST(0.9995870565753632), + FRAC_CONST(0.9996024414500382), + FRAC_CONST(0.9996173517908444), + FRAC_CONST(0.9996317992094352), + FRAC_CONST(0.9996457951111574), + FRAC_CONST(0.9996593506970310), + FRAC_CONST(0.9996724769657434), + FRAC_CONST(0.9996851847156547), + FRAC_CONST(0.9996974845468164), + FRAC_CONST(0.9997093868630000), + FRAC_CONST(0.9997209018737374), + FRAC_CONST(0.9997320395963699), + FRAC_CONST(0.9997428098581069), + FRAC_CONST(0.9997532222980933), + FRAC_CONST(0.9997632863694836), + FRAC_CONST(0.9997730113415246), + FRAC_CONST(0.9997824063016426), + FRAC_CONST(0.9997914801575380), + FRAC_CONST(0.9998002416392840), + FRAC_CONST(0.9998086993014300), + FRAC_CONST(0.9998168615251084), + FRAC_CONST(0.9998247365201450), + FRAC_CONST(0.9998323323271717), + FRAC_CONST(0.9998396568197407), + FRAC_CONST(0.9998467177064404), + FRAC_CONST(0.9998535225330116), + FRAC_CONST(0.9998600786844637), + FRAC_CONST(0.9998663933871905), + FRAC_CONST(0.9998724737110845), + FRAC_CONST(0.9998783265716498), + FRAC_CONST(0.9998839587321121), + FRAC_CONST(0.9998893768055266), + FRAC_CONST(0.9998945872568815), + FRAC_CONST(0.9998995964051983), + FRAC_CONST(0.9999044104256269), + FRAC_CONST(0.9999090353515359), + FRAC_CONST(0.9999134770765971), + FRAC_CONST(0.9999177413568642), + FRAC_CONST(0.9999218338128448), + FRAC_CONST(0.9999257599315647), + FRAC_CONST(0.9999295250686255), + FRAC_CONST(0.9999331344502529), + FRAC_CONST(0.9999365931753376), + FRAC_CONST(0.9999399062174669), + FRAC_CONST(0.9999430784269460), + FRAC_CONST(0.9999461145328103), + FRAC_CONST(0.9999490191448277), + FRAC_CONST(0.9999517967554878), + FRAC_CONST(0.9999544517419835), + FRAC_CONST(0.9999569883681778), + FRAC_CONST(0.9999594107865607), + FRAC_CONST(0.9999617230401926), + FRAC_CONST(0.9999639290646355), + FRAC_CONST(0.9999660326898712), + FRAC_CONST(0.9999680376422052), + FRAC_CONST(0.9999699475461585), + FRAC_CONST(0.9999717659263435), + FRAC_CONST(0.9999734962093266), + FRAC_CONST(0.9999751417254756), + FRAC_CONST(0.9999767057107922), + FRAC_CONST(0.9999781913087290), + FRAC_CONST(0.9999796015719915), + FRAC_CONST(0.9999809394643231), + FRAC_CONST(0.9999822078622751), + FRAC_CONST(0.9999834095569596), + FRAC_CONST(0.9999845472557860), + FRAC_CONST(0.9999856235841805), + FRAC_CONST(0.9999866410872889), + FRAC_CONST(0.9999876022316609), + FRAC_CONST(0.9999885094069193), + FRAC_CONST(0.9999893649274085), + FRAC_CONST(0.9999901710338274), + FRAC_CONST(0.9999909298948430), + FRAC_CONST(0.9999916436086862), + FRAC_CONST(0.9999923142047299), + FRAC_CONST(0.9999929436450469), + FRAC_CONST(0.9999935338259505), + FRAC_CONST(0.9999940865795161), + FRAC_CONST(0.9999946036750835), + FRAC_CONST(0.9999950868207405), + FRAC_CONST(0.9999955376647868), + FRAC_CONST(0.9999959577971798), + FRAC_CONST(0.9999963487509599), + FRAC_CONST(0.9999967120036571), + FRAC_CONST(0.9999970489786785), + FRAC_CONST(0.9999973610466748), + FRAC_CONST(0.9999976495268890), + FRAC_CONST(0.9999979156884846), + FRAC_CONST(0.9999981607518545), + FRAC_CONST(0.9999983858899099), + FRAC_CONST(0.9999985922293493), + FRAC_CONST(0.9999987808519092), + FRAC_CONST(0.9999989527955938), + FRAC_CONST(0.9999991090558848), + FRAC_CONST(0.9999992505869332), + FRAC_CONST(0.9999993783027293), + FRAC_CONST(0.9999994930782556), + FRAC_CONST(0.9999995957506171), + FRAC_CONST(0.9999996871201549), + FRAC_CONST(0.9999997679515386), + FRAC_CONST(0.9999998389748399), + FRAC_CONST(0.9999999008865869), + FRAC_CONST(0.9999999543507984) +}; +#endif + +ALIGN static const real_t kbd_short_128[] = +{ + FRAC_CONST(4.3795702929468881e-005), + FRAC_CONST(0.00011867384265436617), + FRAC_CONST(0.0002307165763996192), + FRAC_CONST(0.00038947282760568383), + FRAC_CONST(0.00060581272288302553), + FRAC_CONST(0.00089199695169487453), + FRAC_CONST(0.0012617254423430522), + FRAC_CONST(0.0017301724373162003), + FRAC_CONST(0.0023140071937421476), + FRAC_CONST(0.0030313989666022221), + FRAC_CONST(0.0039020049735530842), + FRAC_CONST(0.0049469401815512024), + FRAC_CONST(0.0061887279335368318), + FRAC_CONST(0.0076512306364647726), + FRAC_CONST(0.0093595599562652423), + FRAC_CONST(0.011339966208377799), + FRAC_CONST(0.013619706891715299), + FRAC_CONST(0.016226894586323766), + FRAC_CONST(0.019190324717288168), + FRAC_CONST(0.022539283975960878), + FRAC_CONST(0.026303340480472455), + FRAC_CONST(0.030512117046644357), + FRAC_CONST(0.03519504922365594), + FRAC_CONST(0.040381130021856941), + FRAC_CONST(0.046098643518702249), + FRAC_CONST(0.052374889768730587), + FRAC_CONST(0.059235903660769147), + FRAC_CONST(0.066706170556282418), + FRAC_CONST(0.074808341703430481), + FRAC_CONST(0.083562952548726227), + FRAC_CONST(0.092988147159339674), + FRAC_CONST(0.1030994120216919), + FRAC_CONST(0.11390932249409955), + FRAC_CONST(0.12542730516149531), + FRAC_CONST(0.13765941926783826), + FRAC_CONST(0.15060816028651081), + FRAC_CONST(0.16427228853114245), + FRAC_CONST(0.17864668550988483), + FRAC_CONST(0.19372224048676889), + FRAC_CONST(0.20948576943658073), + FRAC_CONST(0.22591996826744942), + FRAC_CONST(0.24300340184133981), + FRAC_CONST(0.26071052995068139), + FRAC_CONST(0.27901177101369551), + FRAC_CONST(0.29787360383626599), + FRAC_CONST(0.3172587073594233), + FRAC_CONST(0.33712613787396362), + FRAC_CONST(0.35743154274286698), + FRAC_CONST(0.37812740923363009), + FRAC_CONST(0.39916334663203618), + FRAC_CONST(0.42048639939189658), + FRAC_CONST(0.4420413886774246), + FRAC_CONST(0.4637712792815169), + FRAC_CONST(0.4856175685594023), + FRAC_CONST(0.50752069370766872), + FRAC_CONST(0.52942045344797806), + FRAC_CONST(0.55125643994680196), + FRAC_CONST(0.57296847662071559), + FRAC_CONST(0.59449705734411495), + FRAC_CONST(0.61578378249506627), + FRAC_CONST(0.63677178724712891), + FRAC_CONST(0.65740615754163356), + FRAC_CONST(0.67763432925662526), + FRAC_CONST(0.69740646622548552), + FRAC_CONST(0.71667581294953808), + FRAC_CONST(0.73539901809352737), + FRAC_CONST(0.75353642514900732), + FRAC_CONST(0.77105232699609816), + FRAC_CONST(0.78791518148597028), + FRAC_CONST(0.80409778560147072), + FRAC_CONST(0.81957740622770781), + FRAC_CONST(0.83433586607383625), + FRAC_CONST(0.84835958382689225), + FRAC_CONST(0.86163956818294229), + FRAC_CONST(0.87417136598406997), + FRAC_CONST(0.88595496528524853), + FRAC_CONST(0.89699465477567619), + FRAC_CONST(0.90729884157670959), + FRAC_CONST(0.91687983002436779), + FRAC_CONST(0.92575356460899649), + FRAC_CONST(0.93393934077779084), + FRAC_CONST(0.94145948779657318), + FRAC_CONST(0.94833902830402828), + FRAC_CONST(0.95460531956280026), + FRAC_CONST(0.96028768170574896), + FRAC_CONST(0.96541701848104766), + FRAC_CONST(0.97002543610646474), + FRAC_CONST(0.97414586584250062), + FRAC_CONST(0.97781169577969584), + FRAC_CONST(0.98105641710392333), + FRAC_CONST(0.98391328975491177), + FRAC_CONST(0.98641503193166202), + FRAC_CONST(0.98859353733226141), + FRAC_CONST(0.99047962335771556), + FRAC_CONST(0.9921028127769449), + FRAC_CONST(0.99349115056397752), + FRAC_CONST(0.99467105680259038), + FRAC_CONST(0.9956672157341897), + FRAC_CONST(0.99650250022834352), + FRAC_CONST(0.99719793020823266), + FRAC_CONST(0.99777266288955657), + FRAC_CONST(0.99824401211201486), + FRAC_CONST(0.99862749357391212), + FRAC_CONST(0.99893689243401962), + FRAC_CONST(0.99918434952623147), + FRAC_CONST(0.99938046234161726), + FRAC_CONST(0.99953439696357238), + FRAC_CONST(0.99965400728430465), + FRAC_CONST(0.99974595807027455), + FRAC_CONST(0.99981584876278362), + FRAC_CONST(0.99986833527824281), + FRAC_CONST(0.99990724749057802), + FRAC_CONST(0.99993570051598468), + FRAC_CONST(0.99995619835942084), + FRAC_CONST(0.99997072890647543), + FRAC_CONST(0.9999808496399144), + FRAC_CONST(0.99998776381655818), + FRAC_CONST(0.99999238714961569), + FRAC_CONST(0.99999540529959718), + FRAC_CONST(0.99999732268176988), + FRAC_CONST(0.99999850325054862), + FRAC_CONST(0.99999920402413744), + FRAC_CONST(0.9999996021706401), + FRAC_CONST(0.99999981649545566), + FRAC_CONST(0.99999992415545547), + FRAC_CONST(0.99999997338493041), + FRAC_CONST(0.99999999295825959), + FRAC_CONST(0.99999999904096815) +}; + +#ifdef ALLOW_SMALL_FRAMELENGTH +ALIGN static const real_t kbd_short_120[] = +{ + FRAC_CONST(0.0000452320086910), + FRAC_CONST(0.0001274564692111), + FRAC_CONST(0.0002529398385345), + FRAC_CONST(0.0004335140496648), + FRAC_CONST(0.0006827100966952), + FRAC_CONST(0.0010158708222246), + FRAC_CONST(0.0014502162869659), + FRAC_CONST(0.0020048865156264), + FRAC_CONST(0.0027009618393178), + FRAC_CONST(0.0035614590925043), + FRAC_CONST(0.0046113018122711), + FRAC_CONST(0.0058772627936484), + FRAC_CONST(0.0073878776584103), + FRAC_CONST(0.0091733284512589), + FRAC_CONST(0.0112652966728373), + FRAC_CONST(0.0136967855861945), + FRAC_CONST(0.0165019120857793), + FRAC_CONST(0.0197156688892217), + FRAC_CONST(0.0233736582950619), + FRAC_CONST(0.0275117992367496), + FRAC_CONST(0.0321660098468534), + FRAC_CONST(0.0373718682174417), + FRAC_CONST(0.0431642544948834), + FRAC_CONST(0.0495769778717676), + FRAC_CONST(0.0566423924273392), + FRAC_CONST(0.0643910061132260), + FRAC_CONST(0.0728510874761729), + FRAC_CONST(0.0820482749475221), + FRAC_CONST(0.0920051937045235), + FRAC_CONST(0.1027410852163450), + FRAC_CONST(0.1142714546239370), + FRAC_CONST(0.1266077410648368), + FRAC_CONST(0.1397570159398145), + FRAC_CONST(0.1537217139274270), + FRAC_CONST(0.1684994012857075), + FRAC_CONST(0.1840825856392944), + FRAC_CONST(0.2004585710384133), + FRAC_CONST(0.2176093615976121), + FRAC_CONST(0.2355116164824983), + FRAC_CONST(0.2541366584185075), + FRAC_CONST(0.2734505372545160), + FRAC_CONST(0.2934141494343369), + FRAC_CONST(0.3139834135200387), + FRAC_CONST(0.3351095011824163), + FRAC_CONST(0.3567391223361566), + FRAC_CONST(0.3788148623608774), + FRAC_CONST(0.4012755686250732), + FRAC_CONST(0.4240567828288110), + FRAC_CONST(0.4470912150133537), + FRAC_CONST(0.4703092544619664), + FRAC_CONST(0.4936395121456694), + FRAC_CONST(0.5170093888596962), + FRAC_CONST(0.5403456627591340), + FRAC_CONST(0.5635750896430154), + FRAC_CONST(0.5866250090612892), + FRAC_CONST(0.6094239491338723), + FRAC_CONST(0.6319022228794100), + FRAC_CONST(0.6539925088563087), + FRAC_CONST(0.6756304090216887), + FRAC_CONST(0.6967549769155277), + FRAC_CONST(0.7173092095766250), + FRAC_CONST(0.7372404969921184), + FRAC_CONST(0.7565010233699827), + FRAC_CONST(0.7750481150999984), + FRAC_CONST(0.7928445309277697), + FRAC_CONST(0.8098586906021583), + FRAC_CONST(0.8260648390616000), + FRAC_CONST(0.8414431440907889), + FRAC_CONST(0.8559797262966709), + FRAC_CONST(0.8696666212110165), + FRAC_CONST(0.8825016743142358), + FRAC_CONST(0.8944883707784486), + FRAC_CONST(0.9056356027326216), + FRAC_CONST(0.9159573778427816), + FRAC_CONST(0.9254724739583072), + FRAC_CONST(0.9342040454819434), + FRAC_CONST(0.9421791879559176), + FRAC_CONST(0.9494284680976784), + FRAC_CONST(0.9559854271440150), + FRAC_CONST(0.9618860658493898), + FRAC_CONST(0.9671683198119525), + FRAC_CONST(0.9718715339497299), + FRAC_CONST(0.9760359449042233), + FRAC_CONST(0.9797021798981759), + FRAC_CONST(0.9829107801140203), + FRAC_CONST(0.9857017559923277), + FRAC_CONST(0.9881141809867999), + FRAC_CONST(0.9901858292742826), + FRAC_CONST(0.9919528617340944), + FRAC_CONST(0.9934495632180476), + FRAC_CONST(0.9947081327749199), + FRAC_CONST(0.9957585271195989), + FRAC_CONST(0.9966283562984427), + FRAC_CONST(0.9973428292485683), + FRAC_CONST(0.9979247458259197), + FRAC_CONST(0.9983945309245774), + FRAC_CONST(0.9987703055583410), + FRAC_CONST(0.9990679892449266), + FRAC_CONST(0.9993014277313617), + FRAC_CONST(0.9994825400228521), + FRAC_CONST(0.9996214788122335), + FRAC_CONST(0.9997267987294857), + FRAC_CONST(0.9998056273097539), + FRAC_CONST(0.9998638341781910), + FRAC_CONST(0.9999061946325793), + FRAC_CONST(0.9999365445321382), + FRAC_CONST(0.9999579241373735), + FRAC_CONST(0.9999727092594598), + FRAC_CONST(0.9999827287418790), + FRAC_CONST(0.9999893678912771), + FRAC_CONST(0.9999936579844555), + FRAC_CONST(0.9999963523959187), + FRAC_CONST(0.9999979902130101), + FRAC_CONST(0.9999989484358076), + FRAC_CONST(0.9999994840031031), + FRAC_CONST(0.9999997669534347), + FRAC_CONST(0.9999999060327799), + FRAC_CONST(0.9999999680107184), + FRAC_CONST(0.9999999918774242), + FRAC_CONST(0.9999999989770326) +}; +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/lt_predict.c b/third_party/faad_hdc/upstream/libfaad/lt_predict.c new file mode 100644 index 000000000..b1e72a3b2 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/lt_predict.c @@ -0,0 +1,215 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: lt_predict.c,v 1.27 2007/11/01 12:33:31 menno Exp $ +**/ + + +#include "common.h" +#include "structs.h" + +#ifdef LTP_DEC + +#include +#include "syntax.h" +#include "lt_predict.h" +#include "filtbank.h" +#include "tns.h" + + +/* static function declarations */ +static int16_t real_to_int16(real_t sig_in); + + +/* check if the object type is an object type that can have LTP */ +uint8_t is_ltp_ot(uint8_t object_type) +{ +#ifdef LTP_DEC + if ((object_type == LTP) +#ifdef ERROR_RESILIENCE + || (object_type == ER_LTP) +#endif +#ifdef LD_DEC + || (object_type == LD) +#endif + ) + { + return 1; + } +#endif + + return 0; +} + +ALIGN static const real_t codebook[8] = +{ + REAL_CONST(0.570829), + REAL_CONST(0.696616), + REAL_CONST(0.813004), + REAL_CONST(0.911304), + REAL_CONST(0.984900), + REAL_CONST(1.067894), + REAL_CONST(1.194601), + REAL_CONST(1.369533) +}; + +void lt_prediction(ic_stream *ics, ltp_info *ltp, real_t *spec, + int16_t *lt_pred_stat, fb_info *fb, uint8_t win_shape, + uint8_t win_shape_prev, uint8_t sr_index, + uint8_t object_type, uint16_t frame_len) +{ + uint8_t sfb; + uint16_t bin, i, num_samples; + ALIGN real_t x_est[2048]; + ALIGN real_t X_est[2048]; + + if (ics->window_sequence != EIGHT_SHORT_SEQUENCE) + { + if (ltp->data_present) + { + num_samples = frame_len << 1; + + for(i = 0; i < num_samples; i++) + { + /* The extra lookback M (N/2 for LD, 0 for LTP) is handled + in the buffer updating */ + +#if 0 + x_est[i] = MUL_R_C(lt_pred_stat[num_samples + i - ltp->lag], + codebook[ltp->coef]); +#else + /* lt_pred_stat is a 16 bit int, multiplied with the fixed point real + this gives a real for x_est + */ + x_est[i] = (real_t)lt_pred_stat[num_samples + i - ltp->lag] * codebook[ltp->coef]; +#endif + } + + filter_bank_ltp(fb, ics->window_sequence, win_shape, win_shape_prev, + x_est, X_est, object_type, frame_len); + + tns_encode_frame(ics, &(ics->tns), sr_index, object_type, X_est, + frame_len); + + for (sfb = 0; sfb < ltp->last_band; sfb++) + { + if (ltp->long_used[sfb]) + { + uint16_t low = ics->swb_offset[sfb]; + uint16_t high = min(ics->swb_offset[sfb+1], ics->swb_offset_max); + + for (bin = low; bin < high; bin++) + { + spec[bin] += X_est[bin]; + } + } + } + } + } +} + +#ifdef FIXED_POINT +static INLINE int16_t real_to_int16(real_t sig_in) +{ + if (sig_in >= 0) + { + sig_in += (1 << (REAL_BITS-1)); + if (sig_in >= REAL_CONST(32768)) + return 32767; + } else { + sig_in += -(1 << (REAL_BITS-1)); + if (sig_in <= REAL_CONST(-32768)) + return -32768; + } + + return (int16_t)(sig_in >> REAL_BITS); +} +#else +static INLINE int16_t real_to_int16(real_t sig_in) +{ + if (sig_in >= 0) + { +#ifndef HAS_LRINTF + sig_in += 0.5f; +#endif + if (sig_in >= 32768.0f) + return 32767; + } else { +#ifndef HAS_LRINTF + sig_in += -0.5f; +#endif + if (sig_in <= -32768.0f) + return -32768; + } + + return (int16_t)lrintf(sig_in); +} +#endif + +void lt_update_state(int16_t *lt_pred_stat, real_t *time, real_t *overlap, + uint16_t frame_len, uint8_t object_type) +{ + uint16_t i; + + /* + * The reference point for index i and the content of the buffer + * lt_pred_stat are arranged so that lt_pred_stat(0 ... N/2 - 1) contains the + * last aliased half window from the IMDCT, and lt_pred_stat(N/2 ... N-1) + * is always all zeros. The rest of lt_pred_stat (i<0) contains the previous + * fully reconstructed time domain samples, i.e., output of the decoder. + * + * These values are shifted up by N*2 to avoid (i<0) + * + * For the LD object type an extra 512 samples lookback is accomodated here. + */ +#ifdef LD_DEC + if (object_type == LD) + { + for (i = 0; i < frame_len; i++) + { + lt_pred_stat[i] /* extra 512 */ = lt_pred_stat[i + frame_len]; + lt_pred_stat[frame_len + i] = lt_pred_stat[i + (frame_len * 2)]; + lt_pred_stat[(frame_len * 2) + i] = real_to_int16(time[i]); + lt_pred_stat[(frame_len * 3) + i] = real_to_int16(overlap[i]); + } + } else { +#endif + for (i = 0; i < frame_len; i++) + { + lt_pred_stat[i] = lt_pred_stat[i + frame_len]; + lt_pred_stat[frame_len + i] = real_to_int16(time[i]); + lt_pred_stat[(frame_len * 2) + i] = real_to_int16(overlap[i]); +#if 0 /* set to zero once upon initialisation */ + lt_pred_stat[(frame_len * 3) + i] = 0; +#endif + } +#ifdef LD_DEC + } +#endif +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/lt_predict.h b/third_party/faad_hdc/upstream/libfaad/lt_predict.h new file mode 100644 index 000000000..9ca5e73b1 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/lt_predict.h @@ -0,0 +1,66 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: lt_predict.h,v 1.20 2007/11/01 12:33:31 menno Exp $ +**/ + +#ifdef LTP_DEC + +#ifndef __LT_PREDICT_H__ +#define __LT_PREDICT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "filtbank.h" + +uint8_t is_ltp_ot(uint8_t object_type); + +void lt_prediction(ic_stream *ics, + ltp_info *ltp, + real_t *spec, + int16_t *lt_pred_stat, + fb_info *fb, + uint8_t win_shape, + uint8_t win_shape_prev, + uint8_t sr_index, + uint8_t object_type, + uint16_t frame_len); + +void lt_update_state(int16_t *lt_pred_stat, + real_t *time, + real_t *overlap, + uint16_t frame_len, + uint8_t object_type); + +#ifdef __cplusplus +} +#endif +#endif + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/mdct.c b/third_party/faad_hdc/upstream/libfaad/mdct.c new file mode 100644 index 000000000..1e394ed15 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/mdct.c @@ -0,0 +1,301 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: mdct.c,v 1.47 2007/11/01 12:33:31 menno Exp $ +**/ + +/* + * Fast (I)MDCT Implementation using (I)FFT ((Inverse) Fast Fourier Transform) + * and consists of three steps: pre-(I)FFT complex multiplication, complex + * (I)FFT, post-(I)FFT complex multiplication, + * + * As described in: + * P. Duhamel, Y. Mahieux, and J.P. Petit, "A Fast Algorithm for the + * Implementation of Filter Banks Based on 'Time Domain Aliasing + * Cancellation'," IEEE Proc. on ICASSP'91, 1991, pp. 2209-2212. + * + * + * As of April 6th 2002 completely rewritten. + * This (I)MDCT can now be used for any data size n, where n is divisible by 8. + * + */ + +#include "common.h" +#include "structs.h" + +#include +#ifdef _WIN32_WCE +#define assert(x) +#else +#include +#endif + +#include "cfft.h" +#include "mdct.h" +#include "mdct_tab.h" + + +mdct_info *faad_mdct_init(uint16_t N) +{ + mdct_info *mdct = (mdct_info*)faad_malloc(sizeof(mdct_info)); + + assert(N % 8 == 0); + + mdct->N = N; + + /* NOTE: For "small framelengths" in FIXED_POINT the coefficients need to be + * scaled by sqrt("(nearest power of 2) > N" / N) */ + + /* RE(mdct->sincos[k]) = scale*(real_t)(cos(2.0*M_PI*(k+1./8.) / (real_t)N)); + * IM(mdct->sincos[k]) = scale*(real_t)(sin(2.0*M_PI*(k+1./8.) / (real_t)N)); */ + /* scale is 1 for fixed point, sqrt(N) for floating point */ + switch (N) + { + case 2048: mdct->sincos = (complex_t*)mdct_tab_2048; break; + case 256: mdct->sincos = (complex_t*)mdct_tab_256; break; +#ifdef LD_DEC + case 1024: mdct->sincos = (complex_t*)mdct_tab_1024; break; +#endif +#ifdef ALLOW_SMALL_FRAMELENGTH + case 1920: mdct->sincos = (complex_t*)mdct_tab_1920; break; + case 240: mdct->sincos = (complex_t*)mdct_tab_240; break; +#ifdef LD_DEC + case 960: mdct->sincos = (complex_t*)mdct_tab_960; break; +#endif +#endif +#ifdef SSR_DEC + case 512: mdct->sincos = (complex_t*)mdct_tab_512; break; + case 64: mdct->sincos = (complex_t*)mdct_tab_64; break; +#endif + } + + /* initialise fft */ + mdct->cfft = cffti(N/4); + +#ifdef PROFILE + mdct->cycles = 0; + mdct->fft_cycles = 0; +#endif + + return mdct; +} + +void faad_mdct_end(mdct_info *mdct) +{ + if (mdct != NULL) + { +#ifdef PROFILE + printf("MDCT[%.4d]: %I64d cycles\n", mdct->N, mdct->cycles); + printf("CFFT[%.4d]: %I64d cycles\n", mdct->N/4, mdct->fft_cycles); +#endif + + cfftu(mdct->cfft); + + faad_free(mdct); + } +} + +void faad_imdct(mdct_info *mdct, real_t *X_in, real_t *X_out) +{ + uint16_t k; + + complex_t x; +#ifdef ALLOW_SMALL_FRAMELENGTH +#ifdef FIXED_POINT + real_t scale = 0, b_scale = 0; +#endif +#endif + ALIGN complex_t Z1[512]; + complex_t *sincos = mdct->sincos; + + uint16_t N = mdct->N; + uint16_t N2 = N >> 1; + uint16_t N4 = N >> 2; + uint16_t N8 = N >> 3; + +#ifdef PROFILE + int64_t count1, count2 = faad_get_ts(); +#endif + +#ifdef ALLOW_SMALL_FRAMELENGTH +#ifdef FIXED_POINT + /* detect non-power of 2 */ + if (N & (N-1)) + { + /* adjust scale for non-power of 2 MDCT */ + /* 2048/1920 */ + b_scale = 1; + scale = COEF_CONST(1.0666666666666667); + } +#endif +#endif + + /* pre-IFFT complex multiplication */ + for (k = 0; k < N4; k++) + { + ComplexMult(&IM(Z1[k]), &RE(Z1[k]), + X_in[2*k], X_in[N2 - 1 - 2*k], RE(sincos[k]), IM(sincos[k])); + } + +#ifdef PROFILE + count1 = faad_get_ts(); +#endif + + /* complex IFFT, any non-scaling FFT can be used here */ + cfftb(mdct->cfft, Z1); + +#ifdef PROFILE + count1 = faad_get_ts() - count1; +#endif + + /* post-IFFT complex multiplication */ + for (k = 0; k < N4; k++) + { + RE(x) = RE(Z1[k]); + IM(x) = IM(Z1[k]); + ComplexMult(&IM(Z1[k]), &RE(Z1[k]), + IM(x), RE(x), RE(sincos[k]), IM(sincos[k])); + +#ifdef ALLOW_SMALL_FRAMELENGTH +#ifdef FIXED_POINT + /* non-power of 2 MDCT scaling */ + if (b_scale) + { + RE(Z1[k]) = MUL_C(RE(Z1[k]), scale); + IM(Z1[k]) = MUL_C(IM(Z1[k]), scale); + } +#endif +#endif + } + + /* reordering */ + for (k = 0; k < N8; k+=2) + { + X_out[ 2*k] = IM(Z1[N8 + k]); + X_out[ 2 + 2*k] = IM(Z1[N8 + 1 + k]); + + X_out[ 1 + 2*k] = -RE(Z1[N8 - 1 - k]); + X_out[ 3 + 2*k] = -RE(Z1[N8 - 2 - k]); + + X_out[N4 + 2*k] = RE(Z1[ k]); + X_out[N4 + + 2 + 2*k] = RE(Z1[ 1 + k]); + + X_out[N4 + 1 + 2*k] = -IM(Z1[N4 - 1 - k]); + X_out[N4 + 3 + 2*k] = -IM(Z1[N4 - 2 - k]); + + X_out[N2 + 2*k] = RE(Z1[N8 + k]); + X_out[N2 + + 2 + 2*k] = RE(Z1[N8 + 1 + k]); + + X_out[N2 + 1 + 2*k] = -IM(Z1[N8 - 1 - k]); + X_out[N2 + 3 + 2*k] = -IM(Z1[N8 - 2 - k]); + + X_out[N2 + N4 + 2*k] = -IM(Z1[ k]); + X_out[N2 + N4 + 2 + 2*k] = -IM(Z1[ 1 + k]); + + X_out[N2 + N4 + 1 + 2*k] = RE(Z1[N4 - 1 - k]); + X_out[N2 + N4 + 3 + 2*k] = RE(Z1[N4 - 2 - k]); + } + +#ifdef PROFILE + count2 = faad_get_ts() - count2; + mdct->fft_cycles += count1; + mdct->cycles += (count2 - count1); +#endif +} + +#ifdef LTP_DEC +void faad_mdct(mdct_info *mdct, real_t *X_in, real_t *X_out) +{ + uint16_t k; + + complex_t x; + ALIGN complex_t Z1[512]; + complex_t *sincos = mdct->sincos; + + uint16_t N = mdct->N; + uint16_t N2 = N >> 1; + uint16_t N4 = N >> 2; + uint16_t N8 = N >> 3; + +#ifndef FIXED_POINT + real_t scale = REAL_CONST(N); +#else + real_t scale = REAL_CONST(4.0/N); +#endif + +#ifdef ALLOW_SMALL_FRAMELENGTH +#ifdef FIXED_POINT + /* detect non-power of 2 */ + if (N & (N-1)) + { + /* adjust scale for non-power of 2 MDCT */ + /* *= sqrt(2048/1920) */ + scale = MUL_C(scale, COEF_CONST(1.0327955589886444)); + } +#endif +#endif + + /* pre-FFT complex multiplication */ + for (k = 0; k < N8; k++) + { + uint16_t n = k << 1; + RE(x) = X_in[N - N4 - 1 - n] + X_in[N - N4 + n]; + IM(x) = X_in[ N4 + n] - X_in[ N4 - 1 - n]; + + ComplexMult(&RE(Z1[k]), &IM(Z1[k]), + RE(x), IM(x), RE(sincos[k]), IM(sincos[k])); + + RE(Z1[k]) = MUL_R(RE(Z1[k]), scale); + IM(Z1[k]) = MUL_R(IM(Z1[k]), scale); + + RE(x) = X_in[N2 - 1 - n] - X_in[ n]; + IM(x) = X_in[N2 + n] + X_in[N - 1 - n]; + + ComplexMult(&RE(Z1[k + N8]), &IM(Z1[k + N8]), + RE(x), IM(x), RE(sincos[k + N8]), IM(sincos[k + N8])); + + RE(Z1[k + N8]) = MUL_R(RE(Z1[k + N8]), scale); + IM(Z1[k + N8]) = MUL_R(IM(Z1[k + N8]), scale); + } + + /* complex FFT, any non-scaling FFT can be used here */ + cfftf(mdct->cfft, Z1); + + /* post-FFT complex multiplication */ + for (k = 0; k < N4; k++) + { + uint16_t n = k << 1; + ComplexMult(&RE(x), &IM(x), + RE(Z1[k]), IM(Z1[k]), RE(sincos[k]), IM(sincos[k])); + + X_out[ n] = -RE(x); + X_out[N2 - 1 - n] = IM(x); + X_out[N2 + n] = -IM(x); + X_out[N - 1 - n] = RE(x); + } +} +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/mdct.h b/third_party/faad_hdc/upstream/libfaad/mdct.h new file mode 100644 index 000000000..43b95344d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/mdct.h @@ -0,0 +1,48 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: mdct.h,v 1.30 2007/11/01 12:33:31 menno Exp $ +**/ + +#ifndef __MDCT_H__ +#define __MDCT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +mdct_info *faad_mdct_init(uint16_t N); +void faad_mdct_end(mdct_info *mdct); +void faad_imdct(mdct_info *mdct, real_t *X_in, real_t *X_out); +void faad_mdct(mdct_info *mdct, real_t *X_in, real_t *X_out); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/mdct_tab.h b/third_party/faad_hdc/upstream/libfaad/mdct_tab.h new file mode 100644 index 000000000..471610c1a --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/mdct_tab.h @@ -0,0 +1,3655 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: mdct_tab.h,v 1.7 2007/11/01 12:33:32 menno Exp $ +**/ + +#ifndef __MDCT_TAB_H__ +#define __MDCT_TAB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef FIXED_POINT + +/* 256 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_2048[] = +{ + { FRAC_CONST(0.999999926465718), FRAC_CONST(0.000383495187571) }, + { FRAC_CONST(0.999994043728986), FRAC_CONST(0.003451449920136) }, + { FRAC_CONST(0.999978748667469), FRAC_CONST(0.006519372166339) }, + { FRAC_CONST(0.999954041425130), FRAC_CONST(0.009587233049729) }, + { FRAC_CONST(0.999919922234523), FRAC_CONST(0.012655003694430) }, + { FRAC_CONST(0.999876391416790), FRAC_CONST(0.015722655225417) }, + { FRAC_CONST(0.999823449381662), FRAC_CONST(0.018790158768785) }, + { FRAC_CONST(0.999761096627447), FRAC_CONST(0.021857485452022) }, + { FRAC_CONST(0.999689333741034), FRAC_CONST(0.024924606404281) }, + { FRAC_CONST(0.999608161397882), FRAC_CONST(0.027991492756653) }, + { FRAC_CONST(0.999517580362017), FRAC_CONST(0.031058115642435) }, + { FRAC_CONST(0.999417591486022), FRAC_CONST(0.034124446197403) }, + { FRAC_CONST(0.999308195711029), FRAC_CONST(0.037190455560088) }, + { FRAC_CONST(0.999189394066715), FRAC_CONST(0.040256114872041) }, + { FRAC_CONST(0.999061187671285), FRAC_CONST(0.043321395278110) }, + { FRAC_CONST(0.998923577731466), FRAC_CONST(0.046386267926707) }, + { FRAC_CONST(0.998776565542496), FRAC_CONST(0.049450703970085) }, + { FRAC_CONST(0.998620152488109), FRAC_CONST(0.052514674564603) }, + { FRAC_CONST(0.998454340040525), FRAC_CONST(0.055578150871005) }, + { FRAC_CONST(0.998279129760433), FRAC_CONST(0.058641104054683) }, + { FRAC_CONST(0.998094523296980), FRAC_CONST(0.061703505285957) }, + { FRAC_CONST(0.997900522387752), FRAC_CONST(0.064765325740340) }, + { FRAC_CONST(0.997697128858759), FRAC_CONST(0.067826536598811) }, + { FRAC_CONST(0.997484344624418), FRAC_CONST(0.070887109048088) }, + { FRAC_CONST(0.997262171687536), FRAC_CONST(0.073947014280897) }, + { FRAC_CONST(0.997030612139289), FRAC_CONST(0.077006223496246) }, + { FRAC_CONST(0.996789668159205), FRAC_CONST(0.080064707899691) }, + { FRAC_CONST(0.996539342015138), FRAC_CONST(0.083122438703613) }, + { FRAC_CONST(0.996279636063255), FRAC_CONST(0.086179387127485) }, + { FRAC_CONST(0.996010552748006), FRAC_CONST(0.089235524398144) }, + { FRAC_CONST(0.995732094602106), FRAC_CONST(0.092290821750062) }, + { FRAC_CONST(0.995444264246510), FRAC_CONST(0.095345250425618) }, + { FRAC_CONST(0.995147064390386), FRAC_CONST(0.098398781675364) }, + { FRAC_CONST(0.994840497831093), FRAC_CONST(0.101451386758302) }, + { FRAC_CONST(0.994524567454152), FRAC_CONST(0.104503036942151) }, + { FRAC_CONST(0.994199276233219), FRAC_CONST(0.107553703503616) }, + { FRAC_CONST(0.993864627230060), FRAC_CONST(0.110603357728662) }, + { FRAC_CONST(0.993520623594518), FRAC_CONST(0.113651970912782) }, + { FRAC_CONST(0.993167268564487), FRAC_CONST(0.116699514361268) }, + { FRAC_CONST(0.992804565465879), FRAC_CONST(0.119745959389480) }, + { FRAC_CONST(0.992432517712594), FRAC_CONST(0.122791277323117) }, + { FRAC_CONST(0.992051128806486), FRAC_CONST(0.125835439498487) }, + { FRAC_CONST(0.991660402337333), FRAC_CONST(0.128878417262777) }, + { FRAC_CONST(0.991260341982802), FRAC_CONST(0.131920181974320) }, + { FRAC_CONST(0.990850951508414), FRAC_CONST(0.134960705002869) }, + { FRAC_CONST(0.990432234767506), FRAC_CONST(0.137999957729863) }, + { FRAC_CONST(0.990004195701201), FRAC_CONST(0.141037911548698) }, + { FRAC_CONST(0.989566838338365), FRAC_CONST(0.144074537864995) }, + { FRAC_CONST(0.989120166795573), FRAC_CONST(0.147109808096872) }, + { FRAC_CONST(0.988664185277066), FRAC_CONST(0.150143693675208) }, + { FRAC_CONST(0.988198898074718), FRAC_CONST(0.153176166043918) }, + { FRAC_CONST(0.987724309567987), FRAC_CONST(0.156207196660216) }, + { FRAC_CONST(0.987240424223882), FRAC_CONST(0.159236756994888) }, + { FRAC_CONST(0.986747246596917), FRAC_CONST(0.162264818532558) }, + { FRAC_CONST(0.986244781329065), FRAC_CONST(0.165291352771958) }, + { FRAC_CONST(0.985733033149723), FRAC_CONST(0.168316331226195) }, + { FRAC_CONST(0.985212006875659), FRAC_CONST(0.171339725423019) }, + { FRAC_CONST(0.984681707410971), FRAC_CONST(0.174361506905094) }, + { FRAC_CONST(0.984142139747039), FRAC_CONST(0.177381647230260) }, + { FRAC_CONST(0.983593308962479), FRAC_CONST(0.180400117971807) }, + { FRAC_CONST(0.983035220223096), FRAC_CONST(0.183416890718739) }, + { FRAC_CONST(0.982467878781833), FRAC_CONST(0.186431937076042) }, + { FRAC_CONST(0.981891289978725), FRAC_CONST(0.189445228664950) }, + { FRAC_CONST(0.981305459240845), FRAC_CONST(0.192456737123217) }, + { FRAC_CONST(0.980710392082254), FRAC_CONST(0.195466434105377) }, + { FRAC_CONST(0.980106094103952), FRAC_CONST(0.198474291283016) }, + { FRAC_CONST(0.979492570993821), FRAC_CONST(0.201480280345038) }, + { FRAC_CONST(0.978869828526574), FRAC_CONST(0.204484372997927) }, + { FRAC_CONST(0.978237872563701), FRAC_CONST(0.207486540966021) }, + { FRAC_CONST(0.977596709053412), FRAC_CONST(0.210486755991770) }, + { FRAC_CONST(0.976946344030582), FRAC_CONST(0.213484989836008) }, + { FRAC_CONST(0.976286783616694), FRAC_CONST(0.216481214278217) }, + { FRAC_CONST(0.975618034019782), FRAC_CONST(0.219475401116790) }, + { FRAC_CONST(0.974940101534372), FRAC_CONST(0.222467522169302) }, + { FRAC_CONST(0.974252992541423), FRAC_CONST(0.225457549272769) }, + { FRAC_CONST(0.973556713508266), FRAC_CONST(0.228445454283916) }, + { FRAC_CONST(0.972851270988544), FRAC_CONST(0.231431209079446) }, + { FRAC_CONST(0.972136671622152), FRAC_CONST(0.234414785556295) }, + { FRAC_CONST(0.971412922135171), FRAC_CONST(0.237396155631907) }, + { FRAC_CONST(0.970680029339806), FRAC_CONST(0.240375291244489) }, + { FRAC_CONST(0.969938000134324), FRAC_CONST(0.243352164353285) }, + { FRAC_CONST(0.969186841502986), FRAC_CONST(0.246326746938829) }, + { FRAC_CONST(0.968426560515983), FRAC_CONST(0.249299011003218) }, + { FRAC_CONST(0.967657164329370), FRAC_CONST(0.252268928570371) }, + { FRAC_CONST(0.966878660184996), FRAC_CONST(0.255236471686292) }, + { FRAC_CONST(0.966091055410439), FRAC_CONST(0.258201612419335) }, + { FRAC_CONST(0.965294357418935), FRAC_CONST(0.261164322860466) }, + { FRAC_CONST(0.964488573709308), FRAC_CONST(0.264124575123528) }, + { FRAC_CONST(0.963673711865903), FRAC_CONST(0.267082341345496) }, + { FRAC_CONST(0.962849779558509), FRAC_CONST(0.270037593686751) }, + { FRAC_CONST(0.962016784542291), FRAC_CONST(0.272990304331330) }, + { FRAC_CONST(0.961174734657714), FRAC_CONST(0.275940445487197) }, + { FRAC_CONST(0.960323637830474), FRAC_CONST(0.278887989386500) }, + { FRAC_CONST(0.959463502071418), FRAC_CONST(0.281832908285833) }, + { FRAC_CONST(0.958594335476470), FRAC_CONST(0.284775174466498) }, + { FRAC_CONST(0.957716146226559), FRAC_CONST(0.287714760234765) }, + { FRAC_CONST(0.956828942587535), FRAC_CONST(0.290651637922133) }, + { FRAC_CONST(0.955932732910098), FRAC_CONST(0.293585779885591) }, + { FRAC_CONST(0.955027525629714), FRAC_CONST(0.296517158507877) }, + { FRAC_CONST(0.954113329266539), FRAC_CONST(0.299445746197740) }, + { FRAC_CONST(0.953190152425337), FRAC_CONST(0.302371515390196) }, + { FRAC_CONST(0.952258003795400), FRAC_CONST(0.305294438546792) }, + { FRAC_CONST(0.951316892150466), FRAC_CONST(0.308214488155861) }, + { FRAC_CONST(0.950366826348636), FRAC_CONST(0.311131636732785) }, + { FRAC_CONST(0.949407815332292), FRAC_CONST(0.314045856820251) }, + { FRAC_CONST(0.948439868128010), FRAC_CONST(0.316957120988508) }, + { FRAC_CONST(0.947462993846478), FRAC_CONST(0.319865401835631) }, + { FRAC_CONST(0.946477201682409), FRAC_CONST(0.322770671987771) }, + { FRAC_CONST(0.945482500914454), FRAC_CONST(0.325672904099420) }, + { FRAC_CONST(0.944478900905116), FRAC_CONST(0.328572070853664) }, + { FRAC_CONST(0.943466411100659), FRAC_CONST(0.331468144962441) }, + { FRAC_CONST(0.942445041031025), FRAC_CONST(0.334361099166799) }, + { FRAC_CONST(0.941414800309736), FRAC_CONST(0.337250906237151) }, + { FRAC_CONST(0.940375698633812), FRAC_CONST(0.340137538973532) }, + { FRAC_CONST(0.939327745783671), FRAC_CONST(0.343020970205856) }, + { FRAC_CONST(0.938270951623047), FRAC_CONST(0.345901172794169) }, + { FRAC_CONST(0.937205326098888), FRAC_CONST(0.348778119628908) }, + { FRAC_CONST(0.936130879241267), FRAC_CONST(0.351651783631155) }, + { FRAC_CONST(0.935047621163287), FRAC_CONST(0.354522137752887) }, + { FRAC_CONST(0.933955562060987), FRAC_CONST(0.357389154977241) }, + { FRAC_CONST(0.932854712213241), FRAC_CONST(0.360252808318757) }, + { FRAC_CONST(0.931745081981669), FRAC_CONST(0.363113070823639) }, + { FRAC_CONST(0.930626681810532), FRAC_CONST(0.365969915570009) }, + { FRAC_CONST(0.929499522226639), FRAC_CONST(0.368823315668154) }, + { FRAC_CONST(0.928363613839244), FRAC_CONST(0.371673244260787) }, + { FRAC_CONST(0.927218967339952), FRAC_CONST(0.374519674523293) }, + { FRAC_CONST(0.926065593502609), FRAC_CONST(0.377362579663988) }, + { FRAC_CONST(0.924903503183211), FRAC_CONST(0.380201932924366) }, + { FRAC_CONST(0.923732707319793), FRAC_CONST(0.383037707579352) }, + { FRAC_CONST(0.922553216932333), FRAC_CONST(0.385869876937555) }, + { FRAC_CONST(0.921365043122642), FRAC_CONST(0.388698414341519) }, + { FRAC_CONST(0.920168197074266), FRAC_CONST(0.391523293167972) }, + { FRAC_CONST(0.918962690052376), FRAC_CONST(0.394344486828080) }, + { FRAC_CONST(0.917748533403661), FRAC_CONST(0.397161968767692) }, + { FRAC_CONST(0.916525738556228), FRAC_CONST(0.399975712467595) }, + { FRAC_CONST(0.915294317019487), FRAC_CONST(0.402785691443764) }, + { FRAC_CONST(0.914054280384047), FRAC_CONST(0.405591879247604) }, + { FRAC_CONST(0.912805640321604), FRAC_CONST(0.408394249466208) }, + { FRAC_CONST(0.911548408584834), FRAC_CONST(0.411192775722600) }, + { FRAC_CONST(0.910282597007282), FRAC_CONST(0.413987431675985) }, + { FRAC_CONST(0.909008217503247), FRAC_CONST(0.416778191021998) }, + { FRAC_CONST(0.907725282067676), FRAC_CONST(0.419565027492947) }, + { FRAC_CONST(0.906433802776045), FRAC_CONST(0.422347914858067) }, + { FRAC_CONST(0.905133791784250), FRAC_CONST(0.425126826923762) }, + { FRAC_CONST(0.903825261328488), FRAC_CONST(0.427901737533854) }, + { FRAC_CONST(0.902508223725146), FRAC_CONST(0.430672620569827) }, + { FRAC_CONST(0.901182691370685), FRAC_CONST(0.433439449951074) }, + { FRAC_CONST(0.899848676741519), FRAC_CONST(0.436202199635144) }, + { FRAC_CONST(0.898506192393902), FRAC_CONST(0.438960843617984) }, + { FRAC_CONST(0.897155250963809), FRAC_CONST(0.441715355934187) }, + { FRAC_CONST(0.895795865166814), FRAC_CONST(0.444465710657234) }, + { FRAC_CONST(0.894428047797974), FRAC_CONST(0.447211881899738) }, + { FRAC_CONST(0.893051811731707), FRAC_CONST(0.449953843813691) }, + { FRAC_CONST(0.891667169921672), FRAC_CONST(0.452691570590701) }, + { FRAC_CONST(0.890274135400645), FRAC_CONST(0.455425036462242) }, + { FRAC_CONST(0.888872721280396), FRAC_CONST(0.458154215699893) }, + { FRAC_CONST(0.887462940751569), FRAC_CONST(0.460879082615579) }, + { FRAC_CONST(0.886044807083556), FRAC_CONST(0.463599611561814) }, + { FRAC_CONST(0.884618333624370), FRAC_CONST(0.466315776931944) }, + { FRAC_CONST(0.883183533800523), FRAC_CONST(0.469027553160387) }, + { FRAC_CONST(0.881740421116898), FRAC_CONST(0.471734914722871) }, + { FRAC_CONST(0.880289009156621), FRAC_CONST(0.474437836136679) }, + { FRAC_CONST(0.878829311580933), FRAC_CONST(0.477136291960885) }, + { FRAC_CONST(0.877361342129065), FRAC_CONST(0.479830256796594) }, + { FRAC_CONST(0.875885114618104), FRAC_CONST(0.482519705287184) }, + { FRAC_CONST(0.874400642942865), FRAC_CONST(0.485204612118542) }, + { FRAC_CONST(0.872907941075761), FRAC_CONST(0.487884952019301) }, + { FRAC_CONST(0.871407023066671), FRAC_CONST(0.490560699761082) }, + { FRAC_CONST(0.869897903042806), FRAC_CONST(0.493231830158728) }, + { FRAC_CONST(0.868380595208580), FRAC_CONST(0.495898318070542) }, + { FRAC_CONST(0.866855113845470), FRAC_CONST(0.498560138398525) }, + { FRAC_CONST(0.865321473311890), FRAC_CONST(0.501217266088610) }, + { FRAC_CONST(0.863779688043047), FRAC_CONST(0.503869676130899) }, + { FRAC_CONST(0.862229772550811), FRAC_CONST(0.506517343559899) }, + { FRAC_CONST(0.860671741423578), FRAC_CONST(0.509160243454755) }, + { FRAC_CONST(0.859105609326130), FRAC_CONST(0.511798350939487) }, + { FRAC_CONST(0.857531390999499), FRAC_CONST(0.514431641183223) }, + { FRAC_CONST(0.855949101260827), FRAC_CONST(0.517060089400432) }, + { FRAC_CONST(0.854358755003227), FRAC_CONST(0.519683670851158) }, + { FRAC_CONST(0.852760367195645), FRAC_CONST(0.522302360841255) }, + { FRAC_CONST(0.851153952882715), FRAC_CONST(0.524916134722613) }, + { FRAC_CONST(0.849539527184621), FRAC_CONST(0.527524967893398) }, + { FRAC_CONST(0.847917105296951), FRAC_CONST(0.530128835798279) }, + { FRAC_CONST(0.846286702490560), FRAC_CONST(0.532727713928659) }, + { FRAC_CONST(0.844648334111418), FRAC_CONST(0.535321577822907) }, + { FRAC_CONST(0.843002015580473), FRAC_CONST(0.537910403066589) }, + { FRAC_CONST(0.841347762393502), FRAC_CONST(0.540494165292695) }, + { FRAC_CONST(0.839685590120966), FRAC_CONST(0.543072840181872) }, + { FRAC_CONST(0.838015514407864), FRAC_CONST(0.545646403462649) }, + { FRAC_CONST(0.836337550973584), FRAC_CONST(0.548214830911668) }, + { FRAC_CONST(0.834651715611756), FRAC_CONST(0.550778098353912) }, + { FRAC_CONST(0.832958024190107), FRAC_CONST(0.553336181662932) }, + { FRAC_CONST(0.831256492650303), FRAC_CONST(0.555889056761074) }, + { FRAC_CONST(0.829547137007809), FRAC_CONST(0.558436699619704) }, + { FRAC_CONST(0.827829973351730), FRAC_CONST(0.560979086259438) }, + { FRAC_CONST(0.826105017844665), FRAC_CONST(0.563516192750365) }, + { FRAC_CONST(0.824372286722551), FRAC_CONST(0.566047995212271) }, + { FRAC_CONST(0.822631796294515), FRAC_CONST(0.568574469814869) }, + { FRAC_CONST(0.820883562942715), FRAC_CONST(0.571095592778017) }, + { FRAC_CONST(0.819127603122188), FRAC_CONST(0.573611340371945) }, + { FRAC_CONST(0.817363933360698), FRAC_CONST(0.576121688917478) }, + { FRAC_CONST(0.815592570258577), FRAC_CONST(0.578626614786261) }, + { FRAC_CONST(0.813813530488567), FRAC_CONST(0.581126094400978) }, + { FRAC_CONST(0.812026830795670), FRAC_CONST(0.583620104235573) }, + { FRAC_CONST(0.810232487996982), FRAC_CONST(0.586108620815476) }, + { FRAC_CONST(0.808430518981543), FRAC_CONST(0.588591620717823) }, + { FRAC_CONST(0.806620940710170), FRAC_CONST(0.591069080571671) }, + { FRAC_CONST(0.804803770215303), FRAC_CONST(0.593540977058226) }, + { FRAC_CONST(0.802979024600843), FRAC_CONST(0.596007286911057) }, + { FRAC_CONST(0.801146721041991), FRAC_CONST(0.598467986916314) }, + { FRAC_CONST(0.799306876785086), FRAC_CONST(0.600923053912954) }, + { FRAC_CONST(0.797459509147442), FRAC_CONST(0.603372464792950) }, + { FRAC_CONST(0.795604635517188), FRAC_CONST(0.605816196501515) }, + { FRAC_CONST(0.793742273353100), FRAC_CONST(0.608254226037314) }, + { FRAC_CONST(0.791872440184440), FRAC_CONST(0.610686530452686) }, + { FRAC_CONST(0.789995153610791), FRAC_CONST(0.613113086853855) }, + { FRAC_CONST(0.788110431301888), FRAC_CONST(0.615533872401147) }, + { FRAC_CONST(0.786218290997456), FRAC_CONST(0.617948864309208) }, + { FRAC_CONST(0.784318750507039), FRAC_CONST(0.620358039847214) }, + { FRAC_CONST(0.782411827709837), FRAC_CONST(0.622761376339086) }, + { FRAC_CONST(0.780497540554532), FRAC_CONST(0.625158851163708) }, + { FRAC_CONST(0.778575907059125), FRAC_CONST(0.627550441755132) }, + { FRAC_CONST(0.776646945310762), FRAC_CONST(0.629936125602796) }, + { FRAC_CONST(0.774710673465566), FRAC_CONST(0.632315880251738) }, + { FRAC_CONST(0.772767109748464), FRAC_CONST(0.634689683302798) }, + { FRAC_CONST(0.770816272453019), FRAC_CONST(0.637057512412839) }, + { FRAC_CONST(0.768858179941253), FRAC_CONST(0.639419345294951) }, + { FRAC_CONST(0.766892850643481), FRAC_CONST(0.641775159718664) }, + { FRAC_CONST(0.764920303058128), FRAC_CONST(0.644124933510155) }, + { FRAC_CONST(0.762940555751566), FRAC_CONST(0.646468644552458) }, + { FRAC_CONST(0.760953627357928), FRAC_CONST(0.648806270785673) }, + { FRAC_CONST(0.758959536578942), FRAC_CONST(0.651137790207170) }, + { FRAC_CONST(0.756958302183750), FRAC_CONST(0.653463180871802) }, + { FRAC_CONST(0.754949943008733), FRAC_CONST(0.655782420892106) }, + { FRAC_CONST(0.752934477957330), FRAC_CONST(0.658095488438511) }, + { FRAC_CONST(0.750911925999868), FRAC_CONST(0.660402361739545) }, + { FRAC_CONST(0.748882306173375), FRAC_CONST(0.662703019082037) }, + { FRAC_CONST(0.746845637581407), FRAC_CONST(0.664997438811325) }, + { FRAC_CONST(0.744801939393863), FRAC_CONST(0.667285599331456) }, + { FRAC_CONST(0.742751230846809), FRAC_CONST(0.669567479105392) }, + { FRAC_CONST(0.740693531242296), FRAC_CONST(0.671843056655212) }, + { FRAC_CONST(0.738628859948175), FRAC_CONST(0.674112310562312) }, + { FRAC_CONST(0.736557236397919), FRAC_CONST(0.676375219467612) }, + { FRAC_CONST(0.734478680090438), FRAC_CONST(0.678631762071749) }, + { FRAC_CONST(0.732393210589896), FRAC_CONST(0.680881917135287) }, + { FRAC_CONST(0.730300847525525), FRAC_CONST(0.683125663478909) }, + { FRAC_CONST(0.728201610591445), FRAC_CONST(0.685362979983619) }, + { FRAC_CONST(0.726095519546471), FRAC_CONST(0.687593845590942) }, + { FRAC_CONST(0.723982594213936), FRAC_CONST(0.689818239303122) }, + { FRAC_CONST(0.721862854481496), FRAC_CONST(0.692036140183319) }, + { FRAC_CONST(0.719736320300951), FRAC_CONST(0.694247527355803) }, + { FRAC_CONST(0.717603011688049), FRAC_CONST(0.696452380006158) }, + { FRAC_CONST(0.715462948722304), FRAC_CONST(0.698650677381469) }, + { FRAC_CONST(0.713316151546803), FRAC_CONST(0.700842398790526) }, + { FRAC_CONST(0.711162640368018), FRAC_CONST(0.703027523604011) }, + { FRAC_CONST(0.709002435455618), FRAC_CONST(0.705206031254698) }, + { FRAC_CONST(0.706835557142274), FRAC_CONST(0.707377901237642) }, + { FRAC_CONST(0.704662025823469), FRAC_CONST(0.709543113110377) }, + { FRAC_CONST(0.702481861957308), FRAC_CONST(0.711701646493103) }, + { FRAC_CONST(0.700295086064324), FRAC_CONST(0.713853481068882) }, + { FRAC_CONST(0.698101718727284), FRAC_CONST(0.715998596583829) }, + { FRAC_CONST(0.695901780590997), FRAC_CONST(0.718136972847297) }, + { FRAC_CONST(0.693695292362118), FRAC_CONST(0.720268589732077) }, + { FRAC_CONST(0.691482274808956), FRAC_CONST(0.722393427174578) }, + { FRAC_CONST(0.689262748761273), FRAC_CONST(0.724511465175020) }, + { FRAC_CONST(0.687036735110096), FRAC_CONST(0.726622683797623) }, + { FRAC_CONST(0.684804254807511), FRAC_CONST(0.728727063170794) }, + { FRAC_CONST(0.682565328866473), FRAC_CONST(0.730824583487312) }, + { FRAC_CONST(0.680319978360607), FRAC_CONST(0.732915225004518) }, + { FRAC_CONST(0.678068224424007), FRAC_CONST(0.734998968044497) }, + { FRAC_CONST(0.675810088251037), FRAC_CONST(0.737075792994266) }, + { FRAC_CONST(0.673545591096136), FRAC_CONST(0.739145680305957) }, + { FRAC_CONST(0.671274754273613), FRAC_CONST(0.741208610497004) }, + { FRAC_CONST(0.668997599157450), FRAC_CONST(0.743264564150321) }, + { FRAC_CONST(0.666714147181098), FRAC_CONST(0.745313521914490) }, + { FRAC_CONST(0.664424419837275), FRAC_CONST(0.747355464503940) }, + { FRAC_CONST(0.662128438677769), FRAC_CONST(0.749390372699130) }, + { FRAC_CONST(0.659826225313227), FRAC_CONST(0.751418227346727) }, + { FRAC_CONST(0.657517801412960), FRAC_CONST(0.753439009359794) }, + { FRAC_CONST(0.655203188704732), FRAC_CONST(0.755452699717958) }, + { FRAC_CONST(0.652882408974559), FRAC_CONST(0.757459279467601) }, + { FRAC_CONST(0.650555484066504), FRAC_CONST(0.759458729722028) }, + { FRAC_CONST(0.648222435882470), FRAC_CONST(0.761451031661654) }, + { FRAC_CONST(0.645883286381996), FRAC_CONST(0.763436166534172) }, + { FRAC_CONST(0.643538057582048), FRAC_CONST(0.765414115654738) }, + { FRAC_CONST(0.641186771556811), FRAC_CONST(0.767384860406142) }, + { FRAC_CONST(0.638829450437486), FRAC_CONST(0.769348382238982) }, + { FRAC_CONST(0.636466116412077), FRAC_CONST(0.771304662671845) }, + { FRAC_CONST(0.634096791725184), FRAC_CONST(0.773253683291473) }, + { FRAC_CONST(0.631721498677792), FRAC_CONST(0.775195425752941) }, + { FRAC_CONST(0.629340259627066), FRAC_CONST(0.777129871779832) }, + { FRAC_CONST(0.626953096986133), FRAC_CONST(0.779057003164401) }, + { FRAC_CONST(0.624560033223877), FRAC_CONST(0.780976801767754) }, + { FRAC_CONST(0.622161090864727), FRAC_CONST(0.782889249520015) }, + { FRAC_CONST(0.619756292488441), FRAC_CONST(0.784794328420499) }, + { FRAC_CONST(0.617345660729897), FRAC_CONST(0.786692020537877) }, + { FRAC_CONST(0.614929218278880), FRAC_CONST(0.788582308010347) }, + { FRAC_CONST(0.612506987879866), FRAC_CONST(0.790465173045805) }, + { FRAC_CONST(0.610078992331810), FRAC_CONST(0.792340597922007) }, + { FRAC_CONST(0.607645254487931), FRAC_CONST(0.794208564986741) }, + { FRAC_CONST(0.605205797255497), FRAC_CONST(0.796069056657988) }, + { FRAC_CONST(0.602760643595607), FRAC_CONST(0.797922055424093) }, + { FRAC_CONST(0.600309816522980), FRAC_CONST(0.799767543843926) }, + { FRAC_CONST(0.597853339105734), FRAC_CONST(0.801605504547046) }, + { FRAC_CONST(0.595391234465169), FRAC_CONST(0.803435920233868) }, + { FRAC_CONST(0.592923525775551), FRAC_CONST(0.805258773675822) }, + { FRAC_CONST(0.590450236263896), FRAC_CONST(0.807074047715518) }, + { FRAC_CONST(0.587971389209745), FRAC_CONST(0.808881725266904) }, + { FRAC_CONST(0.585487007944951), FRAC_CONST(0.810681789315431) }, + { FRAC_CONST(0.582997115853458), FRAC_CONST(0.812474222918210) }, + { FRAC_CONST(0.580501736371077), FRAC_CONST(0.814259009204175) }, + { FRAC_CONST(0.578000892985270), FRAC_CONST(0.816036131374237) }, + { FRAC_CONST(0.575494609234928), FRAC_CONST(0.817805572701444) }, + { FRAC_CONST(0.572982908710149), FRAC_CONST(0.819567316531142) }, + { FRAC_CONST(0.570465815052013), FRAC_CONST(0.821321346281127) }, + { FRAC_CONST(0.567943351952366), FRAC_CONST(0.823067645441802) }, + { FRAC_CONST(0.565415543153590), FRAC_CONST(0.824806197576334) }, + { FRAC_CONST(0.562882412448385), FRAC_CONST(0.826536986320810) }, + { FRAC_CONST(0.560343983679541), FRAC_CONST(0.828259995384386) }, + { FRAC_CONST(0.557800280739717), FRAC_CONST(0.829975208549444) }, + { FRAC_CONST(0.555251327571214), FRAC_CONST(0.831682609671745) }, + { FRAC_CONST(0.552697148165750), FRAC_CONST(0.833382182680580) }, + { FRAC_CONST(0.550137766564234), FRAC_CONST(0.835073911578919) }, + { FRAC_CONST(0.547573206856540), FRAC_CONST(0.836757780443567) }, + { FRAC_CONST(0.545003493181281), FRAC_CONST(0.838433773425308) }, + { FRAC_CONST(0.542428649725581), FRAC_CONST(0.840101874749058) }, + { FRAC_CONST(0.539848700724848), FRAC_CONST(0.841762068714012) }, + { FRAC_CONST(0.537263670462543), FRAC_CONST(0.843414339693793) }, + { FRAC_CONST(0.534673583269956), FRAC_CONST(0.845058672136595) }, + { FRAC_CONST(0.532078463525974), FRAC_CONST(0.846695050565337) }, + { FRAC_CONST(0.529478335656852), FRAC_CONST(0.848323459577802) }, + { FRAC_CONST(0.526873224135985), FRAC_CONST(0.849943883846782) }, + { FRAC_CONST(0.524263153483673), FRAC_CONST(0.851556308120229) }, + { FRAC_CONST(0.521648148266897), FRAC_CONST(0.853160717221390) }, + { FRAC_CONST(0.519028233099081), FRAC_CONST(0.854757096048957) }, + { FRAC_CONST(0.516403432639864), FRAC_CONST(0.856345429577204) }, + { FRAC_CONST(0.513773771594868), FRAC_CONST(0.857925702856130) }, + { FRAC_CONST(0.511139274715464), FRAC_CONST(0.859497901011602) }, + { FRAC_CONST(0.508499966798541), FRAC_CONST(0.861062009245491) }, + { FRAC_CONST(0.505855872686269), FRAC_CONST(0.862618012835817) }, + { FRAC_CONST(0.503207017265869), FRAC_CONST(0.864165897136879) }, + { FRAC_CONST(0.500553425469378), FRAC_CONST(0.865705647579402) }, + { FRAC_CONST(0.497895122273411), FRAC_CONST(0.867237249670668) }, + { FRAC_CONST(0.495232132698931), FRAC_CONST(0.868760688994655) }, + { FRAC_CONST(0.492564481811011), FRAC_CONST(0.870275951212172) }, + { FRAC_CONST(0.489892194718595), FRAC_CONST(0.871783022060993) }, + { FRAC_CONST(0.487215296574269), FRAC_CONST(0.873281887355994) }, + { FRAC_CONST(0.484533812574016), FRAC_CONST(0.874772532989284) }, + { FRAC_CONST(0.481847767956986), FRAC_CONST(0.876254944930338) }, + { FRAC_CONST(0.479157188005253), FRAC_CONST(0.877729109226132) }, + { FRAC_CONST(0.476462098043581), FRAC_CONST(0.879195012001267) }, + { FRAC_CONST(0.473762523439183), FRAC_CONST(0.880652639458111) }, + { FRAC_CONST(0.471058489601483), FRAC_CONST(0.882101977876918) }, + { FRAC_CONST(0.468350021981877), FRAC_CONST(0.883543013615962) }, + { FRAC_CONST(0.465637146073494), FRAC_CONST(0.884975733111667) }, + { FRAC_CONST(0.462919887410955), FRAC_CONST(0.886400122878730) }, + { FRAC_CONST(0.460198271570134), FRAC_CONST(0.887816169510255) }, + { FRAC_CONST(0.457472324167916), FRAC_CONST(0.889223859677868) }, + { FRAC_CONST(0.454742070861955), FRAC_CONST(0.890623180131856) }, + { FRAC_CONST(0.452007537350437), FRAC_CONST(0.892014117701280) }, + { FRAC_CONST(0.449268749371830), FRAC_CONST(0.893396659294108) }, + { FRAC_CONST(0.446525732704651), FRAC_CONST(0.894770791897330) }, + { FRAC_CONST(0.443778513167218), FRAC_CONST(0.896136502577087) }, + { FRAC_CONST(0.441027116617407), FRAC_CONST(0.897493778478790) }, + { FRAC_CONST(0.438271568952410), FRAC_CONST(0.898842606827242) }, + { FRAC_CONST(0.435511896108492), FRAC_CONST(0.900182974926757) }, + { FRAC_CONST(0.432748124060744), FRAC_CONST(0.901514870161279) }, + { FRAC_CONST(0.429980278822841), FRAC_CONST(0.902838279994503) }, + { FRAC_CONST(0.427208386446796), FRAC_CONST(0.904153191969992) }, + { FRAC_CONST(0.424432473022717), FRAC_CONST(0.905459593711293) }, + { FRAC_CONST(0.421652564678558), FRAC_CONST(0.906757472922057) }, + { FRAC_CONST(0.418868687579875), FRAC_CONST(0.908046817386148) }, + { FRAC_CONST(0.416080867929579), FRAC_CONST(0.909327614967767) }, + { FRAC_CONST(0.413289131967691), FRAC_CONST(0.910599853611559) }, + { FRAC_CONST(0.410493505971093), FRAC_CONST(0.911863521342729) }, + { FRAC_CONST(0.407694016253280), FRAC_CONST(0.913118606267154) }, + { FRAC_CONST(0.404890689164118), FRAC_CONST(0.914365096571498) }, + { FRAC_CONST(0.402083551089587), FRAC_CONST(0.915602980523320) }, + { FRAC_CONST(0.399272628451541), FRAC_CONST(0.916832246471184) }, + { FRAC_CONST(0.396457947707454), FRAC_CONST(0.918052882844770) }, + { FRAC_CONST(0.393639535350173), FRAC_CONST(0.919264878154985) }, + { FRAC_CONST(0.390817417907669), FRAC_CONST(0.920468220994067) }, + { FRAC_CONST(0.387991621942785), FRAC_CONST(0.921662900035695) }, + { FRAC_CONST(0.385162174052990), FRAC_CONST(0.922848904035094) }, + { FRAC_CONST(0.382329100870125), FRAC_CONST(0.924026221829144) }, + { FRAC_CONST(0.379492429060153), FRAC_CONST(0.925194842336480) }, + { FRAC_CONST(0.376652185322910), FRAC_CONST(0.926354754557603) }, + { FRAC_CONST(0.373808396391851), FRAC_CONST(0.927505947574975) }, + { FRAC_CONST(0.370961089033802), FRAC_CONST(0.928648410553131) }, + { FRAC_CONST(0.368110290048703), FRAC_CONST(0.929782132738772) }, + { FRAC_CONST(0.365256026269360), FRAC_CONST(0.930907103460875) }, + { FRAC_CONST(0.362398324561191), FRAC_CONST(0.932023312130786) }, + { FRAC_CONST(0.359537211821973), FRAC_CONST(0.933130748242325) }, + { FRAC_CONST(0.356672714981588), FRAC_CONST(0.934229401371881) }, + { FRAC_CONST(0.353804861001772), FRAC_CONST(0.935319261178512) }, + { FRAC_CONST(0.350933676875858), FRAC_CONST(0.936400317404042) }, + { FRAC_CONST(0.348059189628526), FRAC_CONST(0.937472559873159) }, + { FRAC_CONST(0.345181426315543), FRAC_CONST(0.938535978493509) }, + { FRAC_CONST(0.342300414023514), FRAC_CONST(0.939590563255789) }, + { FRAC_CONST(0.339416179869623), FRAC_CONST(0.940636304233848) }, + { FRAC_CONST(0.336528751001382), FRAC_CONST(0.941673191584771) }, + { FRAC_CONST(0.333638154596371), FRAC_CONST(0.942701215548982) }, + { FRAC_CONST(0.330744417861983), FRAC_CONST(0.943720366450326) }, + { FRAC_CONST(0.327847568035171), FRAC_CONST(0.944730634696168) }, + { FRAC_CONST(0.324947632382188), FRAC_CONST(0.945732010777477) }, + { FRAC_CONST(0.322044638198335), FRAC_CONST(0.946724485268921) }, + { FRAC_CONST(0.319138612807696), FRAC_CONST(0.947708048828952) }, + { FRAC_CONST(0.316229583562890), FRAC_CONST(0.948682692199895) }, + { FRAC_CONST(0.313317577844809), FRAC_CONST(0.949648406208035) }, + { FRAC_CONST(0.310402623062359), FRAC_CONST(0.950605181763705) }, + { FRAC_CONST(0.307484746652204), FRAC_CONST(0.951553009861369) }, + { FRAC_CONST(0.304563976078509), FRAC_CONST(0.952491881579706) }, + { FRAC_CONST(0.301640338832679), FRAC_CONST(0.953421788081700) }, + { FRAC_CONST(0.298713862433100), FRAC_CONST(0.954342720614716) }, + { FRAC_CONST(0.295784574424884), FRAC_CONST(0.955254670510587) }, + { FRAC_CONST(0.292852502379605), FRAC_CONST(0.956157629185692) }, + { FRAC_CONST(0.289917673895041), FRAC_CONST(0.957051588141041) }, + { FRAC_CONST(0.286980116594916), FRAC_CONST(0.957936538962351) }, + { FRAC_CONST(0.284039858128637), FRAC_CONST(0.958812473320129) }, + { FRAC_CONST(0.281096926171038), FRAC_CONST(0.959679382969747) }, + { FRAC_CONST(0.278151348422115), FRAC_CONST(0.960537259751520) }, + { FRAC_CONST(0.275203152606767), FRAC_CONST(0.961386095590786) }, + { FRAC_CONST(0.272252366474537), FRAC_CONST(0.962225882497979) }, + { FRAC_CONST(0.269299017799346), FRAC_CONST(0.963056612568704) }, + { FRAC_CONST(0.266343134379238), FRAC_CONST(0.963878277983814) }, + { FRAC_CONST(0.263384744036113), FRAC_CONST(0.964690871009481) }, + { FRAC_CONST(0.260423874615468), FRAC_CONST(0.965494383997270) }, + { FRAC_CONST(0.257460553986133), FRAC_CONST(0.966288809384210) }, + { FRAC_CONST(0.254494810040011), FRAC_CONST(0.967074139692867) }, + { FRAC_CONST(0.251526670691813), FRAC_CONST(0.967850367531414) }, + { FRAC_CONST(0.248556163878797), FRAC_CONST(0.968617485593698) }, + { FRAC_CONST(0.245583317560504), FRAC_CONST(0.969375486659311) }, + { FRAC_CONST(0.242608159718497), FRAC_CONST(0.970124363593660) }, + { FRAC_CONST(0.239630718356094), FRAC_CONST(0.970864109348029) }, + { FRAC_CONST(0.236651021498106), FRAC_CONST(0.971594716959650) }, + { FRAC_CONST(0.233669097190577), FRAC_CONST(0.972316179551765) }, + { FRAC_CONST(0.230684973500512), FRAC_CONST(0.973028490333694) }, + { FRAC_CONST(0.227698678515621), FRAC_CONST(0.973731642600896) }, + { FRAC_CONST(0.224710240344050), FRAC_CONST(0.974425629735035) }, + { FRAC_CONST(0.221719687114115), FRAC_CONST(0.975110445204039) }, + { FRAC_CONST(0.218727046974045), FRAC_CONST(0.975786082562164) }, + { FRAC_CONST(0.215732348091706), FRAC_CONST(0.976452535450054) }, + { FRAC_CONST(0.212735618654346), FRAC_CONST(0.977109797594801) }, + { FRAC_CONST(0.209736886868323), FRAC_CONST(0.977757862810003) }, + { FRAC_CONST(0.206736180958844), FRAC_CONST(0.978396724995823) }, + { FRAC_CONST(0.203733529169694), FRAC_CONST(0.979026378139048) }, + { FRAC_CONST(0.200728959762976), FRAC_CONST(0.979646816313141) }, + { FRAC_CONST(0.197722501018842), FRAC_CONST(0.980258033678304) }, + { FRAC_CONST(0.194714181235226), FRAC_CONST(0.980860024481524) }, + { FRAC_CONST(0.191704028727580), FRAC_CONST(0.981452783056636) }, + { FRAC_CONST(0.188692071828605), FRAC_CONST(0.982036303824369) }, + { FRAC_CONST(0.185678338887988), FRAC_CONST(0.982610581292405) }, + { FRAC_CONST(0.182662858272129), FRAC_CONST(0.983175610055424) }, + { FRAC_CONST(0.179645658363882), FRAC_CONST(0.983731384795162) }, + { FRAC_CONST(0.176626767562281), FRAC_CONST(0.984277900280454) }, + { FRAC_CONST(0.173606214282275), FRAC_CONST(0.984815151367289) }, + { FRAC_CONST(0.170584026954464), FRAC_CONST(0.985343132998855) }, + { FRAC_CONST(0.167560234024824), FRAC_CONST(0.985861840205587) }, + { FRAC_CONST(0.164534863954446), FRAC_CONST(0.986371268105216) }, + { FRAC_CONST(0.161507945219266), FRAC_CONST(0.986871411902812) }, + { FRAC_CONST(0.158479506309796), FRAC_CONST(0.987362266890832) }, + { FRAC_CONST(0.155449575730856), FRAC_CONST(0.987843828449162) }, + { FRAC_CONST(0.152418182001307), FRAC_CONST(0.988316092045160) }, + { FRAC_CONST(0.149385353653780), FRAC_CONST(0.988779053233702) }, + { FRAC_CONST(0.146351119234411), FRAC_CONST(0.989232707657220) }, + { FRAC_CONST(0.143315507302572), FRAC_CONST(0.989677051045747) }, + { FRAC_CONST(0.140278546430595), FRAC_CONST(0.990112079216954) }, + { FRAC_CONST(0.137240265203516), FRAC_CONST(0.990537788076189) }, + { FRAC_CONST(0.134200692218792), FRAC_CONST(0.990954173616519) }, + { FRAC_CONST(0.131159856086043), FRAC_CONST(0.991361231918763) }, + { FRAC_CONST(0.128117785426777), FRAC_CONST(0.991758959151536) }, + { FRAC_CONST(0.125074508874121), FRAC_CONST(0.992147351571276) }, + { FRAC_CONST(0.122030055072553), FRAC_CONST(0.992526405522286) }, + { FRAC_CONST(0.118984452677633), FRAC_CONST(0.992896117436766) }, + { FRAC_CONST(0.115937730355728), FRAC_CONST(0.993256483834846) }, + { FRAC_CONST(0.112889916783750), FRAC_CONST(0.993607501324622) }, + { FRAC_CONST(0.109841040648883), FRAC_CONST(0.993949166602181) }, + { FRAC_CONST(0.106791130648307), FRAC_CONST(0.994281476451642) }, + { FRAC_CONST(0.103740215488939), FRAC_CONST(0.994604427745176) }, + { FRAC_CONST(0.100688323887154), FRAC_CONST(0.994918017443043) }, + { FRAC_CONST(0.097635484568517), FRAC_CONST(0.995222242593618) }, + { FRAC_CONST(0.094581726267515), FRAC_CONST(0.995517100333418) }, + { FRAC_CONST(0.091527077727285), FRAC_CONST(0.995802587887129) }, + { FRAC_CONST(0.088471567699341), FRAC_CONST(0.996078702567634) }, + { FRAC_CONST(0.085415224943307), FRAC_CONST(0.996345441776036) }, + { FRAC_CONST(0.082358078226647), FRAC_CONST(0.996602803001684) }, + { FRAC_CONST(0.079300156324388), FRAC_CONST(0.996850783822197) }, + { FRAC_CONST(0.076241488018856), FRAC_CONST(0.997089381903483) }, + { FRAC_CONST(0.073182102099403), FRAC_CONST(0.997318594999769) }, + { FRAC_CONST(0.070122027362134), FRAC_CONST(0.997538420953611) }, + { FRAC_CONST(0.067061292609637), FRAC_CONST(0.997748857695926) }, + { FRAC_CONST(0.063999926650714), FRAC_CONST(0.997949903246001) }, + { FRAC_CONST(0.060937958300107), FRAC_CONST(0.998141555711521) }, + { FRAC_CONST(0.057875416378229), FRAC_CONST(0.998323813288578) }, + { FRAC_CONST(0.054812329710890), FRAC_CONST(0.998496674261695) }, + { FRAC_CONST(0.051748727129028), FRAC_CONST(0.998660137003838) }, + { FRAC_CONST(0.048684637468439), FRAC_CONST(0.998814199976435) }, + { FRAC_CONST(0.045620089569500), FRAC_CONST(0.998958861729386) }, + { FRAC_CONST(0.042555112276904), FRAC_CONST(0.999094120901079) }, + { FRAC_CONST(0.039489734439384), FRAC_CONST(0.999219976218404) }, + { FRAC_CONST(0.036423984909444), FRAC_CONST(0.999336426496761) }, + { FRAC_CONST(0.033357892543086), FRAC_CONST(0.999443470640078) }, + { FRAC_CONST(0.030291486199539), FRAC_CONST(0.999541107640813) }, + { FRAC_CONST(0.027224794740988), FRAC_CONST(0.999629336579970) }, + { FRAC_CONST(0.024157847032300), FRAC_CONST(0.999708156627105) }, + { FRAC_CONST(0.021090671940755), FRAC_CONST(0.999777567040333) }, + { FRAC_CONST(0.018023298335774), FRAC_CONST(0.999837567166337) }, + { FRAC_CONST(0.014955755088644), FRAC_CONST(0.999888156440373) }, + { FRAC_CONST(0.011888071072252), FRAC_CONST(0.999929334386276) }, + { FRAC_CONST(0.008820275160808), FRAC_CONST(0.999961100616463) }, + { FRAC_CONST(0.005752396229574), FRAC_CONST(0.999983454831938) }, + { FRAC_CONST(0.002684463154596), FRAC_CONST(0.999996396822294) } +}; + +/* 64 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_256[] = +{ + { FRAC_CONST(0.999995293809576), FRAC_CONST(0.003067956762966) }, + { FRAC_CONST(0.999618822495179), FRAC_CONST(0.027608145778966) }, + { FRAC_CONST(0.998640218180265), FRAC_CONST(0.052131704680283) }, + { FRAC_CONST(0.997060070339483), FRAC_CONST(0.076623861392031) }, + { FRAC_CONST(0.994879330794806), FRAC_CONST(0.101069862754828) }, + { FRAC_CONST(0.992099313142192), FRAC_CONST(0.125454983411546) }, + { FRAC_CONST(0.988721691960324), FRAC_CONST(0.149764534677322) }, + { FRAC_CONST(0.984748501801904), FRAC_CONST(0.173983873387464) }, + { FRAC_CONST(0.980182135968117), FRAC_CONST(0.198098410717954) }, + { FRAC_CONST(0.975025345066994), FRAC_CONST(0.222093620973204) }, + { FRAC_CONST(0.969281235356549), FRAC_CONST(0.245955050335795) }, + { FRAC_CONST(0.962953266873684), FRAC_CONST(0.269668325572915) }, + { FRAC_CONST(0.956045251349996), FRAC_CONST(0.293219162694259) }, + { FRAC_CONST(0.948561349915730), FRAC_CONST(0.316593375556166) }, + { FRAC_CONST(0.940506070593268), FRAC_CONST(0.339776884406827) }, + { FRAC_CONST(0.931884265581668), FRAC_CONST(0.362755724367397) }, + { FRAC_CONST(0.922701128333879), FRAC_CONST(0.385516053843919) }, + { FRAC_CONST(0.912962190428398), FRAC_CONST(0.408044162864979) }, + { FRAC_CONST(0.902673318237259), FRAC_CONST(0.430326481340083) }, + { FRAC_CONST(0.891840709392343), FRAC_CONST(0.452349587233771) }, + { FRAC_CONST(0.880470889052161), FRAC_CONST(0.474100214650550) }, + { FRAC_CONST(0.868570705971341), FRAC_CONST(0.495565261825773) }, + { FRAC_CONST(0.856147328375194), FRAC_CONST(0.516731799017650) }, + { FRAC_CONST(0.843208239641845), FRAC_CONST(0.537587076295645) }, + { FRAC_CONST(0.829761233794523), FRAC_CONST(0.558118531220556) }, + { FRAC_CONST(0.815814410806734), FRAC_CONST(0.578313796411656) }, + { FRAC_CONST(0.801376171723140), FRAC_CONST(0.598160706996342) }, + { FRAC_CONST(0.786455213599086), FRAC_CONST(0.617647307937804) }, + { FRAC_CONST(0.771060524261814), FRAC_CONST(0.636761861236284) }, + { FRAC_CONST(0.755201376896537), FRAC_CONST(0.655492852999615) }, + { FRAC_CONST(0.738887324460615), FRAC_CONST(0.673829000378756) }, + { FRAC_CONST(0.722128193929215), FRAC_CONST(0.691759258364158) }, + { FRAC_CONST(0.704934080375905), FRAC_CONST(0.709272826438866) }, + { FRAC_CONST(0.687315340891759), FRAC_CONST(0.726359155084346) }, + { FRAC_CONST(0.669282588346636), FRAC_CONST(0.743007952135122) }, + { FRAC_CONST(0.650846684996381), FRAC_CONST(0.759209188978388) }, + { FRAC_CONST(0.632018735939809), FRAC_CONST(0.774953106594874) }, + { FRAC_CONST(0.612810082429410), FRAC_CONST(0.790230221437310) }, + { FRAC_CONST(0.593232295039800), FRAC_CONST(0.805031331142964) }, + { FRAC_CONST(0.573297166698042), FRAC_CONST(0.819347520076797) }, + { FRAC_CONST(0.553016705580028), FRAC_CONST(0.833170164701913) }, + { FRAC_CONST(0.532403127877198), FRAC_CONST(0.846490938774052) }, + { FRAC_CONST(0.511468850437971), FRAC_CONST(0.859301818357008) }, + { FRAC_CONST(0.490226483288291), FRAC_CONST(0.871595086655951) }, + { FRAC_CONST(0.468688822035828), FRAC_CONST(0.883363338665732) }, + { FRAC_CONST(0.446868840162374), FRAC_CONST(0.894599485631383) }, + { FRAC_CONST(0.424779681209109), FRAC_CONST(0.905296759318119) }, + { FRAC_CONST(0.402434650859419), FRAC_CONST(0.915448716088268) }, + { FRAC_CONST(0.379847208924051), FRAC_CONST(0.925049240782678) }, + { FRAC_CONST(0.357030961233430), FRAC_CONST(0.934092550404259) }, + { FRAC_CONST(0.333999651442009), FRAC_CONST(0.942573197601447) }, + { FRAC_CONST(0.310767152749611), FRAC_CONST(0.950486073949482) }, + { FRAC_CONST(0.287347459544730), FRAC_CONST(0.957826413027533) }, + { FRAC_CONST(0.263754678974832), FRAC_CONST(0.964589793289813) }, + { FRAC_CONST(0.240003022448742), FRAC_CONST(0.970772140728950) }, + { FRAC_CONST(0.216106797076220), FRAC_CONST(0.976369731330021) }, + { FRAC_CONST(0.192080397049892), FRAC_CONST(0.981379193313755) }, + { FRAC_CONST(0.167938294974731), FRAC_CONST(0.985797509167567) }, + { FRAC_CONST(0.143695033150295), FRAC_CONST(0.989622017463201) }, + { FRAC_CONST(0.119365214810991), FRAC_CONST(0.992850414459865) }, + { FRAC_CONST(0.094963495329639), FRAC_CONST(0.995480755491927) }, + { FRAC_CONST(0.070504573389614), FRAC_CONST(0.997511456140303) }, + { FRAC_CONST(0.046003182130915), FRAC_CONST(0.998941293186857) }, + { FRAC_CONST(0.021474080275470), FRAC_CONST(0.999769405351215) } +}; + +#ifdef LD_DEC +/* 256 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_1024[] = +{ + { FRAC_CONST(0.999999705862882), FRAC_CONST(0.000766990318743) }, + { FRAC_CONST(0.999976174986898), FRAC_CONST(0.006902858724730) }, + { FRAC_CONST(0.999914995573113), FRAC_CONST(0.013038467241987) }, + { FRAC_CONST(0.999816169924900), FRAC_CONST(0.019173584868323) }, + { FRAC_CONST(0.999679701762988), FRAC_CONST(0.025307980620025) }, + { FRAC_CONST(0.999505596225325), FRAC_CONST(0.031441423540560) }, + { FRAC_CONST(0.999293859866888), FRAC_CONST(0.037573682709270) }, + { FRAC_CONST(0.999044500659429), FRAC_CONST(0.043704527250063) }, + { FRAC_CONST(0.998757527991183), FRAC_CONST(0.049833726340107) }, + { FRAC_CONST(0.998432952666508), FRAC_CONST(0.055961049218521) }, + { FRAC_CONST(0.998070786905482), FRAC_CONST(0.062086265195060) }, + { FRAC_CONST(0.997671044343441), FRAC_CONST(0.068209143658806) }, + { FRAC_CONST(0.997233740030466), FRAC_CONST(0.074329454086846) }, + { FRAC_CONST(0.996758890430818), FRAC_CONST(0.080446966052950) }, + { FRAC_CONST(0.996246513422316), FRAC_CONST(0.086561449236251) }, + { FRAC_CONST(0.995696628295664), FRAC_CONST(0.092672673429913) }, + { FRAC_CONST(0.995109255753726), FRAC_CONST(0.098780408549800) }, + { FRAC_CONST(0.994484417910748), FRAC_CONST(0.104884424643135) }, + { FRAC_CONST(0.993822138291520), FRAC_CONST(0.110984491897163) }, + { FRAC_CONST(0.993122441830496), FRAC_CONST(0.117080380647801) }, + { FRAC_CONST(0.992385354870852), FRAC_CONST(0.123171861388280) }, + { FRAC_CONST(0.991610905163495), FRAC_CONST(0.129258704777796) }, + { FRAC_CONST(0.990799121866020), FRAC_CONST(0.135340681650134) }, + { FRAC_CONST(0.989950035541609), FRAC_CONST(0.141417563022303) }, + { FRAC_CONST(0.989063678157882), FRAC_CONST(0.147489120103154) }, + { FRAC_CONST(0.988140083085693), FRAC_CONST(0.153555124301993) }, + { FRAC_CONST(0.987179285097874), FRAC_CONST(0.159615347237193) }, + { FRAC_CONST(0.986181320367928), FRAC_CONST(0.165669560744784) }, + { FRAC_CONST(0.985146226468662), FRAC_CONST(0.171717536887050) }, + { FRAC_CONST(0.984074042370776), FRAC_CONST(0.177759047961107) }, + { FRAC_CONST(0.982964808441396), FRAC_CONST(0.183793866507478) }, + { FRAC_CONST(0.981818566442553), FRAC_CONST(0.189821765318656) }, + { FRAC_CONST(0.980635359529608), FRAC_CONST(0.195842517447658) }, + { FRAC_CONST(0.979415232249635), FRAC_CONST(0.201855896216568) }, + { FRAC_CONST(0.978158230539735), FRAC_CONST(0.207861675225075) }, + { FRAC_CONST(0.976864401725313), FRAC_CONST(0.213859628358994) }, + { FRAC_CONST(0.975533794518291), FRAC_CONST(0.219849529798779) }, + { FRAC_CONST(0.974166459015280), FRAC_CONST(0.225831154028026) }, + { FRAC_CONST(0.972762446695689), FRAC_CONST(0.231804275841965) }, + { FRAC_CONST(0.971321810419786), FRAC_CONST(0.237768670355934) }, + { FRAC_CONST(0.969844604426715), FRAC_CONST(0.243724113013852) }, + { FRAC_CONST(0.968330884332445), FRAC_CONST(0.249670379596669) }, + { FRAC_CONST(0.966780707127683), FRAC_CONST(0.255607246230807) }, + { FRAC_CONST(0.965194131175725), FRAC_CONST(0.261534489396596) }, + { FRAC_CONST(0.963571216210257), FRAC_CONST(0.267451885936678) }, + { FRAC_CONST(0.961912023333112), FRAC_CONST(0.273359213064419) }, + { FRAC_CONST(0.960216615011963), FRAC_CONST(0.279256248372291) }, + { FRAC_CONST(0.958485055077976), FRAC_CONST(0.285142769840249) }, + { FRAC_CONST(0.956717408723403), FRAC_CONST(0.291018555844085) }, + { FRAC_CONST(0.954913742499131), FRAC_CONST(0.296883385163778) }, + { FRAC_CONST(0.953074124312172), FRAC_CONST(0.302737036991819) }, + { FRAC_CONST(0.951198623423113), FRAC_CONST(0.308579290941525) }, + { FRAC_CONST(0.949287310443502), FRAC_CONST(0.314409927055337) }, + { FRAC_CONST(0.947340257333192), FRAC_CONST(0.320228725813100) }, + { FRAC_CONST(0.945357537397632), FRAC_CONST(0.326035468140330) }, + { FRAC_CONST(0.943339225285108), FRAC_CONST(0.331829935416461) }, + { FRAC_CONST(0.941285396983929), FRAC_CONST(0.337611909483075) }, + { FRAC_CONST(0.939196129819570), FRAC_CONST(0.343381172652115) }, + { FRAC_CONST(0.937071502451759), FRAC_CONST(0.349137507714085) }, + { FRAC_CONST(0.934911594871516), FRAC_CONST(0.354880697946223) }, + { FRAC_CONST(0.932716488398140), FRAC_CONST(0.360610527120662) }, + { FRAC_CONST(0.930486265676150), FRAC_CONST(0.366326779512574) }, + { FRAC_CONST(0.928221010672169), FRAC_CONST(0.372029239908285) }, + { FRAC_CONST(0.925920808671770), FRAC_CONST(0.377717693613386) }, + { FRAC_CONST(0.923585746276257), FRAC_CONST(0.383391926460809) }, + { FRAC_CONST(0.921215911399409), FRAC_CONST(0.389051724818894) }, + { FRAC_CONST(0.918811393264170), FRAC_CONST(0.394696875599434) }, + { FRAC_CONST(0.916372282399289), FRAC_CONST(0.400327166265690) }, + { FRAC_CONST(0.913898670635912), FRAC_CONST(0.405942384840403) }, + { FRAC_CONST(0.911390651104122), FRAC_CONST(0.411542319913765) }, + { FRAC_CONST(0.908848318229439), FRAC_CONST(0.417126760651388) }, + { FRAC_CONST(0.906271767729258), FRAC_CONST(0.422695496802233) }, + { FRAC_CONST(0.903661096609248), FRAC_CONST(0.428248318706532) }, + { FRAC_CONST(0.901016403159702), FRAC_CONST(0.433785017303679) }, + { FRAC_CONST(0.898337786951834), FRAC_CONST(0.439305384140100) }, + { FRAC_CONST(0.895625348834030), FRAC_CONST(0.444809211377105) }, + { FRAC_CONST(0.892879190928052), FRAC_CONST(0.450296291798709) }, + { FRAC_CONST(0.890099416625192), FRAC_CONST(0.455766418819435) }, + { FRAC_CONST(0.887286130582383), FRAC_CONST(0.461219386492092) }, + { FRAC_CONST(0.884439438718254), FRAC_CONST(0.466654989515531) }, + { FRAC_CONST(0.881559448209144), FRAC_CONST(0.472073023242369) }, + { FRAC_CONST(0.878646267485068), FRAC_CONST(0.477473283686698) }, + { FRAC_CONST(0.875700006225635), FRAC_CONST(0.482855567531766) }, + { FRAC_CONST(0.872720775355914), FRAC_CONST(0.488219672137627) }, + { FRAC_CONST(0.869708687042266), FRAC_CONST(0.493565395548775) }, + { FRAC_CONST(0.866663854688111), FRAC_CONST(0.498892536501745) }, + { FRAC_CONST(0.863586392929668), FRAC_CONST(0.504200894432690) }, + { FRAC_CONST(0.860476417631632), FRAC_CONST(0.509490269484936) }, + { FRAC_CONST(0.857334045882816), FRAC_CONST(0.514760462516501) }, + { FRAC_CONST(0.854159395991739), FRAC_CONST(0.520011275107596) }, + { FRAC_CONST(0.850952587482176), FRAC_CONST(0.525242509568095) }, + { FRAC_CONST(0.847713741088654), FRAC_CONST(0.530453968944976) }, + { FRAC_CONST(0.844442978751911), FRAC_CONST(0.535645457029741) }, + { FRAC_CONST(0.841140423614298), FRAC_CONST(0.540816778365797) }, + { FRAC_CONST(0.837806200015151), FRAC_CONST(0.545967738255818) }, + { FRAC_CONST(0.834440433486103), FRAC_CONST(0.551098142769075) }, + { FRAC_CONST(0.831043250746362), FRAC_CONST(0.556207798748740) }, + { FRAC_CONST(0.827614779697938), FRAC_CONST(0.561296513819151) }, + { FRAC_CONST(0.824155149420829), FRAC_CONST(0.566364096393064) }, + { FRAC_CONST(0.820664490168157), FRAC_CONST(0.571410355678857) }, + { FRAC_CONST(0.817142933361273), FRAC_CONST(0.576435101687722) }, + { FRAC_CONST(0.813590611584799), FRAC_CONST(0.581438145240810) }, + { FRAC_CONST(0.810007658581641), FRAC_CONST(0.586419297976361) }, + { FRAC_CONST(0.806394209247956), FRAC_CONST(0.591378372356788) }, + { FRAC_CONST(0.802750399628069), FRAC_CONST(0.596315181675744) }, + { FRAC_CONST(0.799076366909352), FRAC_CONST(0.601229540065149) }, + { FRAC_CONST(0.795372249417061), FRAC_CONST(0.606121262502186) }, + { FRAC_CONST(0.791638186609126), FRAC_CONST(0.610990164816272) }, + { FRAC_CONST(0.787874319070900), FRAC_CONST(0.615836063695985) }, + { FRAC_CONST(0.784080788509870), FRAC_CONST(0.620658776695972) }, + { FRAC_CONST(0.780257737750317), FRAC_CONST(0.625458122243814) }, + { FRAC_CONST(0.776405310727940), FRAC_CONST(0.630233919646864) }, + { FRAC_CONST(0.772523652484441), FRAC_CONST(0.634985989099049) }, + { FRAC_CONST(0.768612909162058), FRAC_CONST(0.639714151687640) }, + { FRAC_CONST(0.764673227998067), FRAC_CONST(0.644418229399988) }, + { FRAC_CONST(0.760704757319237), FRAC_CONST(0.649098045130226) }, + { FRAC_CONST(0.756707646536246), FRAC_CONST(0.653753422685936) }, + { FRAC_CONST(0.752682046138055), FRAC_CONST(0.658384186794785) }, + { FRAC_CONST(0.748628107686245), FRAC_CONST(0.662990163111121) }, + { FRAC_CONST(0.744545983809307), FRAC_CONST(0.667571178222540) }, + { FRAC_CONST(0.740435828196898), FRAC_CONST(0.672127059656412) }, + { FRAC_CONST(0.736297795594053), FRAC_CONST(0.676657635886375) }, + { FRAC_CONST(0.732132041795361), FRAC_CONST(0.681162736338795) }, + { FRAC_CONST(0.727938723639099), FRAC_CONST(0.685642191399187) }, + { FRAC_CONST(0.723717999001324), FRAC_CONST(0.690095832418600) }, + { FRAC_CONST(0.719470026789933), FRAC_CONST(0.694523491719966) }, + { FRAC_CONST(0.715194966938680), FRAC_CONST(0.698925002604414) }, + { FRAC_CONST(0.710892980401152), FRAC_CONST(0.703300199357549) }, + { FRAC_CONST(0.706564229144710), FRAC_CONST(0.707648917255684) }, + { FRAC_CONST(0.702208876144392), FRAC_CONST(0.711970992572050) }, + { FRAC_CONST(0.697827085376777), FRAC_CONST(0.716266262582953) }, + { FRAC_CONST(0.693419021813812), FRAC_CONST(0.720534565573905) }, + { FRAC_CONST(0.688984851416597), FRAC_CONST(0.724775740845711) }, + { FRAC_CONST(0.684524741129142), FRAC_CONST(0.728989628720519) }, + { FRAC_CONST(0.680038858872079), FRAC_CONST(0.733176070547833) }, + { FRAC_CONST(0.675527373536339), FRAC_CONST(0.737334908710483) }, + { FRAC_CONST(0.670990454976794), FRAC_CONST(0.741465986630563) }, + { FRAC_CONST(0.666428274005865), FRAC_CONST(0.745569148775325) }, + { FRAC_CONST(0.661841002387087), FRAC_CONST(0.749644240663033) }, + { FRAC_CONST(0.657228812828643), FRAC_CONST(0.753691108868781) }, + { FRAC_CONST(0.652591878976863), FRAC_CONST(0.757709601030268) }, + { FRAC_CONST(0.647930375409685), FRAC_CONST(0.761699565853535) }, + { FRAC_CONST(0.643244477630086), FRAC_CONST(0.765660853118662) }, + { FRAC_CONST(0.638534362059467), FRAC_CONST(0.769593313685423) }, + { FRAC_CONST(0.633800206031017), FRAC_CONST(0.773496799498899) }, + { FRAC_CONST(0.629042187783036), FRAC_CONST(0.777371163595056) }, + { FRAC_CONST(0.624260486452221), FRAC_CONST(0.781216260106276) }, + { FRAC_CONST(0.619455282066924), FRAC_CONST(0.785031944266848) }, + { FRAC_CONST(0.614626755540375), FRAC_CONST(0.788818072418420) }, + { FRAC_CONST(0.609775088663868), FRAC_CONST(0.792574502015408) }, + { FRAC_CONST(0.604900464099920), FRAC_CONST(0.796301091630359) }, + { FRAC_CONST(0.600003065375389), FRAC_CONST(0.799997700959282) }, + { FRAC_CONST(0.595083076874570), FRAC_CONST(0.803664190826924) }, + { FRAC_CONST(0.590140683832249), FRAC_CONST(0.807300423192014) }, + { FRAC_CONST(0.585176072326730), FRAC_CONST(0.810906261152460) }, + { FRAC_CONST(0.580189429272832), FRAC_CONST(0.814481568950499) }, + { FRAC_CONST(0.575180942414845), FRAC_CONST(0.818026211977813) }, + { FRAC_CONST(0.570150800319470), FRAC_CONST(0.821540056780598) }, + { FRAC_CONST(0.565099192368714), FRAC_CONST(0.825022971064580) }, + { FRAC_CONST(0.560026308752760), FRAC_CONST(0.828474823700007) }, + { FRAC_CONST(0.554932340462810), FRAC_CONST(0.831895484726578) }, + { FRAC_CONST(0.549817479283891), FRAC_CONST(0.835284825358337) }, + { FRAC_CONST(0.544681917787635), FRAC_CONST(0.838642717988527) }, + { FRAC_CONST(0.539525849325029), FRAC_CONST(0.841969036194388) }, + { FRAC_CONST(0.534349468019138), FRAC_CONST(0.845263654741918) }, + { FRAC_CONST(0.529152968757791), FRAC_CONST(0.848526449590593) }, + { FRAC_CONST(0.523936547186249), FRAC_CONST(0.851757297898029) }, + { FRAC_CONST(0.518700399699835), FRAC_CONST(0.854956078024615) }, + { FRAC_CONST(0.513444723436544), FRAC_CONST(0.858122669538086) }, + { FRAC_CONST(0.508169716269615), FRAC_CONST(0.861256953218062) }, + { FRAC_CONST(0.502875576800087), FRAC_CONST(0.864358811060534) }, + { FRAC_CONST(0.497562504349319), FRAC_CONST(0.867428126282307) }, + { FRAC_CONST(0.492230698951486), FRAC_CONST(0.870464783325398) }, + { FRAC_CONST(0.486880361346047), FRAC_CONST(0.873468667861385) }, + { FRAC_CONST(0.481511692970190), FRAC_CONST(0.876439666795714) }, + { FRAC_CONST(0.476124895951244), FRAC_CONST(0.879377668271953) }, + { FRAC_CONST(0.470720173099072), FRAC_CONST(0.882282561676009) }, + { FRAC_CONST(0.465297727898435), FRAC_CONST(0.885154237640285) }, + { FRAC_CONST(0.459857764501330), FRAC_CONST(0.887992588047806) }, + { FRAC_CONST(0.454400487719304), FRAC_CONST(0.890797506036281) }, + { FRAC_CONST(0.448926103015743), FRAC_CONST(0.893568886002136) }, + { FRAC_CONST(0.443434816498138), FRAC_CONST(0.896306623604480) }, + { FRAC_CONST(0.437926834910323), FRAC_CONST(0.899010615769039) }, + { FRAC_CONST(0.432402365624690), FRAC_CONST(0.901680760692038) }, + { FRAC_CONST(0.426861616634386), FRAC_CONST(0.904316957844028) }, + { FRAC_CONST(0.421304796545480), FRAC_CONST(0.906919107973678) }, + { FRAC_CONST(0.415732114569105), FRAC_CONST(0.909487113111505) }, + { FRAC_CONST(0.410143780513590), FRAC_CONST(0.912020876573568) }, + { FRAC_CONST(0.404540004776553), FRAC_CONST(0.914520302965104) }, + { FRAC_CONST(0.398920998336983), FRAC_CONST(0.916985298184123) }, + { FRAC_CONST(0.393286972747297), FRAC_CONST(0.919415769424947) }, + { FRAC_CONST(0.387638140125373), FRAC_CONST(0.921811625181708) }, + { FRAC_CONST(0.381974713146567), FRAC_CONST(0.924172775251791) }, + { FRAC_CONST(0.376296905035705), FRAC_CONST(0.926499130739231) }, + { FRAC_CONST(0.370604929559052), FRAC_CONST(0.928790604058057) }, + { FRAC_CONST(0.364899001016267), FRAC_CONST(0.931047108935595) }, + { FRAC_CONST(0.359179334232337), FRAC_CONST(0.933268560415712) }, + { FRAC_CONST(0.353446144549481), FRAC_CONST(0.935454874862015) }, + { FRAC_CONST(0.347699647819051), FRAC_CONST(0.937605969961000) }, + { FRAC_CONST(0.341940060393402), FRAC_CONST(0.939721764725153) }, + { FRAC_CONST(0.336167599117745), FRAC_CONST(0.941802179495998) }, + { FRAC_CONST(0.330382481321983), FRAC_CONST(0.943847135947093) }, + { FRAC_CONST(0.324584924812532), FRAC_CONST(0.945856557086984) }, + { FRAC_CONST(0.318775147864118), FRAC_CONST(0.947830367262101) }, + { FRAC_CONST(0.312953369211560), FRAC_CONST(0.949768492159607) }, + { FRAC_CONST(0.307119808041533), FRAC_CONST(0.951670858810194) }, + { FRAC_CONST(0.301274683984318), FRAC_CONST(0.953537395590833) }, + { FRAC_CONST(0.295418217105532), FRAC_CONST(0.955368032227470) }, + { FRAC_CONST(0.289550627897843), FRAC_CONST(0.957162699797670) }, + { FRAC_CONST(0.283672137272669), FRAC_CONST(0.958921330733213) }, + { FRAC_CONST(0.277782966551858), FRAC_CONST(0.960643858822638) }, + { FRAC_CONST(0.271883337459360), FRAC_CONST(0.962330219213737) }, + { FRAC_CONST(0.265973472112876), FRAC_CONST(0.963980348415994) }, + { FRAC_CONST(0.260053593015495), FRAC_CONST(0.965594184302977) }, + { FRAC_CONST(0.254123923047321), FRAC_CONST(0.967171666114677) }, + { FRAC_CONST(0.248184685457075), FRAC_CONST(0.968712734459795) }, + { FRAC_CONST(0.242236103853696), FRAC_CONST(0.970217331317979) }, + { FRAC_CONST(0.236278402197920), FRAC_CONST(0.971685400042009) }, + { FRAC_CONST(0.230311804793846), FRAC_CONST(0.973116885359925) }, + { FRAC_CONST(0.224336536280494), FRAC_CONST(0.974511733377116) }, + { FRAC_CONST(0.218352821623346), FRAC_CONST(0.975869891578341) }, + { FRAC_CONST(0.212360886105879), FRAC_CONST(0.977191308829712) }, + { FRAC_CONST(0.206360955321076), FRAC_CONST(0.978475935380617) }, + { FRAC_CONST(0.200353255162940), FRAC_CONST(0.979723722865591) }, + { FRAC_CONST(0.194338011817989), FRAC_CONST(0.980934624306142) }, + { FRAC_CONST(0.188315451756732), FRAC_CONST(0.982108594112514) }, + { FRAC_CONST(0.182285801725153), FRAC_CONST(0.983245588085407) }, + { FRAC_CONST(0.176249288736168), FRAC_CONST(0.984345563417642) }, + { FRAC_CONST(0.170206140061078), FRAC_CONST(0.985408478695768) }, + { FRAC_CONST(0.164156583221016), FRAC_CONST(0.986434293901627) }, + { FRAC_CONST(0.158100845978377), FRAC_CONST(0.987422970413855) }, + { FRAC_CONST(0.152039156328246), FRAC_CONST(0.988374471009341) }, + { FRAC_CONST(0.145971742489812), FRAC_CONST(0.989288759864625) }, + { FRAC_CONST(0.139898832897777), FRAC_CONST(0.990165802557248) }, + { FRAC_CONST(0.133820656193755), FRAC_CONST(0.991005566067049) }, + { FRAC_CONST(0.127737441217662), FRAC_CONST(0.991808018777406) }, + { FRAC_CONST(0.121649416999106), FRAC_CONST(0.992573130476429) }, + { FRAC_CONST(0.115556812748755), FRAC_CONST(0.993300872358093) }, + { FRAC_CONST(0.109459857849718), FRAC_CONST(0.993991217023329) }, + { FRAC_CONST(0.103358781848900), FRAC_CONST(0.994644138481051) }, + { FRAC_CONST(0.097253814448363), FRAC_CONST(0.995259612149133) }, + { FRAC_CONST(0.091145185496681), FRAC_CONST(0.995837614855342) }, + { FRAC_CONST(0.085033124980280), FRAC_CONST(0.996378124838200) }, + { FRAC_CONST(0.078917863014785), FRAC_CONST(0.996881121747814) }, + { FRAC_CONST(0.072799629836352), FRAC_CONST(0.997346586646633) }, + { FRAC_CONST(0.066678655793002), FRAC_CONST(0.997774502010168) }, + { FRAC_CONST(0.060555171335948), FRAC_CONST(0.998164851727646) }, + { FRAC_CONST(0.054429407010919), FRAC_CONST(0.998517621102622) }, + { FRAC_CONST(0.048301593449480), FRAC_CONST(0.998832796853528) }, + { FRAC_CONST(0.042171961360348), FRAC_CONST(0.999110367114175) }, + { FRAC_CONST(0.036040741520706), FRAC_CONST(0.999350321434199) }, + { FRAC_CONST(0.029908164767517), FRAC_CONST(0.999552650779457) }, + { FRAC_CONST(0.023774461988828), FRAC_CONST(0.999717347532362) }, + { FRAC_CONST(0.017639864115082), FRAC_CONST(0.999844405492175) }, + { FRAC_CONST(0.011504602110423), FRAC_CONST(0.999933819875236) }, + { FRAC_CONST(0.005368906963996), FRAC_CONST(0.999985587315143) } +}; +#endif // LD_DEC + +#ifdef ALLOW_SMALL_FRAMELENGTH +/* 480 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_1920[] = +{ + { FRAC_CONST(0.999999916334328), FRAC_CONST(0.000409061532028) }, + { FRAC_CONST(0.999993223088129), FRAC_CONST(0.003681545574400) }, + { FRAC_CONST(0.999975820717897), FRAC_CONST(0.006953990190376) }, + { FRAC_CONST(0.999947709409999), FRAC_CONST(0.010226360334704) }, + { FRAC_CONST(0.999908889465485), FRAC_CONST(0.013498620962929) }, + { FRAC_CONST(0.999859361300084), FRAC_CONST(0.016770737031768) }, + { FRAC_CONST(0.999799125444203), FRAC_CONST(0.020042673499487) }, + { FRAC_CONST(0.999728182542920), FRAC_CONST(0.023314395326274) }, + { FRAC_CONST(0.999646533355977), FRAC_CONST(0.026585867474619) }, + { FRAC_CONST(0.999554178757770), FRAC_CONST(0.029857054909681) }, + { FRAC_CONST(0.999451119737344), FRAC_CONST(0.033127922599673) }, + { FRAC_CONST(0.999337357398377), FRAC_CONST(0.036398435516228) }, + { FRAC_CONST(0.999212892959173), FRAC_CONST(0.039668558634781) }, + { FRAC_CONST(0.999077727752645), FRAC_CONST(0.042938256934941) }, + { FRAC_CONST(0.998931863226306), FRAC_CONST(0.046207495400865) }, + { FRAC_CONST(0.998775300942246), FRAC_CONST(0.049476239021636) }, + { FRAC_CONST(0.998608042577122), FRAC_CONST(0.052744452791636) }, + { FRAC_CONST(0.998430089922136), FRAC_CONST(0.056012101710921) }, + { FRAC_CONST(0.998241444883019), FRAC_CONST(0.059279150785597) }, + { FRAC_CONST(0.998042109480008), FRAC_CONST(0.062545565028192) }, + { FRAC_CONST(0.997832085847824), FRAC_CONST(0.065811309458034) }, + { FRAC_CONST(0.997611376235651), FRAC_CONST(0.069076349101624) }, + { FRAC_CONST(0.997379983007114), FRAC_CONST(0.072340648993011) }, + { FRAC_CONST(0.997137908640245), FRAC_CONST(0.075604174174166) }, + { FRAC_CONST(0.996885155727469), FRAC_CONST(0.078866889695354) }, + { FRAC_CONST(0.996621726975566), FRAC_CONST(0.082128760615515) }, + { FRAC_CONST(0.996347625205645), FRAC_CONST(0.085389752002632) }, + { FRAC_CONST(0.996062853353117), FRAC_CONST(0.088649828934106) }, + { FRAC_CONST(0.995767414467660), FRAC_CONST(0.091908956497133) }, + { FRAC_CONST(0.995461311713186), FRAC_CONST(0.095167099789075) }, + { FRAC_CONST(0.995144548367810), FRAC_CONST(0.098424223917834) }, + { FRAC_CONST(0.994817127823813), FRAC_CONST(0.101680294002229) }, + { FRAC_CONST(0.994479053587606), FRAC_CONST(0.104935275172364) }, + { FRAC_CONST(0.994130329279692), FRAC_CONST(0.108189132570007) }, + { FRAC_CONST(0.993770958634630), FRAC_CONST(0.111441831348957) }, + { FRAC_CONST(0.993400945500988), FRAC_CONST(0.114693336675426) }, + { FRAC_CONST(0.993020293841312), FRAC_CONST(0.117943613728403) }, + { FRAC_CONST(0.992629007732074), FRAC_CONST(0.121192627700032) }, + { FRAC_CONST(0.992227091363634), FRAC_CONST(0.124440343795983) }, + { FRAC_CONST(0.991814549040194), FRAC_CONST(0.127686727235827) }, + { FRAC_CONST(0.991391385179751), FRAC_CONST(0.130931743253405) }, + { FRAC_CONST(0.990957604314048), FRAC_CONST(0.134175357097202) }, + { FRAC_CONST(0.990513211088533), FRAC_CONST(0.137417534030720) }, + { FRAC_CONST(0.990058210262297), FRAC_CONST(0.140658239332849) }, + { FRAC_CONST(0.989592606708036), FRAC_CONST(0.143897438298239) }, + { FRAC_CONST(0.989116405411988), FRAC_CONST(0.147135096237670) }, + { FRAC_CONST(0.988629611473887), FRAC_CONST(0.150371178478428) }, + { FRAC_CONST(0.988132230106905), FRAC_CONST(0.153605650364672) }, + { FRAC_CONST(0.987624266637598), FRAC_CONST(0.156838477257806) }, + { FRAC_CONST(0.987105726505845), FRAC_CONST(0.160069624536852) }, + { FRAC_CONST(0.986576615264794), FRAC_CONST(0.163299057598817) }, + { FRAC_CONST(0.986036938580803), FRAC_CONST(0.166526741859069) }, + { FRAC_CONST(0.985486702233375), FRAC_CONST(0.169752642751702) }, + { FRAC_CONST(0.984925912115099), FRAC_CONST(0.172976725729910) }, + { FRAC_CONST(0.984354574231587), FRAC_CONST(0.176198956266353) }, + { FRAC_CONST(0.983772694701407), FRAC_CONST(0.179419299853531) }, + { FRAC_CONST(0.983180279756024), FRAC_CONST(0.182637722004152) }, + { FRAC_CONST(0.982577335739725), FRAC_CONST(0.185854188251500) }, + { FRAC_CONST(0.981963869109555), FRAC_CONST(0.189068664149806) }, + { FRAC_CONST(0.981339886435250), FRAC_CONST(0.192281115274616) }, + { FRAC_CONST(0.980705394399163), FRAC_CONST(0.195491507223158) }, + { FRAC_CONST(0.980060399796194), FRAC_CONST(0.198699805614714) }, + { FRAC_CONST(0.979404909533716), FRAC_CONST(0.201905976090986) }, + { FRAC_CONST(0.978738930631504), FRAC_CONST(0.205109984316464) }, + { FRAC_CONST(0.978062470221657), FRAC_CONST(0.208311795978794) }, + { FRAC_CONST(0.977375535548522), FRAC_CONST(0.211511376789145) }, + { FRAC_CONST(0.976678133968618), FRAC_CONST(0.214708692482577) }, + { FRAC_CONST(0.975970272950556), FRAC_CONST(0.217903708818409) }, + { FRAC_CONST(0.975251960074958), FRAC_CONST(0.221096391580581) }, + { FRAC_CONST(0.974523203034377), FRAC_CONST(0.224286706578026) }, + { FRAC_CONST(0.973784009633218), FRAC_CONST(0.227474619645035) }, + { FRAC_CONST(0.973034387787646), FRAC_CONST(0.230660096641619) }, + { FRAC_CONST(0.972274345525510), FRAC_CONST(0.233843103453878) }, + { FRAC_CONST(0.971503890986252), FRAC_CONST(0.237023605994367) }, + { FRAC_CONST(0.970723032420820), FRAC_CONST(0.240201570202459) }, + { FRAC_CONST(0.969931778191584), FRAC_CONST(0.243376962044711) }, + { FRAC_CONST(0.969130136772239), FRAC_CONST(0.246549747515226) }, + { FRAC_CONST(0.968318116747721), FRAC_CONST(0.249719892636022) }, + { FRAC_CONST(0.967495726814114), FRAC_CONST(0.252887363457390) }, + { FRAC_CONST(0.966662975778551), FRAC_CONST(0.256052126058264) }, + { FRAC_CONST(0.965819872559127), FRAC_CONST(0.259214146546579) }, + { FRAC_CONST(0.964966426184802), FRAC_CONST(0.262373391059634) }, + { FRAC_CONST(0.964102645795299), FRAC_CONST(0.265529825764461) }, + { FRAC_CONST(0.963228540641012), FRAC_CONST(0.268683416858178) }, + { FRAC_CONST(0.962344120082907), FRAC_CONST(0.271834130568359) }, + { FRAC_CONST(0.961449393592416), FRAC_CONST(0.274981933153391) }, + { FRAC_CONST(0.960544370751341), FRAC_CONST(0.278126790902837) }, + { FRAC_CONST(0.959629061251750), FRAC_CONST(0.281268670137799) }, + { FRAC_CONST(0.958703474895872), FRAC_CONST(0.284407537211272) }, + { FRAC_CONST(0.957767621595993), FRAC_CONST(0.287543358508512) }, + { FRAC_CONST(0.956821511374351), FRAC_CONST(0.290676100447394) }, + { FRAC_CONST(0.955865154363025), FRAC_CONST(0.293805729478766) }, + { FRAC_CONST(0.954898560803832), FRAC_CONST(0.296932212086818) }, + { FRAC_CONST(0.953921741048211), FRAC_CONST(0.300055514789431) }, + { FRAC_CONST(0.952934705557117), FRAC_CONST(0.303175604138543) }, + { FRAC_CONST(0.951937464900908), FRAC_CONST(0.306292446720504) }, + { FRAC_CONST(0.950930029759229), FRAC_CONST(0.309406009156434) }, + { FRAC_CONST(0.949912410920903), FRAC_CONST(0.312516258102580) }, + { FRAC_CONST(0.948884619283808), FRAC_CONST(0.315623160250676) }, + { FRAC_CONST(0.947846665854767), FRAC_CONST(0.318726682328294) }, + { FRAC_CONST(0.946798561749429), FRAC_CONST(0.321826791099207) }, + { FRAC_CONST(0.945740318192145), FRAC_CONST(0.324923453363742) }, + { FRAC_CONST(0.944671946515855), FRAC_CONST(0.328016635959131) }, + { FRAC_CONST(0.943593458161960), FRAC_CONST(0.331106305759876) }, + { FRAC_CONST(0.942504864680205), FRAC_CONST(0.334192429678095) }, + { FRAC_CONST(0.941406177728551), FRAC_CONST(0.337274974663880) }, + { FRAC_CONST(0.940297409073052), FRAC_CONST(0.340353907705650) }, + { FRAC_CONST(0.939178570587730), FRAC_CONST(0.343429195830507) }, + { FRAC_CONST(0.938049674254446), FRAC_CONST(0.346500806104585) }, + { FRAC_CONST(0.936910732162774), FRAC_CONST(0.349568705633406) }, + { FRAC_CONST(0.935761756509868), FRAC_CONST(0.352632861562230) }, + { FRAC_CONST(0.934602759600334), FRAC_CONST(0.355693241076410) }, + { FRAC_CONST(0.933433753846097), FRAC_CONST(0.358749811401739) }, + { FRAC_CONST(0.932254751766271), FRAC_CONST(0.361802539804806) }, + { FRAC_CONST(0.931065765987021), FRAC_CONST(0.364851393593340) }, + { FRAC_CONST(0.929866809241428), FRAC_CONST(0.367896340116568) }, + { FRAC_CONST(0.928657894369357), FRAC_CONST(0.370937346765559) }, + { FRAC_CONST(0.927439034317314), FRAC_CONST(0.373974380973575) }, + { FRAC_CONST(0.926210242138311), FRAC_CONST(0.377007410216418) }, + { FRAC_CONST(0.924971530991726), FRAC_CONST(0.380036402012783) }, + { FRAC_CONST(0.923722914143160), FRAC_CONST(0.383061323924602) }, + { FRAC_CONST(0.922464404964295), FRAC_CONST(0.386082143557389) }, + { FRAC_CONST(0.921196016932755), FRAC_CONST(0.389098828560595) }, + { FRAC_CONST(0.919917763631956), FRAC_CONST(0.392111346627946) }, + { FRAC_CONST(0.918629658750963), FRAC_CONST(0.395119665497795) }, + { FRAC_CONST(0.917331716084346), FRAC_CONST(0.398123752953462) }, + { FRAC_CONST(0.916023949532027), FRAC_CONST(0.401123576823585) }, + { FRAC_CONST(0.914706373099136), FRAC_CONST(0.404119104982459) }, + { FRAC_CONST(0.913379000895858), FRAC_CONST(0.407110305350386) }, + { FRAC_CONST(0.912041847137282), FRAC_CONST(0.410097145894012) }, + { FRAC_CONST(0.910694926143251), FRAC_CONST(0.413079594626675) }, + { FRAC_CONST(0.909338252338207), FRAC_CONST(0.416057619608744) }, + { FRAC_CONST(0.907971840251037), FRAC_CONST(0.419031188947965) }, + { FRAC_CONST(0.906595704514915), FRAC_CONST(0.422000270799800) }, + { FRAC_CONST(0.905209859867151), FRAC_CONST(0.424964833367766) }, + { FRAC_CONST(0.903814321149027), FRAC_CONST(0.427924844903780) }, + { FRAC_CONST(0.902409103305641), FRAC_CONST(0.430880273708497) }, + { FRAC_CONST(0.900994221385748), FRAC_CONST(0.433831088131649) }, + { FRAC_CONST(0.899569690541596), FRAC_CONST(0.436777256572384) }, + { FRAC_CONST(0.898135526028766), FRAC_CONST(0.439718747479604) }, + { FRAC_CONST(0.896691743206008), FRAC_CONST(0.442655529352306) }, + { FRAC_CONST(0.895238357535076), FRAC_CONST(0.445587570739915) }, + { FRAC_CONST(0.893775384580563), FRAC_CONST(0.448514840242624) }, + { FRAC_CONST(0.892302840009734), FRAC_CONST(0.451437306511726) }, + { FRAC_CONST(0.890820739592359), FRAC_CONST(0.454354938249958) }, + { FRAC_CONST(0.889329099200541), FRAC_CONST(0.457267704211826) }, + { FRAC_CONST(0.887827934808551), FRAC_CONST(0.460175573203949) }, + { FRAC_CONST(0.886317262492655), FRAC_CONST(0.463078514085383) }, + { FRAC_CONST(0.884797098430938), FRAC_CONST(0.465976495767966) }, + { FRAC_CONST(0.883267458903136), FRAC_CONST(0.468869487216642) }, + { FRAC_CONST(0.881728360290461), FRAC_CONST(0.471757457449795) }, + { FRAC_CONST(0.880179819075421), FRAC_CONST(0.474640375539586) }, + { FRAC_CONST(0.878621851841649), FRAC_CONST(0.477518210612278) }, + { FRAC_CONST(0.877054475273722), FRAC_CONST(0.480390931848569) }, + { FRAC_CONST(0.875477706156984), FRAC_CONST(0.483258508483922) }, + { FRAC_CONST(0.873891561377366), FRAC_CONST(0.486120909808896) }, + { FRAC_CONST(0.872296057921204), FRAC_CONST(0.488978105169472) }, + { FRAC_CONST(0.870691212875058), FRAC_CONST(0.491830063967383) }, + { FRAC_CONST(0.869077043425529), FRAC_CONST(0.494676755660442) }, + { FRAC_CONST(0.867453566859076), FRAC_CONST(0.497518149762867) }, + { FRAC_CONST(0.865820800561827), FRAC_CONST(0.500354215845611) }, + { FRAC_CONST(0.864178762019399), FRAC_CONST(0.503184923536685) }, + { FRAC_CONST(0.862527468816704), FRAC_CONST(0.506010242521482) }, + { FRAC_CONST(0.860866938637767), FRAC_CONST(0.508830142543107) }, + { FRAC_CONST(0.859197189265532), FRAC_CONST(0.511644593402696) }, + { FRAC_CONST(0.857518238581672), FRAC_CONST(0.514453564959741) }, + { FRAC_CONST(0.855830104566401), FRAC_CONST(0.517257027132414) }, + { FRAC_CONST(0.854132805298278), FRAC_CONST(0.520054949897887) }, + { FRAC_CONST(0.852426358954015), FRAC_CONST(0.522847303292655) }, + { FRAC_CONST(0.850710783808280), FRAC_CONST(0.525634057412856) }, + { FRAC_CONST(0.848986098233506), FRAC_CONST(0.528415182414593) }, + { FRAC_CONST(0.847252320699689), FRAC_CONST(0.531190648514252) }, + { FRAC_CONST(0.845509469774194), FRAC_CONST(0.533960425988819) }, + { FRAC_CONST(0.843757564121554), FRAC_CONST(0.536724485176205) }, + { FRAC_CONST(0.841996622503271), FRAC_CONST(0.539482796475555) }, + { FRAC_CONST(0.840226663777615), FRAC_CONST(0.542235330347571) }, + { FRAC_CONST(0.838447706899422), FRAC_CONST(0.544982057314827) }, + { FRAC_CONST(0.836659770919891), FRAC_CONST(0.547722947962084) }, + { FRAC_CONST(0.834862874986380), FRAC_CONST(0.550457972936605) }, + { FRAC_CONST(0.833057038342201), FRAC_CONST(0.553187102948470) }, + { FRAC_CONST(0.831242280326413), FRAC_CONST(0.555910308770889) }, + { FRAC_CONST(0.829418620373617), FRAC_CONST(0.558627561240515) }, + { FRAC_CONST(0.827586078013746), FRAC_CONST(0.561338831257758) }, + { FRAC_CONST(0.825744672871856), FRAC_CONST(0.564044089787093) }, + { FRAC_CONST(0.823894424667918), FRAC_CONST(0.566743307857377) }, + { FRAC_CONST(0.822035353216601), FRAC_CONST(0.569436456562150) }, + { FRAC_CONST(0.820167478427070), FRAC_CONST(0.572123507059955) }, + { FRAC_CONST(0.818290820302761), FRAC_CONST(0.574804430574639) }, + { FRAC_CONST(0.816405398941175), FRAC_CONST(0.577479198395666) }, + { FRAC_CONST(0.814511234533661), FRAC_CONST(0.580147781878420) }, + { FRAC_CONST(0.812608347365198), FRAC_CONST(0.582810152444517) }, + { FRAC_CONST(0.810696757814178), FRAC_CONST(0.585466281582107) }, + { FRAC_CONST(0.808776486352191), FRAC_CONST(0.588116140846181) }, + { FRAC_CONST(0.806847553543799), FRAC_CONST(0.590759701858874) }, + { FRAC_CONST(0.804909980046325), FRAC_CONST(0.593396936309773) }, + { FRAC_CONST(0.802963786609623), FRAC_CONST(0.596027815956215) }, + { FRAC_CONST(0.801008994075862), FRAC_CONST(0.598652312623592) }, + { FRAC_CONST(0.799045623379300), FRAC_CONST(0.601270398205654) }, + { FRAC_CONST(0.797073695546059), FRAC_CONST(0.603882044664808) }, + { FRAC_CONST(0.795093231693901), FRAC_CONST(0.606487224032418) }, + { FRAC_CONST(0.793104253032005), FRAC_CONST(0.609085908409106) }, + { FRAC_CONST(0.791106780860733), FRAC_CONST(0.611678069965050) }, + { FRAC_CONST(0.789100836571407), FRAC_CONST(0.614263680940283) }, + { FRAC_CONST(0.787086441646080), FRAC_CONST(0.616842713644988) }, + { FRAC_CONST(0.785063617657302), FRAC_CONST(0.619415140459796) }, + { FRAC_CONST(0.783032386267894), FRAC_CONST(0.621980933836084) }, + { FRAC_CONST(0.780992769230711), FRAC_CONST(0.624540066296266) }, + { FRAC_CONST(0.778944788388414), FRAC_CONST(0.627092510434089) }, + { FRAC_CONST(0.776888465673232), FRAC_CONST(0.629638238914927) }, + { FRAC_CONST(0.774823823106730), FRAC_CONST(0.632177224476073) }, + { FRAC_CONST(0.772750882799570), FRAC_CONST(0.634709439927031) }, + { FRAC_CONST(0.770669666951277), FRAC_CONST(0.637234858149809) }, + { FRAC_CONST(0.768580197850002), FRAC_CONST(0.639753452099206) }, + { FRAC_CONST(0.766482497872280), FRAC_CONST(0.642265194803105) }, + { FRAC_CONST(0.764376589482793), FRAC_CONST(0.644770059362758) }, + { FRAC_CONST(0.762262495234126), FRAC_CONST(0.647268018953079) }, + { FRAC_CONST(0.760140237766532), FRAC_CONST(0.649759046822928) }, + { FRAC_CONST(0.758009839807683), FRAC_CONST(0.652243116295397) }, + { FRAC_CONST(0.755871324172429), FRAC_CONST(0.654720200768098) }, + { FRAC_CONST(0.753724713762555), FRAC_CONST(0.657190273713446) }, + { FRAC_CONST(0.751570031566534), FRAC_CONST(0.659653308678945) }, + { FRAC_CONST(0.749407300659280), FRAC_CONST(0.662109279287469) }, + { FRAC_CONST(0.747236544201905), FRAC_CONST(0.664558159237545) }, + { FRAC_CONST(0.745057785441466), FRAC_CONST(0.666999922303638) }, + { FRAC_CONST(0.742871047710719), FRAC_CONST(0.669434542336425) }, + { FRAC_CONST(0.740676354427868), FRAC_CONST(0.671861993263083) }, + { FRAC_CONST(0.738473729096316), FRAC_CONST(0.674282249087562) }, + { FRAC_CONST(0.736263195304409), FRAC_CONST(0.676695283890867) }, + { FRAC_CONST(0.734044776725190), FRAC_CONST(0.679101071831334) }, + { FRAC_CONST(0.731818497116138), FRAC_CONST(0.681499587144906) }, + { FRAC_CONST(0.729584380318920), FRAC_CONST(0.683890804145412) }, + { FRAC_CONST(0.727342450259131), FRAC_CONST(0.686274697224838) }, + { FRAC_CONST(0.725092730946042), FRAC_CONST(0.688651240853606) }, + { FRAC_CONST(0.722835246472338), FRAC_CONST(0.691020409580841) }, + { FRAC_CONST(0.720570021013866), FRAC_CONST(0.693382178034651) }, + { FRAC_CONST(0.718297078829369), FRAC_CONST(0.695736520922392) }, + { FRAC_CONST(0.716016444260233), FRAC_CONST(0.698083413030944) }, + { FRAC_CONST(0.713728141730222), FRAC_CONST(0.700422829226978) }, + { FRAC_CONST(0.711432195745216), FRAC_CONST(0.702754744457225) }, + { FRAC_CONST(0.709128630892954), FRAC_CONST(0.705079133748748) }, + { FRAC_CONST(0.706817471842764), FRAC_CONST(0.707395972209203) }, + { FRAC_CONST(0.704498743345302), FRAC_CONST(0.709705235027113) }, + { FRAC_CONST(0.702172470232289), FRAC_CONST(0.712006897472128) }, + { FRAC_CONST(0.699838677416240), FRAC_CONST(0.714300934895292) }, + { FRAC_CONST(0.697497389890200), FRAC_CONST(0.716587322729308) }, + { FRAC_CONST(0.695148632727480), FRAC_CONST(0.718866036488799) }, + { FRAC_CONST(0.692792431081381), FRAC_CONST(0.721137051770570) }, + { FRAC_CONST(0.690428810184929), FRAC_CONST(0.723400344253874) }, + { FRAC_CONST(0.688057795350606), FRAC_CONST(0.725655889700665) }, + { FRAC_CONST(0.685679411970075), FRAC_CONST(0.727903663955865) }, + { FRAC_CONST(0.683293685513912), FRAC_CONST(0.730143642947616) }, + { FRAC_CONST(0.680900641531330), FRAC_CONST(0.732375802687543) }, + { FRAC_CONST(0.678500305649909), FRAC_CONST(0.734600119271009) }, + { FRAC_CONST(0.676092703575316), FRAC_CONST(0.736816568877370) }, + { FRAC_CONST(0.673677861091036), FRAC_CONST(0.739025127770231) }, + { FRAC_CONST(0.671255804058092), FRAC_CONST(0.741225772297702) }, + { FRAC_CONST(0.668826558414768), FRAC_CONST(0.743418478892647) }, + { FRAC_CONST(0.666390150176334), FRAC_CONST(0.745603224072940) }, + { FRAC_CONST(0.663946605434765), FRAC_CONST(0.747779984441716) }, + { FRAC_CONST(0.661495950358462), FRAC_CONST(0.749948736687619) }, + { FRAC_CONST(0.659038211191971), FRAC_CONST(0.752109457585056) }, + { FRAC_CONST(0.656573414255705), FRAC_CONST(0.754262123994441) }, + { FRAC_CONST(0.654101585945659), FRAC_CONST(0.756406712862448) }, + { FRAC_CONST(0.651622752733128), FRAC_CONST(0.758543201222251) }, + { FRAC_CONST(0.649136941164425), FRAC_CONST(0.760671566193777) }, + { FRAC_CONST(0.646644177860593), FRAC_CONST(0.762791784983948) }, + { FRAC_CONST(0.644144489517126), FRAC_CONST(0.764903834886923) }, + { FRAC_CONST(0.641637902903677), FRAC_CONST(0.767007693284345) }, + { FRAC_CONST(0.639124444863776), FRAC_CONST(0.769103337645580) }, + { FRAC_CONST(0.636604142314538), FRAC_CONST(0.771190745527961) }, + { FRAC_CONST(0.634077022246379), FRAC_CONST(0.773269894577026) }, + { FRAC_CONST(0.631543111722725), FRAC_CONST(0.775340762526760) }, + { FRAC_CONST(0.629002437879721), FRAC_CONST(0.777403327199831) }, + { FRAC_CONST(0.626455027925944), FRAC_CONST(0.779457566507828) }, + { FRAC_CONST(0.623900909142107), FRAC_CONST(0.781503458451498) }, + { FRAC_CONST(0.621340108880771), FRAC_CONST(0.783540981120982) }, + { FRAC_CONST(0.618772654566049), FRAC_CONST(0.785570112696050) }, + { FRAC_CONST(0.616198573693314), FRAC_CONST(0.787590831446332) }, + { FRAC_CONST(0.613617893828905), FRAC_CONST(0.789603115731555) }, + { FRAC_CONST(0.611030642609828), FRAC_CONST(0.791606944001769) }, + { FRAC_CONST(0.608436847743468), FRAC_CONST(0.793602294797585) }, + { FRAC_CONST(0.605836537007281), FRAC_CONST(0.795589146750397) }, + { FRAC_CONST(0.603229738248508), FRAC_CONST(0.797567478582619) }, + { FRAC_CONST(0.600616479383869), FRAC_CONST(0.799537269107905) }, + { FRAC_CONST(0.597996788399267), FRAC_CONST(0.801498497231381) }, + { FRAC_CONST(0.595370693349487), FRAC_CONST(0.803451141949871) }, + { FRAC_CONST(0.592738222357898), FRAC_CONST(0.805395182352117) }, + { FRAC_CONST(0.590099403616149), FRAC_CONST(0.807330597619008) }, + { FRAC_CONST(0.587454265383869), FRAC_CONST(0.809257367023803) }, + { FRAC_CONST(0.584802835988364), FRAC_CONST(0.811175469932349) }, + { FRAC_CONST(0.582145143824311), FRAC_CONST(0.813084885803304) }, + { FRAC_CONST(0.579481217353460), FRAC_CONST(0.814985594188359) }, + { FRAC_CONST(0.576811085104321), FRAC_CONST(0.816877574732454) }, + { FRAC_CONST(0.574134775671867), FRAC_CONST(0.818760807173997) }, + { FRAC_CONST(0.571452317717222), FRAC_CONST(0.820635271345081) }, + { FRAC_CONST(0.568763739967354), FRAC_CONST(0.822500947171703) }, + { FRAC_CONST(0.566069071214772), FRAC_CONST(0.824357814673971) }, + { FRAC_CONST(0.563368340317214), FRAC_CONST(0.826205853966327) }, + { FRAC_CONST(0.560661576197336), FRAC_CONST(0.828045045257756) }, + { FRAC_CONST(0.557948807842409), FRAC_CONST(0.829875368851995) }, + { FRAC_CONST(0.555230064304002), FRAC_CONST(0.831696805147750) }, + { FRAC_CONST(0.552505374697674), FRAC_CONST(0.833509334638900) }, + { FRAC_CONST(0.549774768202663), FRAC_CONST(0.835312937914713) }, + { FRAC_CONST(0.547038274061568), FRAC_CONST(0.837107595660044) }, + { FRAC_CONST(0.544295921580046), FRAC_CONST(0.838893288655553) }, + { FRAC_CONST(0.541547740126486), FRAC_CONST(0.840669997777901) }, + { FRAC_CONST(0.538793759131706), FRAC_CONST(0.842437703999961) }, + { FRAC_CONST(0.536034008088628), FRAC_CONST(0.844196388391019) }, + { FRAC_CONST(0.533268516551970), FRAC_CONST(0.845946032116980) }, + { FRAC_CONST(0.530497314137923), FRAC_CONST(0.847686616440563) }, + { FRAC_CONST(0.527720430523840), FRAC_CONST(0.849418122721510) }, + { FRAC_CONST(0.524937895447912), FRAC_CONST(0.851140532416778) }, + { FRAC_CONST(0.522149738708856), FRAC_CONST(0.852853827080745) }, + { FRAC_CONST(0.519355990165590), FRAC_CONST(0.854557988365401) }, + { FRAC_CONST(0.516556679736915), FRAC_CONST(0.856252998020546) }, + { FRAC_CONST(0.513751837401199), FRAC_CONST(0.857938837893991) }, + { FRAC_CONST(0.510941493196049), FRAC_CONST(0.859615489931744) }, + { FRAC_CONST(0.508125677217994), FRAC_CONST(0.861282936178208) }, + { FRAC_CONST(0.505304419622159), FRAC_CONST(0.862941158776375) }, + { FRAC_CONST(0.502477750621949), FRAC_CONST(0.864590139968012) }, + { FRAC_CONST(0.499645700488717), FRAC_CONST(0.866229862093855) }, + { FRAC_CONST(0.496808299551444), FRAC_CONST(0.867860307593799) }, + { FRAC_CONST(0.493965578196415), FRAC_CONST(0.869481459007080) }, + { FRAC_CONST(0.491117566866892), FRAC_CONST(0.871093298972471) }, + { FRAC_CONST(0.488264296062789), FRAC_CONST(0.872695810228461) }, + { FRAC_CONST(0.485405796340343), FRAC_CONST(0.874288975613440) }, + { FRAC_CONST(0.482542098311789), FRAC_CONST(0.875872778065888) }, + { FRAC_CONST(0.479673232645033), FRAC_CONST(0.877447200624553) }, + { FRAC_CONST(0.476799230063322), FRAC_CONST(0.879012226428633) }, + { FRAC_CONST(0.473920121344914), FRAC_CONST(0.880567838717962) }, + { FRAC_CONST(0.471035937322751), FRAC_CONST(0.882114020833179) }, + { FRAC_CONST(0.468146708884125), FRAC_CONST(0.883650756215917) }, + { FRAC_CONST(0.465252466970353), FRAC_CONST(0.885178028408975) }, + { FRAC_CONST(0.462353242576441), FRAC_CONST(0.886695821056495) }, + { FRAC_CONST(0.459449066750752), FRAC_CONST(0.888204117904136) }, + { FRAC_CONST(0.456539970594675), FRAC_CONST(0.889702902799251) }, + { FRAC_CONST(0.453625985262295), FRAC_CONST(0.891192159691058) }, + { FRAC_CONST(0.450707141960053), FRAC_CONST(0.892671872630812) }, + { FRAC_CONST(0.447783471946415), FRAC_CONST(0.894142025771977) }, + { FRAC_CONST(0.444855006531538), FRAC_CONST(0.895602603370393) }, + { FRAC_CONST(0.441921777076935), FRAC_CONST(0.897053589784447) }, + { FRAC_CONST(0.438983814995137), FRAC_CONST(0.898494969475242) }, + { FRAC_CONST(0.436041151749356), FRAC_CONST(0.899926727006758) }, + { FRAC_CONST(0.433093818853152), FRAC_CONST(0.901348847046022) }, + { FRAC_CONST(0.430141847870093), FRAC_CONST(0.902761314363272) }, + { FRAC_CONST(0.427185270413416), FRAC_CONST(0.904164113832116) }, + { FRAC_CONST(0.424224118145690), FRAC_CONST(0.905557230429701) }, + { FRAC_CONST(0.421258422778478), FRAC_CONST(0.906940649236866) }, + { FRAC_CONST(0.418288216071994), FRAC_CONST(0.908314355438308) }, + { FRAC_CONST(0.415313529834766), FRAC_CONST(0.909678334322736) }, + { FRAC_CONST(0.412334395923293), FRAC_CONST(0.911032571283032) }, + { FRAC_CONST(0.409350846241706), FRAC_CONST(0.912377051816407) }, + { FRAC_CONST(0.406362912741425), FRAC_CONST(0.913711761524555) }, + { FRAC_CONST(0.403370627420818), FRAC_CONST(0.915036686113806) }, + { FRAC_CONST(0.400374022324857), FRAC_CONST(0.916351811395282) }, + { FRAC_CONST(0.397373129544774), FRAC_CONST(0.917657123285050) }, + { FRAC_CONST(0.394367981217720), FRAC_CONST(0.918952607804266) }, + { FRAC_CONST(0.391358609526420), FRAC_CONST(0.920238251079332) }, + { FRAC_CONST(0.388345046698826), FRAC_CONST(0.921514039342042) }, + { FRAC_CONST(0.385327325007776), FRAC_CONST(0.922779958929729) }, + { FRAC_CONST(0.382305476770645), FRAC_CONST(0.924035996285410) }, + { FRAC_CONST(0.379279534348999), FRAC_CONST(0.925282137957935) }, + { FRAC_CONST(0.376249530148250), FRAC_CONST(0.926518370602127) }, + { FRAC_CONST(0.373215496617310), FRAC_CONST(0.927744680978929) }, + { FRAC_CONST(0.370177466248239), FRAC_CONST(0.928961055955541) }, + { FRAC_CONST(0.367135471575903), FRAC_CONST(0.930167482505564) }, + { FRAC_CONST(0.364089545177621), FRAC_CONST(0.931363947709140) }, + { FRAC_CONST(0.361039719672816), FRAC_CONST(0.932550438753087) }, + { FRAC_CONST(0.357986027722671), FRAC_CONST(0.933726942931039) }, + { FRAC_CONST(0.354928502029772), FRAC_CONST(0.934893447643582) }, + { FRAC_CONST(0.351867175337763), FRAC_CONST(0.936049940398387) }, + { FRAC_CONST(0.348802080430994), FRAC_CONST(0.937196408810347) }, + { FRAC_CONST(0.345733250134169), FRAC_CONST(0.938332840601705) }, + { FRAC_CONST(0.342660717311994), FRAC_CONST(0.939459223602190) }, + { FRAC_CONST(0.339584514868829), FRAC_CONST(0.940575545749145) }, + { FRAC_CONST(0.336504675748328), FRAC_CONST(0.941681795087657) }, + { FRAC_CONST(0.333421232933097), FRAC_CONST(0.942777959770684) }, + { FRAC_CONST(0.330334219444328), FRAC_CONST(0.943864028059183) }, + { FRAC_CONST(0.327243668341457), FRAC_CONST(0.944939988322235) }, + { FRAC_CONST(0.324149612721804), FRAC_CONST(0.946005829037171) }, + { FRAC_CONST(0.321052085720218), FRAC_CONST(0.947061538789691) }, + { FRAC_CONST(0.317951120508725), FRAC_CONST(0.948107106273994) }, + { FRAC_CONST(0.314846750296171), FRAC_CONST(0.949142520292891) }, + { FRAC_CONST(0.311739008327867), FRAC_CONST(0.950167769757930) }, + { FRAC_CONST(0.308627927885232), FRAC_CONST(0.951182843689513) }, + { FRAC_CONST(0.305513542285440), FRAC_CONST(0.952187731217013) }, + { FRAC_CONST(0.302395884881056), FRAC_CONST(0.953182421578893) }, + { FRAC_CONST(0.299274989059689), FRAC_CONST(0.954166904122818) }, + { FRAC_CONST(0.296150888243624), FRAC_CONST(0.955141168305771) }, + { FRAC_CONST(0.293023615889471), FRAC_CONST(0.956105203694164) }, + { FRAC_CONST(0.289893205487806), FRAC_CONST(0.957058999963955) }, + { FRAC_CONST(0.286759690562807), FRAC_CONST(0.958002546900750) }, + { FRAC_CONST(0.283623104671904), FRAC_CONST(0.958935834399920) }, + { FRAC_CONST(0.280483481405410), FRAC_CONST(0.959858852466706) }, + { FRAC_CONST(0.277340854386169), FRAC_CONST(0.960771591216325) }, + { FRAC_CONST(0.274195257269191), FRAC_CONST(0.961674040874080) }, + { FRAC_CONST(0.271046723741295), FRAC_CONST(0.962566191775459) }, + { FRAC_CONST(0.267895287520743), FRAC_CONST(0.963448034366243) }, + { FRAC_CONST(0.264740982356888), FRAC_CONST(0.964319559202607) }, + { FRAC_CONST(0.261583842029803), FRAC_CONST(0.965180756951218) }, + { FRAC_CONST(0.258423900349924), FRAC_CONST(0.966031618389343) }, + { FRAC_CONST(0.255261191157689), FRAC_CONST(0.966872134404937) }, + { FRAC_CONST(0.252095748323171), FRAC_CONST(0.967702295996750) }, + { FRAC_CONST(0.248927605745720), FRAC_CONST(0.968522094274417) }, + { FRAC_CONST(0.245756797353599), FRAC_CONST(0.969331520458559) }, + { FRAC_CONST(0.242583357103617), FRAC_CONST(0.970130565880871) }, + { FRAC_CONST(0.239407318980770), FRAC_CONST(0.970919221984218) }, + { FRAC_CONST(0.236228716997876), FRAC_CONST(0.971697480322728) }, + { FRAC_CONST(0.233047585195206), FRAC_CONST(0.972465332561878) }, + { FRAC_CONST(0.229863957640129), FRAC_CONST(0.973222770478587) }, + { FRAC_CONST(0.226677868426735), FRAC_CONST(0.973969785961306) }, + { FRAC_CONST(0.223489351675482), FRAC_CONST(0.974706371010097) }, + { FRAC_CONST(0.220298441532823), FRAC_CONST(0.975432517736727) }, + { FRAC_CONST(0.217105172170841), FRAC_CONST(0.976148218364747) }, + { FRAC_CONST(0.213909577786886), FRAC_CONST(0.976853465229579) }, + { FRAC_CONST(0.210711692603206), FRAC_CONST(0.977548250778596) }, + { FRAC_CONST(0.207511550866582), FRAC_CONST(0.978232567571202) }, + { FRAC_CONST(0.204309186847962), FRAC_CONST(0.978906408278914) }, + { FRAC_CONST(0.201104634842092), FRAC_CONST(0.979569765685441) }, + { FRAC_CONST(0.197897929167148), FRAC_CONST(0.980222632686756) }, + { FRAC_CONST(0.194689104164373), FRAC_CONST(0.980865002291179) }, + { FRAC_CONST(0.191478194197704), FRAC_CONST(0.981496867619447) }, + { FRAC_CONST(0.188265233653407), FRAC_CONST(0.982118221904791) }, + { FRAC_CONST(0.185050256939710), FRAC_CONST(0.982729058493005) }, + { FRAC_CONST(0.181833298486427), FRAC_CONST(0.983329370842520) }, + { FRAC_CONST(0.178614392744603), FRAC_CONST(0.983919152524473) }, + { FRAC_CONST(0.175393574186129), FRAC_CONST(0.984498397222776) }, + { FRAC_CONST(0.172170877303385), FRAC_CONST(0.985067098734184) }, + { FRAC_CONST(0.168946336608867), FRAC_CONST(0.985625250968360) }, + { FRAC_CONST(0.165719986634814), FRAC_CONST(0.986172847947943) }, + { FRAC_CONST(0.162491861932842), FRAC_CONST(0.986709883808609) }, + { FRAC_CONST(0.159261997073573), FRAC_CONST(0.987236352799134) }, + { FRAC_CONST(0.156030426646266), FRAC_CONST(0.987752249281460) }, + { FRAC_CONST(0.152797185258443), FRAC_CONST(0.988257567730749) }, + { FRAC_CONST(0.149562307535523), FRAC_CONST(0.988752302735447) }, + { FRAC_CONST(0.146325828120446), FRAC_CONST(0.989236448997339) }, + { FRAC_CONST(0.143087781673307), FRAC_CONST(0.989710001331608) }, + { FRAC_CONST(0.139848202870981), FRAC_CONST(0.990172954666889) }, + { FRAC_CONST(0.136607126406757), FRAC_CONST(0.990625304045323) }, + { FRAC_CONST(0.133364586989957), FRAC_CONST(0.991067044622612) }, + { FRAC_CONST(0.130120619345575), FRAC_CONST(0.991498171668069) }, + { FRAC_CONST(0.126875258213898), FRAC_CONST(0.991918680564670) }, + { FRAC_CONST(0.123628538350136), FRAC_CONST(0.992328566809103) }, + { FRAC_CONST(0.120380494524051), FRAC_CONST(0.992727826011815) }, + { FRAC_CONST(0.117131161519582), FRAC_CONST(0.993116453897061) }, + { FRAC_CONST(0.113880574134475), FRAC_CONST(0.993494446302948) }, + { FRAC_CONST(0.110628767179910), FRAC_CONST(0.993861799181482) }, + { FRAC_CONST(0.107375775480128), FRAC_CONST(0.994218508598608) }, + { FRAC_CONST(0.104121633872055), FRAC_CONST(0.994564570734255) }, + { FRAC_CONST(0.100866377204933), FRAC_CONST(0.994899981882376) }, + { FRAC_CONST(0.097610040339947), FRAC_CONST(0.995224738450986) }, + { FRAC_CONST(0.094352658149849), FRAC_CONST(0.995538836962204) }, + { FRAC_CONST(0.091094265518583), FRAC_CONST(0.995842274052287) }, + { FRAC_CONST(0.087834897340919), FRAC_CONST(0.996135046471667) }, + { FRAC_CONST(0.084574588522070), FRAC_CONST(0.996417151084987) }, + { FRAC_CONST(0.081313373977324), FRAC_CONST(0.996688584871134) }, + { FRAC_CONST(0.078051288631670), FRAC_CONST(0.996949344923269) }, + { FRAC_CONST(0.074788367419420), FRAC_CONST(0.997199428448862) }, + { FRAC_CONST(0.071524645283840), FRAC_CONST(0.997438832769720) }, + { FRAC_CONST(0.068260157176771), FRAC_CONST(0.997667555322013) }, + { FRAC_CONST(0.064994938058259), FRAC_CONST(0.997885593656308) }, + { FRAC_CONST(0.061729022896176), FRAC_CONST(0.998092945437590) }, + { FRAC_CONST(0.058462446665851), FRAC_CONST(0.998289608445286) }, + { FRAC_CONST(0.055195244349690), FRAC_CONST(0.998475580573295) }, + { FRAC_CONST(0.051927450936806), FRAC_CONST(0.998650859830004) }, + { FRAC_CONST(0.048659101422640), FRAC_CONST(0.998815444338313) }, + { FRAC_CONST(0.045390230808591), FRAC_CONST(0.998969332335654) }, + { FRAC_CONST(0.042120874101635), FRAC_CONST(0.999112522174011) }, + { FRAC_CONST(0.038851066313958), FRAC_CONST(0.999245012319936) }, + { FRAC_CONST(0.035580842462574), FRAC_CONST(0.999366801354564) }, + { FRAC_CONST(0.032310237568951), FRAC_CONST(0.999477887973635) }, + { FRAC_CONST(0.029039286658643), FRAC_CONST(0.999578270987499) }, + { FRAC_CONST(0.025768024760904), FRAC_CONST(0.999667949321134) }, + { FRAC_CONST(0.022496486908322), FRAC_CONST(0.999746922014158) }, + { FRAC_CONST(0.019224708136438), FRAC_CONST(0.999815188220837) }, + { FRAC_CONST(0.015952723483375), FRAC_CONST(0.999872747210095) }, + { FRAC_CONST(0.012680567989461), FRAC_CONST(0.999919598365521) }, + { FRAC_CONST(0.009408276696850), FRAC_CONST(0.999955741185376) }, + { FRAC_CONST(0.006135884649155), FRAC_CONST(0.999981175282601) }, + { FRAC_CONST(0.002863426891064), FRAC_CONST(0.999995900384816) } +}; + +#ifdef LD_DEC +/* 240 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_960[] = +{ + { FRAC_CONST(0.999999665337326), FRAC_CONST(0.000818122995607) }, + { FRAC_CONST(0.999972892444367), FRAC_CONST(0.007363041249780) }, + { FRAC_CONST(0.999903284040864), FRAC_CONST(0.013907644095771) }, + { FRAC_CONST(0.999790843108610), FRAC_CONST(0.020451651184577) }, + { FRAC_CONST(0.999635574464198), FRAC_CONST(0.026994782192715) }, + { FRAC_CONST(0.999437484758823), FRAC_CONST(0.033536756834230) }, + { FRAC_CONST(0.999196582477986), FRAC_CONST(0.040077294872701) }, + { FRAC_CONST(0.998912877941140), FRAC_CONST(0.046616116133247) }, + { FRAC_CONST(0.998586383301244), FRAC_CONST(0.053152940514528) }, + { FRAC_CONST(0.998217112544241), FRAC_CONST(0.059687488000744) }, + { FRAC_CONST(0.997805081488460), FRAC_CONST(0.066219478673630) }, + { FRAC_CONST(0.997350307783942), FRAC_CONST(0.072748632724445) }, + { FRAC_CONST(0.996852810911678), FRAC_CONST(0.079274670465961) }, + { FRAC_CONST(0.996312612182778), FRAC_CONST(0.085797312344440) }, + { FRAC_CONST(0.995729734737558), FRAC_CONST(0.092316278951614) }, + { FRAC_CONST(0.995104203544548), FRAC_CONST(0.098831291036650) }, + { FRAC_CONST(0.994436045399422), FRAC_CONST(0.105342069518114) }, + { FRAC_CONST(0.993725288923851), FRAC_CONST(0.111848335495926) }, + { FRAC_CONST(0.992971964564277), FRAC_CONST(0.118349810263305) }, + { FRAC_CONST(0.992176104590608), FRAC_CONST(0.124846215318711) }, + { FRAC_CONST(0.991337743094838), FRAC_CONST(0.131337272377774) }, + { FRAC_CONST(0.990456915989581), FRAC_CONST(0.137822703385212) }, + { FRAC_CONST(0.989533661006540), FRAC_CONST(0.144302230526747) }, + { FRAC_CONST(0.988568017694885), FRAC_CONST(0.150775576241001) }, + { FRAC_CONST(0.987560027419562), FRAC_CONST(0.157242463231389) }, + { FRAC_CONST(0.986509733359519), FRAC_CONST(0.163702614477995) }, + { FRAC_CONST(0.985417180505858), FRAC_CONST(0.170155753249442) }, + { FRAC_CONST(0.984282415659907), FRAC_CONST(0.176601603114742) }, + { FRAC_CONST(0.983105487431216), FRAC_CONST(0.183039887955141) }, + { FRAC_CONST(0.981886446235473), FRAC_CONST(0.189470331975943) }, + { FRAC_CONST(0.980625344292344), FRAC_CONST(0.195892659718330) }, + { FRAC_CONST(0.979322235623241), FRAC_CONST(0.202306596071156) }, + { FRAC_CONST(0.977977176049000), FRAC_CONST(0.208711866282735) }, + { FRAC_CONST(0.976590223187499), FRAC_CONST(0.215108195972610) }, + { FRAC_CONST(0.975161436451181), FRAC_CONST(0.221495311143304) }, + { FRAC_CONST(0.973690877044515), FRAC_CONST(0.227872938192063) }, + { FRAC_CONST(0.972178607961371), FRAC_CONST(0.234240803922570) }, + { FRAC_CONST(0.970624693982323), FRAC_CONST(0.240598635556650) }, + { FRAC_CONST(0.969029201671875), FRAC_CONST(0.246946160745958) }, + { FRAC_CONST(0.967392199375607), FRAC_CONST(0.253283107583640) }, + { FRAC_CONST(0.965713757217249), FRAC_CONST(0.259609204615985) }, + { FRAC_CONST(0.963993947095677), FRAC_CONST(0.265924180854051) }, + { FRAC_CONST(0.962232842681832), FRAC_CONST(0.272227765785273) }, + { FRAC_CONST(0.960430519415566), FRAC_CONST(0.278519689385053) }, + { FRAC_CONST(0.958587054502409), FRAC_CONST(0.284799682128326) }, + { FRAC_CONST(0.956702526910263), FRAC_CONST(0.291067475001103) }, + { FRAC_CONST(0.954777017366017), FRAC_CONST(0.297322799511998) }, + { FRAC_CONST(0.952810608352092), FRAC_CONST(0.303565387703730) }, + { FRAC_CONST(0.950803384102905), FRAC_CONST(0.309794972164597) }, + { FRAC_CONST(0.948755430601263), FRAC_CONST(0.316011286039934) }, + { FRAC_CONST(0.946666835574676), FRAC_CONST(0.322214063043544) }, + { FRAC_CONST(0.944537688491606), FRAC_CONST(0.328403037469105) }, + { FRAC_CONST(0.942368080557626), FRAC_CONST(0.334577944201551) }, + { FRAC_CONST(0.940158104711519), FRAC_CONST(0.340738518728429) }, + { FRAC_CONST(0.937907855621296), FRAC_CONST(0.346884497151231) }, + { FRAC_CONST(0.935617429680138), FRAC_CONST(0.353015616196696) }, + { FRAC_CONST(0.933286925002268), FRAC_CONST(0.359131613228090) }, + { FRAC_CONST(0.930916441418752), FRAC_CONST(0.365232226256457) }, + { FRAC_CONST(0.928506080473216), FRAC_CONST(0.371317193951838) }, + { FRAC_CONST(0.926055945417500), FRAC_CONST(0.377386255654469) }, + { FRAC_CONST(0.923566141207236), FRAC_CONST(0.383439151385947) }, + { FRAC_CONST(0.921036774497350), FRAC_CONST(0.389475621860365) }, + { FRAC_CONST(0.918467953637492), FRAC_CONST(0.395495408495417) }, + { FRAC_CONST(0.915859788667400), FRAC_CONST(0.401498253423481) }, + { FRAC_CONST(0.913212391312179), FRAC_CONST(0.407483899502658) }, + { FRAC_CONST(0.910525874977521), FRAC_CONST(0.413452090327791) }, + { FRAC_CONST(0.907800354744844), FRAC_CONST(0.419402570241451) }, + { FRAC_CONST(0.905035947366364), FRAC_CONST(0.425335084344881) }, + { FRAC_CONST(0.902232771260093), FRAC_CONST(0.431249378508924) }, + { FRAC_CONST(0.899390946504764), FRAC_CONST(0.437145199384900) }, + { FRAC_CONST(0.896510594834693), FRAC_CONST(0.443022294415467) }, + { FRAC_CONST(0.893591839634558), FRAC_CONST(0.448880411845433) }, + { FRAC_CONST(0.890634805934118), FRAC_CONST(0.454719300732547) }, + { FRAC_CONST(0.887639620402854), FRAC_CONST(0.460538710958240) }, + { FRAC_CONST(0.884606411344546), FRAC_CONST(0.466338393238348) }, + { FRAC_CONST(0.881535308691775), FRAC_CONST(0.472118099133784) }, + { FRAC_CONST(0.878426444000357), FRAC_CONST(0.477877581061184) }, + { FRAC_CONST(0.875279950443708), FRAC_CONST(0.483616592303511) }, + { FRAC_CONST(0.872095962807140), FRAC_CONST(0.489334887020625) }, + { FRAC_CONST(0.868874617482085), FRAC_CONST(0.495032220259813) }, + { FRAC_CONST(0.865616052460258), FRAC_CONST(0.500708347966279) }, + { FRAC_CONST(0.862320407327736), FRAC_CONST(0.506363026993605) }, + { FRAC_CONST(0.858987823258990), FRAC_CONST(0.511996015114162) }, + { FRAC_CONST(0.855618443010829), FRAC_CONST(0.517607071029487) }, + { FRAC_CONST(0.852212410916289), FRAC_CONST(0.523195954380619) }, + { FRAC_CONST(0.848769872878448), FRAC_CONST(0.528762425758396) }, + { FRAC_CONST(0.845290976364179), FRAC_CONST(0.534306246713712) }, + { FRAC_CONST(0.841775870397828), FRAC_CONST(0.539827179767727) }, + { FRAC_CONST(0.838224705554838), FRAC_CONST(0.545324988422046) }, + { FRAC_CONST(0.834637633955290), FRAC_CONST(0.550799437168844) }, + { FRAC_CONST(0.831014809257393), FRAC_CONST(0.556250291500956) }, + { FRAC_CONST(0.827356386650900), FRAC_CONST(0.561677317921925) }, + { FRAC_CONST(0.823662522850458), FRAC_CONST(0.567080283956001) }, + { FRAC_CONST(0.819933376088899), FRAC_CONST(0.572458958158102) }, + { FRAC_CONST(0.816169106110459), FRAC_CONST(0.577813110123727) }, + { FRAC_CONST(0.812369874163934), FRAC_CONST(0.583142510498826) }, + { FRAC_CONST(0.808535842995778), FRAC_CONST(0.588446930989624) }, + { FRAC_CONST(0.804667176843123), FRAC_CONST(0.593726144372402) }, + { FRAC_CONST(0.800764041426753), FRAC_CONST(0.598979924503229) }, + { FRAC_CONST(0.796826603943998), FRAC_CONST(0.604208046327650) }, + { FRAC_CONST(0.792855033061574), FRAC_CONST(0.609410285890327) }, + { FRAC_CONST(0.788849498908361), FRAC_CONST(0.614586420344631) }, + { FRAC_CONST(0.784810173068109), FRAC_CONST(0.619736227962191) }, + { FRAC_CONST(0.780737228572094), FRAC_CONST(0.624859488142386) }, + { FRAC_CONST(0.776630839891703), FRAC_CONST(0.629955981421804) }, + { FRAC_CONST(0.772491182930959), FRAC_CONST(0.635025489483633) }, + { FRAC_CONST(0.768318435018988), FRAC_CONST(0.640067795167023) }, + { FRAC_CONST(0.764112774902423), FRAC_CONST(0.645082682476378) }, + { FRAC_CONST(0.759874382737746), FRAC_CONST(0.650069936590618) }, + { FRAC_CONST(0.755603440083571), FRAC_CONST(0.655029343872374) }, + { FRAC_CONST(0.751300129892866), FRAC_CONST(0.659960691877147) }, + { FRAC_CONST(0.746964636505118), FRAC_CONST(0.664863769362399) }, + { FRAC_CONST(0.742597145638433), FRAC_CONST(0.669738366296610) }, + { FRAC_CONST(0.738197844381584), FRAC_CONST(0.674584273868271) }, + { FRAC_CONST(0.733766921185995), FRAC_CONST(0.679401284494831) }, + { FRAC_CONST(0.729304565857668), FRAC_CONST(0.684189191831585) }, + { FRAC_CONST(0.724810969549055), FRAC_CONST(0.688947790780520) }, + { FRAC_CONST(0.720286324750863), FRAC_CONST(0.693676877499095) }, + { FRAC_CONST(0.715730825283819), FRAC_CONST(0.698376249408973) }, + { FRAC_CONST(0.711144666290356), FRAC_CONST(0.703045705204703) }, + { FRAC_CONST(0.706528044226263), FRAC_CONST(0.707685044862340) }, + { FRAC_CONST(0.701881156852263), FRAC_CONST(0.712294069648014) }, + { FRAC_CONST(0.697204203225545), FRAC_CONST(0.716872582126442) }, + { FRAC_CONST(0.692497383691237), FRAC_CONST(0.721420386169390) }, + { FRAC_CONST(0.687760899873822), FRAC_CONST(0.725937286964068) }, + { FRAC_CONST(0.682994954668502), FRAC_CONST(0.730423091021479) }, + { FRAC_CONST(0.678199752232508), FRAC_CONST(0.734877606184707) }, + { FRAC_CONST(0.673375497976352), FRAC_CONST(0.739300641637149) }, + { FRAC_CONST(0.668522398555031), FRAC_CONST(0.743692007910687) }, + { FRAC_CONST(0.663640661859171), FRAC_CONST(0.748051516893805) }, + { FRAC_CONST(0.658730497006124), FRAC_CONST(0.752378981839648) }, + { FRAC_CONST(0.653792114331011), FRAC_CONST(0.756674217374021) }, + { FRAC_CONST(0.648825725377709), FRAC_CONST(0.760937039503328) }, + { FRAC_CONST(0.643831542889792), FRAC_CONST(0.765167265622459) }, + { FRAC_CONST(0.638809780801414), FRAC_CONST(0.769364714522605) }, + { FRAC_CONST(0.633760654228152), FRAC_CONST(0.773529206399025) }, + { FRAC_CONST(0.628684379457781), FRAC_CONST(0.777660562858748) }, + { FRAC_CONST(0.623581173941019), FRAC_CONST(0.781758606928213) }, + { FRAC_CONST(0.618451256282204), FRAC_CONST(0.785823163060853) }, + { FRAC_CONST(0.613294846229936), FRAC_CONST(0.789854057144609) }, + { FRAC_CONST(0.608112164667659), FRAC_CONST(0.793851116509396) }, + { FRAC_CONST(0.602903433604202), FRAC_CONST(0.797814169934493) }, + { FRAC_CONST(0.597668876164268), FRAC_CONST(0.801743047655882) }, + { FRAC_CONST(0.592408716578875), FRAC_CONST(0.805637581373517) }, + { FRAC_CONST(0.587123180175754), FRAC_CONST(0.809497604258536) }, + { FRAC_CONST(0.581812493369691), FRAC_CONST(0.813322950960406) }, + { FRAC_CONST(0.576476883652835), FRAC_CONST(0.817113457614006) }, + { FRAC_CONST(0.571116579584947), FRAC_CONST(0.820868961846646) }, + { FRAC_CONST(0.565731810783613), FRAC_CONST(0.824589302785025) }, + { FRAC_CONST(0.560322807914407), FRAC_CONST(0.828274321062119) }, + { FRAC_CONST(0.554889802681009), FRAC_CONST(0.831923858824010) }, + { FRAC_CONST(0.549433027815281), FRAC_CONST(0.835537759736646) }, + { FRAC_CONST(0.543952717067296), FRAC_CONST(0.839115868992540) }, + { FRAC_CONST(0.538449105195327), FRAC_CONST(0.842658033317402) }, + { FRAC_CONST(0.532922427955790), FRAC_CONST(0.846164100976699) }, + { FRAC_CONST(0.527372922093142), FRAC_CONST(0.849633921782164) }, + { FRAC_CONST(0.521800825329746), FRAC_CONST(0.853067347098221) }, + { FRAC_CONST(0.516206376355680), FRAC_CONST(0.856464229848356) }, + { FRAC_CONST(0.510589814818519), FRAC_CONST(0.859824424521420) }, + { FRAC_CONST(0.504951381313066), FRAC_CONST(0.863147787177854) }, + { FRAC_CONST(0.499291317371047), FRAC_CONST(0.866434175455865) }, + { FRAC_CONST(0.493609865450762), FRAC_CONST(0.869683448577516) }, + { FRAC_CONST(0.487907268926702), FRAC_CONST(0.872895467354761) }, + { FRAC_CONST(0.482183772079123), FRAC_CONST(0.876070094195407) }, + { FRAC_CONST(0.476439620083580), FRAC_CONST(0.879207193109004) }, + { FRAC_CONST(0.470675059000427), FRAC_CONST(0.882306629712678) }, + { FRAC_CONST(0.464890335764274), FRAC_CONST(0.885368271236879) }, + { FRAC_CONST(0.459085698173413), FRAC_CONST(0.888391986531075) }, + { FRAC_CONST(0.453261394879198), FRAC_CONST(0.891377646069366) }, + { FRAC_CONST(0.447417675375397), FRAC_CONST(0.894325121956035) }, + { FRAC_CONST(0.441554789987504), FRAC_CONST(0.897234287931024) }, + { FRAC_CONST(0.435672989862017), FRAC_CONST(0.900105019375345) }, + { FRAC_CONST(0.429772526955677), FRAC_CONST(0.902937193316419) }, + { FRAC_CONST(0.423853654024676), FRAC_CONST(0.905730688433339) }, + { FRAC_CONST(0.417916624613831), FRAC_CONST(0.908485385062073) }, + { FRAC_CONST(0.411961693045722), FRAC_CONST(0.911201165200584) }, + { FRAC_CONST(0.405989114409798), FRAC_CONST(0.913877912513892) }, + { FRAC_CONST(0.399999144551449), FRAC_CONST(0.916515512339049) }, + { FRAC_CONST(0.393992040061048), FRAC_CONST(0.919113851690058) }, + { FRAC_CONST(0.387968058262959), FRAC_CONST(0.921672819262709) }, + { FRAC_CONST(0.381927457204511), FRAC_CONST(0.924192305439348) }, + { FRAC_CONST(0.375870495644949), FRAC_CONST(0.926672202293573) }, + { FRAC_CONST(0.369797433044349), FRAC_CONST(0.929112403594856) }, + { FRAC_CONST(0.363708529552499), FRAC_CONST(0.931512804813095) }, + { FRAC_CONST(0.357604045997758), FRAC_CONST(0.933873303123091) }, + { FRAC_CONST(0.351484243875885), FRAC_CONST(0.936193797408954) }, + { FRAC_CONST(0.345349385338836), FRAC_CONST(0.938474188268430) }, + { FRAC_CONST(0.339199733183530), FRAC_CONST(0.940714378017165) }, + { FRAC_CONST(0.333035550840599), FRAC_CONST(0.942914270692887) }, + { FRAC_CONST(0.326857102363098), FRAC_CONST(0.945073772059514) }, + { FRAC_CONST(0.320664652415198), FRAC_CONST(0.947192789611197) }, + { FRAC_CONST(0.314458466260842), FRAC_CONST(0.949271232576274) }, + { FRAC_CONST(0.308238809752391), FRAC_CONST(0.951309011921168) }, + { FRAC_CONST(0.302005949319228), FRAC_CONST(0.953306040354194) }, + { FRAC_CONST(0.295760151956351), FRAC_CONST(0.955262232329299) }, + { FRAC_CONST(0.289501685212929), FRAC_CONST(0.957177504049732) }, + { FRAC_CONST(0.283230817180850), FRAC_CONST(0.959051773471624) }, + { FRAC_CONST(0.276947816483228), FRAC_CONST(0.960884960307514) }, + { FRAC_CONST(0.270652952262902), FRAC_CONST(0.962676986029777) }, + { FRAC_CONST(0.264346494170904), FRAC_CONST(0.964427773873996) }, + { FRAC_CONST(0.258028712354909), FRAC_CONST(0.966137248842248) }, + { FRAC_CONST(0.251699877447663), FRAC_CONST(0.967805337706313) }, + { FRAC_CONST(0.245360260555389), FRAC_CONST(0.969431969010818) }, + { FRAC_CONST(0.239010133246176), FRAC_CONST(0.971017073076290) }, + { FRAC_CONST(0.232649767538342), FRAC_CONST(0.972560582002147) }, + { FRAC_CONST(0.226279435888785), FRAC_CONST(0.974062429669605) }, + { FRAC_CONST(0.219899411181310), FRAC_CONST(0.975522551744506) }, + { FRAC_CONST(0.213509966714943), FRAC_CONST(0.976940885680082) }, + { FRAC_CONST(0.207111376192219), FRAC_CONST(0.978317370719628) }, + { FRAC_CONST(0.200703913707458), FRAC_CONST(0.979651947899104) }, + { FRAC_CONST(0.194287853735029), FRAC_CONST(0.980944560049668) }, + { FRAC_CONST(0.187863471117585), FRAC_CONST(0.982195151800116) }, + { FRAC_CONST(0.181431041054297), FRAC_CONST(0.983403669579260) }, + { FRAC_CONST(0.174990839089060), FRAC_CONST(0.984570061618221) }, + { FRAC_CONST(0.168543141098691), FRAC_CONST(0.985694277952645) }, + { FRAC_CONST(0.162088223281113), FRAC_CONST(0.986776270424848) }, + { FRAC_CONST(0.155626362143520), FRAC_CONST(0.987815992685872) }, + { FRAC_CONST(0.149157834490539), FRAC_CONST(0.988813400197476) }, + { FRAC_CONST(0.142682917412363), FRAC_CONST(0.989768450234042) }, + { FRAC_CONST(0.136201888272891), FRAC_CONST(0.990681101884405) }, + { FRAC_CONST(0.129715024697841), FRAC_CONST(0.991551316053606) }, + { FRAC_CONST(0.123222604562857), FRAC_CONST(0.992379055464567) }, + { FRAC_CONST(0.116724905981611), FRAC_CONST(0.993164284659685) }, + { FRAC_CONST(0.110222207293883), FRAC_CONST(0.993906970002356) }, + { FRAC_CONST(0.103714787053643), FRAC_CONST(0.994607079678411) }, + { FRAC_CONST(0.097202924017115), FRAC_CONST(0.995264583697482) }, + { FRAC_CONST(0.090686897130838), FRAC_CONST(0.995879453894286) }, + { FRAC_CONST(0.084166985519718), FRAC_CONST(0.996451663929828) }, + { FRAC_CONST(0.077643468475068), FRAC_CONST(0.996981189292537) }, + { FRAC_CONST(0.071116625442645), FRAC_CONST(0.997468007299307) }, + { FRAC_CONST(0.064586736010684), FRAC_CONST(0.997912097096476) }, + { FRAC_CONST(0.058054079897912), FRAC_CONST(0.998313439660714) }, + { FRAC_CONST(0.051518936941578), FRAC_CONST(0.998672017799843) }, + { FRAC_CONST(0.044981587085452), FRAC_CONST(0.998987816153567) }, + { FRAC_CONST(0.038442310367847), FRAC_CONST(0.999260821194138) }, + { FRAC_CONST(0.031901386909611), FRAC_CONST(0.999491021226926) }, + { FRAC_CONST(0.025359096902136), FRAC_CONST(0.999678406390929) }, + { FRAC_CONST(0.018815720595351), FRAC_CONST(0.999822968659191) }, + { FRAC_CONST(0.012271538285720), FRAC_CONST(0.999924701839145) }, + { FRAC_CONST(0.005726830304231), FRAC_CONST(0.999983601572879) } +}; +#endif // LD_DEC + +/* 60 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_240[] = +{ + { FRAC_CONST(0.999994645401696), FRAC_CONST(0.003272486506527) }, + { FRAC_CONST(0.999566308502021), FRAC_CONST(0.029448173247963) }, + { FRAC_CONST(0.998452918783950), FRAC_CONST(0.055603677682425) }, + { FRAC_CONST(0.996655239309180), FRAC_CONST(0.081721074133668) }, + { FRAC_CONST(0.994174502117428), FRAC_CONST(0.107782463042759) }, + { FRAC_CONST(0.991012407382049), FRAC_CONST(0.133769983235535) }, + { FRAC_CONST(0.987171122244825), FRAC_CONST(0.159665824163761) }, + { FRAC_CONST(0.982653279330712), FRAC_CONST(0.185452238111591) }, + { FRAC_CONST(0.977461974943572), FRAC_CONST(0.211111552358965) }, + { FRAC_CONST(0.971600766944121), FRAC_CONST(0.236626181293610) }, + { FRAC_CONST(0.965073672311547), FRAC_CONST(0.261978638463337) }, + { FRAC_CONST(0.957885164390477), FRAC_CONST(0.287151548560387) }, + { FRAC_CONST(0.950040169825165), FRAC_CONST(0.312127659329594) }, + { FRAC_CONST(0.941544065183021), FRAC_CONST(0.336889853392220) }, + { FRAC_CONST(0.932402673269775), FRAC_CONST(0.361421159977355) }, + { FRAC_CONST(0.922622259138823), FRAC_CONST(0.385704766552831) }, + { FRAC_CONST(0.912209525797468), FRAC_CONST(0.409724030347695) }, + { FRAC_CONST(0.901171609613013), FRAC_CONST(0.433462489758331) }, + { FRAC_CONST(0.889516075421856), FRAC_CONST(0.456903875630421) }, + { FRAC_CONST(0.877250911344924), FRAC_CONST(0.480032122409011) }, + { FRAC_CONST(0.864384523313017), FRAC_CONST(0.502831379149042) }, + { FRAC_CONST(0.850925729305802), FRAC_CONST(0.525286020378792) }, + { FRAC_CONST(0.836883753308409), FRAC_CONST(0.547380656808797) }, + { FRAC_CONST(0.822268218989775), FRAC_CONST(0.569100145878898) }, + { FRAC_CONST(0.807089143107059), FRAC_CONST(0.590429602136201) }, + { FRAC_CONST(0.791356928640660), FRAC_CONST(0.611354407436816) }, + { FRAC_CONST(0.775082357664531), FRAC_CONST(0.631860220964409) }, + { FRAC_CONST(0.758276583956687), FRAC_CONST(0.651932989058674) }, + { FRAC_CONST(0.740951125354959), FRAC_CONST(0.671558954847018) }, + { FRAC_CONST(0.723117855863248), FRAC_CONST(0.690724667672829) }, + { FRAC_CONST(0.704788997513670), FRAC_CONST(0.709416992313883) }, + { FRAC_CONST(0.685977111990193), FRAC_CONST(0.727623117984575) }, + { FRAC_CONST(0.666695092019479), FRAC_CONST(0.745330567115786) }, + { FRAC_CONST(0.646956152534857), FRAC_CONST(0.762527203906388) }, + { FRAC_CONST(0.626773821619469), FRAC_CONST(0.779201242640517) }, + { FRAC_CONST(0.606161931234795), FRAC_CONST(0.795341255764910) }, + { FRAC_CONST(0.585134607740916), FRAC_CONST(0.810936181720784) }, + { FRAC_CONST(0.563706262215017), FRAC_CONST(0.825975332524873) }, + { FRAC_CONST(0.541891580574752), FRAC_CONST(0.840448401094438) }, + { FRAC_CONST(0.519705513513249), FRAC_CONST(0.854345468311227) }, + { FRAC_CONST(0.497163266252654), FRAC_CONST(0.867657009819544) }, + { FRAC_CONST(0.474280288123229), FRAC_CONST(0.880373902553765) }, + { FRAC_CONST(0.451072261975153), FRAC_CONST(0.892487430990834) }, + { FRAC_CONST(0.427555093430282), FRAC_CONST(0.903989293123443) }, + { FRAC_CONST(0.403744899981227), FRAC_CONST(0.914871606149819) }, + { FRAC_CONST(0.379657999945233), FRAC_CONST(0.925126911876195) }, + { FRAC_CONST(0.355310901280416), FRAC_CONST(0.934748181828292) }, + { FRAC_CONST(0.330720290272038), FRAC_CONST(0.943728822068278) }, + { FRAC_CONST(0.305903020096554), FRAC_CONST(0.952062677713924) }, + { FRAC_CONST(0.280876099271292), FRAC_CONST(0.959744037156857) }, + { FRAC_CONST(0.255656679997665), FRAC_CONST(0.966767635977008) }, + { FRAC_CONST(0.230262046405902), FRAC_CONST(0.973128660550580) }, + { FRAC_CONST(0.204709602709380), FRAC_CONST(0.978822751349072) }, + { FRAC_CONST(0.179016861276633), FRAC_CONST(0.983846005927077) }, + { FRAC_CONST(0.153201430629259), FRAC_CONST(0.988194981596825) }, + { FRAC_CONST(0.127281003373913), FRAC_CONST(0.991866697787626) }, + { FRAC_CONST(0.101273344076683), FRAC_CONST(0.994858638088611) }, + { FRAC_CONST(0.075196277088140), FRAC_CONST(0.997168751973348) }, + { FRAC_CONST(0.049067674327418), FRAC_CONST(0.998795456205172) }, + { FRAC_CONST(0.022905443033697), FRAC_CONST(0.999737635922260) } +}; +#endif // ALLOW_SMALL_FRAMELENGTH + +#ifdef SSR_DEC +/* 128 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_512[] = +{ + { FRAC_CONST(0.999998823451702), FRAC_CONST(0.001533980186285) }, + { FRAC_CONST(0.999904701082853), FRAC_CONST(0.013805388528060) }, + { FRAC_CONST(0.999659996743959), FRAC_CONST(0.026074717829104) }, + { FRAC_CONST(0.999264747286594), FRAC_CONST(0.038340120373553) }, + { FRAC_CONST(0.998719012233873), FRAC_CONST(0.050599749036899) }, + { FRAC_CONST(0.998022873771486), FRAC_CONST(0.062851757564161) }, + { FRAC_CONST(0.997176436735326), FRAC_CONST(0.075094300847921) }, + { FRAC_CONST(0.996179828595697), FRAC_CONST(0.087325535206192) }, + { FRAC_CONST(0.995033199438119), FRAC_CONST(0.099543618660069) }, + { FRAC_CONST(0.993736721940725), FRAC_CONST(0.111746711211127) }, + { FRAC_CONST(0.992290591348257), FRAC_CONST(0.123932975118512) }, + { FRAC_CONST(0.990695025442665), FRAC_CONST(0.136100575175706) }, + { FRAC_CONST(0.988950264510303), FRAC_CONST(0.148247678986896) }, + { FRAC_CONST(0.987056571305751), FRAC_CONST(0.160372457242928) }, + { FRAC_CONST(0.985014231012240), FRAC_CONST(0.172473083996796) }, + { FRAC_CONST(0.982823551198705), FRAC_CONST(0.184547736938620) }, + { FRAC_CONST(0.980484861773469), FRAC_CONST(0.196594597670080) }, + { FRAC_CONST(0.977998514934557), FRAC_CONST(0.208611851978263) }, + { FRAC_CONST(0.975364885116657), FRAC_CONST(0.220597690108874) }, + { FRAC_CONST(0.972584368934732), FRAC_CONST(0.232550307038775) }, + { FRAC_CONST(0.969657385124292), FRAC_CONST(0.244467902747824) }, + { FRAC_CONST(0.966584374478333), FRAC_CONST(0.256348682489943) }, + { FRAC_CONST(0.963365799780954), FRAC_CONST(0.268190857063403) }, + { FRAC_CONST(0.960002145737666), FRAC_CONST(0.279992643080273) }, + { FRAC_CONST(0.956493918902395), FRAC_CONST(0.291752263234989) }, + { FRAC_CONST(0.952841647601199), FRAC_CONST(0.303467946572011) }, + { FRAC_CONST(0.949045881852701), FRAC_CONST(0.315137928752522) }, + { FRAC_CONST(0.945107193285261), FRAC_CONST(0.326760452320132) }, + { FRAC_CONST(0.941026175050889), FRAC_CONST(0.338333766965541) }, + { FRAC_CONST(0.936803441735922), FRAC_CONST(0.349856129790135) }, + { FRAC_CONST(0.932439629268462), FRAC_CONST(0.361325805568454) }, + { FRAC_CONST(0.927935394822618), FRAC_CONST(0.372741067009516) }, + { FRAC_CONST(0.923291416719528), FRAC_CONST(0.384100195016935) }, + { FRAC_CONST(0.918508394325212), FRAC_CONST(0.395401478947816) }, + { FRAC_CONST(0.913587047945251), FRAC_CONST(0.406643216870369) }, + { FRAC_CONST(0.908528118716306), FRAC_CONST(0.417823715820212) }, + { FRAC_CONST(0.903332368494512), FRAC_CONST(0.428941292055329) }, + { FRAC_CONST(0.898000579740740), FRAC_CONST(0.439994271309633) }, + { FRAC_CONST(0.892533555402765), FRAC_CONST(0.450980989045104) }, + { FRAC_CONST(0.886932118794342), FRAC_CONST(0.461899790702463) }, + { FRAC_CONST(0.881197113471222), FRAC_CONST(0.472749031950343) }, + { FRAC_CONST(0.875329403104111), FRAC_CONST(0.483527078932919) }, + { FRAC_CONST(0.869329871348607), FRAC_CONST(0.494232308515960) }, + { FRAC_CONST(0.863199421712124), FRAC_CONST(0.504863108531268) }, + { FRAC_CONST(0.856938977417829), FRAC_CONST(0.515417878019463) }, + { FRAC_CONST(0.850549481265603), FRAC_CONST(0.525895027471085) }, + { FRAC_CONST(0.844031895490066), FRAC_CONST(0.536292979065963) }, + { FRAC_CONST(0.837387201615662), FRAC_CONST(0.546610166910835) }, + { FRAC_CONST(0.830616400308846), FRAC_CONST(0.556845037275160) }, + { FRAC_CONST(0.823720511227391), FRAC_CONST(0.566996048825109) }, + { FRAC_CONST(0.816700572866828), FRAC_CONST(0.577061672855679) }, + { FRAC_CONST(0.809557642404051), FRAC_CONST(0.587040393520918) }, + { FRAC_CONST(0.802292795538116), FRAC_CONST(0.596930708062197) }, + { FRAC_CONST(0.794907126328237), FRAC_CONST(0.606731127034524) }, + { FRAC_CONST(0.787401747029031), FRAC_CONST(0.616440174530854) }, + { FRAC_CONST(0.779777787923015), FRAC_CONST(0.626056388404344) }, + { FRAC_CONST(0.772036397150385), FRAC_CONST(0.635578320488556) }, + { FRAC_CONST(0.764178740536117), FRAC_CONST(0.645004536815544) }, + { FRAC_CONST(0.756206001414395), FRAC_CONST(0.654333617831800) }, + { FRAC_CONST(0.748119380450404), FRAC_CONST(0.663564158612040) }, + { FRAC_CONST(0.739920095459516), FRAC_CONST(0.672694769070773) }, + { FRAC_CONST(0.731609381223893), FRAC_CONST(0.681724074171650) }, + { FRAC_CONST(0.723188489306527), FRAC_CONST(0.690650714134535) }, + { FRAC_CONST(0.714658687862769), FRAC_CONST(0.699473344640284) }, + { FRAC_CONST(0.706021261449340), FRAC_CONST(0.708190637033195) }, + { FRAC_CONST(0.697277510830887), FRAC_CONST(0.716801278521100) }, + { FRAC_CONST(0.688428752784091), FRAC_CONST(0.725303972373061) }, + { FRAC_CONST(0.679476319899365), FRAC_CONST(0.733697438114660) }, + { FRAC_CONST(0.670421560380173), FRAC_CONST(0.741980411720831) }, + { FRAC_CONST(0.661265837839992), FRAC_CONST(0.750151645806215) }, + { FRAC_CONST(0.652010531096960), FRAC_CONST(0.758209909813015) }, + { FRAC_CONST(0.642657033966227), FRAC_CONST(0.766153990196313) }, + { FRAC_CONST(0.633206755050057), FRAC_CONST(0.773982690606823) }, + { FRAC_CONST(0.623661117525695), FRAC_CONST(0.781694832071059) }, + { FRAC_CONST(0.614021558931038), FRAC_CONST(0.789289253168886) }, + { FRAC_CONST(0.604289530948156), FRAC_CONST(0.796764810208419) }, + { FRAC_CONST(0.594466499184665), FRAC_CONST(0.804120377398266) }, + { FRAC_CONST(0.584553942953015), FRAC_CONST(0.811354847017064) }, + { FRAC_CONST(0.574553355047716), FRAC_CONST(0.818467129580299) }, + { FRAC_CONST(0.564466241520520), FRAC_CONST(0.825456154004377) }, + { FRAC_CONST(0.554294121453620), FRAC_CONST(0.832320867767930) }, + { FRAC_CONST(0.544038526730884), FRAC_CONST(0.839060237070313) }, + { FRAC_CONST(0.533701001807153), FRAC_CONST(0.845673246987299) }, + { FRAC_CONST(0.523283103475656), FRAC_CONST(0.852158901623920) }, + { FRAC_CONST(0.512786400633563), FRAC_CONST(0.858516224264443) }, + { FRAC_CONST(0.502212474045711), FRAC_CONST(0.864744257519462) }, + { FRAC_CONST(0.491562916106550), FRAC_CONST(0.870842063470079) }, + { FRAC_CONST(0.480839330600334), FRAC_CONST(0.876808723809146) }, + { FRAC_CONST(0.470043332459596), FRAC_CONST(0.882643339979563) }, + { FRAC_CONST(0.459176547521944), FRAC_CONST(0.888345033309596) }, + { FRAC_CONST(0.448240612285220), FRAC_CONST(0.893912945145203) }, + { FRAC_CONST(0.437237173661044), FRAC_CONST(0.899346236979341) }, + { FRAC_CONST(0.426167888726800), FRAC_CONST(0.904644090578246) }, + { FRAC_CONST(0.415034424476082), FRAC_CONST(0.909805708104652) }, + { FRAC_CONST(0.403838457567654), FRAC_CONST(0.914830312237946) }, + { FRAC_CONST(0.392581674072952), FRAC_CONST(0.919717146291227) }, + { FRAC_CONST(0.381265769222162), FRAC_CONST(0.924465474325263) }, + { FRAC_CONST(0.369892447148934), FRAC_CONST(0.929074581259316) }, + { FRAC_CONST(0.358463420633737), FRAC_CONST(0.933543772978836) }, + { FRAC_CONST(0.346980410845924), FRAC_CONST(0.937872376439990) }, + { FRAC_CONST(0.335445147084532), FRAC_CONST(0.942059739771017) }, + { FRAC_CONST(0.323859366517853), FRAC_CONST(0.946105232370403) }, + { FRAC_CONST(0.312224813921825), FRAC_CONST(0.950008245001843) }, + { FRAC_CONST(0.300543241417273), FRAC_CONST(0.953768189885990) }, + { FRAC_CONST(0.288816408206049), FRAC_CONST(0.957384500788976) }, + { FRAC_CONST(0.277046080306100), FRAC_CONST(0.960856633107680) }, + { FRAC_CONST(0.265234030285512), FRAC_CONST(0.964184063951746) }, + { FRAC_CONST(0.253382036995570), FRAC_CONST(0.967366292222329) }, + { FRAC_CONST(0.241491885302869), FRAC_CONST(0.970402838687556) }, + { FRAC_CONST(0.229565365820519), FRAC_CONST(0.973293246054698) }, + { FRAC_CONST(0.217604274638484), FRAC_CONST(0.976037079039039) }, + { FRAC_CONST(0.205610413053099), FRAC_CONST(0.978633924429423) }, + { FRAC_CONST(0.193585587295804), FRAC_CONST(0.981083391150487) }, + { FRAC_CONST(0.181531608261125), FRAC_CONST(0.983385110321551) }, + { FRAC_CONST(0.169450291233968), FRAC_CONST(0.985538735312176) }, + { FRAC_CONST(0.157343455616238), FRAC_CONST(0.987543941794359) }, + { FRAC_CONST(0.145212924652848), FRAC_CONST(0.989400427791380) }, + { FRAC_CONST(0.133060525157139), FRAC_CONST(0.991107913723277) }, + { FRAC_CONST(0.120888087235777), FRAC_CONST(0.992666142448948) }, + { FRAC_CONST(0.108697444013139), FRAC_CONST(0.994074879304879) }, + { FRAC_CONST(0.096490431355253), FRAC_CONST(0.995333912140482) }, + { FRAC_CONST(0.084268887593324), FRAC_CONST(0.996443051350043) }, + { FRAC_CONST(0.072034653246889), FRAC_CONST(0.997402129901275) }, + { FRAC_CONST(0.059789570746640), FRAC_CONST(0.998211003360478) }, + { FRAC_CONST(0.047535484156959), FRAC_CONST(0.998869549914284) }, + { FRAC_CONST(0.035274238898214), FRAC_CONST(0.999377670388003) }, + { FRAC_CONST(0.023007681468839), FRAC_CONST(0.999735288260562) }, + { FRAC_CONST(0.010737659167265), FRAC_CONST(0.999942349676024) } +}; + +/* 16 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_64[] = +{ + { FRAC_CONST(0.999924701839145), FRAC_CONST(0.012271538285720) }, + { FRAC_CONST(0.993906970002356), FRAC_CONST(0.110222207293883) }, + { FRAC_CONST(0.978317370719628), FRAC_CONST(0.207111376192219) }, + { FRAC_CONST(0.953306040354194), FRAC_CONST(0.302005949319228) }, + { FRAC_CONST(0.919113851690058), FRAC_CONST(0.393992040061048) }, + { FRAC_CONST(0.876070094195407), FRAC_CONST(0.482183772079123) }, + { FRAC_CONST(0.824589302785025), FRAC_CONST(0.565731810783613) }, + { FRAC_CONST(0.765167265622459), FRAC_CONST(0.643831542889791) }, + { FRAC_CONST(0.698376249408973), FRAC_CONST(0.715730825283819) }, + { FRAC_CONST(0.624859488142386), FRAC_CONST(0.780737228572094) }, + { FRAC_CONST(0.545324988422046), FRAC_CONST(0.838224705554838) }, + { FRAC_CONST(0.460538710958240), FRAC_CONST(0.887639620402854) }, + { FRAC_CONST(0.371317193951838), FRAC_CONST(0.928506080473215) }, + { FRAC_CONST(0.278519689385053), FRAC_CONST(0.960430519415566) }, + { FRAC_CONST(0.183039887955141), FRAC_CONST(0.983105487431216) }, + { FRAC_CONST(0.085797312344440), FRAC_CONST(0.996312612182778) } +}; +#endif // SSR_DEC + +#else // FIXED_POINT + +/* 256 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_2048[] = +{ + { FRAC_CONST(0.031249997702054), FRAC_CONST(0.000011984224612) }, + { FRAC_CONST(0.031249813866531), FRAC_CONST(0.000107857810004) }, + { FRAC_CONST(0.031249335895858), FRAC_CONST(0.000203730380198) }, + { FRAC_CONST(0.031248563794535), FRAC_CONST(0.000299601032804) }, + { FRAC_CONST(0.031247497569829), FRAC_CONST(0.000395468865451) }, + { FRAC_CONST(0.031246137231775), FRAC_CONST(0.000491332975794) }, + { FRAC_CONST(0.031244482793177), FRAC_CONST(0.000587192461525) }, + { FRAC_CONST(0.031242534269608), FRAC_CONST(0.000683046420376) }, + { FRAC_CONST(0.031240291679407), FRAC_CONST(0.000778893950134) }, + { FRAC_CONST(0.031237755043684), FRAC_CONST(0.000874734148645) }, + { FRAC_CONST(0.031234924386313), FRAC_CONST(0.000970566113826) }, + { FRAC_CONST(0.031231799733938), FRAC_CONST(0.001066388943669) }, + { FRAC_CONST(0.031228381115970), FRAC_CONST(0.001162201736253) }, + { FRAC_CONST(0.031224668564585), FRAC_CONST(0.001258003589751) }, + { FRAC_CONST(0.031220662114728), FRAC_CONST(0.001353793602441) }, + { FRAC_CONST(0.031216361804108), FRAC_CONST(0.001449570872710) }, + { FRAC_CONST(0.031211767673203), FRAC_CONST(0.001545334499065) }, + { FRAC_CONST(0.031206879765253), FRAC_CONST(0.001641083580144) }, + { FRAC_CONST(0.031201698126266), FRAC_CONST(0.001736817214719) }, + { FRAC_CONST(0.031196222805014), FRAC_CONST(0.001832534501709) }, + { FRAC_CONST(0.031190453853031), FRAC_CONST(0.001928234540186) }, + { FRAC_CONST(0.031184391324617), FRAC_CONST(0.002023916429386) }, + { FRAC_CONST(0.031178035276836), FRAC_CONST(0.002119579268713) }, + { FRAC_CONST(0.031171385769513), FRAC_CONST(0.002215222157753) }, + { FRAC_CONST(0.031164442865236), FRAC_CONST(0.002310844196278) }, + { FRAC_CONST(0.031157206629353), FRAC_CONST(0.002406444484258) }, + { FRAC_CONST(0.031149677129975), FRAC_CONST(0.002502022121865) }, + { FRAC_CONST(0.031141854437973), FRAC_CONST(0.002597576209488) }, + { FRAC_CONST(0.031133738626977), FRAC_CONST(0.002693105847734) }, + { FRAC_CONST(0.031125329773375), FRAC_CONST(0.002788610137442) }, + { FRAC_CONST(0.031116627956316), FRAC_CONST(0.002884088179689) }, + { FRAC_CONST(0.031107633257703), FRAC_CONST(0.002979539075801) }, + { FRAC_CONST(0.031098345762200), FRAC_CONST(0.003074961927355) }, + { FRAC_CONST(0.031088765557222), FRAC_CONST(0.003170355836197) }, + { FRAC_CONST(0.031078892732942), FRAC_CONST(0.003265719904442) }, + { FRAC_CONST(0.031068727382288), FRAC_CONST(0.003361053234488) }, + { FRAC_CONST(0.031058269600939), FRAC_CONST(0.003456354929021) }, + { FRAC_CONST(0.031047519487329), FRAC_CONST(0.003551624091024) }, + { FRAC_CONST(0.031036477142640), FRAC_CONST(0.003646859823790) }, + { FRAC_CONST(0.031025142670809), FRAC_CONST(0.003742061230921) }, + { FRAC_CONST(0.031013516178519), FRAC_CONST(0.003837227416347) }, + { FRAC_CONST(0.031001597775203), FRAC_CONST(0.003932357484328) }, + { FRAC_CONST(0.030989387573042), FRAC_CONST(0.004027450539462) }, + { FRAC_CONST(0.030976885686963), FRAC_CONST(0.004122505686697) }, + { FRAC_CONST(0.030964092234638), FRAC_CONST(0.004217522031340) }, + { FRAC_CONST(0.030951007336485), FRAC_CONST(0.004312498679058) }, + { FRAC_CONST(0.030937631115663), FRAC_CONST(0.004407434735897) }, + { FRAC_CONST(0.030923963698074), FRAC_CONST(0.004502329308281) }, + { FRAC_CONST(0.030910005212362), FRAC_CONST(0.004597181503027) }, + { FRAC_CONST(0.030895755789908), FRAC_CONST(0.004691990427350) }, + { FRAC_CONST(0.030881215564835), FRAC_CONST(0.004786755188872) }, + { FRAC_CONST(0.030866384674000), FRAC_CONST(0.004881474895632) }, + { FRAC_CONST(0.030851263256996), FRAC_CONST(0.004976148656090) }, + { FRAC_CONST(0.030835851456154), FRAC_CONST(0.005070775579142) }, + { FRAC_CONST(0.030820149416533), FRAC_CONST(0.005165354774124) }, + { FRAC_CONST(0.030804157285929), FRAC_CONST(0.005259885350819) }, + { FRAC_CONST(0.030787875214864), FRAC_CONST(0.005354366419469) }, + { FRAC_CONST(0.030771303356593), FRAC_CONST(0.005448797090784) }, + { FRAC_CONST(0.030754441867095), FRAC_CONST(0.005543176475946) }, + { FRAC_CONST(0.030737290905077), FRAC_CONST(0.005637503686619) }, + { FRAC_CONST(0.030719850631972), FRAC_CONST(0.005731777834961) }, + { FRAC_CONST(0.030702121211932), FRAC_CONST(0.005825998033626) }, + { FRAC_CONST(0.030684102811835), FRAC_CONST(0.005920163395780) }, + { FRAC_CONST(0.030665795601276), FRAC_CONST(0.006014273035101) }, + { FRAC_CONST(0.030647199752570), FRAC_CONST(0.006108326065793) }, + { FRAC_CONST(0.030628315440748), FRAC_CONST(0.006202321602594) }, + { FRAC_CONST(0.030609142843557), FRAC_CONST(0.006296258760782) }, + { FRAC_CONST(0.030589682141455), FRAC_CONST(0.006390136656185) }, + { FRAC_CONST(0.030569933517616), FRAC_CONST(0.006483954405188) }, + { FRAC_CONST(0.030549897157919), FRAC_CONST(0.006577711124743) }, + { FRAC_CONST(0.030529573250956), FRAC_CONST(0.006671405932375) }, + { FRAC_CONST(0.030508961988022), FRAC_CONST(0.006765037946194) }, + { FRAC_CONST(0.030488063563118), FRAC_CONST(0.006858606284900) }, + { FRAC_CONST(0.030466878172949), FRAC_CONST(0.006952110067791) }, + { FRAC_CONST(0.030445406016919), FRAC_CONST(0.007045548414774) }, + { FRAC_CONST(0.030423647297133), FRAC_CONST(0.007138920446372) }, + { FRAC_CONST(0.030401602218392), FRAC_CONST(0.007232225283733) }, + { FRAC_CONST(0.030379270988192), FRAC_CONST(0.007325462048634) }, + { FRAC_CONST(0.030356653816724), FRAC_CONST(0.007418629863497) }, + { FRAC_CONST(0.030333750916869), FRAC_CONST(0.007511727851390) }, + { FRAC_CONST(0.030310562504198), FRAC_CONST(0.007604755136040) }, + { FRAC_CONST(0.030287088796968), FRAC_CONST(0.007697710841838) }, + { FRAC_CONST(0.030263330016124), FRAC_CONST(0.007790594093851) }, + { FRAC_CONST(0.030239286385293), FRAC_CONST(0.007883404017824) }, + { FRAC_CONST(0.030214958130781), FRAC_CONST(0.007976139740197) }, + { FRAC_CONST(0.030190345481576), FRAC_CONST(0.008068800388104) }, + { FRAC_CONST(0.030165448669342), FRAC_CONST(0.008161385089390) }, + { FRAC_CONST(0.030140267928416), FRAC_CONST(0.008253892972610) }, + { FRAC_CONST(0.030114803495809), FRAC_CONST(0.008346323167047) }, + { FRAC_CONST(0.030089055611203), FRAC_CONST(0.008438674802711) }, + { FRAC_CONST(0.030063024516947), FRAC_CONST(0.008530947010354) }, + { FRAC_CONST(0.030036710458054), FRAC_CONST(0.008623138921475) }, + { FRAC_CONST(0.030010113682202), FRAC_CONST(0.008715249668328) }, + { FRAC_CONST(0.029983234439732), FRAC_CONST(0.008807278383932) }, + { FRAC_CONST(0.029956072983640), FRAC_CONST(0.008899224202078) }, + { FRAC_CONST(0.029928629569580), FRAC_CONST(0.008991086257336) }, + { FRAC_CONST(0.029900904455860), FRAC_CONST(0.009082863685067) }, + { FRAC_CONST(0.029872897903441), FRAC_CONST(0.009174555621425) }, + { FRAC_CONST(0.029844610175929), FRAC_CONST(0.009266161203371) }, + { FRAC_CONST(0.029816041539579), FRAC_CONST(0.009357679568679) }, + { FRAC_CONST(0.029787192263292), FRAC_CONST(0.009449109855944) }, + { FRAC_CONST(0.029758062618606), FRAC_CONST(0.009540451204587) }, + { FRAC_CONST(0.029728652879702), FRAC_CONST(0.009631702754871) }, + { FRAC_CONST(0.029698963323395), FRAC_CONST(0.009722863647900) }, + { FRAC_CONST(0.029668994229134), FRAC_CONST(0.009813933025633) }, + { FRAC_CONST(0.029638745879000), FRAC_CONST(0.009904910030891) }, + { FRAC_CONST(0.029608218557702), FRAC_CONST(0.009995793807363) }, + { FRAC_CONST(0.029577412552575), FRAC_CONST(0.010086583499618) }, + { FRAC_CONST(0.029546328153577), FRAC_CONST(0.010177278253107) }, + { FRAC_CONST(0.029514965653285), FRAC_CONST(0.010267877214177) }, + { FRAC_CONST(0.029483325346896), FRAC_CONST(0.010358379530076) }, + { FRAC_CONST(0.029451407532220), FRAC_CONST(0.010448784348962) }, + { FRAC_CONST(0.029419212509679), FRAC_CONST(0.010539090819911) }, + { FRAC_CONST(0.029386740582307), FRAC_CONST(0.010629298092923) }, + { FRAC_CONST(0.029353992055740), FRAC_CONST(0.010719405318933) }, + { FRAC_CONST(0.029320967238220), FRAC_CONST(0.010809411649818) }, + { FRAC_CONST(0.029287666440590), FRAC_CONST(0.010899316238403) }, + { FRAC_CONST(0.029254089976290), FRAC_CONST(0.010989118238474) }, + { FRAC_CONST(0.029220238161353), FRAC_CONST(0.011078816804778) }, + { FRAC_CONST(0.029186111314406), FRAC_CONST(0.011168411093039) }, + { FRAC_CONST(0.029151709756664), FRAC_CONST(0.011257900259961) }, + { FRAC_CONST(0.029117033811927), FRAC_CONST(0.011347283463239) }, + { FRAC_CONST(0.029082083806579), FRAC_CONST(0.011436559861563) }, + { FRAC_CONST(0.029046860069582), FRAC_CONST(0.011525728614630) }, + { FRAC_CONST(0.029011362932476), FRAC_CONST(0.011614788883150) }, + { FRAC_CONST(0.028975592729373), FRAC_CONST(0.011703739828853) }, + { FRAC_CONST(0.028939549796957), FRAC_CONST(0.011792580614500) }, + { FRAC_CONST(0.028903234474475), FRAC_CONST(0.011881310403886) }, + { FRAC_CONST(0.028866647103744), FRAC_CONST(0.011969928361855) }, + { FRAC_CONST(0.028829788029135), FRAC_CONST(0.012058433654299) }, + { FRAC_CONST(0.028792657597583), FRAC_CONST(0.012146825448172) }, + { FRAC_CONST(0.028755256158571), FRAC_CONST(0.012235102911499) }, + { FRAC_CONST(0.028717584064137), FRAC_CONST(0.012323265213377) }, + { FRAC_CONST(0.028679641668864), FRAC_CONST(0.012411311523990) }, + { FRAC_CONST(0.028641429329882), FRAC_CONST(0.012499241014612) }, + { FRAC_CONST(0.028602947406859), FRAC_CONST(0.012587052857618) }, + { FRAC_CONST(0.028564196262001), FRAC_CONST(0.012674746226488) }, + { FRAC_CONST(0.028525176260050), FRAC_CONST(0.012762320295819) }, + { FRAC_CONST(0.028485887768276), FRAC_CONST(0.012849774241331) }, + { FRAC_CONST(0.028446331156478), FRAC_CONST(0.012937107239875) }, + { FRAC_CONST(0.028406506796976), FRAC_CONST(0.013024318469437) }, + { FRAC_CONST(0.028366415064615), FRAC_CONST(0.013111407109155) }, + { FRAC_CONST(0.028326056336751), FRAC_CONST(0.013198372339315) }, + { FRAC_CONST(0.028285430993258), FRAC_CONST(0.013285213341368) }, + { FRAC_CONST(0.028244539416515), FRAC_CONST(0.013371929297933) }, + { FRAC_CONST(0.028203381991411), FRAC_CONST(0.013458519392807) }, + { FRAC_CONST(0.028161959105334), FRAC_CONST(0.013544982810971) }, + { FRAC_CONST(0.028120271148172), FRAC_CONST(0.013631318738598) }, + { FRAC_CONST(0.028078318512309), FRAC_CONST(0.013717526363062) }, + { FRAC_CONST(0.028036101592619), FRAC_CONST(0.013803604872943) }, + { FRAC_CONST(0.027993620786463), FRAC_CONST(0.013889553458039) }, + { FRAC_CONST(0.027950876493687), FRAC_CONST(0.013975371309367) }, + { FRAC_CONST(0.027907869116616), FRAC_CONST(0.014061057619178) }, + { FRAC_CONST(0.027864599060052), FRAC_CONST(0.014146611580959) }, + { FRAC_CONST(0.027821066731270), FRAC_CONST(0.014232032389445) }, + { FRAC_CONST(0.027777272540012), FRAC_CONST(0.014317319240622) }, + { FRAC_CONST(0.027733216898487), FRAC_CONST(0.014402471331737) }, + { FRAC_CONST(0.027688900221361), FRAC_CONST(0.014487487861307) }, + { FRAC_CONST(0.027644322925762), FRAC_CONST(0.014572368029123) }, + { FRAC_CONST(0.027599485431266), FRAC_CONST(0.014657111036262) }, + { FRAC_CONST(0.027554388159903), FRAC_CONST(0.014741716085090) }, + { FRAC_CONST(0.027509031536144), FRAC_CONST(0.014826182379271) }, + { FRAC_CONST(0.027463415986904), FRAC_CONST(0.014910509123778) }, + { FRAC_CONST(0.027417541941533), FRAC_CONST(0.014994695524894) }, + { FRAC_CONST(0.027371409831816), FRAC_CONST(0.015078740790225) }, + { FRAC_CONST(0.027325020091965), FRAC_CONST(0.015162644128704) }, + { FRAC_CONST(0.027278373158618), FRAC_CONST(0.015246404750603) }, + { FRAC_CONST(0.027231469470833), FRAC_CONST(0.015330021867534) }, + { FRAC_CONST(0.027184309470088), FRAC_CONST(0.015413494692460) }, + { FRAC_CONST(0.027136893600268), FRAC_CONST(0.015496822439704) }, + { FRAC_CONST(0.027089222307671), FRAC_CONST(0.015580004324954) }, + { FRAC_CONST(0.027041296040997), FRAC_CONST(0.015663039565269) }, + { FRAC_CONST(0.026993115251345), FRAC_CONST(0.015745927379091) }, + { FRAC_CONST(0.026944680392213), FRAC_CONST(0.015828666986247) }, + { FRAC_CONST(0.026895991919487), FRAC_CONST(0.015911257607961) }, + { FRAC_CONST(0.026847050291442), FRAC_CONST(0.015993698466859) }, + { FRAC_CONST(0.026797855968734), FRAC_CONST(0.016075988786976) }, + { FRAC_CONST(0.026748409414401), FRAC_CONST(0.016158127793763) }, + { FRAC_CONST(0.026698711093851), FRAC_CONST(0.016240114714099) }, + { FRAC_CONST(0.026648761474864), FRAC_CONST(0.016321948776289) }, + { FRAC_CONST(0.026598561027585), FRAC_CONST(0.016403629210082) }, + { FRAC_CONST(0.026548110224519), FRAC_CONST(0.016485155246669) }, + { FRAC_CONST(0.026497409540530), FRAC_CONST(0.016566526118696) }, + { FRAC_CONST(0.026446459452830), FRAC_CONST(0.016647741060271) }, + { FRAC_CONST(0.026395260440982), FRAC_CONST(0.016728799306966) }, + { FRAC_CONST(0.026343812986890), FRAC_CONST(0.016809700095831) }, + { FRAC_CONST(0.026292117574797), FRAC_CONST(0.016890442665397) }, + { FRAC_CONST(0.026240174691280), FRAC_CONST(0.016971026255683) }, + { FRAC_CONST(0.026187984825246), FRAC_CONST(0.017051450108208) }, + { FRAC_CONST(0.026135548467924), FRAC_CONST(0.017131713465990) }, + { FRAC_CONST(0.026082866112867), FRAC_CONST(0.017211815573560) }, + { FRAC_CONST(0.026029938255941), FRAC_CONST(0.017291755676967) }, + { FRAC_CONST(0.025976765395322), FRAC_CONST(0.017371533023784) }, + { FRAC_CONST(0.025923348031494), FRAC_CONST(0.017451146863116) }, + { FRAC_CONST(0.025869686667242), FRAC_CONST(0.017530596445607) }, + { FRAC_CONST(0.025815781807646), FRAC_CONST(0.017609881023449) }, + { FRAC_CONST(0.025761633960080), FRAC_CONST(0.017688999850383) }, + { FRAC_CONST(0.025707243634204), FRAC_CONST(0.017767952181715) }, + { FRAC_CONST(0.025652611341960), FRAC_CONST(0.017846737274313) }, + { FRAC_CONST(0.025597737597568), FRAC_CONST(0.017925354386623) }, + { FRAC_CONST(0.025542622917522), FRAC_CONST(0.018003802778671) }, + { FRAC_CONST(0.025487267820581), FRAC_CONST(0.018082081712071) }, + { FRAC_CONST(0.025431672827768), FRAC_CONST(0.018160190450031) }, + { FRAC_CONST(0.025375838462365), FRAC_CONST(0.018238128257362) }, + { FRAC_CONST(0.025319765249906), FRAC_CONST(0.018315894400484) }, + { FRAC_CONST(0.025263453718173), FRAC_CONST(0.018393488147432) }, + { FRAC_CONST(0.025206904397193), FRAC_CONST(0.018470908767865) }, + { FRAC_CONST(0.025150117819228), FRAC_CONST(0.018548155533070) }, + { FRAC_CONST(0.025093094518776), FRAC_CONST(0.018625227715971) }, + { FRAC_CONST(0.025035835032562), FRAC_CONST(0.018702124591135) }, + { FRAC_CONST(0.024978339899534), FRAC_CONST(0.018778845434780) }, + { FRAC_CONST(0.024920609660858), FRAC_CONST(0.018855389524780) }, + { FRAC_CONST(0.024862644859912), FRAC_CONST(0.018931756140672) }, + { FRAC_CONST(0.024804446042284), FRAC_CONST(0.019007944563666) }, + { FRAC_CONST(0.024746013755764), FRAC_CONST(0.019083954076646) }, + { FRAC_CONST(0.024687348550337), FRAC_CONST(0.019159783964183) }, + { FRAC_CONST(0.024628450978184), FRAC_CONST(0.019235433512536) }, + { FRAC_CONST(0.024569321593670), FRAC_CONST(0.019310902009663) }, + { FRAC_CONST(0.024509960953345), FRAC_CONST(0.019386188745225) }, + { FRAC_CONST(0.024450369615932), FRAC_CONST(0.019461293010596) }, + { FRAC_CONST(0.024390548142329), FRAC_CONST(0.019536214098866) }, + { FRAC_CONST(0.024330497095598), FRAC_CONST(0.019610951304848) }, + { FRAC_CONST(0.024270217040961), FRAC_CONST(0.019685503925087) }, + { FRAC_CONST(0.024209708545799), FRAC_CONST(0.019759871257867) }, + { FRAC_CONST(0.024148972179639), FRAC_CONST(0.019834052603212) }, + { FRAC_CONST(0.024088008514157), FRAC_CONST(0.019908047262901) }, + { FRAC_CONST(0.024026818123164), FRAC_CONST(0.019981854540467) }, + { FRAC_CONST(0.023965401582609), FRAC_CONST(0.020055473741208) }, + { FRAC_CONST(0.023903759470567), FRAC_CONST(0.020128904172192) }, + { FRAC_CONST(0.023841892367236), FRAC_CONST(0.020202145142264) }, + { FRAC_CONST(0.023779800854935), FRAC_CONST(0.020275195962052) }, + { FRAC_CONST(0.023717485518092), FRAC_CONST(0.020348055943974) }, + { FRAC_CONST(0.023654946943242), FRAC_CONST(0.020420724402244) }, + { FRAC_CONST(0.023592185719023), FRAC_CONST(0.020493200652878) }, + { FRAC_CONST(0.023529202436167), FRAC_CONST(0.020565484013703) }, + { FRAC_CONST(0.023465997687496), FRAC_CONST(0.020637573804361) }, + { FRAC_CONST(0.023402572067918), FRAC_CONST(0.020709469346314) }, + { FRAC_CONST(0.023338926174419), FRAC_CONST(0.020781169962854) }, + { FRAC_CONST(0.023275060606058), FRAC_CONST(0.020852674979108) }, + { FRAC_CONST(0.023210975963963), FRAC_CONST(0.020923983722044) }, + { FRAC_CONST(0.023146672851322), FRAC_CONST(0.020995095520475) }, + { FRAC_CONST(0.023082151873380), FRAC_CONST(0.021066009705072) }, + { FRAC_CONST(0.023017413637435), FRAC_CONST(0.021136725608363) }, + { FRAC_CONST(0.022952458752826), FRAC_CONST(0.021207242564742) }, + { FRAC_CONST(0.022887287830934), FRAC_CONST(0.021277559910478) }, + { FRAC_CONST(0.022821901485173), FRAC_CONST(0.021347676983716) }, + { FRAC_CONST(0.022756300330983), FRAC_CONST(0.021417593124488) }, + { FRAC_CONST(0.022690484985827), FRAC_CONST(0.021487307674717) }, + { FRAC_CONST(0.022624456069185), FRAC_CONST(0.021556819978223) }, + { FRAC_CONST(0.022558214202547), FRAC_CONST(0.021626129380729) }, + { FRAC_CONST(0.022491760009405), FRAC_CONST(0.021695235229869) }, + { FRAC_CONST(0.022425094115252), FRAC_CONST(0.021764136875192) }, + { FRAC_CONST(0.022358217147572), FRAC_CONST(0.021832833668171) }, + { FRAC_CONST(0.022291129735838), FRAC_CONST(0.021901324962204) }, + { FRAC_CONST(0.022223832511501), FRAC_CONST(0.021969610112625) }, + { FRAC_CONST(0.022156326107988), FRAC_CONST(0.022037688476709) }, + { FRAC_CONST(0.022088611160696), FRAC_CONST(0.022105559413676) }, + { FRAC_CONST(0.022020688306983), FRAC_CONST(0.022173222284699) }, + { FRAC_CONST(0.021952558186166), FRAC_CONST(0.022240676452909) }, + { FRAC_CONST(0.021884221439510), FRAC_CONST(0.022307921283403) }, + { FRAC_CONST(0.021815678710228), FRAC_CONST(0.022374956143245) }, + { FRAC_CONST(0.021746930643469), FRAC_CONST(0.022441780401478) }, + { FRAC_CONST(0.021677977886316), FRAC_CONST(0.022508393429127) }, + { FRAC_CONST(0.021608821087780), FRAC_CONST(0.022574794599206) }, + { FRAC_CONST(0.021539460898790), FRAC_CONST(0.022640983286719) }, + { FRAC_CONST(0.021469897972190), FRAC_CONST(0.022706958868676) }, + { FRAC_CONST(0.021400132962735), FRAC_CONST(0.022772720724087) }, + { FRAC_CONST(0.021330166527077), FRAC_CONST(0.022838268233979) }, + { FRAC_CONST(0.021259999323769), FRAC_CONST(0.022903600781391) }, + { FRAC_CONST(0.021189632013250), FRAC_CONST(0.022968717751391) }, + { FRAC_CONST(0.021119065257845), FRAC_CONST(0.023033618531071) }, + { FRAC_CONST(0.021048299721754), FRAC_CONST(0.023098302509561) }, + { FRAC_CONST(0.020977336071050), FRAC_CONST(0.023162769078031) }, + { FRAC_CONST(0.020906174973670), FRAC_CONST(0.023227017629698) }, + { FRAC_CONST(0.020834817099409), FRAC_CONST(0.023291047559828) }, + { FRAC_CONST(0.020763263119915), FRAC_CONST(0.023354858265748) }, + { FRAC_CONST(0.020691513708680), FRAC_CONST(0.023418449146848) }, + { FRAC_CONST(0.020619569541038), FRAC_CONST(0.023481819604585) }, + { FRAC_CONST(0.020547431294155), FRAC_CONST(0.023544969042494) }, + { FRAC_CONST(0.020475099647023), FRAC_CONST(0.023607896866186) }, + { FRAC_CONST(0.020402575280455), FRAC_CONST(0.023670602483363) }, + { FRAC_CONST(0.020329858877078), FRAC_CONST(0.023733085303813) }, + { FRAC_CONST(0.020256951121327), FRAC_CONST(0.023795344739427) }, + { FRAC_CONST(0.020183852699437), FRAC_CONST(0.023857380204193) }, + { FRAC_CONST(0.020110564299439), FRAC_CONST(0.023919191114211) }, + { FRAC_CONST(0.020037086611150), FRAC_CONST(0.023980776887692) }, + { FRAC_CONST(0.019963420326171), FRAC_CONST(0.024042136944968) }, + { FRAC_CONST(0.019889566137877), FRAC_CONST(0.024103270708495) }, + { FRAC_CONST(0.019815524741412), FRAC_CONST(0.024164177602859) }, + { FRAC_CONST(0.019741296833681), FRAC_CONST(0.024224857054779) }, + { FRAC_CONST(0.019666883113346), FRAC_CONST(0.024285308493120) }, + { FRAC_CONST(0.019592284280817), FRAC_CONST(0.024345531348888) }, + { FRAC_CONST(0.019517501038246), FRAC_CONST(0.024405525055242) }, + { FRAC_CONST(0.019442534089523), FRAC_CONST(0.024465289047500) }, + { FRAC_CONST(0.019367384140264), FRAC_CONST(0.024524822763141) }, + { FRAC_CONST(0.019292051897809), FRAC_CONST(0.024584125641809) }, + { FRAC_CONST(0.019216538071215), FRAC_CONST(0.024643197125323) }, + { FRAC_CONST(0.019140843371246), FRAC_CONST(0.024702036657681) }, + { FRAC_CONST(0.019064968510369), FRAC_CONST(0.024760643685063) }, + { FRAC_CONST(0.018988914202748), FRAC_CONST(0.024819017655836) }, + { FRAC_CONST(0.018912681164234), FRAC_CONST(0.024877158020562) }, + { FRAC_CONST(0.018836270112363), FRAC_CONST(0.024935064232003) }, + { FRAC_CONST(0.018759681766343), FRAC_CONST(0.024992735745123) }, + { FRAC_CONST(0.018682916847054), FRAC_CONST(0.025050172017095) }, + { FRAC_CONST(0.018605976077037), FRAC_CONST(0.025107372507308) }, + { FRAC_CONST(0.018528860180486), FRAC_CONST(0.025164336677369) }, + { FRAC_CONST(0.018451569883247), FRAC_CONST(0.025221063991110) }, + { FRAC_CONST(0.018374105912805), FRAC_CONST(0.025277553914591) }, + { FRAC_CONST(0.018296468998280), FRAC_CONST(0.025333805916107) }, + { FRAC_CONST(0.018218659870421), FRAC_CONST(0.025389819466194) }, + { FRAC_CONST(0.018140679261596), FRAC_CONST(0.025445594037630) }, + { FRAC_CONST(0.018062527905790), FRAC_CONST(0.025501129105445) }, + { FRAC_CONST(0.017984206538592), FRAC_CONST(0.025556424146920) }, + { FRAC_CONST(0.017905715897192), FRAC_CONST(0.025611478641598) }, + { FRAC_CONST(0.017827056720375), FRAC_CONST(0.025666292071285) }, + { FRAC_CONST(0.017748229748511), FRAC_CONST(0.025720863920056) }, + { FRAC_CONST(0.017669235723550), FRAC_CONST(0.025775193674260) }, + { FRAC_CONST(0.017590075389012), FRAC_CONST(0.025829280822525) }, + { FRAC_CONST(0.017510749489986), FRAC_CONST(0.025883124855762) }, + { FRAC_CONST(0.017431258773116), FRAC_CONST(0.025936725267170) }, + { FRAC_CONST(0.017351603986600), FRAC_CONST(0.025990081552242) }, + { FRAC_CONST(0.017271785880180), FRAC_CONST(0.026043193208768) }, + { FRAC_CONST(0.017191805205132), FRAC_CONST(0.026096059736841) }, + { FRAC_CONST(0.017111662714267), FRAC_CONST(0.026148680638861) }, + { FRAC_CONST(0.017031359161915), FRAC_CONST(0.026201055419541) }, + { FRAC_CONST(0.016950895303924), FRAC_CONST(0.026253183585908) }, + { FRAC_CONST(0.016870271897651), FRAC_CONST(0.026305064647313) }, + { FRAC_CONST(0.016789489701954), FRAC_CONST(0.026356698115431) }, + { FRAC_CONST(0.016708549477186), FRAC_CONST(0.026408083504269) }, + { FRAC_CONST(0.016627451985187), FRAC_CONST(0.026459220330167) }, + { FRAC_CONST(0.016546197989277), FRAC_CONST(0.026510108111806) }, + { FRAC_CONST(0.016464788254250), FRAC_CONST(0.026560746370212) }, + { FRAC_CONST(0.016383223546365), FRAC_CONST(0.026611134628757) }, + { FRAC_CONST(0.016301504633341), FRAC_CONST(0.026661272413168) }, + { FRAC_CONST(0.016219632284346), FRAC_CONST(0.026711159251530) }, + { FRAC_CONST(0.016137607269996), FRAC_CONST(0.026760794674288) }, + { FRAC_CONST(0.016055430362340), FRAC_CONST(0.026810178214254) }, + { FRAC_CONST(0.015973102334858), FRAC_CONST(0.026859309406613) }, + { FRAC_CONST(0.015890623962454), FRAC_CONST(0.026908187788922) }, + { FRAC_CONST(0.015807996021446), FRAC_CONST(0.026956812901119) }, + { FRAC_CONST(0.015725219289558), FRAC_CONST(0.027005184285527) }, + { FRAC_CONST(0.015642294545918), FRAC_CONST(0.027053301486856) }, + { FRAC_CONST(0.015559222571044), FRAC_CONST(0.027101164052208) }, + { FRAC_CONST(0.015476004146842), FRAC_CONST(0.027148771531083) }, + { FRAC_CONST(0.015392640056594), FRAC_CONST(0.027196123475380) }, + { FRAC_CONST(0.015309131084956), FRAC_CONST(0.027243219439406) }, + { FRAC_CONST(0.015225478017946), FRAC_CONST(0.027290058979875) }, + { FRAC_CONST(0.015141681642938), FRAC_CONST(0.027336641655915) }, + { FRAC_CONST(0.015057742748656), FRAC_CONST(0.027382967029073) }, + { FRAC_CONST(0.014973662125164), FRAC_CONST(0.027429034663317) }, + { FRAC_CONST(0.014889440563862), FRAC_CONST(0.027474844125040) }, + { FRAC_CONST(0.014805078857474), FRAC_CONST(0.027520394983066) }, + { FRAC_CONST(0.014720577800046), FRAC_CONST(0.027565686808654) }, + { FRAC_CONST(0.014635938186934), FRAC_CONST(0.027610719175499) }, + { FRAC_CONST(0.014551160814797), FRAC_CONST(0.027655491659740) }, + { FRAC_CONST(0.014466246481592), FRAC_CONST(0.027700003839960) }, + { FRAC_CONST(0.014381195986567), FRAC_CONST(0.027744255297195) }, + { FRAC_CONST(0.014296010130247), FRAC_CONST(0.027788245614933) }, + { FRAC_CONST(0.014210689714436), FRAC_CONST(0.027831974379120) }, + { FRAC_CONST(0.014125235542201), FRAC_CONST(0.027875441178165) }, + { FRAC_CONST(0.014039648417870), FRAC_CONST(0.027918645602941) }, + { FRAC_CONST(0.013953929147020), FRAC_CONST(0.027961587246792) }, + { FRAC_CONST(0.013868078536476), FRAC_CONST(0.028004265705534) }, + { FRAC_CONST(0.013782097394294), FRAC_CONST(0.028046680577462) }, + { FRAC_CONST(0.013695986529763), FRAC_CONST(0.028088831463351) }, + { FRAC_CONST(0.013609746753390), FRAC_CONST(0.028130717966461) }, + { FRAC_CONST(0.013523378876898), FRAC_CONST(0.028172339692540) }, + { FRAC_CONST(0.013436883713214), FRAC_CONST(0.028213696249828) }, + { FRAC_CONST(0.013350262076462), FRAC_CONST(0.028254787249062) }, + { FRAC_CONST(0.013263514781960), FRAC_CONST(0.028295612303478) }, + { FRAC_CONST(0.013176642646205), FRAC_CONST(0.028336171028814) }, + { FRAC_CONST(0.013089646486871), FRAC_CONST(0.028376463043317) }, + { FRAC_CONST(0.013002527122799), FRAC_CONST(0.028416487967743) }, + { FRAC_CONST(0.012915285373990), FRAC_CONST(0.028456245425361) }, + { FRAC_CONST(0.012827922061597), FRAC_CONST(0.028495735041960) }, + { FRAC_CONST(0.012740438007915), FRAC_CONST(0.028534956445849) }, + { FRAC_CONST(0.012652834036379), FRAC_CONST(0.028573909267859) }, + { FRAC_CONST(0.012565110971550), FRAC_CONST(0.028612593141354) }, + { FRAC_CONST(0.012477269639111), FRAC_CONST(0.028651007702224) }, + { FRAC_CONST(0.012389310865858), FRAC_CONST(0.028689152588899) }, + { FRAC_CONST(0.012301235479693), FRAC_CONST(0.028727027442343) }, + { FRAC_CONST(0.012213044309615), FRAC_CONST(0.028764631906065) }, + { FRAC_CONST(0.012124738185712), FRAC_CONST(0.028801965626115) }, + { FRAC_CONST(0.012036317939156), FRAC_CONST(0.028839028251097) }, + { FRAC_CONST(0.011947784402191), FRAC_CONST(0.028875819432161) }, + { FRAC_CONST(0.011859138408130), FRAC_CONST(0.028912338823015) }, + { FRAC_CONST(0.011770380791341), FRAC_CONST(0.028948586079925) }, + { FRAC_CONST(0.011681512387245), FRAC_CONST(0.028984560861718) }, + { FRAC_CONST(0.011592534032306), FRAC_CONST(0.029020262829785) }, + { FRAC_CONST(0.011503446564022), FRAC_CONST(0.029055691648087) }, + { FRAC_CONST(0.011414250820918), FRAC_CONST(0.029090846983152) }, + { FRAC_CONST(0.011324947642537), FRAC_CONST(0.029125728504087) }, + { FRAC_CONST(0.011235537869437), FRAC_CONST(0.029160335882573) }, + { FRAC_CONST(0.011146022343175), FRAC_CONST(0.029194668792871) }, + { FRAC_CONST(0.011056401906305), FRAC_CONST(0.029228726911828) }, + { FRAC_CONST(0.010966677402371), FRAC_CONST(0.029262509918876) }, + { FRAC_CONST(0.010876849675891), FRAC_CONST(0.029296017496036) }, + { FRAC_CONST(0.010786919572361), FRAC_CONST(0.029329249327922) }, + { FRAC_CONST(0.010696887938235), FRAC_CONST(0.029362205101743) }, + { FRAC_CONST(0.010606755620926), FRAC_CONST(0.029394884507308) }, + { FRAC_CONST(0.010516523468793), FRAC_CONST(0.029427287237024) }, + { FRAC_CONST(0.010426192331137), FRAC_CONST(0.029459412985906) }, + { FRAC_CONST(0.010335763058187), FRAC_CONST(0.029491261451573) }, + { FRAC_CONST(0.010245236501099), FRAC_CONST(0.029522832334255) }, + { FRAC_CONST(0.010154613511943), FRAC_CONST(0.029554125336796) }, + { FRAC_CONST(0.010063894943698), FRAC_CONST(0.029585140164654) }, + { FRAC_CONST(0.009973081650240), FRAC_CONST(0.029615876525905) }, + { FRAC_CONST(0.009882174486340), FRAC_CONST(0.029646334131247) }, + { FRAC_CONST(0.009791174307650), FRAC_CONST(0.029676512694001) }, + { FRAC_CONST(0.009700081970699), FRAC_CONST(0.029706411930116) }, + { FRAC_CONST(0.009608898332881), FRAC_CONST(0.029736031558168) }, + { FRAC_CONST(0.009517624252453), FRAC_CONST(0.029765371299366) }, + { FRAC_CONST(0.009426260588521), FRAC_CONST(0.029794430877553) }, + { FRAC_CONST(0.009334808201034), FRAC_CONST(0.029823210019210) }, + { FRAC_CONST(0.009243267950778), FRAC_CONST(0.029851708453456) }, + { FRAC_CONST(0.009151640699363), FRAC_CONST(0.029879925912053) }, + { FRAC_CONST(0.009059927309220), FRAC_CONST(0.029907862129408) }, + { FRAC_CONST(0.008968128643591), FRAC_CONST(0.029935516842573) }, + { FRAC_CONST(0.008876245566520), FRAC_CONST(0.029962889791254) }, + { FRAC_CONST(0.008784278942845), FRAC_CONST(0.029989980717805) }, + { FRAC_CONST(0.008692229638191), FRAC_CONST(0.030016789367235) }, + { FRAC_CONST(0.008600098518961), FRAC_CONST(0.030043315487212) }, + { FRAC_CONST(0.008507886452329), FRAC_CONST(0.030069558828062) }, + { FRAC_CONST(0.008415594306230), FRAC_CONST(0.030095519142772) }, + { FRAC_CONST(0.008323222949351), FRAC_CONST(0.030121196186994) }, + { FRAC_CONST(0.008230773251129), FRAC_CONST(0.030146589719046) }, + { FRAC_CONST(0.008138246081733), FRAC_CONST(0.030171699499915) }, + { FRAC_CONST(0.008045642312067), FRAC_CONST(0.030196525293257) }, + { FRAC_CONST(0.007952962813750), FRAC_CONST(0.030221066865402) }, + { FRAC_CONST(0.007860208459119), FRAC_CONST(0.030245323985357) }, + { FRAC_CONST(0.007767380121212), FRAC_CONST(0.030269296424803) }, + { FRAC_CONST(0.007674478673766), FRAC_CONST(0.030292983958103) }, + { FRAC_CONST(0.007581504991203), FRAC_CONST(0.030316386362302) }, + { FRAC_CONST(0.007488459948628), FRAC_CONST(0.030339503417126) }, + { FRAC_CONST(0.007395344421816), FRAC_CONST(0.030362334904989) }, + { FRAC_CONST(0.007302159287206), FRAC_CONST(0.030384880610993) }, + { FRAC_CONST(0.007208905421891), FRAC_CONST(0.030407140322928) }, + { FRAC_CONST(0.007115583703613), FRAC_CONST(0.030429113831278) }, + { FRAC_CONST(0.007022195010752), FRAC_CONST(0.030450800929220) }, + { FRAC_CONST(0.006928740222316), FRAC_CONST(0.030472201412626) }, + { FRAC_CONST(0.006835220217939), FRAC_CONST(0.030493315080068) }, + { FRAC_CONST(0.006741635877866), FRAC_CONST(0.030514141732814) }, + { FRAC_CONST(0.006647988082948), FRAC_CONST(0.030534681174838) }, + { FRAC_CONST(0.006554277714635), FRAC_CONST(0.030554933212813) }, + { FRAC_CONST(0.006460505654964), FRAC_CONST(0.030574897656119) }, + { FRAC_CONST(0.006366672786553), FRAC_CONST(0.030594574316845) }, + { FRAC_CONST(0.006272779992593), FRAC_CONST(0.030613963009786) }, + { FRAC_CONST(0.006178828156839), FRAC_CONST(0.030633063552447) }, + { FRAC_CONST(0.006084818163601), FRAC_CONST(0.030651875765048) }, + { FRAC_CONST(0.005990750897737), FRAC_CONST(0.030670399470520) }, + { FRAC_CONST(0.005896627244644), FRAC_CONST(0.030688634494512) }, + { FRAC_CONST(0.005802448090250), FRAC_CONST(0.030706580665388) }, + { FRAC_CONST(0.005708214321004), FRAC_CONST(0.030724237814232) }, + { FRAC_CONST(0.005613926823871), FRAC_CONST(0.030741605774849) }, + { FRAC_CONST(0.005519586486321), FRAC_CONST(0.030758684383764) }, + { FRAC_CONST(0.005425194196321), FRAC_CONST(0.030775473480228) }, + { FRAC_CONST(0.005330750842327), FRAC_CONST(0.030791972906214) }, + { FRAC_CONST(0.005236257313276), FRAC_CONST(0.030808182506425) }, + { FRAC_CONST(0.005141714498576), FRAC_CONST(0.030824102128288) }, + { FRAC_CONST(0.005047123288102), FRAC_CONST(0.030839731621963) }, + { FRAC_CONST(0.004952484572181), FRAC_CONST(0.030855070840339) }, + { FRAC_CONST(0.004857799241589), FRAC_CONST(0.030870119639036) }, + { FRAC_CONST(0.004763068187541), FRAC_CONST(0.030884877876411) }, + { FRAC_CONST(0.004668292301681), FRAC_CONST(0.030899345413553) }, + { FRAC_CONST(0.004573472476075), FRAC_CONST(0.030913522114288) }, + { FRAC_CONST(0.004478609603205), FRAC_CONST(0.030927407845180) }, + { FRAC_CONST(0.004383704575956), FRAC_CONST(0.030941002475530) }, + { FRAC_CONST(0.004288758287610), FRAC_CONST(0.030954305877381) }, + { FRAC_CONST(0.004193771631837), FRAC_CONST(0.030967317925516) }, + { FRAC_CONST(0.004098745502689), FRAC_CONST(0.030980038497461) }, + { FRAC_CONST(0.004003680794587), FRAC_CONST(0.030992467473486) }, + { FRAC_CONST(0.003908578402316), FRAC_CONST(0.031004604736602) }, + { FRAC_CONST(0.003813439221017), FRAC_CONST(0.031016450172571) }, + { FRAC_CONST(0.003718264146176), FRAC_CONST(0.031028003669899) }, + { FRAC_CONST(0.003623054073616), FRAC_CONST(0.031039265119839) }, + { FRAC_CONST(0.003527809899492), FRAC_CONST(0.031050234416394) }, + { FRAC_CONST(0.003432532520278), FRAC_CONST(0.031060911456318) }, + { FRAC_CONST(0.003337222832760), FRAC_CONST(0.031071296139114) }, + { FRAC_CONST(0.003241881734029), FRAC_CONST(0.031081388367037) }, + { FRAC_CONST(0.003146510121474), FRAC_CONST(0.031091188045095) }, + { FRAC_CONST(0.003051108892766), FRAC_CONST(0.031100695081051) }, + { FRAC_CONST(0.002955678945860), FRAC_CONST(0.031109909385419) }, + { FRAC_CONST(0.002860221178978), FRAC_CONST(0.031118830871473) }, + { FRAC_CONST(0.002764736490604), FRAC_CONST(0.031127459455239) }, + { FRAC_CONST(0.002669225779478), FRAC_CONST(0.031135795055501) }, + { FRAC_CONST(0.002573689944583), FRAC_CONST(0.031143837593803) }, + { FRAC_CONST(0.002478129885137), FRAC_CONST(0.031151586994444) }, + { FRAC_CONST(0.002382546500589), FRAC_CONST(0.031159043184484) }, + { FRAC_CONST(0.002286940690606), FRAC_CONST(0.031166206093743) }, + { FRAC_CONST(0.002191313355067), FRAC_CONST(0.031173075654800) }, + { FRAC_CONST(0.002095665394051), FRAC_CONST(0.031179651802998) }, + { FRAC_CONST(0.001999997707835), FRAC_CONST(0.031185934476438) }, + { FRAC_CONST(0.001904311196878), FRAC_CONST(0.031191923615985) }, + { FRAC_CONST(0.001808606761820), FRAC_CONST(0.031197619165268) }, + { FRAC_CONST(0.001712885303465), FRAC_CONST(0.031203021070678) }, + { FRAC_CONST(0.001617147722782), FRAC_CONST(0.031208129281370) }, + { FRAC_CONST(0.001521394920889), FRAC_CONST(0.031212943749264) }, + { FRAC_CONST(0.001425627799047), FRAC_CONST(0.031217464429043) }, + { FRAC_CONST(0.001329847258653), FRAC_CONST(0.031221691278159) }, + { FRAC_CONST(0.001234054201231), FRAC_CONST(0.031225624256825) }, + { FRAC_CONST(0.001138249528420), FRAC_CONST(0.031229263328024) }, + { FRAC_CONST(0.001042434141971), FRAC_CONST(0.031232608457502) }, + { FRAC_CONST(0.000946608943736), FRAC_CONST(0.031235659613775) }, + { FRAC_CONST(0.000850774835656), FRAC_CONST(0.031238416768124) }, + { FRAC_CONST(0.000754932719759), FRAC_CONST(0.031240879894597) }, + { FRAC_CONST(0.000659083498149), FRAC_CONST(0.031243048970010) }, + { FRAC_CONST(0.000563228072993), FRAC_CONST(0.031244923973948) }, + { FRAC_CONST(0.000467367346520), FRAC_CONST(0.031246504888762) }, + { FRAC_CONST(0.000371502221008), FRAC_CONST(0.031247791699571) }, + { FRAC_CONST(0.000275633598775), FRAC_CONST(0.031248784394264) }, + { FRAC_CONST(0.000179762382174), FRAC_CONST(0.031249482963498) }, + { FRAC_CONST(0.000083889473581), FRAC_CONST(0.031249887400697) } +}; + +/* 64 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_256[] = +{ + { FRAC_CONST(0.088387931675923), FRAC_CONST(0.000271171628935) }, + { FRAC_CONST(0.088354655998507), FRAC_CONST(0.002440238387037) }, + { FRAC_CONST(0.088268158780110), FRAC_CONST(0.004607835236780) }, + { FRAC_CONST(0.088128492123423), FRAC_CONST(0.006772656498875) }, + { FRAC_CONST(0.087935740158418), FRAC_CONST(0.008933398165942) }, + { FRAC_CONST(0.087690018991670), FRAC_CONST(0.011088758687994) }, + { FRAC_CONST(0.087391476636423), FRAC_CONST(0.013237439756448) }, + { FRAC_CONST(0.087040292923427), FRAC_CONST(0.015378147086172) }, + { FRAC_CONST(0.086636679392621), FRAC_CONST(0.017509591195118) }, + { FRAC_CONST(0.086180879165703), FRAC_CONST(0.019630488181053) }, + { FRAC_CONST(0.085673166799686), FRAC_CONST(0.021739560494940) }, + { FRAC_CONST(0.085113848121515), FRAC_CONST(0.023835537710479) }, + { FRAC_CONST(0.084503260043847), FRAC_CONST(0.025917157289369) }, + { FRAC_CONST(0.083841770362110), FRAC_CONST(0.027983165341813) }, + { FRAC_CONST(0.083129777532952), FRAC_CONST(0.030032317381813) }, + { FRAC_CONST(0.082367710434230), FRAC_CONST(0.032063379076803) }, + { FRAC_CONST(0.081556028106671), FRAC_CONST(0.034075126991164) }, + { FRAC_CONST(0.080695219477356), FRAC_CONST(0.036066349323177) }, + { FRAC_CONST(0.079785803065216), FRAC_CONST(0.038035846634965) }, + { FRAC_CONST(0.078828326668693), FRAC_CONST(0.039982432574992) }, + { FRAC_CONST(0.077823367035766), FRAC_CONST(0.041904934592675) }, + { FRAC_CONST(0.076771529516540), FRAC_CONST(0.043802194644686) }, + { FRAC_CONST(0.075673447698606), FRAC_CONST(0.045673069892513) }, + { FRAC_CONST(0.074529783025390), FRAC_CONST(0.047516433390863) }, + { FRAC_CONST(0.073341224397728), FRAC_CONST(0.049331174766491) }, + { FRAC_CONST(0.072108487758894), FRAC_CONST(0.051116200887052) }, + { FRAC_CONST(0.070832315663343), FRAC_CONST(0.052870436519557) }, + { FRAC_CONST(0.069513476829429), FRAC_CONST(0.054592824978055) }, + { FRAC_CONST(0.068152765676348), FRAC_CONST(0.056282328760143) }, + { FRAC_CONST(0.066751001845620), FRAC_CONST(0.057937930171918) }, + { FRAC_CONST(0.065309029707361), FRAC_CONST(0.059558631940996) }, + { FRAC_CONST(0.063827717851668), FRAC_CONST(0.061143457817234) }, + { FRAC_CONST(0.062307958565413), FRAC_CONST(0.062691453160784) }, + { FRAC_CONST(0.060750667294763), FRAC_CONST(0.064201685517134) }, + { FRAC_CONST(0.059156782093749), FRAC_CONST(0.065673245178784) }, + { FRAC_CONST(0.057527263059216), FRAC_CONST(0.067105245733220) }, + { FRAC_CONST(0.055863091752499), FRAC_CONST(0.068496824596852) }, + { FRAC_CONST(0.054165270608165), FRAC_CONST(0.069847143534609) }, + { FRAC_CONST(0.052434822330188), FRAC_CONST(0.071155389164853) }, + { FRAC_CONST(0.050672789275903), FRAC_CONST(0.072420773449336) }, + { FRAC_CONST(0.048880232828135), FRAC_CONST(0.073642534167879) }, + { FRAC_CONST(0.047058232755862), FRAC_CONST(0.074819935377512) }, + { FRAC_CONST(0.045207886563797), FRAC_CONST(0.075952267855771) }, + { FRAC_CONST(0.043330308831298), FRAC_CONST(0.077038849527912) }, + { FRAC_CONST(0.041426630540984), FRAC_CONST(0.078079025877766) }, + { FRAC_CONST(0.039497998397473), FRAC_CONST(0.079072170341994) }, + { FRAC_CONST(0.037545574136653), FRAC_CONST(0.080017684687506) }, + { FRAC_CONST(0.035570533825892), FRAC_CONST(0.080914999371817) }, + { FRAC_CONST(0.033574067155622), FRAC_CONST(0.081763573886112) }, + { FRAC_CONST(0.031557376722714), FRAC_CONST(0.082562897080836) }, + { FRAC_CONST(0.029521677306074), FRAC_CONST(0.083312487473584) }, + { FRAC_CONST(0.027468195134911), FRAC_CONST(0.084011893539132) }, + { FRAC_CONST(0.025398167150101), FRAC_CONST(0.084660693981419) }, + { FRAC_CONST(0.023312840259098), FRAC_CONST(0.085258497987320) }, + { FRAC_CONST(0.021213470584847), FRAC_CONST(0.085804945462053) }, + { FRAC_CONST(0.019101322709138), FRAC_CONST(0.086299707246093) }, + { FRAC_CONST(0.016977668910873), FRAC_CONST(0.086742485313442) }, + { FRAC_CONST(0.014843788399692), FRAC_CONST(0.087133012951149) }, + { FRAC_CONST(0.012700966545425), FRAC_CONST(0.087471054919968) }, + { FRAC_CONST(0.010550494103830), FRAC_CONST(0.087756407596056) }, + { FRAC_CONST(0.008393666439096), FRAC_CONST(0.087988899093631) }, + { FRAC_CONST(0.006231782743558), FRAC_CONST(0.088168389368510) }, + { FRAC_CONST(0.004066145255116), FRAC_CONST(0.088294770302461) }, + { FRAC_CONST(0.001898058472816), FRAC_CONST(0.088367965768336) } +}; + +#ifdef LD_DEC +/* 128 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_1024[] = +{ + { FRAC_CONST(0.044194160825012), FRAC_CONST(0.000033896503468) }, + { FRAC_CONST(0.044193120897389), FRAC_CONST(0.000305066138364) }, + { FRAC_CONST(0.044190417123742), FRAC_CONST(0.000576224287693) }, + { FRAC_CONST(0.044186049605866), FRAC_CONST(0.000847360742503) }, + { FRAC_CONST(0.044180018508197), FRAC_CONST(0.001118465294660) }, + { FRAC_CONST(0.044172324057802), FRAC_CONST(0.001389527737231) }, + { FRAC_CONST(0.044162966544372), FRAC_CONST(0.001660537864867) }, + { FRAC_CONST(0.044151946320213), FRAC_CONST(0.001931485474192) }, + { FRAC_CONST(0.044139263800230), FRAC_CONST(0.002202360364180) }, + { FRAC_CONST(0.044124919461912), FRAC_CONST(0.002473152336546) }, + { FRAC_CONST(0.044108913845316), FRAC_CONST(0.002743851196123) }, + { FRAC_CONST(0.044091247553044), FRAC_CONST(0.003014446751254) }, + { FRAC_CONST(0.044071921250223), FRAC_CONST(0.003284928814169) }, + { FRAC_CONST(0.044050935664476), FRAC_CONST(0.003555287201370) }, + { FRAC_CONST(0.044028291585898), FRAC_CONST(0.003825511734018) }, + { FRAC_CONST(0.044003989867028), FRAC_CONST(0.004095592238311) }, + { FRAC_CONST(0.043978031422810), FRAC_CONST(0.004365518545871) }, + { FRAC_CONST(0.043950417230565), FRAC_CONST(0.004635280494126) }, + { FRAC_CONST(0.043921148329953), FRAC_CONST(0.004904867926689) }, + { FRAC_CONST(0.043890225822930), FRAC_CONST(0.005174270693748) }, + { FRAC_CONST(0.043857650873712), FRAC_CONST(0.005443478652439) }, + { FRAC_CONST(0.043823424708727), FRAC_CONST(0.005712481667236) }, + { FRAC_CONST(0.043787548616571), FRAC_CONST(0.005981269610326) }, + { FRAC_CONST(0.043750023947958), FRAC_CONST(0.006249832361997) }, + { FRAC_CONST(0.043710852115672), FRAC_CONST(0.006518159811011) }, + { FRAC_CONST(0.043670034594508), FRAC_CONST(0.006786241854993) }, + { FRAC_CONST(0.043627572921225), FRAC_CONST(0.007054068400804) }, + { FRAC_CONST(0.043583468694479), FRAC_CONST(0.007321629364927) }, + { FRAC_CONST(0.043537723574771), FRAC_CONST(0.007588914673843) }, + { FRAC_CONST(0.043490339284377), FRAC_CONST(0.007855914264410) }, + { FRAC_CONST(0.043441317607290), FRAC_CONST(0.008122618084246) }, + { FRAC_CONST(0.043390660389149), FRAC_CONST(0.008389016092101) }, + { FRAC_CONST(0.043338369537168), FRAC_CONST(0.008655098258243) }, + { FRAC_CONST(0.043284447020070), FRAC_CONST(0.008920854564826) }, + { FRAC_CONST(0.043228894868005), FRAC_CONST(0.009186275006278) }, + { FRAC_CONST(0.043171715172482), FRAC_CONST(0.009451349589667) }, + { FRAC_CONST(0.043112910086283), FRAC_CONST(0.009716068335087) }, + { FRAC_CONST(0.043052481823387), FRAC_CONST(0.009980421276025) }, + { FRAC_CONST(0.042990432658884), FRAC_CONST(0.010244398459743) }, + { FRAC_CONST(0.042926764928889), FRAC_CONST(0.010507989947649) }, + { FRAC_CONST(0.042861481030457), FRAC_CONST(0.010771185815673) }, + { FRAC_CONST(0.042794583421490), FRAC_CONST(0.011033976154639) }, + { FRAC_CONST(0.042726074620644), FRAC_CONST(0.011296351070639) }, + { FRAC_CONST(0.042655957207238), FRAC_CONST(0.011558300685406) }, + { FRAC_CONST(0.042584233821153), FRAC_CONST(0.011819815136685) }, + { FRAC_CONST(0.042510907162732), FRAC_CONST(0.012080884578604) }, + { FRAC_CONST(0.042435979992684), FRAC_CONST(0.012341499182048) }, + { FRAC_CONST(0.042359455131975), FRAC_CONST(0.012601649135022) }, + { FRAC_CONST(0.042281335461721), FRAC_CONST(0.012861324643029) }, + { FRAC_CONST(0.042201623923085), FRAC_CONST(0.013120515929433) }, + { FRAC_CONST(0.042120323517160), FRAC_CONST(0.013379213235827) }, + { FRAC_CONST(0.042037437304862), FRAC_CONST(0.013637406822406) }, + { FRAC_CONST(0.041952968406809), FRAC_CONST(0.013895086968325) }, + { FRAC_CONST(0.041866920003207), FRAC_CONST(0.014152243972073) }, + { FRAC_CONST(0.041779295333730), FRAC_CONST(0.014408868151835) }, + { FRAC_CONST(0.041690097697398), FRAC_CONST(0.014664949845855) }, + { FRAC_CONST(0.041599330452450), FRAC_CONST(0.014920479412801) }, + { FRAC_CONST(0.041506997016224), FRAC_CONST(0.015175447232131) }, + { FRAC_CONST(0.041413100865019), FRAC_CONST(0.015429843704450) }, + { FRAC_CONST(0.041317645533974), FRAC_CONST(0.015683659251874) }, + { FRAC_CONST(0.041220634616927), FRAC_CONST(0.015936884318392) }, + { FRAC_CONST(0.041122071766285), FRAC_CONST(0.016189509370223) }, + { FRAC_CONST(0.041021960692883), FRAC_CONST(0.016441524896177) }, + { FRAC_CONST(0.040920305165846), FRAC_CONST(0.016692921408010) }, + { FRAC_CONST(0.040817109012449), FRAC_CONST(0.016943689440788) }, + { FRAC_CONST(0.040712376117967), FRAC_CONST(0.017193819553235) }, + { FRAC_CONST(0.040606110425535), FRAC_CONST(0.017443302328094) }, + { FRAC_CONST(0.040498315935996), FRAC_CONST(0.017692128372479) }, + { FRAC_CONST(0.040388996707752), FRAC_CONST(0.017940288318230) }, + { FRAC_CONST(0.040278156856609), FRAC_CONST(0.018187772822267) }, + { FRAC_CONST(0.040165800555627), FRAC_CONST(0.018434572566936) }, + { FRAC_CONST(0.040051932034955), FRAC_CONST(0.018680678260367) }, + { FRAC_CONST(0.039936555581679), FRAC_CONST(0.018926080636820) }, + { FRAC_CONST(0.039819675539659), FRAC_CONST(0.019170770457035) }, + { FRAC_CONST(0.039701296309360), FRAC_CONST(0.019414738508577) }, + { FRAC_CONST(0.039581422347694), FRAC_CONST(0.019657975606187) }, + { FRAC_CONST(0.039460058167849), FRAC_CONST(0.019900472592126) }, + { FRAC_CONST(0.039337208339116), FRAC_CONST(0.020142220336521) }, + { FRAC_CONST(0.039212877486723), FRAC_CONST(0.020383209737704) }, + { FRAC_CONST(0.039087070291656), FRAC_CONST(0.020623431722561) }, + { FRAC_CONST(0.038959791490485), FRAC_CONST(0.020862877246870) }, + { FRAC_CONST(0.038831045875184), FRAC_CONST(0.021101537295642) }, + { FRAC_CONST(0.038700838292953), FRAC_CONST(0.021339402883462) }, + { FRAC_CONST(0.038569173646034), FRAC_CONST(0.021576465054824) }, + { FRAC_CONST(0.038436056891527), FRAC_CONST(0.021812714884472) }, + { FRAC_CONST(0.038301493041202), FRAC_CONST(0.022048143477734) }, + { FRAC_CONST(0.038165487161312), FRAC_CONST(0.022282741970855) }, + { FRAC_CONST(0.038028044372402), FRAC_CONST(0.022516501531335) }, + { FRAC_CONST(0.037889169849115), FRAC_CONST(0.022749413358259) }, + { FRAC_CONST(0.037748868819998), FRAC_CONST(0.022981468682628) }, + { FRAC_CONST(0.037607146567305), FRAC_CONST(0.023212658767690) }, + { FRAC_CONST(0.037464008426800), FRAC_CONST(0.023442974909269) }, + { FRAC_CONST(0.037319459787553), FRAC_CONST(0.023672408436094) }, + { FRAC_CONST(0.037173506091737), FRAC_CONST(0.023900950710120) }, + { FRAC_CONST(0.037026152834428), FRAC_CONST(0.024128593126861) }, + { FRAC_CONST(0.036877405563392), FRAC_CONST(0.024355327115708) }, + { FRAC_CONST(0.036727269878879), FRAC_CONST(0.024581144140255) }, + { FRAC_CONST(0.036575751433414), FRAC_CONST(0.024806035698618) }, + { FRAC_CONST(0.036422855931580), FRAC_CONST(0.025029993323758) }, + { FRAC_CONST(0.036268589129807), FRAC_CONST(0.025253008583796) }, + { FRAC_CONST(0.036112956836151), FRAC_CONST(0.025475073082334) }, + { FRAC_CONST(0.035955964910083), FRAC_CONST(0.025696178458769) }, + { FRAC_CONST(0.035797619262257), FRAC_CONST(0.025916316388609) }, + { FRAC_CONST(0.035637925854300), FRAC_CONST(0.026135478583784) }, + { FRAC_CONST(0.035476890698576), FRAC_CONST(0.026353656792963) }, + { FRAC_CONST(0.035314519857970), FRAC_CONST(0.026570842801858) }, + { FRAC_CONST(0.035150819445650), FRAC_CONST(0.026787028433540) }, + { FRAC_CONST(0.034985795624846), FRAC_CONST(0.027002205548742) }, + { FRAC_CONST(0.034819454608610), FRAC_CONST(0.027216366046166) }, + { FRAC_CONST(0.034651802659589), FRAC_CONST(0.027429501862792) }, + { FRAC_CONST(0.034482846089783), FRAC_CONST(0.027641604974175) }, + { FRAC_CONST(0.034312591260311), FRAC_CONST(0.027852667394755) }, + { FRAC_CONST(0.034141044581172), FRAC_CONST(0.028062681178149) }, + { FRAC_CONST(0.033968212511001), FRAC_CONST(0.028271638417458) }, + { FRAC_CONST(0.033794101556828), FRAC_CONST(0.028479531245560) }, + { FRAC_CONST(0.033618718273831), FRAC_CONST(0.028686351835407) }, + { FRAC_CONST(0.033442069265093), FRAC_CONST(0.028892092400321) }, + { FRAC_CONST(0.033264161181349), FRAC_CONST(0.029096745194286) }, + { FRAC_CONST(0.033085000720737), FRAC_CONST(0.029300302512241) }, + { FRAC_CONST(0.032904594628548), FRAC_CONST(0.029502756690366) }, + { FRAC_CONST(0.032722949696969), FRAC_CONST(0.029704100106376) }, + { FRAC_CONST(0.032540072764829), FRAC_CONST(0.029904325179807) }, + { FRAC_CONST(0.032355970717341), FRAC_CONST(0.030103424372297) }, + { FRAC_CONST(0.032170650485843), FRAC_CONST(0.030301390187873) }, + { FRAC_CONST(0.031984119047537), FRAC_CONST(0.030498215173235) }, + { FRAC_CONST(0.031796383425227), FRAC_CONST(0.030693891918034) }, + { FRAC_CONST(0.031607450687052), FRAC_CONST(0.030888413055150) }, + { FRAC_CONST(0.031417327946223), FRAC_CONST(0.031081771260973) }, + { FRAC_CONST(0.031226022360754), FRAC_CONST(0.031273959255676) }, + { FRAC_CONST(0.031033541133193), FRAC_CONST(0.031464969803488) }, + { FRAC_CONST(0.030839891510348), FRAC_CONST(0.031654795712972) }, + { FRAC_CONST(0.030645080783018), FRAC_CONST(0.031843429837288) }, + { FRAC_CONST(0.030449116285718), FRAC_CONST(0.032030865074469) }, + { FRAC_CONST(0.030252005396399), FRAC_CONST(0.032217094367684) }, + { FRAC_CONST(0.030053755536176), FRAC_CONST(0.032402110705505) }, + { FRAC_CONST(0.029854374169043), FRAC_CONST(0.032585907122172) }, + { FRAC_CONST(0.029653868801596), FRAC_CONST(0.032768476697853) }, + { FRAC_CONST(0.029452246982750), FRAC_CONST(0.032949812558907) }, + { FRAC_CONST(0.029249516303451), FRAC_CONST(0.033129907878142) }, + { FRAC_CONST(0.029045684396395), FRAC_CONST(0.033308755875070) }, + { FRAC_CONST(0.028840758935738), FRAC_CONST(0.033486349816166) }, + { FRAC_CONST(0.028634747636808), FRAC_CONST(0.033662683015118) }, + { FRAC_CONST(0.028427658255815), FRAC_CONST(0.033837748833080) }, + { FRAC_CONST(0.028219498589555), FRAC_CONST(0.034011540678924) }, + { FRAC_CONST(0.028010276475123), FRAC_CONST(0.034184052009485) }, + { FRAC_CONST(0.027799999789613), FRAC_CONST(0.034355276329809) }, + { FRAC_CONST(0.027588676449824), FRAC_CONST(0.034525207193396) }, + { FRAC_CONST(0.027376314411959), FRAC_CONST(0.034693838202447) }, + { FRAC_CONST(0.027162921671330), FRAC_CONST(0.034861163008098) }, + { FRAC_CONST(0.026948506262053), FRAC_CONST(0.035027175310665) }, + { FRAC_CONST(0.026733076256746), FRAC_CONST(0.035191868859880) }, + { FRAC_CONST(0.026516639766228), FRAC_CONST(0.035355237455122) }, + { FRAC_CONST(0.026299204939210), FRAC_CONST(0.035517274945657) }, + { FRAC_CONST(0.026080779961991), FRAC_CONST(0.035677975230865) }, + { FRAC_CONST(0.025861373058146), FRAC_CONST(0.035837332260471) }, + { FRAC_CONST(0.025640992488223), FRAC_CONST(0.035995340034772) }, + { FRAC_CONST(0.025419646549425), FRAC_CONST(0.036151992604866) }, + { FRAC_CONST(0.025197343575302), FRAC_CONST(0.036307284072871) }, + { FRAC_CONST(0.024974091935435), FRAC_CONST(0.036461208592152) }, + { FRAC_CONST(0.024749900035122), FRAC_CONST(0.036613760367538) }, + { FRAC_CONST(0.024524776315061), FRAC_CONST(0.036764933655540) }, + { FRAC_CONST(0.024298729251033), FRAC_CONST(0.036914722764569) }, + { FRAC_CONST(0.024071767353583), FRAC_CONST(0.037063122055150) }, + { FRAC_CONST(0.023843899167697), FRAC_CONST(0.037210125940135) }, + { FRAC_CONST(0.023615133272485), FRAC_CONST(0.037355728884908) }, + { FRAC_CONST(0.023385478280852), FRAC_CONST(0.037499925407603) }, + { FRAC_CONST(0.023154942839179), FRAC_CONST(0.037642710079302) }, + { FRAC_CONST(0.022923535626995), FRAC_CONST(0.037784077524241) }, + { FRAC_CONST(0.022691265356652), FRAC_CONST(0.037924022420018) }, + { FRAC_CONST(0.022458140772993), FRAC_CONST(0.038062539497785) }, + { FRAC_CONST(0.022224170653027), FRAC_CONST(0.038199623542453) }, + { FRAC_CONST(0.021989363805598), FRAC_CONST(0.038335269392885) }, + { FRAC_CONST(0.021753729071049), FRAC_CONST(0.038469471942092) }, + { FRAC_CONST(0.021517275320897), FRAC_CONST(0.038602226137423) }, + { FRAC_CONST(0.021280011457490), FRAC_CONST(0.038733526980758) }, + { FRAC_CONST(0.021041946413679), FRAC_CONST(0.038863369528695) }, + { FRAC_CONST(0.020803089152479), FRAC_CONST(0.038991748892734) }, + { FRAC_CONST(0.020563448666730), FRAC_CONST(0.039118660239466) }, + { FRAC_CONST(0.020323033978761), FRAC_CONST(0.039244098790750) }, + { FRAC_CONST(0.020081854140050), FRAC_CONST(0.039368059823895) }, + { FRAC_CONST(0.019839918230880), FRAC_CONST(0.039490538671839) }, + { FRAC_CONST(0.019597235360003), FRAC_CONST(0.039611530723322) }, + { FRAC_CONST(0.019353814664291), FRAC_CONST(0.039731031423061) }, + { FRAC_CONST(0.019109665308395), FRAC_CONST(0.039849036271924) }, + { FRAC_CONST(0.018864796484402), FRAC_CONST(0.039965540827094) }, + { FRAC_CONST(0.018619217411483), FRAC_CONST(0.040080540702240) }, + { FRAC_CONST(0.018372937335552), FRAC_CONST(0.040194031567683) }, + { FRAC_CONST(0.018125965528915), FRAC_CONST(0.040306009150554) }, + { FRAC_CONST(0.017878311289921), FRAC_CONST(0.040416469234963) }, + { FRAC_CONST(0.017629983942612), FRAC_CONST(0.040525407662148) }, + { FRAC_CONST(0.017380992836371), FRAC_CONST(0.040632820330639) }, + { FRAC_CONST(0.017131347345575), FRAC_CONST(0.040738703196411) }, + { FRAC_CONST(0.016881056869233), FRAC_CONST(0.040843052273033) }, + { FRAC_CONST(0.016630130830641), FRAC_CONST(0.040945863631822) }, + { FRAC_CONST(0.016378578677023), FRAC_CONST(0.041047133401988) }, + { FRAC_CONST(0.016126409879175), FRAC_CONST(0.041146857770781) }, + { FRAC_CONST(0.015873633931110), FRAC_CONST(0.041245032983635) }, + { FRAC_CONST(0.015620260349699), FRAC_CONST(0.041341655344309) }, + { FRAC_CONST(0.015366298674314), FRAC_CONST(0.041436721215026) }, + { FRAC_CONST(0.015111758466470), FRAC_CONST(0.041530227016609) }, + { FRAC_CONST(0.014856649309460), FRAC_CONST(0.041622169228618) }, + { FRAC_CONST(0.014600980808001), FRAC_CONST(0.041712544389481) }, + { FRAC_CONST(0.014344762587867), FRAC_CONST(0.041801349096623) }, + { FRAC_CONST(0.014088004295529), FRAC_CONST(0.041888580006598) }, + { FRAC_CONST(0.013830715597792), FRAC_CONST(0.041974233835211) }, + { FRAC_CONST(0.013572906181430), FRAC_CONST(0.042058307357645) }, + { FRAC_CONST(0.013314585752822), FRAC_CONST(0.042140797408577) }, + { FRAC_CONST(0.013055764037585), FRAC_CONST(0.042221700882306) }, + { FRAC_CONST(0.012796450780212), FRAC_CONST(0.042301014732860) }, + { FRAC_CONST(0.012536655743699), FRAC_CONST(0.042378735974118) }, + { FRAC_CONST(0.012276388709183), FRAC_CONST(0.042454861679919) }, + { FRAC_CONST(0.012015659475571), FRAC_CONST(0.042529388984173) }, + { FRAC_CONST(0.011754477859172), FRAC_CONST(0.042602315080970) }, + { FRAC_CONST(0.011492853693324), FRAC_CONST(0.042673637224683) }, + { FRAC_CONST(0.011230796828031), FRAC_CONST(0.042743352730074) }, + { FRAC_CONST(0.010968317129584), FRAC_CONST(0.042811458972393) }, + { FRAC_CONST(0.010705424480197), FRAC_CONST(0.042877953387479) }, + { FRAC_CONST(0.010442128777629), FRAC_CONST(0.042942833471854) }, + { FRAC_CONST(0.010178439934815), FRAC_CONST(0.043006096782821) }, + { FRAC_CONST(0.009914367879490), FRAC_CONST(0.043067740938551) }, + { FRAC_CONST(0.009649922553818), FRAC_CONST(0.043127763618177) }, + { FRAC_CONST(0.009385113914016), FRAC_CONST(0.043186162561878) }, + { FRAC_CONST(0.009119951929979), FRAC_CONST(0.043242935570968) }, + { FRAC_CONST(0.008854446584907), FRAC_CONST(0.043298080507974) }, + { FRAC_CONST(0.008588607874926), FRAC_CONST(0.043351595296722) }, + { FRAC_CONST(0.008322445808712), FRAC_CONST(0.043403477922409) }, + { FRAC_CONST(0.008055970407118), FRAC_CONST(0.043453726431684) }, + { FRAC_CONST(0.007789191702791), FRAC_CONST(0.043502338932719) }, + { FRAC_CONST(0.007522119739798), FRAC_CONST(0.043549313595281) }, + { FRAC_CONST(0.007254764573250), FRAC_CONST(0.043594648650800) }, + { FRAC_CONST(0.006987136268915), FRAC_CONST(0.043638342392438) }, + { FRAC_CONST(0.006719244902849), FRAC_CONST(0.043680393175148) }, + { FRAC_CONST(0.006451100561010), FRAC_CONST(0.043720799415744) }, + { FRAC_CONST(0.006182713338881), FRAC_CONST(0.043759559592953) }, + { FRAC_CONST(0.005914093341090), FRAC_CONST(0.043796672247476) }, + { FRAC_CONST(0.005645250681027), FRAC_CONST(0.043832135982044) }, + { FRAC_CONST(0.005376195480466), FRAC_CONST(0.043865949461465) }, + { FRAC_CONST(0.005106937869184), FRAC_CONST(0.043898111412683) }, + { FRAC_CONST(0.004837487984578), FRAC_CONST(0.043928620624817) }, + { FRAC_CONST(0.004567855971284), FRAC_CONST(0.043957475949213) }, + { FRAC_CONST(0.004298051980793), FRAC_CONST(0.043984676299484) }, + { FRAC_CONST(0.004028086171076), FRAC_CONST(0.044010220651553) }, + { FRAC_CONST(0.003757968706190), FRAC_CONST(0.044034108043689) }, + { FRAC_CONST(0.003487709755907), FRAC_CONST(0.044056337576546) }, + { FRAC_CONST(0.003217319495322), FRAC_CONST(0.044076908413193) }, + { FRAC_CONST(0.002946808104477), FRAC_CONST(0.044095819779151) }, + { FRAC_CONST(0.002676185767973), FRAC_CONST(0.044113070962418) }, + { FRAC_CONST(0.002405462674586), FRAC_CONST(0.044128661313495) }, + { FRAC_CONST(0.002134649016890), FRAC_CONST(0.044142590245416) }, + { FRAC_CONST(0.001863754990865), FRAC_CONST(0.044154857233763) }, + { FRAC_CONST(0.001592790795518), FRAC_CONST(0.044165461816692) }, + { FRAC_CONST(0.001321766632497), FRAC_CONST(0.044174403594946) }, + { FRAC_CONST(0.001050692705710), FRAC_CONST(0.044181682231873) }, + { FRAC_CONST(0.000779579220936), FRAC_CONST(0.044187297453434) }, + { FRAC_CONST(0.000508436385446), FRAC_CONST(0.044191249048222) }, + { FRAC_CONST(0.000237274407613), FRAC_CONST(0.044193536867459) } +}; +#endif // LD_DEC + +#ifdef ALLOW_SMALL_FRAMELENGTH +/* 480 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_1920[] = +{ + { FRAC_CONST(0.032274858518097), FRAC_CONST(0.000013202404176) }, + { FRAC_CONST(0.032274642494505), FRAC_CONST(0.000118821372483) }, + { FRAC_CONST(0.032274080835421), FRAC_CONST(0.000224439068308) }, + { FRAC_CONST(0.032273173546860), FRAC_CONST(0.000330054360572) }, + { FRAC_CONST(0.032271920638538), FRAC_CONST(0.000435666118218) }, + { FRAC_CONST(0.032270322123873), FRAC_CONST(0.000541273210231) }, + { FRAC_CONST(0.032268378019984), FRAC_CONST(0.000646874505642) }, + { FRAC_CONST(0.032266088347691), FRAC_CONST(0.000752468873546) }, + { FRAC_CONST(0.032263453131514), FRAC_CONST(0.000858055183114) }, + { FRAC_CONST(0.032260472399674), FRAC_CONST(0.000963632303600) }, + { FRAC_CONST(0.032257146184092), FRAC_CONST(0.001069199104358) }, + { FRAC_CONST(0.032253474520390), FRAC_CONST(0.001174754454853) }, + { FRAC_CONST(0.032249457447888), FRAC_CONST(0.001280297224671) }, + { FRAC_CONST(0.032245095009606), FRAC_CONST(0.001385826283535) }, + { FRAC_CONST(0.032240387252262), FRAC_CONST(0.001491340501313) }, + { FRAC_CONST(0.032235334226272), FRAC_CONST(0.001596838748031) }, + { FRAC_CONST(0.032229935985750), FRAC_CONST(0.001702319893890) }, + { FRAC_CONST(0.032224192588507), FRAC_CONST(0.001807782809271) }, + { FRAC_CONST(0.032218104096050), FRAC_CONST(0.001913226364749) }, + { FRAC_CONST(0.032211670573582), FRAC_CONST(0.002018649431111) }, + { FRAC_CONST(0.032204892090000), FRAC_CONST(0.002124050879359) }, + { FRAC_CONST(0.032197768717898), FRAC_CONST(0.002229429580728) }, + { FRAC_CONST(0.032190300533560), FRAC_CONST(0.002334784406698) }, + { FRAC_CONST(0.032182487616965), FRAC_CONST(0.002440114229003) }, + { FRAC_CONST(0.032174330051782), FRAC_CONST(0.002545417919644) }, + { FRAC_CONST(0.032165827925374), FRAC_CONST(0.002650694350905) }, + { FRAC_CONST(0.032156981328790), FRAC_CONST(0.002755942395358) }, + { FRAC_CONST(0.032147790356771), FRAC_CONST(0.002861160925883) }, + { FRAC_CONST(0.032138255107744), FRAC_CONST(0.002966348815672) }, + { FRAC_CONST(0.032128375683825), FRAC_CONST(0.003071504938250) }, + { FRAC_CONST(0.032118152190814), FRAC_CONST(0.003176628167476) }, + { FRAC_CONST(0.032107584738196), FRAC_CONST(0.003281717377568) }, + { FRAC_CONST(0.032096673439141), FRAC_CONST(0.003386771443102) }, + { FRAC_CONST(0.032085418410500), FRAC_CONST(0.003491789239036) }, + { FRAC_CONST(0.032073819772804), FRAC_CONST(0.003596769640711) }, + { FRAC_CONST(0.032061877650267), FRAC_CONST(0.003701711523874) }, + { FRAC_CONST(0.032049592170778), FRAC_CONST(0.003806613764680) }, + { FRAC_CONST(0.032036963465906), FRAC_CONST(0.003911475239711) }, + { FRAC_CONST(0.032023991670893), FRAC_CONST(0.004016294825985) }, + { FRAC_CONST(0.032010676924657), FRAC_CONST(0.004121071400967) }, + { FRAC_CONST(0.031997019369789), FRAC_CONST(0.004225803842586) }, + { FRAC_CONST(0.031983019152549), FRAC_CONST(0.004330491029241) }, + { FRAC_CONST(0.031968676422869), FRAC_CONST(0.004435131839816) }, + { FRAC_CONST(0.031953991334348), FRAC_CONST(0.004539725153692) }, + { FRAC_CONST(0.031938964044252), FRAC_CONST(0.004644269850758) }, + { FRAC_CONST(0.031923594713510), FRAC_CONST(0.004748764811426) }, + { FRAC_CONST(0.031907883506716), FRAC_CONST(0.004853208916638) }, + { FRAC_CONST(0.031891830592124), FRAC_CONST(0.004957601047881) }, + { FRAC_CONST(0.031875436141648), FRAC_CONST(0.005061940087200) }, + { FRAC_CONST(0.031858700330859), FRAC_CONST(0.005166224917208) }, + { FRAC_CONST(0.031841623338985), FRAC_CONST(0.005270454421097) }, + { FRAC_CONST(0.031824205348907), FRAC_CONST(0.005374627482653) }, + { FRAC_CONST(0.031806446547156), FRAC_CONST(0.005478742986267) }, + { FRAC_CONST(0.031788347123916), FRAC_CONST(0.005582799816945) }, + { FRAC_CONST(0.031769907273017), FRAC_CONST(0.005686796860323) }, + { FRAC_CONST(0.031751127191935), FRAC_CONST(0.005790733002674) }, + { FRAC_CONST(0.031732007081789), FRAC_CONST(0.005894607130928) }, + { FRAC_CONST(0.031712547147340), FRAC_CONST(0.005998418132675) }, + { FRAC_CONST(0.031692747596989), FRAC_CONST(0.006102164896182) }, + { FRAC_CONST(0.031672608642773), FRAC_CONST(0.006205846310406) }, + { FRAC_CONST(0.031652130500364), FRAC_CONST(0.006309461265002) }, + { FRAC_CONST(0.031631313389067), FRAC_CONST(0.006413008650337) }, + { FRAC_CONST(0.031610157531816), FRAC_CONST(0.006516487357501) }, + { FRAC_CONST(0.031588663155172), FRAC_CONST(0.006619896278321) }, + { FRAC_CONST(0.031566830489325), FRAC_CONST(0.006723234305370) }, + { FRAC_CONST(0.031544659768083), FRAC_CONST(0.006826500331981) }, + { FRAC_CONST(0.031522151228878), FRAC_CONST(0.006929693252258) }, + { FRAC_CONST(0.031499305112758), FRAC_CONST(0.007032811961088) }, + { FRAC_CONST(0.031476121664387), FRAC_CONST(0.007135855354151) }, + { FRAC_CONST(0.031452601132040), FRAC_CONST(0.007238822327937) }, + { FRAC_CONST(0.031428743767604), FRAC_CONST(0.007341711779751) }, + { FRAC_CONST(0.031404549826572), FRAC_CONST(0.007444522607730) }, + { FRAC_CONST(0.031380019568042), FRAC_CONST(0.007547253710853) }, + { FRAC_CONST(0.031355153254712), FRAC_CONST(0.007649903988952) }, + { FRAC_CONST(0.031329951152882), FRAC_CONST(0.007752472342725) }, + { FRAC_CONST(0.031304413532445), FRAC_CONST(0.007854957673748) }, + { FRAC_CONST(0.031278540666888), FRAC_CONST(0.007957358884484) }, + { FRAC_CONST(0.031252332833290), FRAC_CONST(0.008059674878300) }, + { FRAC_CONST(0.031225790312316), FRAC_CONST(0.008161904559473) }, + { FRAC_CONST(0.031198913388214), FRAC_CONST(0.008264046833205) }, + { FRAC_CONST(0.031171702348814), FRAC_CONST(0.008366100605636) }, + { FRAC_CONST(0.031144157485525), FRAC_CONST(0.008468064783849) }, + { FRAC_CONST(0.031116279093331), FRAC_CONST(0.008569938275893) }, + { FRAC_CONST(0.031088067470786), FRAC_CONST(0.008671719990782) }, + { FRAC_CONST(0.031059522920014), FRAC_CONST(0.008773408838517) }, + { FRAC_CONST(0.031030645746705), FRAC_CONST(0.008875003730092) }, + { FRAC_CONST(0.031001436260110), FRAC_CONST(0.008976503577507) }, + { FRAC_CONST(0.030971894773039), FRAC_CONST(0.009077907293780) }, + { FRAC_CONST(0.030942021601857), FRAC_CONST(0.009179213792959) }, + { FRAC_CONST(0.030911817066483), FRAC_CONST(0.009280421990133) }, + { FRAC_CONST(0.030881281490382), FRAC_CONST(0.009381530801444) }, + { FRAC_CONST(0.030850415200566), FRAC_CONST(0.009482539144097) }, + { FRAC_CONST(0.030819218527589), FRAC_CONST(0.009583445936373) }, + { FRAC_CONST(0.030787691805541), FRAC_CONST(0.009684250097643) }, + { FRAC_CONST(0.030755835372048), FRAC_CONST(0.009784950548375) }, + { FRAC_CONST(0.030723649568268), FRAC_CONST(0.009885546210147) }, + { FRAC_CONST(0.030691134738883), FRAC_CONST(0.009986036005661) }, + { FRAC_CONST(0.030658291232103), FRAC_CONST(0.010086418858753) }, + { FRAC_CONST(0.030625119399655), FRAC_CONST(0.010186693694402) }, + { FRAC_CONST(0.030591619596781), FRAC_CONST(0.010286859438745) }, + { FRAC_CONST(0.030557792182239), FRAC_CONST(0.010386915019088) }, + { FRAC_CONST(0.030523637518292), FRAC_CONST(0.010486859363916) }, + { FRAC_CONST(0.030489155970710), FRAC_CONST(0.010586691402906) }, + { FRAC_CONST(0.030454347908763), FRAC_CONST(0.010686410066936) }, + { FRAC_CONST(0.030419213705216), FRAC_CONST(0.010786014288099) }, + { FRAC_CONST(0.030383753736329), FRAC_CONST(0.010885502999714) }, + { FRAC_CONST(0.030347968381849), FRAC_CONST(0.010984875136338) }, + { FRAC_CONST(0.030311858025010), FRAC_CONST(0.011084129633775) }, + { FRAC_CONST(0.030275423052523), FRAC_CONST(0.011183265429088) }, + { FRAC_CONST(0.030238663854579), FRAC_CONST(0.011282281460612) }, + { FRAC_CONST(0.030201580824838), FRAC_CONST(0.011381176667967) }, + { FRAC_CONST(0.030164174360430), FRAC_CONST(0.011479949992062) }, + { FRAC_CONST(0.030126444861948), FRAC_CONST(0.011578600375117) }, + { FRAC_CONST(0.030088392733446), FRAC_CONST(0.011677126760663) }, + { FRAC_CONST(0.030050018382430), FRAC_CONST(0.011775528093563) }, + { FRAC_CONST(0.030011322219859), FRAC_CONST(0.011873803320018) }, + { FRAC_CONST(0.029972304660138), FRAC_CONST(0.011971951387578) }, + { FRAC_CONST(0.029932966121114), FRAC_CONST(0.012069971245157) }, + { FRAC_CONST(0.029893307024070), FRAC_CONST(0.012167861843041) }, + { FRAC_CONST(0.029853327793724), FRAC_CONST(0.012265622132901) }, + { FRAC_CONST(0.029813028858222), FRAC_CONST(0.012363251067801) }, + { FRAC_CONST(0.029772410649132), FRAC_CONST(0.012460747602215) }, + { FRAC_CONST(0.029731473601443), FRAC_CONST(0.012558110692033) }, + { FRAC_CONST(0.029690218153558), FRAC_CONST(0.012655339294575) }, + { FRAC_CONST(0.029648644747289), FRAC_CONST(0.012752432368600) }, + { FRAC_CONST(0.029606753827855), FRAC_CONST(0.012849388874320) }, + { FRAC_CONST(0.029564545843872), FRAC_CONST(0.012946207773407) }, + { FRAC_CONST(0.029522021247356), FRAC_CONST(0.013042888029011) }, + { FRAC_CONST(0.029479180493710), FRAC_CONST(0.013139428605762) }, + { FRAC_CONST(0.029436024041725), FRAC_CONST(0.013235828469789) }, + { FRAC_CONST(0.029392552353570), FRAC_CONST(0.013332086588727) }, + { FRAC_CONST(0.029348765894794), FRAC_CONST(0.013428201931728) }, + { FRAC_CONST(0.029304665134313), FRAC_CONST(0.013524173469475) }, + { FRAC_CONST(0.029260250544412), FRAC_CONST(0.013620000174189) }, + { FRAC_CONST(0.029215522600735), FRAC_CONST(0.013715681019643) }, + { FRAC_CONST(0.029170481782283), FRAC_CONST(0.013811214981173) }, + { FRAC_CONST(0.029125128571406), FRAC_CONST(0.013906601035686) }, + { FRAC_CONST(0.029079463453801), FRAC_CONST(0.014001838161674) }, + { FRAC_CONST(0.029033486918505), FRAC_CONST(0.014096925339225) }, + { FRAC_CONST(0.028987199457889), FRAC_CONST(0.014191861550031) }, + { FRAC_CONST(0.028940601567655), FRAC_CONST(0.014286645777401) }, + { FRAC_CONST(0.028893693746829), FRAC_CONST(0.014381277006273) }, + { FRAC_CONST(0.028846476497755), FRAC_CONST(0.014475754223221) }, + { FRAC_CONST(0.028798950326094), FRAC_CONST(0.014570076416472) }, + { FRAC_CONST(0.028751115740811), FRAC_CONST(0.014664242575910) }, + { FRAC_CONST(0.028702973254178), FRAC_CONST(0.014758251693091) }, + { FRAC_CONST(0.028654523381760), FRAC_CONST(0.014852102761253) }, + { FRAC_CONST(0.028605766642418), FRAC_CONST(0.014945794775326) }, + { FRAC_CONST(0.028556703558297), FRAC_CONST(0.015039326731945) }, + { FRAC_CONST(0.028507334654823), FRAC_CONST(0.015132697629457) }, + { FRAC_CONST(0.028457660460698), FRAC_CONST(0.015225906467935) }, + { FRAC_CONST(0.028407681507891), FRAC_CONST(0.015318952249187) }, + { FRAC_CONST(0.028357398331639), FRAC_CONST(0.015411833976768) }, + { FRAC_CONST(0.028306811470432), FRAC_CONST(0.015504550655988) }, + { FRAC_CONST(0.028255921466016), FRAC_CONST(0.015597101293927) }, + { FRAC_CONST(0.028204728863381), FRAC_CONST(0.015689484899442) }, + { FRAC_CONST(0.028153234210760), FRAC_CONST(0.015781700483179) }, + { FRAC_CONST(0.028101438059619), FRAC_CONST(0.015873747057582) }, + { FRAC_CONST(0.028049340964652), FRAC_CONST(0.015965623636907) }, + { FRAC_CONST(0.027996943483779), FRAC_CONST(0.016057329237229) }, + { FRAC_CONST(0.027944246178133), FRAC_CONST(0.016148862876456) }, + { FRAC_CONST(0.027891249612061), FRAC_CONST(0.016240223574335) }, + { FRAC_CONST(0.027837954353113), FRAC_CONST(0.016331410352467) }, + { FRAC_CONST(0.027784360972039), FRAC_CONST(0.016422422234315) }, + { FRAC_CONST(0.027730470042780), FRAC_CONST(0.016513258245214) }, + { FRAC_CONST(0.027676282142466), FRAC_CONST(0.016603917412384) }, + { FRAC_CONST(0.027621797851405), FRAC_CONST(0.016694398764938) }, + { FRAC_CONST(0.027567017753080), FRAC_CONST(0.016784701333894) }, + { FRAC_CONST(0.027511942434143), FRAC_CONST(0.016874824152183) }, + { FRAC_CONST(0.027456572484404), FRAC_CONST(0.016964766254662) }, + { FRAC_CONST(0.027400908496833), FRAC_CONST(0.017054526678124) }, + { FRAC_CONST(0.027344951067546), FRAC_CONST(0.017144104461307) }, + { FRAC_CONST(0.027288700795801), FRAC_CONST(0.017233498644904) }, + { FRAC_CONST(0.027232158283994), FRAC_CONST(0.017322708271577) }, + { FRAC_CONST(0.027175324137651), FRAC_CONST(0.017411732385960) }, + { FRAC_CONST(0.027118198965418), FRAC_CONST(0.017500570034678) }, + { FRAC_CONST(0.027060783379060), FRAC_CONST(0.017589220266351) }, + { FRAC_CONST(0.027003077993454), FRAC_CONST(0.017677682131607) }, + { FRAC_CONST(0.026945083426576), FRAC_CONST(0.017765954683088) }, + { FRAC_CONST(0.026886800299502), FRAC_CONST(0.017854036975468) }, + { FRAC_CONST(0.026828229236397), FRAC_CONST(0.017941928065456) }, + { FRAC_CONST(0.026769370864511), FRAC_CONST(0.018029627011808) }, + { FRAC_CONST(0.026710225814170), FRAC_CONST(0.018117132875340) }, + { FRAC_CONST(0.026650794718768), FRAC_CONST(0.018204444718934) }, + { FRAC_CONST(0.026591078214767), FRAC_CONST(0.018291561607551) }, + { FRAC_CONST(0.026531076941680), FRAC_CONST(0.018378482608238) }, + { FRAC_CONST(0.026470791542075), FRAC_CONST(0.018465206790142) }, + { FRAC_CONST(0.026410222661558), FRAC_CONST(0.018551733224515) }, + { FRAC_CONST(0.026349370948775), FRAC_CONST(0.018638060984730) }, + { FRAC_CONST(0.026288237055398), FRAC_CONST(0.018724189146286) }, + { FRAC_CONST(0.026226821636121), FRAC_CONST(0.018810116786819) }, + { FRAC_CONST(0.026165125348656), FRAC_CONST(0.018895842986112) }, + { FRAC_CONST(0.026103148853718), FRAC_CONST(0.018981366826109) }, + { FRAC_CONST(0.026040892815028), FRAC_CONST(0.019066687390916) }, + { FRAC_CONST(0.025978357899296), FRAC_CONST(0.019151803766819) }, + { FRAC_CONST(0.025915544776223), FRAC_CONST(0.019236715042290) }, + { FRAC_CONST(0.025852454118485), FRAC_CONST(0.019321420307998) }, + { FRAC_CONST(0.025789086601733), FRAC_CONST(0.019405918656817) }, + { FRAC_CONST(0.025725442904582), FRAC_CONST(0.019490209183837) }, + { FRAC_CONST(0.025661523708606), FRAC_CONST(0.019574290986376) }, + { FRAC_CONST(0.025597329698327), FRAC_CONST(0.019658163163984) }, + { FRAC_CONST(0.025532861561211), FRAC_CONST(0.019741824818458) }, + { FRAC_CONST(0.025468119987662), FRAC_CONST(0.019825275053848) }, + { FRAC_CONST(0.025403105671008), FRAC_CONST(0.019908512976470) }, + { FRAC_CONST(0.025337819307501), FRAC_CONST(0.019991537694913) }, + { FRAC_CONST(0.025272261596305), FRAC_CONST(0.020074348320047) }, + { FRAC_CONST(0.025206433239491), FRAC_CONST(0.020156943965039) }, + { FRAC_CONST(0.025140334942028), FRAC_CONST(0.020239323745355) }, + { FRAC_CONST(0.025073967411776), FRAC_CONST(0.020321486778774) }, + { FRAC_CONST(0.025007331359476), FRAC_CONST(0.020403432185395) }, + { FRAC_CONST(0.024940427498748), FRAC_CONST(0.020485159087650) }, + { FRAC_CONST(0.024873256546079), FRAC_CONST(0.020566666610309) }, + { FRAC_CONST(0.024805819220816), FRAC_CONST(0.020647953880491) }, + { FRAC_CONST(0.024738116245157), FRAC_CONST(0.020729020027676) }, + { FRAC_CONST(0.024670148344147), FRAC_CONST(0.020809864183709) }, + { FRAC_CONST(0.024601916245669), FRAC_CONST(0.020890485482816) }, + { FRAC_CONST(0.024533420680433), FRAC_CONST(0.020970883061607) }, + { FRAC_CONST(0.024464662381971), FRAC_CONST(0.021051056059087) }, + { FRAC_CONST(0.024395642086630), FRAC_CONST(0.021131003616670) }, + { FRAC_CONST(0.024326360533561), FRAC_CONST(0.021210724878181) }, + { FRAC_CONST(0.024256818464715), FRAC_CONST(0.021290218989868) }, + { FRAC_CONST(0.024187016624830), FRAC_CONST(0.021369485100415) }, + { FRAC_CONST(0.024116955761430), FRAC_CONST(0.021448522360944) }, + { FRAC_CONST(0.024046636624808), FRAC_CONST(0.021527329925030) }, + { FRAC_CONST(0.023976059968027), FRAC_CONST(0.021605906948708) }, + { FRAC_CONST(0.023905226546906), FRAC_CONST(0.021684252590480) }, + { FRAC_CONST(0.023834137120014), FRAC_CONST(0.021762366011328) }, + { FRAC_CONST(0.023762792448662), FRAC_CONST(0.021840246374720) }, + { FRAC_CONST(0.023691193296893), FRAC_CONST(0.021917892846620) }, + { FRAC_CONST(0.023619340431478), FRAC_CONST(0.021995304595495) }, + { FRAC_CONST(0.023547234621902), FRAC_CONST(0.022072480792330) }, + { FRAC_CONST(0.023474876640361), FRAC_CONST(0.022149420610628) }, + { FRAC_CONST(0.023402267261751), FRAC_CONST(0.022226123226426) }, + { FRAC_CONST(0.023329407263659), FRAC_CONST(0.022302587818300) }, + { FRAC_CONST(0.023256297426359), FRAC_CONST(0.022378813567377) }, + { FRAC_CONST(0.023182938532797), FRAC_CONST(0.022454799657339) }, + { FRAC_CONST(0.023109331368588), FRAC_CONST(0.022530545274437) }, + { FRAC_CONST(0.023035476722006), FRAC_CONST(0.022606049607496) }, + { FRAC_CONST(0.022961375383975), FRAC_CONST(0.022681311847926) }, + { FRAC_CONST(0.022887028148061), FRAC_CONST(0.022756331189727) }, + { FRAC_CONST(0.022812435810462), FRAC_CONST(0.022831106829504) }, + { FRAC_CONST(0.022737599170003), FRAC_CONST(0.022905637966469) }, + { FRAC_CONST(0.022662519028125), FRAC_CONST(0.022979923802453) }, + { FRAC_CONST(0.022587196188874), FRAC_CONST(0.023053963541915) }, + { FRAC_CONST(0.022511631458899), FRAC_CONST(0.023127756391950) }, + { FRAC_CONST(0.022435825647437), FRAC_CONST(0.023201301562294) }, + { FRAC_CONST(0.022359779566306), FRAC_CONST(0.023274598265338) }, + { FRAC_CONST(0.022283494029900), FRAC_CONST(0.023347645716133) }, + { FRAC_CONST(0.022206969855176), FRAC_CONST(0.023420443132400) }, + { FRAC_CONST(0.022130207861645), FRAC_CONST(0.023492989734537) }, + { FRAC_CONST(0.022053208871367), FRAC_CONST(0.023565284745628) }, + { FRAC_CONST(0.021975973708940), FRAC_CONST(0.023637327391451) }, + { FRAC_CONST(0.021898503201489), FRAC_CONST(0.023709116900488) }, + { FRAC_CONST(0.021820798178663), FRAC_CONST(0.023780652503931) }, + { FRAC_CONST(0.021742859472618), FRAC_CONST(0.023851933435691) }, + { FRAC_CONST(0.021664687918017), FRAC_CONST(0.023922958932406) }, + { FRAC_CONST(0.021586284352013), FRAC_CONST(0.023993728233451) }, + { FRAC_CONST(0.021507649614247), FRAC_CONST(0.024064240580942) }, + { FRAC_CONST(0.021428784546832), FRAC_CONST(0.024134495219750) }, + { FRAC_CONST(0.021349689994350), FRAC_CONST(0.024204491397504) }, + { FRAC_CONST(0.021270366803840), FRAC_CONST(0.024274228364600) }, + { FRAC_CONST(0.021190815824791), FRAC_CONST(0.024343705374213) }, + { FRAC_CONST(0.021111037909128), FRAC_CONST(0.024412921682298) }, + { FRAC_CONST(0.021031033911210), FRAC_CONST(0.024481876547605) }, + { FRAC_CONST(0.020950804687815), FRAC_CONST(0.024550569231683) }, + { FRAC_CONST(0.020870351098134), FRAC_CONST(0.024618998998889) }, + { FRAC_CONST(0.020789674003759), FRAC_CONST(0.024687165116394) }, + { FRAC_CONST(0.020708774268678), FRAC_CONST(0.024755066854194) }, + { FRAC_CONST(0.020627652759262), FRAC_CONST(0.024822703485116) }, + { FRAC_CONST(0.020546310344257), FRAC_CONST(0.024890074284826) }, + { FRAC_CONST(0.020464747894775), FRAC_CONST(0.024957178531837) }, + { FRAC_CONST(0.020382966284284), FRAC_CONST(0.025024015507516) }, + { FRAC_CONST(0.020300966388600), FRAC_CONST(0.025090584496093) }, + { FRAC_CONST(0.020218749085876), FRAC_CONST(0.025156884784668) }, + { FRAC_CONST(0.020136315256592), FRAC_CONST(0.025222915663218) }, + { FRAC_CONST(0.020053665783549), FRAC_CONST(0.025288676424605) }, + { FRAC_CONST(0.019970801551857), FRAC_CONST(0.025354166364584) }, + { FRAC_CONST(0.019887723448925), FRAC_CONST(0.025419384781811) }, + { FRAC_CONST(0.019804432364452), FRAC_CONST(0.025484330977848) }, + { FRAC_CONST(0.019720929190419), FRAC_CONST(0.025549004257175) }, + { FRAC_CONST(0.019637214821078), FRAC_CONST(0.025613403927192) }, + { FRAC_CONST(0.019553290152943), FRAC_CONST(0.025677529298230) }, + { FRAC_CONST(0.019469156084779), FRAC_CONST(0.025741379683559) }, + { FRAC_CONST(0.019384813517595), FRAC_CONST(0.025804954399392) }, + { FRAC_CONST(0.019300263354632), FRAC_CONST(0.025868252764895) }, + { FRAC_CONST(0.019215506501354), FRAC_CONST(0.025931274102193) }, + { FRAC_CONST(0.019130543865439), FRAC_CONST(0.025994017736379) }, + { FRAC_CONST(0.019045376356769), FRAC_CONST(0.026056482995518) }, + { FRAC_CONST(0.018960004887419), FRAC_CONST(0.026118669210657) }, + { FRAC_CONST(0.018874430371648), FRAC_CONST(0.026180575715833) }, + { FRAC_CONST(0.018788653725892), FRAC_CONST(0.026242201848076) }, + { FRAC_CONST(0.018702675868750), FRAC_CONST(0.026303546947421) }, + { FRAC_CONST(0.018616497720974), FRAC_CONST(0.026364610356909) }, + { FRAC_CONST(0.018530120205464), FRAC_CONST(0.026425391422602) }, + { FRAC_CONST(0.018443544247254), FRAC_CONST(0.026485889493583) }, + { FRAC_CONST(0.018356770773502), FRAC_CONST(0.026546103921965) }, + { FRAC_CONST(0.018269800713483), FRAC_CONST(0.026606034062902) }, + { FRAC_CONST(0.018182634998576), FRAC_CONST(0.026665679274589) }, + { FRAC_CONST(0.018095274562256), FRAC_CONST(0.026725038918274) }, + { FRAC_CONST(0.018007720340083), FRAC_CONST(0.026784112358263) }, + { FRAC_CONST(0.017919973269692), FRAC_CONST(0.026842898961926) }, + { FRAC_CONST(0.017832034290785), FRAC_CONST(0.026901398099707) }, + { FRAC_CONST(0.017743904345116), FRAC_CONST(0.026959609145127) }, + { FRAC_CONST(0.017655584376488), FRAC_CONST(0.027017531474792) }, + { FRAC_CONST(0.017567075330734), FRAC_CONST(0.027075164468401) }, + { FRAC_CONST(0.017478378155718), FRAC_CONST(0.027132507508750) }, + { FRAC_CONST(0.017389493801313), FRAC_CONST(0.027189559981742) }, + { FRAC_CONST(0.017300423219401), FRAC_CONST(0.027246321276391) }, + { FRAC_CONST(0.017211167363854), FRAC_CONST(0.027302790784828) }, + { FRAC_CONST(0.017121727190533), FRAC_CONST(0.027358967902310) }, + { FRAC_CONST(0.017032103657269), FRAC_CONST(0.027414852027226) }, + { FRAC_CONST(0.016942297723858), FRAC_CONST(0.027470442561102) }, + { FRAC_CONST(0.016852310352050), FRAC_CONST(0.027525738908608) }, + { FRAC_CONST(0.016762142505537), FRAC_CONST(0.027580740477564) }, + { FRAC_CONST(0.016671795149944), FRAC_CONST(0.027635446678948) }, + { FRAC_CONST(0.016581269252819), FRAC_CONST(0.027689856926900) }, + { FRAC_CONST(0.016490565783622), FRAC_CONST(0.027743970638730) }, + { FRAC_CONST(0.016399685713714), FRAC_CONST(0.027797787234924) }, + { FRAC_CONST(0.016308630016347), FRAC_CONST(0.027851306139149) }, + { FRAC_CONST(0.016217399666655), FRAC_CONST(0.027904526778260) }, + { FRAC_CONST(0.016125995641641), FRAC_CONST(0.027957448582309) }, + { FRAC_CONST(0.016034418920170), FRAC_CONST(0.028010070984544) }, + { FRAC_CONST(0.015942670482954), FRAC_CONST(0.028062393421421) }, + { FRAC_CONST(0.015850751312545), FRAC_CONST(0.028114415332610) }, + { FRAC_CONST(0.015758662393324), FRAC_CONST(0.028166136160998) }, + { FRAC_CONST(0.015666404711489), FRAC_CONST(0.028217555352697) }, + { FRAC_CONST(0.015573979255046), FRAC_CONST(0.028268672357047) }, + { FRAC_CONST(0.015481387013797), FRAC_CONST(0.028319486626627) }, + { FRAC_CONST(0.015388628979331), FRAC_CONST(0.028369997617257) }, + { FRAC_CONST(0.015295706145012), FRAC_CONST(0.028420204788004) }, + { FRAC_CONST(0.015202619505968), FRAC_CONST(0.028470107601191) }, + { FRAC_CONST(0.015109370059084), FRAC_CONST(0.028519705522399) }, + { FRAC_CONST(0.015015958802984), FRAC_CONST(0.028568998020472) }, + { FRAC_CONST(0.014922386738030), FRAC_CONST(0.028617984567529) }, + { FRAC_CONST(0.014828654866302), FRAC_CONST(0.028666664638963) }, + { FRAC_CONST(0.014734764191593), FRAC_CONST(0.028715037713449) }, + { FRAC_CONST(0.014640715719398), FRAC_CONST(0.028763103272951) }, + { FRAC_CONST(0.014546510456900), FRAC_CONST(0.028810860802724) }, + { FRAC_CONST(0.014452149412962), FRAC_CONST(0.028858309791325) }, + { FRAC_CONST(0.014357633598114), FRAC_CONST(0.028905449730613) }, + { FRAC_CONST(0.014262964024545), FRAC_CONST(0.028952280115756) }, + { FRAC_CONST(0.014168141706090), FRAC_CONST(0.028998800445240) }, + { FRAC_CONST(0.014073167658220), FRAC_CONST(0.029045010220868) }, + { FRAC_CONST(0.013978042898030), FRAC_CONST(0.029090908947771) }, + { FRAC_CONST(0.013882768444231), FRAC_CONST(0.029136496134411) }, + { FRAC_CONST(0.013787345317136), FRAC_CONST(0.029181771292585) }, + { FRAC_CONST(0.013691774538648), FRAC_CONST(0.029226733937433) }, + { FRAC_CONST(0.013596057132255), FRAC_CONST(0.029271383587441) }, + { FRAC_CONST(0.013500194123014), FRAC_CONST(0.029315719764447) }, + { FRAC_CONST(0.013404186537539), FRAC_CONST(0.029359741993647) }, + { FRAC_CONST(0.013308035403995), FRAC_CONST(0.029403449803598) }, + { FRAC_CONST(0.013211741752084), FRAC_CONST(0.029446842726223) }, + { FRAC_CONST(0.013115306613032), FRAC_CONST(0.029489920296820) }, + { FRAC_CONST(0.013018731019584), FRAC_CONST(0.029532682054063) }, + { FRAC_CONST(0.012922016005985), FRAC_CONST(0.029575127540008) }, + { FRAC_CONST(0.012825162607977), FRAC_CONST(0.029617256300097) }, + { FRAC_CONST(0.012728171862781), FRAC_CONST(0.029659067883165) }, + { FRAC_CONST(0.012631044809089), FRAC_CONST(0.029700561841444) }, + { FRAC_CONST(0.012533782487056), FRAC_CONST(0.029741737730567) }, + { FRAC_CONST(0.012436385938281), FRAC_CONST(0.029782595109573) }, + { FRAC_CONST(0.012338856205805), FRAC_CONST(0.029823133540913) }, + { FRAC_CONST(0.012241194334091), FRAC_CONST(0.029863352590452) }, + { FRAC_CONST(0.012143401369021), FRAC_CONST(0.029903251827477) }, + { FRAC_CONST(0.012045478357878), FRAC_CONST(0.029942830824699) }, + { FRAC_CONST(0.011947426349339), FRAC_CONST(0.029982089158259) }, + { FRAC_CONST(0.011849246393462), FRAC_CONST(0.030021026407731) }, + { FRAC_CONST(0.011750939541676), FRAC_CONST(0.030059642156129) }, + { FRAC_CONST(0.011652506846768), FRAC_CONST(0.030097935989909) }, + { FRAC_CONST(0.011553949362874), FRAC_CONST(0.030135907498976) }, + { FRAC_CONST(0.011455268145464), FRAC_CONST(0.030173556276684) }, + { FRAC_CONST(0.011356464251335), FRAC_CONST(0.030210881919845) }, + { FRAC_CONST(0.011257538738598), FRAC_CONST(0.030247884028732) }, + { FRAC_CONST(0.011158492666665), FRAC_CONST(0.030284562207083) }, + { FRAC_CONST(0.011059327096240), FRAC_CONST(0.030320916062102) }, + { FRAC_CONST(0.010960043089307), FRAC_CONST(0.030356945204470) }, + { FRAC_CONST(0.010860641709118), FRAC_CONST(0.030392649248343) }, + { FRAC_CONST(0.010761124020182), FRAC_CONST(0.030428027811361) }, + { FRAC_CONST(0.010661491088253), FRAC_CONST(0.030463080514646) }, + { FRAC_CONST(0.010561743980319), FRAC_CONST(0.030497806982812) }, + { FRAC_CONST(0.010461883764593), FRAC_CONST(0.030532206843968) }, + { FRAC_CONST(0.010361911510496), FRAC_CONST(0.030566279729717) }, + { FRAC_CONST(0.010261828288652), FRAC_CONST(0.030600025275167) }, + { FRAC_CONST(0.010161635170872), FRAC_CONST(0.030633443118931) }, + { FRAC_CONST(0.010061333230142), FRAC_CONST(0.030666532903129) }, + { FRAC_CONST(0.009960923540617), FRAC_CONST(0.030699294273397) }, + { FRAC_CONST(0.009860407177603), FRAC_CONST(0.030731726878888) }, + { FRAC_CONST(0.009759785217550), FRAC_CONST(0.030763830372273) }, + { FRAC_CONST(0.009659058738038), FRAC_CONST(0.030795604409750) }, + { FRAC_CONST(0.009558228817767), FRAC_CONST(0.030827048651045) }, + { FRAC_CONST(0.009457296536545), FRAC_CONST(0.030858162759415) }, + { FRAC_CONST(0.009356262975275), FRAC_CONST(0.030888946401653) }, + { FRAC_CONST(0.009255129215945), FRAC_CONST(0.030919399248091) }, + { FRAC_CONST(0.009153896341616), FRAC_CONST(0.030949520972603) }, + { FRAC_CONST(0.009052565436412), FRAC_CONST(0.030979311252611) }, + { FRAC_CONST(0.008951137585505), FRAC_CONST(0.031008769769084) }, + { FRAC_CONST(0.008849613875105), FRAC_CONST(0.031037896206544) }, + { FRAC_CONST(0.008747995392451), FRAC_CONST(0.031066690253072) }, + { FRAC_CONST(0.008646283225794), FRAC_CONST(0.031095151600306) }, + { FRAC_CONST(0.008544478464390), FRAC_CONST(0.031123279943448) }, + { FRAC_CONST(0.008442582198486), FRAC_CONST(0.031151074981266) }, + { FRAC_CONST(0.008340595519310), FRAC_CONST(0.031178536416098) }, + { FRAC_CONST(0.008238519519057), FRAC_CONST(0.031205663953853) }, + { FRAC_CONST(0.008136355290878), FRAC_CONST(0.031232457304017) }, + { FRAC_CONST(0.008034103928871), FRAC_CONST(0.031258916179656) }, + { FRAC_CONST(0.007931766528065), FRAC_CONST(0.031285040297416) }, + { FRAC_CONST(0.007829344184412), FRAC_CONST(0.031310829377528) }, + { FRAC_CONST(0.007726837994772), FRAC_CONST(0.031336283143813) }, + { FRAC_CONST(0.007624249056906), FRAC_CONST(0.031361401323680) }, + { FRAC_CONST(0.007521578469457), FRAC_CONST(0.031386183648135) }, + { FRAC_CONST(0.007418827331946), FRAC_CONST(0.031410629851778) }, + { FRAC_CONST(0.007315996744755), FRAC_CONST(0.031434739672811) }, + { FRAC_CONST(0.007213087809115), FRAC_CONST(0.031458512853036) }, + { FRAC_CONST(0.007110101627101), FRAC_CONST(0.031481949137863) }, + { FRAC_CONST(0.007007039301610), FRAC_CONST(0.031505048276306) }, + { FRAC_CONST(0.006903901936357), FRAC_CONST(0.031527810020993) }, + { FRAC_CONST(0.006800690635862), FRAC_CONST(0.031550234128164) }, + { FRAC_CONST(0.006697406505433), FRAC_CONST(0.031572320357675) }, + { FRAC_CONST(0.006594050651161), FRAC_CONST(0.031594068473000) }, + { FRAC_CONST(0.006490624179905), FRAC_CONST(0.031615478241233) }, + { FRAC_CONST(0.006387128199278), FRAC_CONST(0.031636549433095) }, + { FRAC_CONST(0.006283563817639), FRAC_CONST(0.031657281822929) }, + { FRAC_CONST(0.006179932144080), FRAC_CONST(0.031677675188707) }, + { FRAC_CONST(0.006076234288412), FRAC_CONST(0.031697729312034) }, + { FRAC_CONST(0.005972471361157), FRAC_CONST(0.031717443978146) }, + { FRAC_CONST(0.005868644473532), FRAC_CONST(0.031736818975914) }, + { FRAC_CONST(0.005764754737440), FRAC_CONST(0.031755854097848) }, + { FRAC_CONST(0.005660803265456), FRAC_CONST(0.031774549140098) }, + { FRAC_CONST(0.005556791170816), FRAC_CONST(0.031792903902453) }, + { FRAC_CONST(0.005452719567407), FRAC_CONST(0.031810918188350) }, + { FRAC_CONST(0.005348589569753), FRAC_CONST(0.031828591804869) }, + { FRAC_CONST(0.005244402293001), FRAC_CONST(0.031845924562742) }, + { FRAC_CONST(0.005140158852914), FRAC_CONST(0.031862916276347) }, + { FRAC_CONST(0.005035860365855), FRAC_CONST(0.031879566763717) }, + { FRAC_CONST(0.004931507948778), FRAC_CONST(0.031895875846539) }, + { FRAC_CONST(0.004827102719212), FRAC_CONST(0.031911843350155) }, + { FRAC_CONST(0.004722645795254), FRAC_CONST(0.031927469103567) }, + { FRAC_CONST(0.004618138295554), FRAC_CONST(0.031942752939435) }, + { FRAC_CONST(0.004513581339303), FRAC_CONST(0.031957694694082) }, + { FRAC_CONST(0.004408976046222), FRAC_CONST(0.031972294207493) }, + { FRAC_CONST(0.004304323536549), FRAC_CONST(0.031986551323320) }, + { FRAC_CONST(0.004199624931030), FRAC_CONST(0.032000465888879) }, + { FRAC_CONST(0.004094881350902), FRAC_CONST(0.032014037755158) }, + { FRAC_CONST(0.003990093917884), FRAC_CONST(0.032027266776813) }, + { FRAC_CONST(0.003885263754166), FRAC_CONST(0.032040152812170) }, + { FRAC_CONST(0.003780391982394), FRAC_CONST(0.032052695723232) }, + { FRAC_CONST(0.003675479725661), FRAC_CONST(0.032064895375674) }, + { FRAC_CONST(0.003570528107494), FRAC_CONST(0.032076751638847) }, + { FRAC_CONST(0.003465538251839), FRAC_CONST(0.032088264385780) }, + { FRAC_CONST(0.003360511283053), FRAC_CONST(0.032099433493181) }, + { FRAC_CONST(0.003255448325892), FRAC_CONST(0.032110258841438) }, + { FRAC_CONST(0.003150350505494), FRAC_CONST(0.032120740314619) }, + { FRAC_CONST(0.003045218947373), FRAC_CONST(0.032130877800478) }, + { FRAC_CONST(0.002940054777404), FRAC_CONST(0.032140671190449) }, + { FRAC_CONST(0.002834859121810), FRAC_CONST(0.032150120379653) }, + { FRAC_CONST(0.002729633107153), FRAC_CONST(0.032159225266897) }, + { FRAC_CONST(0.002624377860318), FRAC_CONST(0.032167985754674) }, + { FRAC_CONST(0.002519094508504), FRAC_CONST(0.032176401749168) }, + { FRAC_CONST(0.002413784179212), FRAC_CONST(0.032184473160250) }, + { FRAC_CONST(0.002308448000231), FRAC_CONST(0.032192199901481) }, + { FRAC_CONST(0.002203087099626), FRAC_CONST(0.032199581890114) }, + { FRAC_CONST(0.002097702605728), FRAC_CONST(0.032206619047093) }, + { FRAC_CONST(0.001992295647121), FRAC_CONST(0.032213311297057) }, + { FRAC_CONST(0.001886867352628), FRAC_CONST(0.032219658568338) }, + { FRAC_CONST(0.001781418851302), FRAC_CONST(0.032225660792960) }, + { FRAC_CONST(0.001675951272410), FRAC_CONST(0.032231317906644) }, + { FRAC_CONST(0.001570465745428), FRAC_CONST(0.032236629848809) }, + { FRAC_CONST(0.001464963400018), FRAC_CONST(0.032241596562566) }, + { FRAC_CONST(0.001359445366028), FRAC_CONST(0.032246217994727) }, + { FRAC_CONST(0.001253912773470), FRAC_CONST(0.032250494095799) }, + { FRAC_CONST(0.001148366752513), FRAC_CONST(0.032254424819990) }, + { FRAC_CONST(0.001042808433471), FRAC_CONST(0.032258010125204) }, + { FRAC_CONST(0.000937238946789), FRAC_CONST(0.032261249973045) }, + { FRAC_CONST(0.000831659423030), FRAC_CONST(0.032264144328817) }, + { FRAC_CONST(0.000726070992868), FRAC_CONST(0.032266693161525) }, + { FRAC_CONST(0.000620474787068), FRAC_CONST(0.032268896443871) }, + { FRAC_CONST(0.000514871936481), FRAC_CONST(0.032270754152261) }, + { FRAC_CONST(0.000409263572030), FRAC_CONST(0.032272266266801) }, + { FRAC_CONST(0.000303650824695), FRAC_CONST(0.032273432771295) }, + { FRAC_CONST(0.000198034825504), FRAC_CONST(0.032274253653254) }, + { FRAC_CONST(0.000092416705518), FRAC_CONST(0.032274728903884) } +}; + +#ifdef LD_DEC +/* 240 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_960[] = +{ + { FRAC_CONST(0.045643531183573), FRAC_CONST(0.000037342034959) }, + { FRAC_CONST(0.045642309173789), FRAC_CONST(0.000336075315362) }, + { FRAC_CONST(0.045639131999390), FRAC_CONST(0.000634794199417) }, + { FRAC_CONST(0.045633999796474), FRAC_CONST(0.000933485891002) }, + { FRAC_CONST(0.045626912784890), FRAC_CONST(0.001232137595157) }, + { FRAC_CONST(0.045617871268219), FRAC_CONST(0.001530736518639) }, + { FRAC_CONST(0.045606875633772), FRAC_CONST(0.001829269870464) }, + { FRAC_CONST(0.045593926352564), FRAC_CONST(0.002127724862455) }, + { FRAC_CONST(0.045579023979299), FRAC_CONST(0.002426088709795) }, + { FRAC_CONST(0.045562169152346), FRAC_CONST(0.002724348631569) }, + { FRAC_CONST(0.045543362593709), FRAC_CONST(0.003022491851315) }, + { FRAC_CONST(0.045522605108999), FRAC_CONST(0.003320505597570) }, + { FRAC_CONST(0.045499897587396), FRAC_CONST(0.003618377104416) }, + { FRAC_CONST(0.045475241001617), FRAC_CONST(0.003916093612031) }, + { FRAC_CONST(0.045448636407866), FRAC_CONST(0.004213642367228) }, + { FRAC_CONST(0.045420084945797), FRAC_CONST(0.004511010624011) }, + { FRAC_CONST(0.045389587838458), FRAC_CONST(0.004808185644112) }, + { FRAC_CONST(0.045357146392244), FRAC_CONST(0.005105154697544) }, + { FRAC_CONST(0.045322761996840), FRAC_CONST(0.005401905063139) }, + { FRAC_CONST(0.045286436125157), FRAC_CONST(0.005698424029100) }, + { FRAC_CONST(0.045248170333275), FRAC_CONST(0.005994698893542) }, + { FRAC_CONST(0.045207966260374), FRAC_CONST(0.006290716965035) }, + { FRAC_CONST(0.045165825628663), FRAC_CONST(0.006586465563151) }, + { FRAC_CONST(0.045121750243305), FRAC_CONST(0.006881932019003) }, + { FRAC_CONST(0.045075741992343), FRAC_CONST(0.007177103675792) }, + { FRAC_CONST(0.045027802846618), FRAC_CONST(0.007471967889347) }, + { FRAC_CONST(0.044977934859683), FRAC_CONST(0.007766512028667) }, + { FRAC_CONST(0.044926140167717), FRAC_CONST(0.008060723476460) }, + { FRAC_CONST(0.044872420989432), FRAC_CONST(0.008354589629687) }, + { FRAC_CONST(0.044816779625979), FRAC_CONST(0.008648097900101) }, + { FRAC_CONST(0.044759218460849), FRAC_CONST(0.008941235714784) }, + { FRAC_CONST(0.044699739959770), FRAC_CONST(0.009233990516688) }, + { FRAC_CONST(0.044638346670603), FRAC_CONST(0.009526349765171) }, + { FRAC_CONST(0.044575041223233), FRAC_CONST(0.009818300936537) }, + { FRAC_CONST(0.044509826329454), FRAC_CONST(0.010109831524568) }, + { FRAC_CONST(0.044442704782856), FRAC_CONST(0.010400929041064) }, + { FRAC_CONST(0.044373679458701), FRAC_CONST(0.010691581016378) }, + { FRAC_CONST(0.044302753313806), FRAC_CONST(0.010981774999945) }, + { FRAC_CONST(0.044229929386409), FRAC_CONST(0.011271498560822) }, + { FRAC_CONST(0.044155210796046), FRAC_CONST(0.011560739288214) }, + { FRAC_CONST(0.044078600743413), FRAC_CONST(0.011849484792012) }, + { FRAC_CONST(0.044000102510229), FRAC_CONST(0.012137722703321) }, + { FRAC_CONST(0.043919719459097), FRAC_CONST(0.012425440674986) }, + { FRAC_CONST(0.043837455033359), FRAC_CONST(0.012712626382127) }, + { FRAC_CONST(0.043753312756950), FRAC_CONST(0.012999267522665) }, + { FRAC_CONST(0.043667296234245), FRAC_CONST(0.013285351817848) }, + { FRAC_CONST(0.043579409149906), FRAC_CONST(0.013570867012776) }, + { FRAC_CONST(0.043489655268722), FRAC_CONST(0.013855800876928) }, + { FRAC_CONST(0.043398038435451), FRAC_CONST(0.014140141204686) }, + { FRAC_CONST(0.043304562574653), FRAC_CONST(0.014423875815857) }, + { FRAC_CONST(0.043209231690524), FRAC_CONST(0.014706992556195) }, + { FRAC_CONST(0.043112049866720), FRAC_CONST(0.014989479297920) }, + { FRAC_CONST(0.043013021266188), FRAC_CONST(0.015271323940241) }, + { FRAC_CONST(0.042912150130984), FRAC_CONST(0.015552514409871) }, + { FRAC_CONST(0.042809440782090), FRAC_CONST(0.015833038661547) }, + { FRAC_CONST(0.042704897619235), FRAC_CONST(0.016112884678543) }, + { FRAC_CONST(0.042598525120698), FRAC_CONST(0.016392040473187) }, + { FRAC_CONST(0.042490327843124), FRAC_CONST(0.016670494087374) }, + { FRAC_CONST(0.042380310421324), FRAC_CONST(0.016948233593079) }, + { FRAC_CONST(0.042268477568078), FRAC_CONST(0.017225247092864) }, + { FRAC_CONST(0.042154834073934), FRAC_CONST(0.017501522720393) }, + { FRAC_CONST(0.042039384807000), FRAC_CONST(0.017777048640940) }, + { FRAC_CONST(0.041922134712739), FRAC_CONST(0.018051813051888) }, + { FRAC_CONST(0.041803088813754), FRAC_CONST(0.018325804183247) }, + { FRAC_CONST(0.041682252209576), FRAC_CONST(0.018599010298148) }, + { FRAC_CONST(0.041559630076443), FRAC_CONST(0.018871419693350) }, + { FRAC_CONST(0.041435227667079), FRAC_CONST(0.019143020699741) }, + { FRAC_CONST(0.041309050310468), FRAC_CONST(0.019413801682838) }, + { FRAC_CONST(0.041181103411629), FRAC_CONST(0.019683751043285) }, + { FRAC_CONST(0.041051392451382), FRAC_CONST(0.019952857217350) }, + { FRAC_CONST(0.040919922986111), FRAC_CONST(0.020221108677421) }, + { FRAC_CONST(0.040786700647532), FRAC_CONST(0.020488493932496) }, + { FRAC_CONST(0.040651731142446), FRAC_CONST(0.020755001528683) }, + { FRAC_CONST(0.040515020252497), FRAC_CONST(0.021020620049682) }, + { FRAC_CONST(0.040376573833925), FRAC_CONST(0.021285338117280) }, + { FRAC_CONST(0.040236397817314), FRAC_CONST(0.021549144391836) }, + { FRAC_CONST(0.040094498207337), FRAC_CONST(0.021812027572768) }, + { FRAC_CONST(0.039950881082502), FRAC_CONST(0.022073976399034) }, + { FRAC_CONST(0.039805552594888), FRAC_CONST(0.022334979649620) }, + { FRAC_CONST(0.039658518969884), FRAC_CONST(0.022595026144014) }, + { FRAC_CONST(0.039509786505922), FRAC_CONST(0.022854104742690) }, + { FRAC_CONST(0.039359361574204), FRAC_CONST(0.023112204347583) }, + { FRAC_CONST(0.039207250618434), FRAC_CONST(0.023369313902565) }, + { FRAC_CONST(0.039053460154540), FRAC_CONST(0.023625422393919) }, + { FRAC_CONST(0.038897996770393), FRAC_CONST(0.023880518850809) }, + { FRAC_CONST(0.038740867125527), FRAC_CONST(0.024134592345752) }, + { FRAC_CONST(0.038582077950852), FRAC_CONST(0.024387631995085) }, + { FRAC_CONST(0.038421636048370), FRAC_CONST(0.024639626959432) }, + { FRAC_CONST(0.038259548290876), FRAC_CONST(0.024890566444167) }, + { FRAC_CONST(0.038095821621671), FRAC_CONST(0.025140439699877) }, + { FRAC_CONST(0.037930463054261), FRAC_CONST(0.025389236022825) }, + { FRAC_CONST(0.037763479672055), FRAC_CONST(0.025636944755403) }, + { FRAC_CONST(0.037594878628068), FRAC_CONST(0.025883555286595) }, + { FRAC_CONST(0.037424667144605), FRAC_CONST(0.026129057052425) }, + { FRAC_CONST(0.037252852512960), FRAC_CONST(0.026373439536415) }, + { FRAC_CONST(0.037079442093102), FRAC_CONST(0.026616692270033) }, + { FRAC_CONST(0.036904443313354), FRAC_CONST(0.026858804833142) }, + { FRAC_CONST(0.036727863670081), FRAC_CONST(0.027099766854444) }, + { FRAC_CONST(0.036549710727369), FRAC_CONST(0.027339568011930) }, + { FRAC_CONST(0.036369992116697), FRAC_CONST(0.027578198033315) }, + { FRAC_CONST(0.036188715536611), FRAC_CONST(0.027815646696484) }, + { FRAC_CONST(0.036005888752396), FRAC_CONST(0.028051903829926) }, + { FRAC_CONST(0.035821519595745), FRAC_CONST(0.028286959313171) }, + { FRAC_CONST(0.035635615964417), FRAC_CONST(0.028520803077226) }, + { FRAC_CONST(0.035448185821906), FRAC_CONST(0.028753425105002) }, + { FRAC_CONST(0.035259237197095), FRAC_CONST(0.028984815431745) }, + { FRAC_CONST(0.035068778183914), FRAC_CONST(0.029214964145465) }, + { FRAC_CONST(0.034876816940994), FRAC_CONST(0.029443861387355) }, + { FRAC_CONST(0.034683361691315), FRAC_CONST(0.029671497352220) }, + { FRAC_CONST(0.034488420721856), FRAC_CONST(0.029897862288892) }, + { FRAC_CONST(0.034292002383240), FRAC_CONST(0.030122946500652) }, + { FRAC_CONST(0.034094115089375), FRAC_CONST(0.030346740345641) }, + { FRAC_CONST(0.033894767317093), FRAC_CONST(0.030569234237276) }, + { FRAC_CONST(0.033693967605790), FRAC_CONST(0.030790418644658) }, + { FRAC_CONST(0.033491724557057), FRAC_CONST(0.031010284092984) }, + { FRAC_CONST(0.033288046834313), FRAC_CONST(0.031228821163949) }, + { FRAC_CONST(0.033082943162434), FRAC_CONST(0.031446020496153) }, + { FRAC_CONST(0.032876422327378), FRAC_CONST(0.031661872785500) }, + { FRAC_CONST(0.032668493175811), FRAC_CONST(0.031876368785596) }, + { FRAC_CONST(0.032459164614726), FRAC_CONST(0.032089499308145) }, + { FRAC_CONST(0.032248445611061), FRAC_CONST(0.032301255223347) }, + { FRAC_CONST(0.032036345191317), FRAC_CONST(0.032511627460281) }, + { FRAC_CONST(0.031822872441171), FRAC_CONST(0.032720607007302) }, + { FRAC_CONST(0.031608036505083), FRAC_CONST(0.032928184912422) }, + { FRAC_CONST(0.031391846585912), FRAC_CONST(0.033134352283693) }, + { FRAC_CONST(0.031174311944513), FRAC_CONST(0.033339100289593) }, + { FRAC_CONST(0.030955441899347), FRAC_CONST(0.033542420159397) }, + { FRAC_CONST(0.030735245826077), FRAC_CONST(0.033744303183559) }, + { FRAC_CONST(0.030513733157171), FRAC_CONST(0.033944740714083) }, + { FRAC_CONST(0.030290913381494), FRAC_CONST(0.034143724164891) }, + { FRAC_CONST(0.030066796043904), FRAC_CONST(0.034341245012195) }, + { FRAC_CONST(0.029841390744841), FRAC_CONST(0.034537294794860) }, + { FRAC_CONST(0.029614707139919), FRAC_CONST(0.034731865114764) }, + { FRAC_CONST(0.029386754939508), FRAC_CONST(0.034924947637164) }, + { FRAC_CONST(0.029157543908322), FRAC_CONST(0.035116534091046) }, + { FRAC_CONST(0.028927083864999), FRAC_CONST(0.035306616269485) }, + { FRAC_CONST(0.028695384681680), FRAC_CONST(0.035495186029992) }, + { FRAC_CONST(0.028462456283587), FRAC_CONST(0.035682235294866) }, + { FRAC_CONST(0.028228308648598), FRAC_CONST(0.035867756051541) }, + { FRAC_CONST(0.027992951806817), FRAC_CONST(0.036051740352923) }, + { FRAC_CONST(0.027756395840148), FRAC_CONST(0.036234180317738) }, + { FRAC_CONST(0.027518650881862), FRAC_CONST(0.036415068130865) }, + { FRAC_CONST(0.027279727116161), FRAC_CONST(0.036594396043672) }, + { FRAC_CONST(0.027039634777745), FRAC_CONST(0.036772156374348) }, + { FRAC_CONST(0.026798384151369), FRAC_CONST(0.036948341508233) }, + { FRAC_CONST(0.026555985571409), FRAC_CONST(0.037122943898140) }, + { FRAC_CONST(0.026312449421412), FRAC_CONST(0.037295956064686) }, + { FRAC_CONST(0.026067786133656), FRAC_CONST(0.037467370596605) }, + { FRAC_CONST(0.025822006188702), FRAC_CONST(0.037637180151068) }, + { FRAC_CONST(0.025575120114946), FRAC_CONST(0.037805377454000) }, + { FRAC_CONST(0.025327138488165), FRAC_CONST(0.037971955300388) }, + { FRAC_CONST(0.025078071931066), FRAC_CONST(0.038136906554591) }, + { FRAC_CONST(0.024827931112832), FRAC_CONST(0.038300224150647) }, + { FRAC_CONST(0.024576726748663), FRAC_CONST(0.038461901092573) }, + { FRAC_CONST(0.024324469599317), FRAC_CONST(0.038621930454668) }, + { FRAC_CONST(0.024071170470652), FRAC_CONST(0.038780305381806) }, + { FRAC_CONST(0.023816840213160), FRAC_CONST(0.038937019089732) }, + { FRAC_CONST(0.023561489721501), FRAC_CONST(0.039092064865353) }, + { FRAC_CONST(0.023305129934041), FRAC_CONST(0.039245436067023) }, + { FRAC_CONST(0.023047771832380), FRAC_CONST(0.039397126124832) }, + { FRAC_CONST(0.022789426440883), FRAC_CONST(0.039547128540881) }, + { FRAC_CONST(0.022530104826206), FRAC_CONST(0.039695436889566) }, + { FRAC_CONST(0.022269818096825), FRAC_CONST(0.039842044817851) }, + { FRAC_CONST(0.022008577402555), FRAC_CONST(0.039986946045542) }, + { FRAC_CONST(0.021746393934081), FRAC_CONST(0.040130134365550) }, + { FRAC_CONST(0.021483278922467), FRAC_CONST(0.040271603644166) }, + { FRAC_CONST(0.021219243638687), FRAC_CONST(0.040411347821316) }, + { FRAC_CONST(0.020954299393132), FRAC_CONST(0.040549360910825) }, + { FRAC_CONST(0.020688457535133), FRAC_CONST(0.040685637000671) }, + { FRAC_CONST(0.020421729452469), FRAC_CONST(0.040820170253240) }, + { FRAC_CONST(0.020154126570884), FRAC_CONST(0.040952954905576) }, + { FRAC_CONST(0.019885660353596), FRAC_CONST(0.041083985269625) }, + { FRAC_CONST(0.019616342300802), FRAC_CONST(0.041213255732484) }, + { FRAC_CONST(0.019346183949192), FRAC_CONST(0.041340760756635) }, + { FRAC_CONST(0.019075196871451), FRAC_CONST(0.041466494880189) }, + { FRAC_CONST(0.018803392675763), FRAC_CONST(0.041590452717113) }, + { FRAC_CONST(0.018530783005316), FRAC_CONST(0.041712628957466) }, + { FRAC_CONST(0.018257379537800), FRAC_CONST(0.041833018367625) }, + { FRAC_CONST(0.017983193984910), FRAC_CONST(0.041951615790509) }, + { FRAC_CONST(0.017708238091842), FRAC_CONST(0.042068416145797) }, + { FRAC_CONST(0.017432523636792), FRAC_CONST(0.042183414430153) }, + { FRAC_CONST(0.017156062430449), FRAC_CONST(0.042296605717432) }, + { FRAC_CONST(0.016878866315491), FRAC_CONST(0.042407985158896) }, + { FRAC_CONST(0.016600947166078), FRAC_CONST(0.042517547983420) }, + { FRAC_CONST(0.016322316887341), FRAC_CONST(0.042625289497698) }, + { FRAC_CONST(0.016042987414872), FRAC_CONST(0.042731205086442) }, + { FRAC_CONST(0.015762970714219), FRAC_CONST(0.042835290212581) }, + { FRAC_CONST(0.015482278780363), FRAC_CONST(0.042937540417454) }, + { FRAC_CONST(0.015200923637213), FRAC_CONST(0.043037951321002) }, + { FRAC_CONST(0.014918917337087), FRAC_CONST(0.043136518621958) }, + { FRAC_CONST(0.014636271960196), FRAC_CONST(0.043233238098025) }, + { FRAC_CONST(0.014352999614128), FRAC_CONST(0.043328105606063) }, + { FRAC_CONST(0.014069112433327), FRAC_CONST(0.043421117082265) }, + { FRAC_CONST(0.013784622578575), FRAC_CONST(0.043512268542327) }, + { FRAC_CONST(0.013499542236471), FRAC_CONST(0.043601556081625) }, + { FRAC_CONST(0.013213883618907), FRAC_CONST(0.043688975875378) }, + { FRAC_CONST(0.012927658962548), FRAC_CONST(0.043774524178812) }, + { FRAC_CONST(0.012640880528305), FRAC_CONST(0.043858197327323) }, + { FRAC_CONST(0.012353560600813), FRAC_CONST(0.043939991736633) }, + { FRAC_CONST(0.012065711487901), FRAC_CONST(0.044019903902940) }, + { FRAC_CONST(0.011777345520066), FRAC_CONST(0.044097930403073) }, + { FRAC_CONST(0.011488475049948), FRAC_CONST(0.044174067894638) }, + { FRAC_CONST(0.011199112451794), FRAC_CONST(0.044248313116156) }, + { FRAC_CONST(0.010909270120937), FRAC_CONST(0.044320662887211) }, + { FRAC_CONST(0.010618960473257), FRAC_CONST(0.044391114108577) }, + { FRAC_CONST(0.010328195944653), FRAC_CONST(0.044459663762361) }, + { FRAC_CONST(0.010036988990509), FRAC_CONST(0.044526308912122) }, + { FRAC_CONST(0.009745352085163), FRAC_CONST(0.044591046703005) }, + { FRAC_CONST(0.009453297721368), FRAC_CONST(0.044653874361857) }, + { FRAC_CONST(0.009160838409762), FRAC_CONST(0.044714789197351) }, + { FRAC_CONST(0.008867986678328), FRAC_CONST(0.044773788600099) }, + { FRAC_CONST(0.008574755071860), FRAC_CONST(0.044830870042761) }, + { FRAC_CONST(0.008281156151424), FRAC_CONST(0.044886031080160) }, + { FRAC_CONST(0.007987202493820), FRAC_CONST(0.044939269349379) }, + { FRAC_CONST(0.007692906691044), FRAC_CONST(0.044990582569869) }, + { FRAC_CONST(0.007398281349750), FRAC_CONST(0.045039968543542) }, + { FRAC_CONST(0.007103339090706), FRAC_CONST(0.045087425154868) }, + { FRAC_CONST(0.006808092548258), FRAC_CONST(0.045132950370962) }, + { FRAC_CONST(0.006512554369783), FRAC_CONST(0.045176542241676) }, + { FRAC_CONST(0.006216737215155), FRAC_CONST(0.045218198899680) }, + { FRAC_CONST(0.005920653756196), FRAC_CONST(0.045257918560541) }, + { FRAC_CONST(0.005624316676135), FRAC_CONST(0.045295699522801) }, + { FRAC_CONST(0.005327738669067), FRAC_CONST(0.045331540168049) }, + { FRAC_CONST(0.005030932439406), FRAC_CONST(0.045365438960992) }, + { FRAC_CONST(0.004733910701344), FRAC_CONST(0.045397394449517) }, + { FRAC_CONST(0.004436686178303), FRAC_CONST(0.045427405264758) }, + { FRAC_CONST(0.004139271602393), FRAC_CONST(0.045455470121152) }, + { FRAC_CONST(0.003841679713863), FRAC_CONST(0.045481587816494) }, + { FRAC_CONST(0.003543923260561), FRAC_CONST(0.045505757231988) }, + { FRAC_CONST(0.003246014997382), FRAC_CONST(0.045527977332297) }, + { FRAC_CONST(0.002947967685724), FRAC_CONST(0.045548247165585) }, + { FRAC_CONST(0.002649794092941), FRAC_CONST(0.045566565863562) }, + { FRAC_CONST(0.002351506991799), FRAC_CONST(0.045582932641515) }, + { FRAC_CONST(0.002053119159924), FRAC_CONST(0.045597346798344) }, + { FRAC_CONST(0.001754643379257), FRAC_CONST(0.045609807716597) }, + { FRAC_CONST(0.001456092435508), FRAC_CONST(0.045620314862489) }, + { FRAC_CONST(0.001157479117605), FRAC_CONST(0.045628867785927) }, + { FRAC_CONST(0.000858816217149), FRAC_CONST(0.045635466120535) }, + { FRAC_CONST(0.000560116527865), FRAC_CONST(0.045640109583661) }, + { FRAC_CONST(0.000261392845053), FRAC_CONST(0.045642797976394) } +}; +#endif // LD_DEC + +/* 60 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_240[] = +{ + { FRAC_CONST(0.091286604111815), FRAC_CONST(0.000298735779793) }, + { FRAC_CONST(0.091247502481454), FRAC_CONST(0.002688238127538) }, + { FRAC_CONST(0.091145864370807), FRAC_CONST(0.005075898091152) }, + { FRAC_CONST(0.090981759437558), FRAC_CONST(0.007460079287760) }, + { FRAC_CONST(0.090755300151030), FRAC_CONST(0.009839147718664) }, + { FRAC_CONST(0.090466641715108), FRAC_CONST(0.012211472889198) }, + { FRAC_CONST(0.090115981961863), FRAC_CONST(0.014575428926191) }, + { FRAC_CONST(0.089703561215976), FRAC_CONST(0.016929395692256) }, + { FRAC_CONST(0.089229662130024), FRAC_CONST(0.019271759896156) }, + { FRAC_CONST(0.088694609490769), FRAC_CONST(0.021600916198470) }, + { FRAC_CONST(0.088098769996564), FRAC_CONST(0.023915268311810) }, + { FRAC_CONST(0.087442552006035), FRAC_CONST(0.026213230094844) }, + { FRAC_CONST(0.086726405258214), FRAC_CONST(0.028493226639351) }, + { FRAC_CONST(0.085950820564309), FRAC_CONST(0.030753695349588) }, + { FRAC_CONST(0.085116329471329), FRAC_CONST(0.032993087013213) }, + { FRAC_CONST(0.084223503897785), FRAC_CONST(0.035209866863042) }, + { FRAC_CONST(0.083272955741727), FRAC_CONST(0.037402515628894) }, + { FRAC_CONST(0.082265336461381), FRAC_CONST(0.039569530578832) }, + { FRAC_CONST(0.081201336628670), FRAC_CONST(0.041709426549053) }, + { FRAC_CONST(0.080081685455930), FRAC_CONST(0.043820736961749) }, + { FRAC_CONST(0.078907150296148), FRAC_CONST(0.045902014830227) }, + { FRAC_CONST(0.077678536117054), FRAC_CONST(0.047951833750597) }, + { FRAC_CONST(0.076396684949434), FRAC_CONST(0.049968788879362) }, + { FRAC_CONST(0.075062475310050), FRAC_CONST(0.051951497896226) }, + { FRAC_CONST(0.073676821599542), FRAC_CONST(0.053898601951466) }, + { FRAC_CONST(0.072240673475749), FRAC_CONST(0.055808766597225) }, + { FRAC_CONST(0.070755015202858), FRAC_CONST(0.057680682702068) }, + { FRAC_CONST(0.069220864976840), FRAC_CONST(0.059513067348201) }, + { FRAC_CONST(0.067639274227625), FRAC_CONST(0.061304664710718) }, + { FRAC_CONST(0.066011326898512), FRAC_CONST(0.063054246918278) }, + { FRAC_CONST(0.064338138703282), FRAC_CONST(0.064760614894630) }, + { FRAC_CONST(0.062620856361546), FRAC_CONST(0.066422599180399) }, + { FRAC_CONST(0.060860656812842), FRAC_CONST(0.068039060734572) }, + { FRAC_CONST(0.059058746410016), FRAC_CONST(0.069608891715145) }, + { FRAC_CONST(0.057216360092450), FRAC_CONST(0.071131016238378) }, + { FRAC_CONST(0.055334760539699), FRAC_CONST(0.072604391116154) }, + { FRAC_CONST(0.053415237306106), FRAC_CONST(0.074028006570930) }, + { FRAC_CONST(0.051459105937014), FRAC_CONST(0.075400886927784) }, + { FRAC_CONST(0.049467707067153), FRAC_CONST(0.076722091283096) }, + { FRAC_CONST(0.047442405501835), FRAC_CONST(0.077990714149396) }, + { FRAC_CONST(0.045384589281588), FRAC_CONST(0.079205886075941) }, + { FRAC_CONST(0.043295668730857), FRAC_CONST(0.080366774244592) }, + { FRAC_CONST(0.041177075491445), FRAC_CONST(0.081472583040586) }, + { FRAC_CONST(0.039030261541332), FRAC_CONST(0.082522554597810) }, + { FRAC_CONST(0.036856698199564), FRAC_CONST(0.083515969318206) }, + { FRAC_CONST(0.034657875117883), FRAC_CONST(0.084452146364948) }, + { FRAC_CONST(0.032435299259796), FRAC_CONST(0.085330444129049) }, + { FRAC_CONST(0.030190493867775), FRAC_CONST(0.086150260669096) }, + { FRAC_CONST(0.027924997419306), FRAC_CONST(0.086911034123781) }, + { FRAC_CONST(0.025640362572491), FRAC_CONST(0.087612243096981) }, + { FRAC_CONST(0.023338155101933), FRAC_CONST(0.088253407015092) }, + { FRAC_CONST(0.021019952825636), FRAC_CONST(0.088834086456390) }, + { FRAC_CONST(0.018687344523641), FRAC_CONST(0.089353883452193) }, + { FRAC_CONST(0.016341928849164), FRAC_CONST(0.089812441759604) }, + { FRAC_CONST(0.013985313232951), FRAC_CONST(0.090209447105664) }, + { FRAC_CONST(0.011619112781631), FRAC_CONST(0.090544627402740) }, + { FRAC_CONST(0.009244949170797), FRAC_CONST(0.090817752935000) }, + { FRAC_CONST(0.006864449533597), FRAC_CONST(0.091028636515846) }, + { FRAC_CONST(0.004479245345574), FRAC_CONST(0.091177133616206) }, + { FRAC_CONST(0.002090971306534), FRAC_CONST(0.091263142463585) } +}; +#endif // ALLOW_SMALL_FRAMELENGTH + +#ifdef SSR_DEC +/* 128 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_512[] = +{ + { FRAC_CONST(0.062499926465731), FRAC_CONST(0.000095873761643) }, + { FRAC_CONST(0.062494043817678), FRAC_CONST(0.000862836783004) }, + { FRAC_CONST(0.062478749796497), FRAC_CONST(0.001629669864319) }, + { FRAC_CONST(0.062454046705412), FRAC_CONST(0.002396257523347) }, + { FRAC_CONST(0.062419938264617), FRAC_CONST(0.003162484314806) }, + { FRAC_CONST(0.062376429610718), FRAC_CONST(0.003928234847760) }, + { FRAC_CONST(0.062323527295958), FRAC_CONST(0.004693393802995) }, + { FRAC_CONST(0.062261239287231), FRAC_CONST(0.005457845950387) }, + { FRAC_CONST(0.062189574964882), FRAC_CONST(0.006221476166254) }, + { FRAC_CONST(0.062108545121295), FRAC_CONST(0.006984169450695) }, + { FRAC_CONST(0.062018161959266), FRAC_CONST(0.007745810944907) }, + { FRAC_CONST(0.061918439090167), FRAC_CONST(0.008506285948482) }, + { FRAC_CONST(0.061809391531894), FRAC_CONST(0.009265479936681) }, + { FRAC_CONST(0.061691035706609), FRAC_CONST(0.010023278577683) }, + { FRAC_CONST(0.061563389438265), FRAC_CONST(0.010779567749800) }, + { FRAC_CONST(0.061426471949919), FRAC_CONST(0.011534233558664) }, + { FRAC_CONST(0.061280303860842), FRAC_CONST(0.012287162354380) }, + { FRAC_CONST(0.061124907183410), FRAC_CONST(0.013038240748641) }, + { FRAC_CONST(0.060960305319791), FRAC_CONST(0.013787355631805) }, + { FRAC_CONST(0.060786523058421), FRAC_CONST(0.014534394189923) }, + { FRAC_CONST(0.060603586570268), FRAC_CONST(0.015279243921739) }, + { FRAC_CONST(0.060411523404896), FRAC_CONST(0.016021792655621) }, + { FRAC_CONST(0.060210362486310), FRAC_CONST(0.016761928566463) }, + { FRAC_CONST(0.060000134108604), FRAC_CONST(0.017499540192517) }, + { FRAC_CONST(0.059780869931400), FRAC_CONST(0.018234516452187) }, + { FRAC_CONST(0.059552602975075), FRAC_CONST(0.018966746660751) }, + { FRAC_CONST(0.059315367615794), FRAC_CONST(0.019696120547033) }, + { FRAC_CONST(0.059069199580329), FRAC_CONST(0.020422528270008) }, + { FRAC_CONST(0.058814135940681), FRAC_CONST(0.021145860435346) }, + { FRAC_CONST(0.058550215108495), FRAC_CONST(0.021866008111883) }, + { FRAC_CONST(0.058277476829279), FRAC_CONST(0.022582862848028) }, + { FRAC_CONST(0.057995962176414), FRAC_CONST(0.023296316688095) }, + { FRAC_CONST(0.057705713544970), FRAC_CONST(0.024006262188558) }, + { FRAC_CONST(0.057406774645326), FRAC_CONST(0.024712592434239) }, + { FRAC_CONST(0.057099190496578), FRAC_CONST(0.025415201054398) }, + { FRAC_CONST(0.056783007419769), FRAC_CONST(0.026113982238763) }, + { FRAC_CONST(0.056458273030907), FRAC_CONST(0.026808830753458) }, + { FRAC_CONST(0.056125036233796), FRAC_CONST(0.027499641956852) }, + { FRAC_CONST(0.055783347212673), FRAC_CONST(0.028186311815319) }, + { FRAC_CONST(0.055433257424646), FRAC_CONST(0.028868736918904) }, + { FRAC_CONST(0.055074819591951), FRAC_CONST(0.029546814496896) }, + { FRAC_CONST(0.054708087694007), FRAC_CONST(0.030220442433307) }, + { FRAC_CONST(0.054333116959288), FRAC_CONST(0.030889519282247) }, + { FRAC_CONST(0.053949963857008), FRAC_CONST(0.031553944283204) }, + { FRAC_CONST(0.053558686088614), FRAC_CONST(0.032213617376216) }, + { FRAC_CONST(0.053159342579100), FRAC_CONST(0.032868439216943) }, + { FRAC_CONST(0.052751993468129), FRAC_CONST(0.033518311191623) }, + { FRAC_CONST(0.052336700100979), FRAC_CONST(0.034163135431927) }, + { FRAC_CONST(0.051913525019303), FRAC_CONST(0.034802814829698) }, + { FRAC_CONST(0.051482531951712), FRAC_CONST(0.035437253051569) }, + { FRAC_CONST(0.051043785804177), FRAC_CONST(0.036066354553480) }, + { FRAC_CONST(0.050597352650253), FRAC_CONST(0.036690024595057) }, + { FRAC_CONST(0.050143299721132), FRAC_CONST(0.037308169253887) }, + { FRAC_CONST(0.049681695395515), FRAC_CONST(0.037920695439658) }, + { FRAC_CONST(0.049212609189314), FRAC_CONST(0.038527510908178) }, + { FRAC_CONST(0.048736111745188), FRAC_CONST(0.039128524275271) }, + { FRAC_CONST(0.048252274821899), FRAC_CONST(0.039723645030535) }, + { FRAC_CONST(0.047761171283507), FRAC_CONST(0.040312783550971) }, + { FRAC_CONST(0.047262875088400), FRAC_CONST(0.040895851114488) }, + { FRAC_CONST(0.046757461278150), FRAC_CONST(0.041472759913252) }, + { FRAC_CONST(0.046245005966220), FRAC_CONST(0.042043423066923) }, + { FRAC_CONST(0.045725586326493), FRAC_CONST(0.042607754635728) }, + { FRAC_CONST(0.045199280581658), FRAC_CONST(0.043165669633408) }, + { FRAC_CONST(0.044666167991423), FRAC_CONST(0.043717084040018) }, + { FRAC_CONST(0.044126328840584), FRAC_CONST(0.044261914814575) }, + { FRAC_CONST(0.043579844426930), FRAC_CONST(0.044800079907569) }, + { FRAC_CONST(0.043026797049006), FRAC_CONST(0.045331498273316) }, + { FRAC_CONST(0.042467269993710), FRAC_CONST(0.045856089882166) }, + { FRAC_CONST(0.041901347523761), FRAC_CONST(0.046373775732552) }, + { FRAC_CONST(0.041329114865000), FRAC_CONST(0.046884477862888) }, + { FRAC_CONST(0.040750658193560), FRAC_CONST(0.047388119363313) }, + { FRAC_CONST(0.040166064622889), FRAC_CONST(0.047884624387270) }, + { FRAC_CONST(0.039575422190629), FRAC_CONST(0.048373918162926) }, + { FRAC_CONST(0.038978819845356), FRAC_CONST(0.048855927004441) }, + { FRAC_CONST(0.038376347433190), FRAC_CONST(0.049330578323055) }, + { FRAC_CONST(0.037768095684260), FRAC_CONST(0.049797800638026) }, + { FRAC_CONST(0.037154156199042), FRAC_CONST(0.050257523587392) }, + { FRAC_CONST(0.036534621434563), FRAC_CONST(0.050709677938566) }, + { FRAC_CONST(0.035909584690482), FRAC_CONST(0.051154195598769) }, + { FRAC_CONST(0.035279140095032), FRAC_CONST(0.051591009625274) }, + { FRAC_CONST(0.034643382590851), FRAC_CONST(0.052020054235496) }, + { FRAC_CONST(0.034002407920680), FRAC_CONST(0.052441264816895) }, + { FRAC_CONST(0.033356312612947), FRAC_CONST(0.052854577936706) }, + { FRAC_CONST(0.032705193967229), FRAC_CONST(0.053259931351495) }, + { FRAC_CONST(0.032049150039598), FRAC_CONST(0.053657264016528) }, + { FRAC_CONST(0.031388279627857), FRAC_CONST(0.054046516094966) }, + { FRAC_CONST(0.030722682256659), FRAC_CONST(0.054427628966880) }, + { FRAC_CONST(0.030052458162521), FRAC_CONST(0.054800545238072) }, + { FRAC_CONST(0.029377708278725), FRAC_CONST(0.055165208748723) }, + { FRAC_CONST(0.028698534220122), FRAC_CONST(0.055521564581850) }, + { FRAC_CONST(0.028015038267826), FRAC_CONST(0.055869559071575) }, + { FRAC_CONST(0.027327323353815), FRAC_CONST(0.056209139811209) }, + { FRAC_CONST(0.026635493045425), FRAC_CONST(0.056540255661140) }, + { FRAC_CONST(0.025939651529755), FRAC_CONST(0.056862856756541) }, + { FRAC_CONST(0.025239903597978), FRAC_CONST(0.057176894514872) }, + { FRAC_CONST(0.024536354629559), FRAC_CONST(0.057482321643202) }, + { FRAC_CONST(0.023829110576385), FRAC_CONST(0.057779092145329) }, + { FRAC_CONST(0.023118277946808), FRAC_CONST(0.058067161328707) }, + { FRAC_CONST(0.022403963789609), FRAC_CONST(0.058346485811177) }, + { FRAC_CONST(0.021686275677870), FRAC_CONST(0.058617023527499) }, + { FRAC_CONST(0.020965321692783), FRAC_CONST(0.058878733735689) }, + { FRAC_CONST(0.020241210407366), FRAC_CONST(0.059131577023150) }, + { FRAC_CONST(0.019514050870114), FRAC_CONST(0.059375515312615) }, + { FRAC_CONST(0.018783952588580), FRAC_CONST(0.059610511867874) }, + { FRAC_CONST(0.018051025512878), FRAC_CONST(0.059836531299311) }, + { FRAC_CONST(0.017315380019131), FRAC_CONST(0.060053539569230) }, + { FRAC_CONST(0.016577126892844), FRAC_CONST(0.060261503996984) }, + { FRAC_CONST(0.015836377312223), FRAC_CONST(0.060460393263896) }, + { FRAC_CONST(0.015093242831429), FRAC_CONST(0.060650177417972) }, + { FRAC_CONST(0.014347835363782), FRAC_CONST(0.060830827878419) }, + { FRAC_CONST(0.013600267164905), FRAC_CONST(0.061002317439940) }, + { FRAC_CONST(0.012850650815819), FRAC_CONST(0.061164620276839) }, + { FRAC_CONST(0.012099099205988), FRAC_CONST(0.061317711946905) }, + { FRAC_CONST(0.011345725516320), FRAC_CONST(0.061461569395097) }, + { FRAC_CONST(0.010590643202123), FRAC_CONST(0.061596170957011) }, + { FRAC_CONST(0.009833965976015), FRAC_CONST(0.061721496362147) }, + { FRAC_CONST(0.009075807790803), FRAC_CONST(0.061837526736961) }, + { FRAC_CONST(0.008316282822321), FRAC_CONST(0.061944244607705) }, + { FRAC_CONST(0.007555505452236), FRAC_CONST(0.062041633903059) }, + { FRAC_CONST(0.006793590250821), FRAC_CONST(0.062129679956555) }, + { FRAC_CONST(0.006030651959703), FRAC_CONST(0.062208369508780) }, + { FRAC_CONST(0.005266805474583), FRAC_CONST(0.062277690709378) }, + { FRAC_CONST(0.004502165827931), FRAC_CONST(0.062337633118830) }, + { FRAC_CONST(0.003736848171665), FRAC_CONST(0.062388187710030) }, + { FRAC_CONST(0.002970967759810), FRAC_CONST(0.062429346869643) }, + { FRAC_CONST(0.002204639931138), FRAC_CONST(0.062461104399250) }, + { FRAC_CONST(0.001437980091802), FRAC_CONST(0.062483455516285) }, + { FRAC_CONST(0.000671103697954), FRAC_CONST(0.062496396854751) } +}; + +/* 16 (N/4) complex twiddle factors */ +ALIGN static const complex_t mdct_tab_64[] = +{ + { FRAC_CONST(0.176763384336599), FRAC_CONST(0.002169321984356) }, + { FRAC_CONST(0.175699589589310), FRAC_CONST(0.019484717553714) }, + { FRAC_CONST(0.172943711747111), FRAC_CONST(0.036612464641599) }, + { FRAC_CONST(0.168522291420137), FRAC_CONST(0.053387613680577) }, + { FRAC_CONST(0.162477909303132), FRAC_CONST(0.069648610815172) }, + { FRAC_CONST(0.154868776100077), FRAC_CONST(0.085238853753814) }, + { FRAC_CONST(0.145768171923295), FRAC_CONST(0.100008199934509) }, + { FRAC_CONST(0.135263740565902), FRAC_CONST(0.113814412479792) }, + { FRAC_CONST(0.123456645444178), FRAC_CONST(0.126524530015608) }, + { FRAC_CONST(0.110460595338559), FRAC_CONST(0.138016147162030) }, + { FRAC_CONST(0.096400749315926), FRAC_CONST(0.148178593363981) }, + { FRAC_CONST(0.081412511379371), FRAC_CONST(0.156913998709178) }, + { FRAC_CONST(0.065640226453626), FRAC_CONST(0.164138236468888) }, + { FRAC_CONST(0.049235790264535), FRAC_CONST(0.169781733284316) }, + { FRAC_CONST(0.032357186500177), FRAC_CONST(0.173790139196080) }, + { FRAC_CONST(0.015166965341583), FRAC_CONST(0.176124851064031) } +}; +#endif // SSR_DEC + +#endif // FIXED_POINT + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/mp4.c b/third_party/faad_hdc/upstream/libfaad/mp4.c new file mode 100644 index 000000000..dc3569f78 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/mp4.c @@ -0,0 +1,313 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: mp4.c,v 1.41 2016/11/11 11:25:58 knik Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include + +#include "mp4.h" +#include "syntax.h" + +/* defines if an object type can be decoded by this library or not */ +static uint8_t ObjectTypesTable[32] = { + 0, /* 0 NULL */ +#ifdef MAIN_DEC + 1, /* 1 AAC Main */ +#else + 0, /* 1 AAC Main */ +#endif + 1, /* 2 AAC LC */ +#ifdef SSR_DEC + 1, /* 3 AAC SSR */ +#else + 0, /* 3 AAC SSR */ +#endif +#ifdef LTP_DEC + 1, /* 4 AAC LTP */ +#else + 0, /* 4 AAC LTP */ +#endif +#ifdef SBR_DEC + 1, /* 5 SBR */ +#else + 0, /* 5 SBR */ +#endif + 0, /* 6 AAC Scalable */ + 0, /* 7 TwinVQ */ + 0, /* 8 CELP */ + 0, /* 9 HVXC */ + 0, /* 10 Reserved */ + 0, /* 11 Reserved */ + 0, /* 12 TTSI */ + 0, /* 13 Main synthetic */ + 0, /* 14 Wavetable synthesis */ + 0, /* 15 General MIDI */ + 0, /* 16 Algorithmic Synthesis and Audio FX */ + + /* MPEG-4 Version 2 */ +#ifdef ERROR_RESILIENCE + 1, /* 17 ER AAC LC */ + 0, /* 18 (Reserved) */ +#ifdef LTP_DEC + 1, /* 19 ER AAC LTP */ +#else + 0, /* 19 ER AAC LTP */ +#endif + 0, /* 20 ER AAC scalable */ + 0, /* 21 ER TwinVQ */ + 0, /* 22 ER BSAC */ +#ifdef LD_DEC + 1, /* 23 ER AAC LD */ +#else + 0, /* 23 ER AAC LD */ +#endif + 0, /* 24 ER CELP */ + 0, /* 25 ER HVXC */ + 0, /* 26 ER HILN */ + 0, /* 27 ER Parametric */ +#else /* No ER defined */ + 0, /* 17 ER AAC LC */ + 0, /* 18 (Reserved) */ + 0, /* 19 ER AAC LTP */ + 0, /* 20 ER AAC scalable */ + 0, /* 21 ER TwinVQ */ + 0, /* 22 ER BSAC */ + 0, /* 23 ER AAC LD */ + 0, /* 24 ER CELP */ + 0, /* 25 ER HVXC */ + 0, /* 26 ER HILN */ + 0, /* 27 ER Parametric */ +#endif + 0, /* 28 (Reserved) */ +#ifdef PS_DEC + 1, /* 29 AAC LC + SBR + PS */ +#else + 0, /* 29 AAC LC + SBR + PS */ +#endif + 0, /* 30 (Reserved) */ + 0 /* 31 (Reserved) */ +}; + +/* Table 1.6.1 */ +char NeAACDecAudioSpecificConfig(unsigned char *pBuffer, + unsigned long buffer_size, + mp4AudioSpecificConfig *mp4ASC) +{ + return AudioSpecificConfig2(pBuffer, buffer_size, mp4ASC, NULL, 0); +} + +int8_t AudioSpecificConfigFromBitfile(bitfile *ld, + mp4AudioSpecificConfig *mp4ASC, + program_config *pce, uint32_t buffer_size, uint8_t short_form) +{ + int8_t result = 0; + uint32_t startpos = faad_get_processed_bits(ld); +#ifdef SBR_DEC + int8_t bits_to_decode = 0; +#endif + + if (mp4ASC == NULL) + return -8; + + memset(mp4ASC, 0, sizeof(mp4AudioSpecificConfig)); + + mp4ASC->objectTypeIndex = (uint8_t)faad_getbits(ld, 5 + DEBUGVAR(1,1,"parse_audio_decoder_specific_info(): ObjectTypeIndex")); + + mp4ASC->samplingFrequencyIndex = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,2,"parse_audio_decoder_specific_info(): SamplingFrequencyIndex")); + if(mp4ASC->samplingFrequencyIndex==0x0f) + faad_getbits(ld, 24); + + mp4ASC->channelsConfiguration = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,3,"parse_audio_decoder_specific_info(): ChannelsConfiguration")); + + mp4ASC->samplingFrequency = get_sample_rate(mp4ASC->samplingFrequencyIndex); + + if (ObjectTypesTable[mp4ASC->objectTypeIndex] != 1) + { + return -1; + } + + if (mp4ASC->samplingFrequency == 0) + { + return -2; + } + + if (mp4ASC->channelsConfiguration > 7) + { + return -3; + } + +#if (defined(PS_DEC) || defined(DRM_PS)) + /* check if we have a mono file */ + if (mp4ASC->channelsConfiguration == 1) + { + /* upMatrix to 2 channels for implicit signalling of PS */ + mp4ASC->channelsConfiguration = 2; + } +#endif + +#ifdef SBR_DEC + mp4ASC->sbr_present_flag = -1; + if (mp4ASC->objectTypeIndex == 5 || mp4ASC->objectTypeIndex == 29) + { + uint8_t tmp; + + mp4ASC->sbr_present_flag = 1; + tmp = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,5,"parse_audio_decoder_specific_info(): extensionSamplingFrequencyIndex")); + /* check for downsampled SBR */ + if (tmp == mp4ASC->samplingFrequencyIndex) + mp4ASC->downSampledSBR = 1; + mp4ASC->samplingFrequencyIndex = tmp; + if (mp4ASC->samplingFrequencyIndex == 15) + { + mp4ASC->samplingFrequency = (uint32_t)faad_getbits(ld, 24 + DEBUGVAR(1,6,"parse_audio_decoder_specific_info(): extensionSamplingFrequencyIndex")); + } else { + mp4ASC->samplingFrequency = get_sample_rate(mp4ASC->samplingFrequencyIndex); + } + mp4ASC->objectTypeIndex = (uint8_t)faad_getbits(ld, 5 + DEBUGVAR(1,7,"parse_audio_decoder_specific_info(): ObjectTypeIndex")); + } +#endif + + /* get GASpecificConfig */ + if (mp4ASC->objectTypeIndex == 1 || mp4ASC->objectTypeIndex == 2 || + mp4ASC->objectTypeIndex == 3 || mp4ASC->objectTypeIndex == 4 || + mp4ASC->objectTypeIndex == 6 || mp4ASC->objectTypeIndex == 7) + { + result = GASpecificConfig(ld, mp4ASC, pce); + +#ifdef ERROR_RESILIENCE + } else if (mp4ASC->objectTypeIndex >= ER_OBJECT_START) { /* ER */ + result = GASpecificConfig(ld, mp4ASC, pce); + mp4ASC->epConfig = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,143,"parse_audio_decoder_specific_info(): epConfig")); + + if (mp4ASC->epConfig != 0) + result = -5; +#endif + + } else { + result = -4; + } + +#ifdef SSR_DEC + /* shorter frames not allowed for SSR */ + if ((mp4ASC->objectTypeIndex == 4) && mp4ASC->frameLengthFlag) + return -6; +#endif + + +#ifdef SBR_DEC + if(short_form) + bits_to_decode = 0; + else + bits_to_decode = (int8_t)(buffer_size*8 + faad_get_processed_bits(ld) - startpos); + + if ((mp4ASC->objectTypeIndex != 5 && mp4ASC->objectTypeIndex != 29) && (bits_to_decode >= 16)) + { + int16_t syncExtensionType = (int16_t)faad_getbits(ld, 11 + DEBUGVAR(1,9,"parse_audio_decoder_specific_info(): syncExtensionType")); + + if (syncExtensionType == 0x2b7) + { + uint8_t tmp_OTi = (uint8_t)faad_getbits(ld, 5 + DEBUGVAR(1,10,"parse_audio_decoder_specific_info(): extensionAudioObjectType")); + + if (tmp_OTi == 5) + { + mp4ASC->sbr_present_flag = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,11,"parse_audio_decoder_specific_info(): sbr_present_flag")); + + if (mp4ASC->sbr_present_flag) + { + uint8_t tmp; + + /* Don't set OT to SBR until checked that it is actually there */ + mp4ASC->objectTypeIndex = tmp_OTi; + + tmp = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,12,"parse_audio_decoder_specific_info(): extensionSamplingFrequencyIndex")); + + /* check for downsampled SBR */ + if (tmp == mp4ASC->samplingFrequencyIndex) + mp4ASC->downSampledSBR = 1; + mp4ASC->samplingFrequencyIndex = tmp; + + if (mp4ASC->samplingFrequencyIndex == 15) + { + mp4ASC->samplingFrequency = (uint32_t)faad_getbits(ld, 24 + DEBUGVAR(1,13,"parse_audio_decoder_specific_info(): extensionSamplingFrequencyIndex")); + } else { + mp4ASC->samplingFrequency = get_sample_rate(mp4ASC->samplingFrequencyIndex); + } + } + } + } + } + + /* no SBR signalled, this could mean either implicit signalling or no SBR in this file */ + /* MPEG specification states: assume SBR on files with samplerate <= 24000 Hz */ + if (mp4ASC->sbr_present_flag == (char)-1) /* cannot be -1 on systems with unsigned char */ + { + if (mp4ASC->samplingFrequency <= 24000) + { + mp4ASC->samplingFrequency *= 2; + mp4ASC->forceUpSampling = 1; + } else /* > 24000*/ { + mp4ASC->downSampledSBR = 1; + } + } +#endif + + faad_endbits(ld); + + return result; +} + +int8_t AudioSpecificConfig2(uint8_t *pBuffer, + uint32_t buffer_size, + mp4AudioSpecificConfig *mp4ASC, + program_config *pce, + uint8_t short_form) +{ + uint8_t ret = 0; + bitfile ld; + faad_initbits(&ld, pBuffer, buffer_size); + if (ld.error != 0) + return -7; + ret = AudioSpecificConfigFromBitfile(&ld, mp4ASC, pce, buffer_size, short_form); + faad_endbits(&ld); + return ret; +} diff --git a/third_party/faad_hdc/upstream/libfaad/mp4.h b/third_party/faad_hdc/upstream/libfaad/mp4.h new file mode 100644 index 000000000..29cf5129e --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/mp4.h @@ -0,0 +1,53 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: mp4.h,v 1.28 2009/02/05 00:51:03 menno Exp $ +**/ + +#ifndef __MP4_H__ +#define __MP4_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" +#include "neaacdec.h" + +int8_t AudioSpecificConfig2(uint8_t *pBuffer, + uint32_t buffer_size, + mp4AudioSpecificConfig *mp4ASC, + program_config *pce, uint8_t short_form); + +int8_t AudioSpecificConfigFromBitfile(bitfile *ld, + mp4AudioSpecificConfig *mp4ASC, + program_config *pce, uint32_t bsize, uint8_t short_form); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ms.c b/third_party/faad_hdc/upstream/libfaad/ms.c new file mode 100644 index 000000000..eb4d26664 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ms.c @@ -0,0 +1,77 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ms.c,v 1.21 2007/11/01 12:33:32 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include "syntax.h" +#include "ms.h" +#include "is.h" +#include "pns.h" + +void ms_decode(ic_stream *ics, ic_stream *icsr, real_t *l_spec, real_t *r_spec, + uint16_t frame_len) +{ + uint8_t g, b, sfb; + uint8_t group = 0; + uint16_t nshort = frame_len/8; + + uint16_t i, k; + real_t tmp; + + if (ics->ms_mask_present >= 1) + { + for (g = 0; g < ics->num_window_groups; g++) + { + for (b = 0; b < ics->window_group_length[g]; b++) + { + for (sfb = 0; sfb < ics->max_sfb; sfb++) + { + /* If intensity stereo coding or noise substitution is on + for a particular scalefactor band, no M/S stereo decoding + is carried out. + */ + if ((ics->ms_used[g][sfb] || ics->ms_mask_present == 2) && + !is_intensity(icsr, g, sfb) && !is_noise(ics, g, sfb)) + { + for (i = ics->swb_offset[sfb]; i < min(ics->swb_offset[sfb+1], ics->swb_offset_max); i++) + { + k = (group*nshort) + i; + tmp = l_spec[k] - r_spec[k]; + l_spec[k] = l_spec[k] + r_spec[k]; + r_spec[k] = tmp; + } + } + } + group++; + } + } + } +} diff --git a/third_party/faad_hdc/upstream/libfaad/ms.h b/third_party/faad_hdc/upstream/libfaad/ms.h new file mode 100644 index 000000000..328139126 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ms.h @@ -0,0 +1,44 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ms.h,v 1.19 2007/11/01 12:33:32 menno Exp $ +**/ + +#ifndef __MS_H__ +#define __MS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void ms_decode(ic_stream *ics, ic_stream *icsr, real_t *l_spec, real_t *r_spec, + uint16_t frame_len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/output.c b/third_party/faad_hdc/upstream/libfaad/output.c new file mode 100644 index 000000000..f8147857f --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/output.c @@ -0,0 +1,563 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: output.c,v 1.47 2009/01/26 23:51:15 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include "output.h" + +#ifndef FIXED_POINT + + +#define FLOAT_SCALE (1.0f/(1<<15)) + +#define DM_MUL REAL_CONST(0.3203772410170407) // 1/(1+sqrt(2) + 1/sqrt(2)) +#define RSQRT2 REAL_CONST(0.7071067811865475244) // 1/sqrt(2) + + +static INLINE real_t get_sample(real_t **input, uint8_t channel, uint16_t sample, + uint8_t down_matrix, uint8_t *internal_channel) +{ + if (!down_matrix) + return input[internal_channel[channel]][sample]; + + if (channel == 0) + { + return DM_MUL * (input[internal_channel[1]][sample] + + input[internal_channel[0]][sample] * RSQRT2 + + input[internal_channel[3]][sample] * RSQRT2); + } else { + return DM_MUL * (input[internal_channel[2]][sample] + + input[internal_channel[0]][sample] * RSQRT2 + + input[internal_channel[4]][sample] * RSQRT2); + } +} + +#ifndef HAS_LRINTF +#define CLIP(sample, max, min) \ +if (sample >= 0.0f) \ +{ \ + sample += 0.5f; \ + if (sample >= max) \ + sample = max; \ +} else { \ + sample += -0.5f; \ + if (sample <= min) \ + sample = min; \ +} +#else +#define CLIP(sample, max, min) \ +if (sample >= 0.0f) \ +{ \ + if (sample >= max) \ + sample = max; \ +} else { \ + if (sample <= min) \ + sample = min; \ +} +#endif + +#define CONV(a,b) ((a<<1)|(b&0x1)) + +static void to_PCM_16bit(NeAACDecStruct *hDecoder, real_t **input, + uint8_t channels, uint16_t frame_len, + int16_t **sample_buffer) +{ + uint8_t ch, ch1; + uint16_t i; + + switch (CONV(channels,hDecoder->downMatrix)) + { + case CONV(1,0): + case CONV(1,1): + for(i = 0; i < frame_len; i++) + { + real_t inp = input[hDecoder->internal_channel[0]][i]; + + CLIP(inp, 32767.0f, -32768.0f); + + (*sample_buffer)[i] = (int16_t)lrintf(inp); + } + break; + case CONV(2,0): + if (hDecoder->upMatrix) + { + ch = hDecoder->internal_channel[0]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch][i]; + + CLIP(inp0, 32767.0f, -32768.0f); + + (*sample_buffer)[(i*2)+0] = (int16_t)lrintf(inp0); + (*sample_buffer)[(i*2)+1] = (int16_t)lrintf(inp0); + } + } else { + ch = hDecoder->internal_channel[0]; + ch1 = hDecoder->internal_channel[1]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch ][i]; + real_t inp1 = input[ch1][i]; + + CLIP(inp0, 32767.0f, -32768.0f); + CLIP(inp1, 32767.0f, -32768.0f); + + (*sample_buffer)[(i*2)+0] = (int16_t)lrintf(inp0); + (*sample_buffer)[(i*2)+1] = (int16_t)lrintf(inp1); + } + } + break; + default: + for (ch = 0; ch < channels; ch++) + { + for(i = 0; i < frame_len; i++) + { + real_t inp = get_sample(input, ch, i, hDecoder->downMatrix, hDecoder->internal_channel); + + CLIP(inp, 32767.0f, -32768.0f); + + (*sample_buffer)[(i*channels)+ch] = (int16_t)lrintf(inp); + } + } + break; + } +} + +static void to_PCM_24bit(NeAACDecStruct *hDecoder, real_t **input, + uint8_t channels, uint16_t frame_len, + int32_t **sample_buffer) +{ + uint8_t ch, ch1; + uint16_t i; + + switch (CONV(channels,hDecoder->downMatrix)) + { + case CONV(1,0): + case CONV(1,1): + for(i = 0; i < frame_len; i++) + { + real_t inp = input[hDecoder->internal_channel[0]][i]; + + inp *= 256.0f; + CLIP(inp, 8388607.0f, -8388608.0f); + + (*sample_buffer)[i] = (int32_t)lrintf(inp); + } + break; + case CONV(2,0): + if (hDecoder->upMatrix) + { + ch = hDecoder->internal_channel[0]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch][i]; + + inp0 *= 256.0f; + CLIP(inp0, 8388607.0f, -8388608.0f); + + (*sample_buffer)[(i*2)+0] = (int32_t)lrintf(inp0); + (*sample_buffer)[(i*2)+1] = (int32_t)lrintf(inp0); + } + } else { + ch = hDecoder->internal_channel[0]; + ch1 = hDecoder->internal_channel[1]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch ][i]; + real_t inp1 = input[ch1][i]; + + inp0 *= 256.0f; + inp1 *= 256.0f; + CLIP(inp0, 8388607.0f, -8388608.0f); + CLIP(inp1, 8388607.0f, -8388608.0f); + + (*sample_buffer)[(i*2)+0] = (int32_t)lrintf(inp0); + (*sample_buffer)[(i*2)+1] = (int32_t)lrintf(inp1); + } + } + break; + default: + for (ch = 0; ch < channels; ch++) + { + for(i = 0; i < frame_len; i++) + { + real_t inp = get_sample(input, ch, i, hDecoder->downMatrix, hDecoder->internal_channel); + + inp *= 256.0f; + CLIP(inp, 8388607.0f, -8388608.0f); + + (*sample_buffer)[(i*channels)+ch] = (int32_t)lrintf(inp); + } + } + break; + } +} + +static void to_PCM_32bit(NeAACDecStruct *hDecoder, real_t **input, + uint8_t channels, uint16_t frame_len, + int32_t **sample_buffer) +{ + uint8_t ch, ch1; + uint16_t i; + + switch (CONV(channels,hDecoder->downMatrix)) + { + case CONV(1,0): + case CONV(1,1): + for(i = 0; i < frame_len; i++) + { + real_t inp = input[hDecoder->internal_channel[0]][i]; + + inp *= 65536.0f; + CLIP(inp, 2147483647.0f, -2147483648.0f); + + (*sample_buffer)[i] = (int32_t)lrintf(inp); + } + break; + case CONV(2,0): + if (hDecoder->upMatrix) + { + ch = hDecoder->internal_channel[0]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch][i]; + + inp0 *= 65536.0f; + CLIP(inp0, 2147483647.0f, -2147483648.0f); + + (*sample_buffer)[(i*2)+0] = (int32_t)lrintf(inp0); + (*sample_buffer)[(i*2)+1] = (int32_t)lrintf(inp0); + } + } else { + ch = hDecoder->internal_channel[0]; + ch1 = hDecoder->internal_channel[1]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch ][i]; + real_t inp1 = input[ch1][i]; + + inp0 *= 65536.0f; + inp1 *= 65536.0f; + CLIP(inp0, 2147483647.0f, -2147483648.0f); + CLIP(inp1, 2147483647.0f, -2147483648.0f); + + (*sample_buffer)[(i*2)+0] = (int32_t)lrintf(inp0); + (*sample_buffer)[(i*2)+1] = (int32_t)lrintf(inp1); + } + } + break; + default: + for (ch = 0; ch < channels; ch++) + { + for(i = 0; i < frame_len; i++) + { + real_t inp = get_sample(input, ch, i, hDecoder->downMatrix, hDecoder->internal_channel); + + inp *= 65536.0f; + CLIP(inp, 2147483647.0f, -2147483648.0f); + + (*sample_buffer)[(i*channels)+ch] = (int32_t)lrintf(inp); + } + } + break; + } +} + +static void to_PCM_float(NeAACDecStruct *hDecoder, real_t **input, + uint8_t channels, uint16_t frame_len, + float32_t **sample_buffer) +{ + uint8_t ch, ch1; + uint16_t i; + + switch (CONV(channels,hDecoder->downMatrix)) + { + case CONV(1,0): + case CONV(1,1): + for(i = 0; i < frame_len; i++) + { + real_t inp = input[hDecoder->internal_channel[0]][i]; + (*sample_buffer)[i] = inp*FLOAT_SCALE; + } + break; + case CONV(2,0): + if (hDecoder->upMatrix) + { + ch = hDecoder->internal_channel[0]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch][i]; + (*sample_buffer)[(i*2)+0] = inp0*FLOAT_SCALE; + (*sample_buffer)[(i*2)+1] = inp0*FLOAT_SCALE; + } + } else { + ch = hDecoder->internal_channel[0]; + ch1 = hDecoder->internal_channel[1]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch ][i]; + real_t inp1 = input[ch1][i]; + (*sample_buffer)[(i*2)+0] = inp0*FLOAT_SCALE; + (*sample_buffer)[(i*2)+1] = inp1*FLOAT_SCALE; + } + } + break; + default: + for (ch = 0; ch < channels; ch++) + { + for(i = 0; i < frame_len; i++) + { + real_t inp = get_sample(input, ch, i, hDecoder->downMatrix, hDecoder->internal_channel); + (*sample_buffer)[(i*channels)+ch] = inp*FLOAT_SCALE; + } + } + break; + } +} + +static void to_PCM_double(NeAACDecStruct *hDecoder, real_t **input, + uint8_t channels, uint16_t frame_len, + double **sample_buffer) +{ + uint8_t ch, ch1; + uint16_t i; + + switch (CONV(channels,hDecoder->downMatrix)) + { + case CONV(1,0): + case CONV(1,1): + for(i = 0; i < frame_len; i++) + { + real_t inp = input[hDecoder->internal_channel[0]][i]; + (*sample_buffer)[i] = (double)inp*FLOAT_SCALE; + } + break; + case CONV(2,0): + if (hDecoder->upMatrix) + { + ch = hDecoder->internal_channel[0]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch][i]; + (*sample_buffer)[(i*2)+0] = (double)inp0*FLOAT_SCALE; + (*sample_buffer)[(i*2)+1] = (double)inp0*FLOAT_SCALE; + } + } else { + ch = hDecoder->internal_channel[0]; + ch1 = hDecoder->internal_channel[1]; + for(i = 0; i < frame_len; i++) + { + real_t inp0 = input[ch ][i]; + real_t inp1 = input[ch1][i]; + (*sample_buffer)[(i*2)+0] = (double)inp0*FLOAT_SCALE; + (*sample_buffer)[(i*2)+1] = (double)inp1*FLOAT_SCALE; + } + } + break; + default: + for (ch = 0; ch < channels; ch++) + { + for(i = 0; i < frame_len; i++) + { + real_t inp = get_sample(input, ch, i, hDecoder->downMatrix, hDecoder->internal_channel); + (*sample_buffer)[(i*channels)+ch] = (double)inp*FLOAT_SCALE; + } + } + break; + } +} + +void *output_to_PCM(NeAACDecStruct *hDecoder, + real_t **input, void *sample_buffer, uint8_t channels, + uint16_t frame_len, uint8_t format) +{ + int16_t *short_sample_buffer = (int16_t*)sample_buffer; + int32_t *int_sample_buffer = (int32_t*)sample_buffer; + float32_t *float_sample_buffer = (float32_t*)sample_buffer; + double *double_sample_buffer = (double*)sample_buffer; + +#ifdef PROFILE + int64_t count = faad_get_ts(); +#endif + + /* Copy output to a standard PCM buffer */ + switch (format) + { + case FAAD_FMT_16BIT: + to_PCM_16bit(hDecoder, input, channels, frame_len, &short_sample_buffer); + break; + case FAAD_FMT_24BIT: + to_PCM_24bit(hDecoder, input, channels, frame_len, &int_sample_buffer); + break; + case FAAD_FMT_32BIT: + to_PCM_32bit(hDecoder, input, channels, frame_len, &int_sample_buffer); + break; + case FAAD_FMT_FLOAT: + to_PCM_float(hDecoder, input, channels, frame_len, &float_sample_buffer); + break; + case FAAD_FMT_DOUBLE: + to_PCM_double(hDecoder, input, channels, frame_len, &double_sample_buffer); + break; + } + +#ifdef PROFILE + count = faad_get_ts() - count; + hDecoder->output_cycles += count; +#endif + + return sample_buffer; +} + +#else + +#define DM_MUL FRAC_CONST(0.3203772410170407) // 1/(1+sqrt(2) + 1/sqrt(2)) +#define RSQRT2 FRAC_CONST(0.7071067811865475244) // 1/sqrt(2) +#define BOTH FRAC_CONST(0.2265409196609864215998) // 1/(sqrt(2) + 2 + 1) + +static INLINE real_t get_sample(real_t **input, uint8_t channel, uint16_t sample, + uint8_t down_matrix, uint8_t up_matrix, + uint8_t *internal_channel) +{ + real_t C; + if (up_matrix == 1) + return input[internal_channel[0]][sample]; + + if (!down_matrix) + return input[internal_channel[channel]][sample]; + + C = MUL_F(input[internal_channel[0]][sample], BOTH); + if (channel == 0) + { + real_t L_S = MUL_F(input[internal_channel[3]][sample], BOTH); + real_t core = MUL_F(input[internal_channel[1]][sample], DM_MUL); + return core + C + L_S; + } else { + real_t R_S = MUL_F(input[internal_channel[4]][sample], BOTH); + real_t core = MUL_F(input[internal_channel[2]][sample], DM_MUL); + return core + C + R_S; + } +} + +void* output_to_PCM(NeAACDecStruct *hDecoder, + real_t **input, void *sample_buffer, uint8_t channels, + uint16_t frame_len, uint8_t format) +{ + uint8_t ch; + uint16_t i; + int16_t *short_sample_buffer = (int16_t*)sample_buffer; + int32_t *int_sample_buffer = (int32_t*)sample_buffer; + int32_t exp, half, sat_shift_mask; + + /* Copy output to a standard PCM buffer */ + for (ch = 0; ch < channels; ch++) + { + switch (format) + { + case FAAD_FMT_16BIT: + for(i = 0; i < frame_len; i++) + { + int32_t tmp = get_sample(input, ch, i, hDecoder->downMatrix, hDecoder->upMatrix, + hDecoder->internal_channel); + if (tmp >= 0) + { + tmp += (1 << (REAL_BITS-1)); + if (tmp >= REAL_CONST(32767)) + { + tmp = REAL_CONST(32767); + } + } else { + tmp += -(1 << (REAL_BITS-1)); + if (tmp <= REAL_CONST(-32768)) + { + tmp = REAL_CONST(-32768); + } + } + tmp >>= REAL_BITS; + short_sample_buffer[(i*channels)+ch] = (int16_t)tmp; + } + break; + case FAAD_FMT_24BIT: + for(i = 0; i < frame_len; i++) + { + int32_t tmp = get_sample(input, ch, i, hDecoder->downMatrix, hDecoder->upMatrix, + hDecoder->internal_channel); + if (tmp >= 0) + { + tmp += (1 << (REAL_BITS-9)); + tmp >>= (REAL_BITS-8); + if (tmp >= 8388607) + { + tmp = 8388607; + } + } else { + tmp += -(1 << (REAL_BITS-9)); + tmp >>= (REAL_BITS-8); + if (tmp <= -8388608) + { + tmp = -8388608; + } + } + int_sample_buffer[(i*channels)+ch] = (int32_t)tmp; + } + break; + case FAAD_FMT_32BIT: + exp = 16 - REAL_BITS; + half = 1 << (exp - 1); + sat_shift_mask = SAT_SHIFT_MASK(exp); + for(i = 0; i < frame_len; i++) + { + int32_t tmp = get_sample(input, ch, i, hDecoder->downMatrix, hDecoder->upMatrix, + hDecoder->internal_channel); + if (tmp >= 0) + { + tmp += half; + } else { + tmp += -half; + } + tmp = SAT_SHIFT(tmp, exp, sat_shift_mask); + int_sample_buffer[(i*channels)+ch] = tmp; + } + break; + case FAAD_FMT_FIXED: + for(i = 0; i < frame_len; i++) + { + real_t tmp = get_sample(input, ch, i, hDecoder->downMatrix, hDecoder->upMatrix, + hDecoder->internal_channel); + int_sample_buffer[(i*channels)+ch] = (int32_t)tmp; + } + break; + } + } + + return sample_buffer; +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/output.h b/third_party/faad_hdc/upstream/libfaad/output.h new file mode 100644 index 000000000..72abf822d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/output.h @@ -0,0 +1,48 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: output.h,v 1.26 2009/01/26 23:51:15 menno Exp $ +**/ + +#ifndef __OUTPUT_H__ +#define __OUTPUT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void* output_to_PCM(NeAACDecStruct *hDecoder, + real_t **input, + void *samplebuffer, + uint8_t channels, + uint16_t frame_len, + uint8_t format); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/pns.c b/third_party/faad_hdc/upstream/libfaad/pns.c new file mode 100644 index 000000000..6ff2d4081 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/pns.c @@ -0,0 +1,270 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: pns.c,v 1.39 2010/06/04 20:47:56 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include "pns.h" + + +/* static function declarations */ +static void gen_rand_vector(real_t *spec, int16_t scale_factor, uint16_t size, + uint8_t sub, + /* RNG states */ uint32_t *__r1, uint32_t *__r2); + + +#ifdef FIXED_POINT + +static real_t const pow2_table[] = +{ + COEF_CONST(1.0), + COEF_CONST(1.18920711500272), + COEF_CONST(1.41421356237310), + COEF_CONST(1.68179283050743) +}; + +// mean_energy_table[x] == sqrt(3 / x) +static real_t const mean_energy_table[] = +{ + COEF_CONST(0.0), // should not happen + COEF_CONST(1.7320508075688772), + COEF_CONST(1.224744871391589), + COEF_CONST(1.0), // sqrt(3/3) + COEF_CONST(0.8660254037844386), + COEF_CONST(0.7745966692414834), + COEF_CONST(0.7071067811865476), + COEF_CONST(0.6546536707079771), + COEF_CONST(0.6123724356957945), + COEF_CONST(0.5773502691896257), + COEF_CONST(0.5477225575051661), + COEF_CONST(0.5222329678670935), + COEF_CONST(0.5), // sqrt(3/12) + COEF_CONST(0.4803844614152614), + COEF_CONST(0.4629100498862757), + COEF_CONST(0.4472135954999579), +}; +#endif + +/* The function gen_rand_vector(addr, size) generates a vector of length + with signed random values of average energy MEAN_NRG per random + value. A suitable random number generator can be realized using one + multiplication/accumulation per random value. +*/ +static INLINE void gen_rand_vector(real_t *spec, int16_t scale_factor, uint16_t size, + uint8_t sub, + /* RNG states */ uint32_t *__r1, uint32_t *__r2) +{ +#ifndef FIXED_POINT + uint16_t i; + real_t energy = 0.0; + (void)sub; + + scale_factor = min(max(scale_factor, -120), 120); + + for (i = 0; i < size; i++) + { + real_t tmp = (real_t)(int32_t)ne_rng(__r1, __r2); + spec[i] = tmp; + energy += tmp*tmp; + } + + if (energy > 0) + { + real_t scale = (real_t)1.0/(real_t)sqrt(energy); + scale *= (real_t)pow(2.0, 0.25 * scale_factor); + for (i = 0; i < size; i++) + { + spec[i] *= scale; + } + } +#else + uint16_t i; + real_t scale; + int32_t exp, frac; + int32_t idx, mask; + + /* IMDCT pre-scaling */ + scale_factor -= 4 * sub; + + // 52 stands for 2**13 == 8192 factor; larger factor causes overflows later (in cfft). + scale_factor = min(max(scale_factor, -(REAL_BITS * 4)), 52); + + exp = scale_factor >> 2; + frac = scale_factor & 3; + + /* 29 <= REAL_BITS + exp <= 0 */ + mask = (1 << (REAL_BITS + exp)) - 1; + + idx = size; + scale = COEF_CONST(1); + // At most 2 iterations. + while (idx >= 16) + { + idx >>= 2; + scale >>= 1; + } + scale = MUL_C(scale, mean_energy_table[idx]); + if (frac) + scale = MUL_C(scale, pow2_table[frac]); + // scale is less than 4.0 now. + + for (i = 0; i < size; i++) + { + real_t tmp = (int32_t)ne_rng(__r1, __r2); + if (tmp < 0) + tmp = -(tmp & mask); + else + tmp = (tmp & mask); + spec[i] = MUL_C(tmp, scale); + } +#endif +} + +void pns_decode(ic_stream *ics_left, ic_stream *ics_right, + real_t *spec_left, real_t *spec_right, uint16_t frame_len, + uint8_t channel_pair, uint8_t object_type, + /* RNG states */ uint32_t *__r1, uint32_t *__r2) +{ + uint8_t g, sfb, b; + uint16_t begin, end; + + uint8_t group = 0; + uint16_t nshort = frame_len >> 3; + + uint8_t sub = 0; + +#ifdef FIXED_POINT + /* IMDCT scaling */ + if (object_type == LD) + { + sub = 9 /*9*/; + } else { + if (ics_left->window_sequence == EIGHT_SHORT_SEQUENCE) + sub = 7 /*7*/; + else + sub = 10 /*10*/; + } +#else + (void)object_type; +#endif + + for (g = 0; g < ics_left->num_window_groups; g++) + { + /* Do perceptual noise substitution decoding */ + for (b = 0; b < ics_left->window_group_length[g]; b++) + { + uint16_t base = group * nshort; + for (sfb = 0; sfb < ics_left->max_sfb; sfb++) + { + uint32_t r1_dep = 0, r2_dep = 0; + + if (is_noise(ics_left, g, sfb)) + { +#ifdef LTP_DEC + /* Simultaneous use of LTP and PNS is not prevented in the + syntax. If both LTP, and PNS are enabled on the same + scalefactor band, PNS takes precedence, and no prediction + is applied to this band. + */ + ics_left->ltp.long_used[sfb] = 0; + ics_left->ltp2.long_used[sfb] = 0; +#endif + +#ifdef MAIN_DEC + /* For scalefactor bands coded using PNS the corresponding + predictors are switched to "off". + */ + ics_left->pred.prediction_used[sfb] = 0; +#endif + begin = min(base + ics_left->swb_offset[sfb], ics_left->swb_offset_max); + end = min(base + ics_left->swb_offset[sfb+1], ics_left->swb_offset_max); + + r1_dep = *__r1; + r2_dep = *__r2; + + /* Generate random vector */ + gen_rand_vector(&spec_left[begin], + ics_left->scale_factors[g][sfb], end - begin, sub, __r1, __r2); + } + +/* From the spec: + If the same scalefactor band and group is coded by perceptual noise + substitution in both channels of a channel pair, the correlation of + the noise signal can be controlled by means of the ms_used field: While + the default noise generation process works independently for each channel + (separate generation of random vectors), the same random vector is used + for both channels if ms_used[] is set for a particular scalefactor band + and group. In this case, no M/S stereo coding is carried out (because M/S + stereo coding and noise substitution coding are mutually exclusive). + If the same scalefactor band and group is coded by perceptual noise + substitution in only one channel of a channel pair the setting of ms_used[] + is not evaluated. +*/ + if ((ics_right != NULL) + && is_noise(ics_right, g, sfb)) + { +#ifdef LTP_DEC + /* See comment above. */ + ics_right->ltp.long_used[sfb] = 0; + ics_right->ltp2.long_used[sfb] = 0; +#endif +#ifdef MAIN_DEC + /* See comment above. */ + ics_right->pred.prediction_used[sfb] = 0; +#endif + + if (channel_pair && is_noise(ics_left, g, sfb) && + (((ics_left->ms_mask_present == 1) && + (ics_left->ms_used[g][sfb])) || + (ics_left->ms_mask_present == 2))) + { + /*uint16_t c;*/ + + begin = min(base + ics_right->swb_offset[sfb], ics_right->swb_offset_max); + end = min(base + ics_right->swb_offset[sfb+1], ics_right->swb_offset_max); + + /* Generate random vector dependent on left channel*/ + gen_rand_vector(&spec_right[begin], + ics_right->scale_factors[g][sfb], end - begin, sub, &r1_dep, &r2_dep); + + } else /*if (ics_left->ms_mask_present == 0)*/ { + begin = min(base + ics_right->swb_offset[sfb], ics_right->swb_offset_max); + end = min(base + ics_right->swb_offset[sfb+1], ics_right->swb_offset_max); + + /* Generate random vector */ + gen_rand_vector(&spec_right[begin], + ics_right->scale_factors[g][sfb], end - begin, sub, __r1, __r2); + } + } + } /* sfb */ + group++; + } /* b */ + } /* g */ +} diff --git a/third_party/faad_hdc/upstream/libfaad/pns.h b/third_party/faad_hdc/upstream/libfaad/pns.h new file mode 100644 index 000000000..24fc2524f --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/pns.h @@ -0,0 +1,57 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: pns.h,v 1.27 2007/11/01 12:33:33 menno Exp $ +**/ + +#ifndef __PNS_H__ +#define __PNS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "syntax.h" + +#define NOISE_OFFSET 90 + +void pns_decode(ic_stream *ics_left, ic_stream *ics_right, + real_t *spec_left, real_t *spec_right, uint16_t frame_len, + uint8_t channel_pair, uint8_t object_type, + /* RNG states */ uint32_t *__r1, uint32_t *__r2); + +static INLINE uint8_t is_noise(ic_stream *ics, uint8_t group, uint8_t sfb) +{ + if (ics->sfb_cb[group][sfb] == NOISE_HCB) + return 1; + return 0; +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ps_dec.c b/third_party/faad_hdc/upstream/libfaad/ps_dec.c new file mode 100644 index 000000000..6a9a8f9aa --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ps_dec.c @@ -0,0 +1,2040 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ps_dec.c,v 1.16 2009/01/26 22:32:31 menno Exp $ +**/ + +#include "common.h" + +#ifdef PS_DEC + +#include +#include +#include "ps_dec.h" +#include "ps_tables.h" + +/* constants */ +#define NEGATE_IPD_MASK (0x1000) +#define DECAY_SLOPE FRAC_CONST(0.05) +#define COEF_SQRT2 COEF_CONST(1.4142135623731) + +/* tables */ +/* filters are mirrored in coef 6, second half left out */ +static const real_t p8_13_20[7] = +{ + FRAC_CONST(0.00746082949812), + FRAC_CONST(0.02270420949825), + FRAC_CONST(0.04546865930473), + FRAC_CONST(0.07266113929591), + FRAC_CONST(0.09885108575264), + FRAC_CONST(0.11793710567217), + FRAC_CONST(0.125) +}; + +static const real_t p2_13_20[7] = +{ + FRAC_CONST(0.0), + FRAC_CONST(0.01899487526049), + FRAC_CONST(0.0), + FRAC_CONST(-0.07293139167538), + FRAC_CONST(0.0), + FRAC_CONST(0.30596630545168), + FRAC_CONST(0.5) +}; + +static const real_t p12_13_34[7] = +{ + FRAC_CONST(0.04081179924692), + FRAC_CONST(0.03812810994926), + FRAC_CONST(0.05144908135699), + FRAC_CONST(0.06399831151592), + FRAC_CONST(0.07428313801106), + FRAC_CONST(0.08100347892914), + FRAC_CONST(0.08333333333333) +}; + +static const real_t p8_13_34[7] = +{ + FRAC_CONST(0.01565675600122), + FRAC_CONST(0.03752716391991), + FRAC_CONST(0.05417891378782), + FRAC_CONST(0.08417044116767), + FRAC_CONST(0.10307344158036), + FRAC_CONST(0.12222452249753), + FRAC_CONST(0.125) +}; + +static const real_t p4_13_34[7] = +{ + FRAC_CONST(-0.05908211155639), + FRAC_CONST(-0.04871498374946), + FRAC_CONST(0.0), + FRAC_CONST(0.07778723915851), + FRAC_CONST(0.16486303567403), + FRAC_CONST(0.23279856662996), + FRAC_CONST(0.25) +}; + +#ifdef PARAM_32KHZ +static const uint8_t delay_length_d[2][NO_ALLPASS_LINKS] = { + { 1, 2, 3 } /* d_24kHz */, + { 3, 4, 5 } /* d_48kHz */ +}; +#else +static const uint8_t delay_length_d[NO_ALLPASS_LINKS] = { + 3, 4, 5 /* d_48kHz */ +}; +#endif +static const real_t filter_a[NO_ALLPASS_LINKS] = { /* a(m) = exp(-d_48kHz(m)/7) */ + FRAC_CONST(0.65143905753106), + FRAC_CONST(0.56471812200776), + FRAC_CONST(0.48954165955695) +}; + +static const uint8_t group_border20[10+12 + 1] = +{ + 6, 7, 0, 1, 2, 3, /* 6 subqmf subbands */ + 9, 8, /* 2 subqmf subbands */ + 10, 11, /* 2 subqmf subbands */ + 3, 4, 5, 6, 7, 8, 9, 11, 14, 18, 23, 35, 64 +}; + +static const uint8_t group_border34[32+18 + 1] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, /* 12 subqmf subbands */ + 12, 13, 14, 15, 16, 17, 18, 19, /* 8 subqmf subbands */ + 20, 21, 22, 23, /* 4 subqmf subbands */ + 24, 25, 26, 27, /* 4 subqmf subbands */ + 28, 29, 30, 31, /* 4 subqmf subbands */ + 32-27, 33-27, 34-27, 35-27, 36-27, 37-27, 38-27, 40-27, 42-27, 44-27, 46-27, 48-27, 51-27, 54-27, 57-27, 60-27, 64-27, 68-27, 91-27 +}; + +static const uint16_t map_group2bk20[10+12] = +{ + (NEGATE_IPD_MASK | 1), (NEGATE_IPD_MASK | 0), + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 +}; + +static const uint16_t map_group2bk34[32+18] = +{ + 0, 1, 2, 3, 4, 5, 6, 6, 7, (NEGATE_IPD_MASK | 2), (NEGATE_IPD_MASK | 1), (NEGATE_IPD_MASK | 0), + 10, 10, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 9, + 14, 11, 12, 13, + 14, 15, 16, 13, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 +}; + +/* type definitions */ +typedef struct +{ + uint8_t frame_len; + uint8_t resolution20[3]; + uint8_t resolution34[5]; + + qmf_t *work; + qmf_t **buffer; + qmf_t **temp; +} hyb_info; + +/* static function declarations */ +static void ps_data_decode(ps_info *ps); +static hyb_info *hybrid_init(uint8_t numTimeSlotsRate); +static void channel_filter2(hyb_info *hyb, uint8_t frame_len, const real_t *filter, + qmf_t *buffer, qmf_t **X_hybrid); +static void INLINE DCT3_4_unscaled(real_t *y, real_t *x); +static void channel_filter8(hyb_info *hyb, uint8_t frame_len, const real_t *filter, + qmf_t *buffer, qmf_t **X_hybrid); +static void hybrid_analysis(hyb_info *hyb, qmf_t X[32][64], qmf_t X_hybrid[32][32], + uint8_t use34, uint8_t numTimeSlotsRate); +static void hybrid_synthesis(hyb_info *hyb, qmf_t X[32][64], qmf_t X_hybrid[32][32], + uint8_t use34, uint8_t numTimeSlotsRate); +static int8_t delta_clip(int8_t i, int8_t min, int8_t max); +static void delta_decode(uint8_t enable, int8_t *index, int8_t *index_prev, + uint8_t dt_flag, uint8_t nr_par, uint8_t stride, + int8_t min_index, int8_t max_index); +static void delta_modulo_decode(uint8_t enable, int8_t *index, int8_t *index_prev, + uint8_t dt_flag, uint8_t nr_par, uint8_t stride, + int8_t and_modulo); +static void map20indexto34(int8_t *index, uint8_t bins); +#ifdef PS_LOW_POWER +static void map34indexto20(int8_t *index, uint8_t bins); +#endif +static void ps_data_decode(ps_info *ps); +static void ps_decorrelate(ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64], + qmf_t X_hybrid_left[32][32], qmf_t X_hybrid_right[32][32]); +static void ps_mix_phase(ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64], + qmf_t X_hybrid_left[32][32], qmf_t X_hybrid_right[32][32]); + +/* */ + + +static hyb_info *hybrid_init(uint8_t numTimeSlotsRate) +{ + uint8_t i; + + hyb_info *hyb = (hyb_info*)faad_malloc(sizeof(hyb_info)); + + hyb->resolution34[0] = 12; + hyb->resolution34[1] = 8; + hyb->resolution34[2] = 4; + hyb->resolution34[3] = 4; + hyb->resolution34[4] = 4; + + hyb->resolution20[0] = 8; + hyb->resolution20[1] = 2; + hyb->resolution20[2] = 2; + + hyb->frame_len = numTimeSlotsRate; + + hyb->work = (qmf_t*)faad_malloc((hyb->frame_len+12) * sizeof(qmf_t)); + memset(hyb->work, 0, (hyb->frame_len+12) * sizeof(qmf_t)); + + hyb->buffer = (qmf_t**)faad_malloc(5 * sizeof(qmf_t*)); + for (i = 0; i < 5; i++) + { + hyb->buffer[i] = (qmf_t*)faad_malloc(hyb->frame_len * sizeof(qmf_t)); + memset(hyb->buffer[i], 0, hyb->frame_len * sizeof(qmf_t)); + } + + hyb->temp = (qmf_t**)faad_malloc(hyb->frame_len * sizeof(qmf_t*)); + for (i = 0; i < hyb->frame_len; i++) + { + hyb->temp[i] = (qmf_t*)faad_malloc(12 /*max*/ * sizeof(qmf_t)); + } + + return hyb; +} + +static void hybrid_free(hyb_info *hyb) +{ + uint8_t i; + + if (!hyb) return; + + if (hyb->work) + faad_free(hyb->work); + + for (i = 0; i < 5; i++) + { + if (hyb->buffer[i]) + faad_free(hyb->buffer[i]); + } + if (hyb->buffer) + faad_free(hyb->buffer); + + for (i = 0; i < hyb->frame_len; i++) + { + if (hyb->temp[i]) + faad_free(hyb->temp[i]); + } + if (hyb->temp) + faad_free(hyb->temp); + + faad_free(hyb); +} + +/* real filter, size 2 */ +static void channel_filter2(hyb_info *hyb, uint8_t frame_len, const real_t *filter, + qmf_t *buffer, qmf_t **X_hybrid) +{ + uint8_t i; + (void)hyb; /* TODO: remove parameter? */ + + for (i = 0; i < frame_len; i++) + { + real_t r0 = MUL_F(filter[0],(QMF_RE(buffer[0+i]) + QMF_RE(buffer[12+i]))); + real_t r1 = MUL_F(filter[1],(QMF_RE(buffer[1+i]) + QMF_RE(buffer[11+i]))); + real_t r2 = MUL_F(filter[2],(QMF_RE(buffer[2+i]) + QMF_RE(buffer[10+i]))); + real_t r3 = MUL_F(filter[3],(QMF_RE(buffer[3+i]) + QMF_RE(buffer[9+i]))); + real_t r4 = MUL_F(filter[4],(QMF_RE(buffer[4+i]) + QMF_RE(buffer[8+i]))); + real_t r5 = MUL_F(filter[5],(QMF_RE(buffer[5+i]) + QMF_RE(buffer[7+i]))); + real_t r6 = MUL_F(filter[6],QMF_RE(buffer[6+i])); + real_t i0 = MUL_F(filter[0],(QMF_IM(buffer[0+i]) + QMF_IM(buffer[12+i]))); + real_t i1 = MUL_F(filter[1],(QMF_IM(buffer[1+i]) + QMF_IM(buffer[11+i]))); + real_t i2 = MUL_F(filter[2],(QMF_IM(buffer[2+i]) + QMF_IM(buffer[10+i]))); + real_t i3 = MUL_F(filter[3],(QMF_IM(buffer[3+i]) + QMF_IM(buffer[9+i]))); + real_t i4 = MUL_F(filter[4],(QMF_IM(buffer[4+i]) + QMF_IM(buffer[8+i]))); + real_t i5 = MUL_F(filter[5],(QMF_IM(buffer[5+i]) + QMF_IM(buffer[7+i]))); + real_t i6 = MUL_F(filter[6],QMF_IM(buffer[6+i])); + + /* q = 0 */ + QMF_RE(X_hybrid[i][0]) = r0 + r1 + r2 + r3 + r4 + r5 + r6; + QMF_IM(X_hybrid[i][0]) = i0 + i1 + i2 + i3 + i4 + i5 + i6; + + /* q = 1 */ + QMF_RE(X_hybrid[i][1]) = r0 - r1 + r2 - r3 + r4 - r5 + r6; + QMF_IM(X_hybrid[i][1]) = i0 - i1 + i2 - i3 + i4 - i5 + i6; + } +} + +/* complex filter, size 4 */ +static void channel_filter4(hyb_info *hyb, uint8_t frame_len, const real_t *filter, + qmf_t *buffer, qmf_t **X_hybrid) +{ + uint8_t i; + real_t input_re1[2], input_re2[2], input_im1[2], input_im2[2]; + (void)hyb; /* TODO: remove parameter? */ + + for (i = 0; i < frame_len; i++) + { + input_re1[0] = -MUL_F(filter[2], (QMF_RE(buffer[i+2]) + QMF_RE(buffer[i+10]))) + + MUL_F(filter[6], QMF_RE(buffer[i+6])); + input_re1[1] = MUL_F(FRAC_CONST(-0.70710678118655), + (MUL_F(filter[1], (QMF_RE(buffer[i+1]) + QMF_RE(buffer[i+11]))) + + MUL_F(filter[3], (QMF_RE(buffer[i+3]) + QMF_RE(buffer[i+9]))) - + MUL_F(filter[5], (QMF_RE(buffer[i+5]) + QMF_RE(buffer[i+7]))))); + + input_im1[0] = MUL_F(filter[0], (QMF_IM(buffer[i+0]) - QMF_IM(buffer[i+12]))) - + MUL_F(filter[4], (QMF_IM(buffer[i+4]) - QMF_IM(buffer[i+8]))); + input_im1[1] = MUL_F(FRAC_CONST(0.70710678118655), + (MUL_F(filter[1], (QMF_IM(buffer[i+1]) - QMF_IM(buffer[i+11]))) - + MUL_F(filter[3], (QMF_IM(buffer[i+3]) - QMF_IM(buffer[i+9]))) - + MUL_F(filter[5], (QMF_IM(buffer[i+5]) - QMF_IM(buffer[i+7]))))); + + input_re2[0] = MUL_F(filter[0], (QMF_RE(buffer[i+0]) - QMF_RE(buffer[i+12]))) - + MUL_F(filter[4], (QMF_RE(buffer[i+4]) - QMF_RE(buffer[i+8]))); + input_re2[1] = MUL_F(FRAC_CONST(0.70710678118655), + (MUL_F(filter[1], (QMF_RE(buffer[i+1]) - QMF_RE(buffer[i+11]))) - + MUL_F(filter[3], (QMF_RE(buffer[i+3]) - QMF_RE(buffer[i+9]))) - + MUL_F(filter[5], (QMF_RE(buffer[i+5]) - QMF_RE(buffer[i+7]))))); + + input_im2[0] = -MUL_F(filter[2], (QMF_IM(buffer[i+2]) + QMF_IM(buffer[i+10]))) + + MUL_F(filter[6], QMF_IM(buffer[i+6])); + input_im2[1] = MUL_F(FRAC_CONST(-0.70710678118655), + (MUL_F(filter[1], (QMF_IM(buffer[i+1]) + QMF_IM(buffer[i+11]))) + + MUL_F(filter[3], (QMF_IM(buffer[i+3]) + QMF_IM(buffer[i+9]))) - + MUL_F(filter[5], (QMF_IM(buffer[i+5]) + QMF_IM(buffer[i+7]))))); + + /* q == 0 */ + QMF_RE(X_hybrid[i][0]) = input_re1[0] + input_re1[1] + input_im1[0] + input_im1[1]; + QMF_IM(X_hybrid[i][0]) = -input_re2[0] - input_re2[1] + input_im2[0] + input_im2[1]; + + /* q == 1 */ + QMF_RE(X_hybrid[i][1]) = input_re1[0] - input_re1[1] - input_im1[0] + input_im1[1]; + QMF_IM(X_hybrid[i][1]) = input_re2[0] - input_re2[1] + input_im2[0] - input_im2[1]; + + /* q == 2 */ + QMF_RE(X_hybrid[i][2]) = input_re1[0] - input_re1[1] + input_im1[0] - input_im1[1]; + QMF_IM(X_hybrid[i][2]) = -input_re2[0] + input_re2[1] + input_im2[0] - input_im2[1]; + + /* q == 3 */ + QMF_RE(X_hybrid[i][3]) = input_re1[0] + input_re1[1] - input_im1[0] - input_im1[1]; + QMF_IM(X_hybrid[i][3]) = input_re2[0] + input_re2[1] + input_im2[0] + input_im2[1]; + } +} + +static void INLINE DCT3_4_unscaled(real_t *y, real_t *x) +{ + real_t f0, f1, f2, f3, f4, f5, f6, f7, f8; + + f0 = MUL_F(x[2], FRAC_CONST(0.7071067811865476)); + f1 = x[0] - f0; + f2 = x[0] + f0; + f3 = x[1] + x[3]; + f4 = MUL_C(x[1], COEF_CONST(1.3065629648763766)); + f5 = MUL_F(f3, FRAC_CONST(-0.9238795325112866)); + f6 = MUL_F(x[3], FRAC_CONST(-0.5411961001461967)); + f7 = f4 + f5; + f8 = f6 - f5; + y[3] = f2 - f8; + y[0] = f2 + f8; + y[2] = f1 - f7; + y[1] = f1 + f7; +} + +/* complex filter, size 8 */ +static void channel_filter8(hyb_info *hyb, uint8_t frame_len, const real_t *filter, + qmf_t *buffer, qmf_t **X_hybrid) +{ + uint8_t i, n; + real_t input_re1[4], input_re2[4], input_im1[4], input_im2[4]; + real_t x[4]; + (void)hyb; /* TODO: remove parameter? */ + + for (i = 0; i < frame_len; i++) + { + input_re1[0] = MUL_F(filter[6],QMF_RE(buffer[6+i])); + input_re1[1] = MUL_F(filter[5],(QMF_RE(buffer[5+i]) + QMF_RE(buffer[7+i]))); + input_re1[2] = -MUL_F(filter[0],(QMF_RE(buffer[0+i]) + QMF_RE(buffer[12+i]))) + MUL_F(filter[4],(QMF_RE(buffer[4+i]) + QMF_RE(buffer[8+i]))); + input_re1[3] = -MUL_F(filter[1],(QMF_RE(buffer[1+i]) + QMF_RE(buffer[11+i]))) + MUL_F(filter[3],(QMF_RE(buffer[3+i]) + QMF_RE(buffer[9+i]))); + + input_im1[0] = MUL_F(filter[5],(QMF_IM(buffer[7+i]) - QMF_IM(buffer[5+i]))); + input_im1[1] = MUL_F(filter[0],(QMF_IM(buffer[12+i]) - QMF_IM(buffer[0+i]))) + MUL_F(filter[4],(QMF_IM(buffer[8+i]) - QMF_IM(buffer[4+i]))); + input_im1[2] = MUL_F(filter[1],(QMF_IM(buffer[11+i]) - QMF_IM(buffer[1+i]))) + MUL_F(filter[3],(QMF_IM(buffer[9+i]) - QMF_IM(buffer[3+i]))); + input_im1[3] = MUL_F(filter[2],(QMF_IM(buffer[10+i]) - QMF_IM(buffer[2+i]))); + + for (n = 0; n < 4; n++) + { + x[n] = input_re1[n] - input_im1[3-n]; + } + DCT3_4_unscaled(x, x); + QMF_RE(X_hybrid[i][7]) = x[0]; + QMF_RE(X_hybrid[i][5]) = x[2]; + QMF_RE(X_hybrid[i][3]) = x[3]; + QMF_RE(X_hybrid[i][1]) = x[1]; + + for (n = 0; n < 4; n++) + { + x[n] = input_re1[n] + input_im1[3-n]; + } + DCT3_4_unscaled(x, x); + QMF_RE(X_hybrid[i][6]) = x[1]; + QMF_RE(X_hybrid[i][4]) = x[3]; + QMF_RE(X_hybrid[i][2]) = x[2]; + QMF_RE(X_hybrid[i][0]) = x[0]; + + input_im2[0] = MUL_F(filter[6],QMF_IM(buffer[6+i])); + input_im2[1] = MUL_F(filter[5],(QMF_IM(buffer[5+i]) + QMF_IM(buffer[7+i]))); + input_im2[2] = -MUL_F(filter[0],(QMF_IM(buffer[0+i]) + QMF_IM(buffer[12+i]))) + MUL_F(filter[4],(QMF_IM(buffer[4+i]) + QMF_IM(buffer[8+i]))); + input_im2[3] = -MUL_F(filter[1],(QMF_IM(buffer[1+i]) + QMF_IM(buffer[11+i]))) + MUL_F(filter[3],(QMF_IM(buffer[3+i]) + QMF_IM(buffer[9+i]))); + + input_re2[0] = MUL_F(filter[5],(QMF_RE(buffer[7+i]) - QMF_RE(buffer[5+i]))); + input_re2[1] = MUL_F(filter[0],(QMF_RE(buffer[12+i]) - QMF_RE(buffer[0+i]))) + MUL_F(filter[4],(QMF_RE(buffer[8+i]) - QMF_RE(buffer[4+i]))); + input_re2[2] = MUL_F(filter[1],(QMF_RE(buffer[11+i]) - QMF_RE(buffer[1+i]))) + MUL_F(filter[3],(QMF_RE(buffer[9+i]) - QMF_RE(buffer[3+i]))); + input_re2[3] = MUL_F(filter[2],(QMF_RE(buffer[10+i]) - QMF_RE(buffer[2+i]))); + + for (n = 0; n < 4; n++) + { + x[n] = input_im2[n] + input_re2[3-n]; + } + DCT3_4_unscaled(x, x); + QMF_IM(X_hybrid[i][7]) = x[0]; + QMF_IM(X_hybrid[i][5]) = x[2]; + QMF_IM(X_hybrid[i][3]) = x[3]; + QMF_IM(X_hybrid[i][1]) = x[1]; + + for (n = 0; n < 4; n++) + { + x[n] = input_im2[n] - input_re2[3-n]; + } + DCT3_4_unscaled(x, x); + QMF_IM(X_hybrid[i][6]) = x[1]; + QMF_IM(X_hybrid[i][4]) = x[3]; + QMF_IM(X_hybrid[i][2]) = x[2]; + QMF_IM(X_hybrid[i][0]) = x[0]; + } +} + +static void INLINE DCT3_6_unscaled(real_t *y, real_t *x) +{ + real_t f0, f1, f2, f3, f4, f5, f6, f7; + + f0 = MUL_F(x[3], FRAC_CONST(0.70710678118655)); + f1 = x[0] + f0; + f2 = x[0] - f0; + f3 = MUL_F((x[1] - x[5]), FRAC_CONST(0.70710678118655)); + f4 = MUL_F(x[2], FRAC_CONST(0.86602540378444)) + MUL_F(x[4], FRAC_CONST(0.5)); + f5 = f4 - x[4]; + f6 = MUL_F(x[1], FRAC_CONST(0.96592582628907)) + MUL_F(x[5], FRAC_CONST(0.25881904510252)); + f7 = f6 - f3; + y[0] = f1 + f6 + f4; + y[1] = f2 + f3 - x[4]; + y[2] = f7 + f2 - f5; + y[3] = f1 - f7 - f5; + y[4] = f1 - f3 - x[4]; + y[5] = f2 - f6 + f4; +} + +/* complex filter, size 12 */ +static void channel_filter12(hyb_info *hyb, uint8_t frame_len, const real_t *filter, + qmf_t *buffer, qmf_t **X_hybrid) +{ + uint8_t i, n; + real_t input_re1[6], input_re2[6], input_im1[6], input_im2[6]; + real_t out_re1[6], out_re2[6], out_im1[6], out_im2[6]; + (void)hyb; /* TODO: remove parameter? */ + + for (i = 0; i < frame_len; i++) + { + for (n = 0; n < 6; n++) + { + if (n == 0) + { + input_re1[0] = MUL_F(QMF_RE(buffer[6+i]), filter[6]); + input_re2[0] = MUL_F(QMF_IM(buffer[6+i]), filter[6]); + } else { + input_re1[6-n] = MUL_F((QMF_RE(buffer[n+i]) + QMF_RE(buffer[12-n+i])), filter[n]); + input_re2[6-n] = MUL_F((QMF_IM(buffer[n+i]) + QMF_IM(buffer[12-n+i])), filter[n]); + } + input_im2[n] = MUL_F((QMF_RE(buffer[n+i]) - QMF_RE(buffer[12-n+i])), filter[n]); + input_im1[n] = MUL_F((QMF_IM(buffer[n+i]) - QMF_IM(buffer[12-n+i])), filter[n]); + } + + DCT3_6_unscaled(out_re1, input_re1); + DCT3_6_unscaled(out_re2, input_re2); + + DCT3_6_unscaled(out_im1, input_im1); + DCT3_6_unscaled(out_im2, input_im2); + + for (n = 0; n < 6; n += 2) + { + QMF_RE(X_hybrid[i][n]) = out_re1[n] - out_im1[n]; + QMF_IM(X_hybrid[i][n]) = out_re2[n] + out_im2[n]; + QMF_RE(X_hybrid[i][n+1]) = out_re1[n+1] + out_im1[n+1]; + QMF_IM(X_hybrid[i][n+1]) = out_re2[n+1] - out_im2[n+1]; + + QMF_RE(X_hybrid[i][10-n]) = out_re1[n+1] - out_im1[n+1]; + QMF_IM(X_hybrid[i][10-n]) = out_re2[n+1] + out_im2[n+1]; + QMF_RE(X_hybrid[i][11-n]) = out_re1[n] + out_im1[n]; + QMF_IM(X_hybrid[i][11-n]) = out_re2[n] - out_im2[n]; + } + } +} + +/* Hybrid analysis: further split up QMF subbands + * to improve frequency resolution + */ +static void hybrid_analysis(hyb_info *hyb, qmf_t X[32][64], qmf_t X_hybrid[32][32], + uint8_t use34, uint8_t numTimeSlotsRate) +{ + uint8_t k, n, band; + uint8_t offset = 0; + uint8_t qmf_bands = (use34) ? 5 : 3; + uint8_t *resolution = (use34) ? hyb->resolution34 : hyb->resolution20; + + for (band = 0; band < qmf_bands; band++) + { + /* build working buffer */ + memcpy(hyb->work, hyb->buffer[band], 12 * sizeof(qmf_t)); + + /* add new samples */ + for (n = 0; n < hyb->frame_len; n++) + { + QMF_RE(hyb->work[12 + n]) = QMF_RE(X[n + 6 /*delay*/][band]); + QMF_IM(hyb->work[12 + n]) = QMF_IM(X[n + 6 /*delay*/][band]); + } + + /* store samples */ + memcpy(hyb->buffer[band], hyb->work + hyb->frame_len, 12 * sizeof(qmf_t)); + + + switch(resolution[band]) + { + case 2: + /* Type B real filter, Q[p] = 2 */ + channel_filter2(hyb, hyb->frame_len, p2_13_20, hyb->work, hyb->temp); + break; + case 4: + /* Type A complex filter, Q[p] = 4 */ + channel_filter4(hyb, hyb->frame_len, p4_13_34, hyb->work, hyb->temp); + break; + case 8: + /* Type A complex filter, Q[p] = 8 */ + channel_filter8(hyb, hyb->frame_len, (use34) ? p8_13_34 : p8_13_20, + hyb->work, hyb->temp); + break; + case 12: + /* Type A complex filter, Q[p] = 12 */ + channel_filter12(hyb, hyb->frame_len, p12_13_34, hyb->work, hyb->temp); + break; + } + + for (n = 0; n < hyb->frame_len; n++) + { + for (k = 0; k < resolution[band]; k++) + { + QMF_RE(X_hybrid[n][offset + k]) = QMF_RE(hyb->temp[n][k]); + QMF_IM(X_hybrid[n][offset + k]) = QMF_IM(hyb->temp[n][k]); + } + } + offset += resolution[band]; + } + + /* group hybrid channels */ + if (!use34) + { + for (n = 0; n < numTimeSlotsRate; n++) + { + QMF_RE(X_hybrid[n][3]) += QMF_RE(X_hybrid[n][4]); + QMF_IM(X_hybrid[n][3]) += QMF_IM(X_hybrid[n][4]); + QMF_RE(X_hybrid[n][4]) = 0; + QMF_IM(X_hybrid[n][4]) = 0; + + QMF_RE(X_hybrid[n][2]) += QMF_RE(X_hybrid[n][5]); + QMF_IM(X_hybrid[n][2]) += QMF_IM(X_hybrid[n][5]); + QMF_RE(X_hybrid[n][5]) = 0; + QMF_IM(X_hybrid[n][5]) = 0; + } + } +} + +static void hybrid_synthesis(hyb_info *hyb, qmf_t X[32][64], qmf_t X_hybrid[32][32], + uint8_t use34, uint8_t numTimeSlotsRate) +{ + uint8_t k, n, band; + uint8_t offset = 0; + uint8_t qmf_bands = (use34) ? 5 : 3; + uint8_t *resolution = (use34) ? hyb->resolution34 : hyb->resolution20; + (void)numTimeSlotsRate; /* TODO: remove parameter? */ + + for(band = 0; band < qmf_bands; band++) + { + for (n = 0; n < hyb->frame_len; n++) + { + QMF_RE(X[n][band]) = 0; + QMF_IM(X[n][band]) = 0; + + for (k = 0; k < resolution[band]; k++) + { + QMF_RE(X[n][band]) += QMF_RE(X_hybrid[n][offset + k]); + QMF_IM(X[n][band]) += QMF_IM(X_hybrid[n][offset + k]); + } + } + offset += resolution[band]; + } +} + +/* limits the value i to the range [min,max] */ +static int8_t delta_clip(int8_t i, int8_t min, int8_t max) +{ + if (i < min) + return min; + else if (i > max) + return max; + else + return i; +} + +//int iid = 0; + +/* delta decode array */ +static void delta_decode(uint8_t enable, int8_t *index, int8_t *index_prev, + uint8_t dt_flag, uint8_t nr_par, uint8_t stride, + int8_t min_index, int8_t max_index) +{ + int8_t i; + + if (enable == 1) + { + if (dt_flag == 0) + { + /* delta coded in frequency direction */ + index[0] = 0 + index[0]; + index[0] = delta_clip(index[0], min_index, max_index); + + for (i = 1; i < nr_par; i++) + { + index[i] = index[i-1] + index[i]; + index[i] = delta_clip(index[i], min_index, max_index); + } + } else { + /* delta coded in time direction */ + for (i = 0; i < nr_par; i++) + { + //int8_t tmp2; + //int8_t tmp = index[i]; + + //printf("%d %d\n", index_prev[i*stride], index[i]); + //printf("%d\n", index[i]); + + index[i] = index_prev[i*stride] + index[i]; + //tmp2 = index[i]; + index[i] = delta_clip(index[i], min_index, max_index); + + //if (iid) + //{ + // if (index[i] == 7) + // { + // printf("%d %d %d\n", index_prev[i*stride], tmp, tmp2); + // } + //} + } + } + } else { + /* set indices to zero */ + for (i = 0; i < nr_par; i++) + { + index[i] = 0; + } + } + + /* coarse */ + if (stride == 2) + { + for (i = (nr_par<<1)-1; i > 0; i--) + { + index[i] = index[i>>1]; + } + } +} + +/* delta modulo decode array */ +/* in: log2 value of the modulo value to allow using AND instead of MOD */ +static void delta_modulo_decode(uint8_t enable, int8_t *index, int8_t *index_prev, + uint8_t dt_flag, uint8_t nr_par, uint8_t stride, + int8_t and_modulo) +{ + int8_t i; + + if (enable == 1) + { + if (dt_flag == 0) + { + /* delta coded in frequency direction */ + index[0] = 0 + index[0]; + index[0] &= and_modulo; + + for (i = 1; i < nr_par; i++) + { + index[i] = index[i-1] + index[i]; + index[i] &= and_modulo; + } + } else { + /* delta coded in time direction */ + for (i = 0; i < nr_par; i++) + { + index[i] = index_prev[i*stride] + index[i]; + index[i] &= and_modulo; + } + } + } else { + /* set indices to zero */ + for (i = 0; i < nr_par; i++) + { + index[i] = 0; + } + } + + /* coarse */ + if (stride == 2) + { + index[0] = 0; + for (i = (nr_par<<1)-1; i > 0; i--) + { + index[i] = index[i>>1]; + } + } +} + +#ifdef PS_LOW_POWER +static void map34indexto20(int8_t *index, uint8_t bins) +{ + index[0] = (2*index[0]+index[1])/3; + index[1] = (index[1]+2*index[2])/3; + index[2] = (2*index[3]+index[4])/3; + index[3] = (index[4]+2*index[5])/3; + index[4] = (index[6]+index[7])/2; + index[5] = (index[8]+index[9])/2; + index[6] = index[10]; + index[7] = index[11]; + index[8] = (index[12]+index[13])/2; + index[9] = (index[14]+index[15])/2; + index[10] = index[16]; + + if (bins == 34) + { + index[11] = index[17]; + index[12] = index[18]; + index[13] = index[19]; + index[14] = (index[20]+index[21])/2; + index[15] = (index[22]+index[23])/2; + index[16] = (index[24]+index[25])/2; + index[17] = (index[26]+index[27])/2; + index[18] = (index[28]+index[29]+index[30]+index[31])/4; + index[19] = (index[32]+index[33])/2; + } +} +#endif + +static void map20indexto34(int8_t *index, uint8_t bins) +{ + index[0] = index[0]; + index[1] = (index[0] + index[1])/2; + index[2] = index[1]; + index[3] = index[2]; + index[4] = (index[2] + index[3])/2; + index[5] = index[3]; + index[6] = index[4]; + index[7] = index[4]; + index[8] = index[5]; + index[9] = index[5]; + index[10] = index[6]; + index[11] = index[7]; + index[12] = index[8]; + index[13] = index[8]; + index[14] = index[9]; + index[15] = index[9]; + index[16] = index[10]; + + if (bins == 34) + { + index[17] = index[11]; + index[18] = index[12]; + index[19] = index[13]; + index[20] = index[14]; + index[21] = index[14]; + index[22] = index[15]; + index[23] = index[15]; + index[24] = index[16]; + index[25] = index[16]; + index[26] = index[17]; + index[27] = index[17]; + index[28] = index[18]; + index[29] = index[18]; + index[30] = index[18]; + index[31] = index[18]; + index[32] = index[19]; + index[33] = index[19]; + } +} + +/* parse the bitstream data decoded in ps_data() */ +static void ps_data_decode(ps_info *ps) +{ + uint8_t env, bin; + + /* ps data not available, use data from previous frame */ + if (ps->ps_data_available == 0) + { + ps->num_env = 0; + } + + for (env = 0; env < ps->num_env; env++) + { + int8_t *iid_index_prev; + int8_t *icc_index_prev; + int8_t *ipd_index_prev; + int8_t *opd_index_prev; + + int8_t num_iid_steps = (ps->iid_mode < 3) ? 7 : 15 /*fine quant*/; + + if (env == 0) + { + /* take last envelope from previous frame */ + iid_index_prev = ps->iid_index_prev; + icc_index_prev = ps->icc_index_prev; + ipd_index_prev = ps->ipd_index_prev; + opd_index_prev = ps->opd_index_prev; + } else { + /* take index values from previous envelope */ + iid_index_prev = ps->iid_index[env - 1]; + icc_index_prev = ps->icc_index[env - 1]; + ipd_index_prev = ps->ipd_index[env - 1]; + opd_index_prev = ps->opd_index[env - 1]; + } + +// iid = 1; + /* delta decode iid parameters */ + delta_decode(ps->enable_iid, ps->iid_index[env], iid_index_prev, + ps->iid_dt[env], ps->nr_iid_par, + (ps->iid_mode == 0 || ps->iid_mode == 3) ? 2 : 1, + -num_iid_steps, num_iid_steps); +// iid = 0; + + /* delta decode icc parameters */ + delta_decode(ps->enable_icc, ps->icc_index[env], icc_index_prev, + ps->icc_dt[env], ps->nr_icc_par, + (ps->icc_mode == 0 || ps->icc_mode == 3) ? 2 : 1, + 0, 7); + + /* delta modulo decode ipd parameters */ + delta_modulo_decode(ps->enable_ipdopd, ps->ipd_index[env], ipd_index_prev, + ps->ipd_dt[env], ps->nr_ipdopd_par, 1, 7); + + /* delta modulo decode opd parameters */ + delta_modulo_decode(ps->enable_ipdopd, ps->opd_index[env], opd_index_prev, + ps->opd_dt[env], ps->nr_ipdopd_par, 1, 7); + } + + /* handle error case */ + if (ps->num_env == 0) + { + /* force to 1 */ + ps->num_env = 1; + + if (ps->enable_iid) + { + for (bin = 0; bin < 34; bin++) + ps->iid_index[0][bin] = ps->iid_index_prev[bin]; + } else { + for (bin = 0; bin < 34; bin++) + ps->iid_index[0][bin] = 0; + } + + if (ps->enable_icc) + { + for (bin = 0; bin < 34; bin++) + ps->icc_index[0][bin] = ps->icc_index_prev[bin]; + } else { + for (bin = 0; bin < 34; bin++) + ps->icc_index[0][bin] = 0; + } + + if (ps->enable_ipdopd) + { + for (bin = 0; bin < 17; bin++) + { + ps->ipd_index[0][bin] = ps->ipd_index_prev[bin]; + ps->opd_index[0][bin] = ps->opd_index_prev[bin]; + } + } else { + for (bin = 0; bin < 17; bin++) + { + ps->ipd_index[0][bin] = 0; + ps->opd_index[0][bin] = 0; + } + } + } + + /* update previous indices */ + for (bin = 0; bin < 34; bin++) + ps->iid_index_prev[bin] = ps->iid_index[ps->num_env-1][bin]; + for (bin = 0; bin < 34; bin++) + ps->icc_index_prev[bin] = ps->icc_index[ps->num_env-1][bin]; + for (bin = 0; bin < 17; bin++) + { + ps->ipd_index_prev[bin] = ps->ipd_index[ps->num_env-1][bin]; + ps->opd_index_prev[bin] = ps->opd_index[ps->num_env-1][bin]; + } + + ps->ps_data_available = 0; + + if (ps->frame_class == 0) + { + ps->border_position[0] = 0; + for (env = 1; env < ps->num_env; env++) + { + ps->border_position[env] = (env * ps->numTimeSlotsRate) / ps->num_env; + } + ps->border_position[ps->num_env] = ps->numTimeSlotsRate; + } else { + ps->border_position[0] = 0; + + if (ps->border_position[ps->num_env] < ps->numTimeSlotsRate) + { + for (bin = 0; bin < 34; bin++) + { + ps->iid_index[ps->num_env][bin] = ps->iid_index[ps->num_env-1][bin]; + ps->icc_index[ps->num_env][bin] = ps->icc_index[ps->num_env-1][bin]; + } + for (bin = 0; bin < 17; bin++) + { + ps->ipd_index[ps->num_env][bin] = ps->ipd_index[ps->num_env-1][bin]; + ps->opd_index[ps->num_env][bin] = ps->opd_index[ps->num_env-1][bin]; + } + ps->num_env++; + ps->border_position[ps->num_env] = ps->numTimeSlotsRate; + } + + for (env = 1; env < ps->num_env; env++) + { + int8_t thr = ps->numTimeSlotsRate - (ps->num_env - env); + + if (ps->border_position[env] > thr) + { + ps->border_position[env] = thr; + } else { + thr = ps->border_position[env-1]+1; + if (ps->border_position[env] < thr) + { + ps->border_position[env] = thr; + } + } + } + } + + /* make sure that the indices of all parameters can be mapped + * to the same hybrid synthesis filterbank + */ +#ifdef PS_LOW_POWER + for (env = 0; env < ps->num_env; env++) + { + if (ps->iid_mode == 2 || ps->iid_mode == 5) + map34indexto20(ps->iid_index[env], 34); + if (ps->icc_mode == 2 || ps->icc_mode == 5) + map34indexto20(ps->icc_index[env], 34); + + /* disable ipd/opd */ + for (bin = 0; bin < 17; bin++) + { + ps->aaIpdIndex[env][bin] = 0; + ps->aaOpdIndex[env][bin] = 0; + } + } +#else + if (ps->use34hybrid_bands) + { + for (env = 0; env < ps->num_env; env++) + { + if (ps->iid_mode != 2 && ps->iid_mode != 5) + map20indexto34(ps->iid_index[env], 34); + if (ps->icc_mode != 2 && ps->icc_mode != 5) + map20indexto34(ps->icc_index[env], 34); + if (ps->ipd_mode != 2 && ps->ipd_mode != 5) + { + map20indexto34(ps->ipd_index[env], 17); + map20indexto34(ps->opd_index[env], 17); + } + } + } +#endif + +#if 0 + for (env = 0; env < ps->num_env; env++) + { + printf("iid[env:%d]:", env); + for (bin = 0; bin < 34; bin++) + { + printf(" %d", ps->iid_index[env][bin]); + } + printf("\n"); + } + for (env = 0; env < ps->num_env; env++) + { + printf("icc[env:%d]:", env); + for (bin = 0; bin < 34; bin++) + { + printf(" %d", ps->icc_index[env][bin]); + } + printf("\n"); + } + for (env = 0; env < ps->num_env; env++) + { + printf("ipd[env:%d]:", env); + for (bin = 0; bin < 17; bin++) + { + printf(" %d", ps->ipd_index[env][bin]); + } + printf("\n"); + } + for (env = 0; env < ps->num_env; env++) + { + printf("opd[env:%d]:", env); + for (bin = 0; bin < 17; bin++) + { + printf(" %d", ps->opd_index[env][bin]); + } + printf("\n"); + } + printf("\n"); +#endif +} + +/* decorrelate the mono signal using an allpass filter */ +static void ps_decorrelate(ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64], + qmf_t X_hybrid_left[32][32], qmf_t X_hybrid_right[32][32]) +{ + uint8_t gr, n, bk; + uint8_t temp_delay = 0; + uint8_t sb, maxsb; + const complex_t *Phi_Fract_SubQmf; + uint8_t temp_delay_ser[NO_ALLPASS_LINKS]; + real_t P_SmoothPeakDecayDiffNrg, nrg; + real_t P[32][34]; + real_t G_TransientRatio[32][34] = {{0}}; + complex_t inputLeft; + + + /* chose hybrid filterbank: 20 or 34 band case */ + if (ps->use34hybrid_bands) + { + Phi_Fract_SubQmf = Phi_Fract_SubQmf34; + } else{ + Phi_Fract_SubQmf = Phi_Fract_SubQmf20; + } + + /* clear the energy values */ + for (n = 0; n < 32; n++) + { + for (bk = 0; bk < 34; bk++) + { + P[n][bk] = 0; + } + } + + /* calculate the energy in each parameter band b(k) */ + for (gr = 0; gr < ps->num_groups; gr++) + { + /* select the parameter index b(k) to which this group belongs */ + bk = (~NEGATE_IPD_MASK) & ps->map_group2bk[gr]; + + /* select the upper subband border for this group */ + maxsb = (gr < ps->num_hybrid_groups) ? ps->group_border[gr]+1 : ps->group_border[gr+1]; + + for (sb = ps->group_border[gr]; sb < maxsb; sb++) + { + for (n = ps->border_position[0]; n < ps->border_position[ps->num_env]; n++) + { +#ifdef FIXED_POINT + uint32_t in_re, in_im; +#endif + + /* input from hybrid subbands or QMF subbands */ + if (gr < ps->num_hybrid_groups) + { + RE(inputLeft) = QMF_RE(X_hybrid_left[n][sb]); + IM(inputLeft) = QMF_IM(X_hybrid_left[n][sb]); + } else { + RE(inputLeft) = QMF_RE(X_left[n][sb]); + IM(inputLeft) = QMF_IM(X_left[n][sb]); + } + + /* accumulate energy */ +#ifdef FIXED_POINT + /* NOTE: all input is scaled by 2^(-5) because of fixed point QMF + * meaning that P will be scaled by 2^(-10) compared to floating point version + */ + in_re = ((abs(RE(inputLeft))+(1<<(REAL_BITS-1)))>>REAL_BITS); + in_im = ((abs(IM(inputLeft))+(1<<(REAL_BITS-1)))>>REAL_BITS); + P[n][bk] += in_re*in_re + in_im*in_im; +#else + P[n][bk] += MUL_R(RE(inputLeft),RE(inputLeft)) + MUL_R(IM(inputLeft),IM(inputLeft)); +#endif + } + } + } + +#if 0 + for (n = 0; n < 32; n++) + { + for (bk = 0; bk < 34; bk++) + { +#ifdef FIXED_POINT + printf("%d %d: %d\n", n, bk, P[n][bk] /*/(float)REAL_PRECISION*/); +#else + printf("%d %d: %f\n", n, bk, P[n][bk]/1024.0); +#endif + } + } +#endif + + /* calculate transient reduction ratio for each parameter band b(k) */ + for (bk = 0; bk < ps->nr_par_bands; bk++) + { + for (n = ps->border_position[0]; n < ps->border_position[ps->num_env]; n++) + { + const real_t gamma = COEF_CONST(1.5); + + ps->P_PeakDecayNrg[bk] = MUL_F(ps->P_PeakDecayNrg[bk], ps->alpha_decay); + if (ps->P_PeakDecayNrg[bk] < P[n][bk]) + ps->P_PeakDecayNrg[bk] = P[n][bk]; + + /* apply smoothing filter to peak decay energy */ + P_SmoothPeakDecayDiffNrg = ps->P_SmoothPeakDecayDiffNrg_prev[bk]; + P_SmoothPeakDecayDiffNrg += MUL_F((ps->P_PeakDecayNrg[bk] - P[n][bk] - ps->P_SmoothPeakDecayDiffNrg_prev[bk]), ps->alpha_smooth); + ps->P_SmoothPeakDecayDiffNrg_prev[bk] = P_SmoothPeakDecayDiffNrg; + + /* apply smoothing filter to energy */ + nrg = ps->P_prev[bk]; + nrg += MUL_F((P[n][bk] - ps->P_prev[bk]), ps->alpha_smooth); + ps->P_prev[bk] = nrg; + + /* calculate transient ratio */ + if (MUL_C(P_SmoothPeakDecayDiffNrg, gamma) <= nrg) + { + G_TransientRatio[n][bk] = REAL_CONST(1.0); + } else { + G_TransientRatio[n][bk] = DIV_R(nrg, (MUL_C(P_SmoothPeakDecayDiffNrg, gamma))); + } + } + } + +#if 0 + for (n = 0; n < 32; n++) + { + for (bk = 0; bk < 34; bk++) + { +#ifdef FIXED_POINT + printf("%d %d: %f\n", n, bk, G_TransientRatio[n][bk]/(float)REAL_PRECISION); +#else + printf("%d %d: %f\n", n, bk, G_TransientRatio[n][bk]); +#endif + } + } +#endif + + /* apply stereo decorrelation filter to the signal */ + for (gr = 0; gr < ps->num_groups; gr++) + { + if (gr < ps->num_hybrid_groups) + maxsb = ps->group_border[gr] + 1; + else + maxsb = ps->group_border[gr + 1]; + + /* QMF channel */ + for (sb = ps->group_border[gr]; sb < maxsb; sb++) + { + real_t g_DecaySlope; + real_t g_DecaySlope_filt[NO_ALLPASS_LINKS]; + + /* g_DecaySlope: [0..1] */ + if (gr < ps->num_hybrid_groups || sb <= ps->decay_cutoff) + { + g_DecaySlope = FRAC_CONST(1.0); + } else { + int8_t decay = ps->decay_cutoff - sb; + if (decay <= -20 /* -1/DECAY_SLOPE */) + { + g_DecaySlope = 0; + } else { + /* decay(int)*decay_slope(frac) = g_DecaySlope(frac) */ + g_DecaySlope = FRAC_CONST(1.0) + DECAY_SLOPE * decay; + } + } + + /* calculate g_DecaySlope_filt for every n multiplied by filter_a[n] */ + for (n = 0; n < NO_ALLPASS_LINKS; n++) + { + g_DecaySlope_filt[n] = MUL_F(g_DecaySlope, filter_a[n]); + } + + + /* set delay indices */ + temp_delay = ps->saved_delay; + for (n = 0; n < NO_ALLPASS_LINKS; n++) + temp_delay_ser[n] = ps->delay_buf_index_ser[n]; + + for (n = ps->border_position[0]; n < ps->border_position[ps->num_env]; n++) + { + complex_t tmp, tmp0, R0; + uint8_t m; + + if (gr < ps->num_hybrid_groups) + { + /* hybrid filterbank input */ + RE(inputLeft) = QMF_RE(X_hybrid_left[n][sb]); + IM(inputLeft) = QMF_IM(X_hybrid_left[n][sb]); + } else { + /* QMF filterbank input */ + RE(inputLeft) = QMF_RE(X_left[n][sb]); + IM(inputLeft) = QMF_IM(X_left[n][sb]); + } + + if (sb > ps->nr_allpass_bands && gr >= ps->num_hybrid_groups) + { + /* delay */ + + /* never hybrid subbands here, always QMF subbands */ + RE(tmp) = RE(ps->delay_Qmf[ps->delay_buf_index_delay[sb]][sb]); + IM(tmp) = IM(ps->delay_Qmf[ps->delay_buf_index_delay[sb]][sb]); + RE(R0) = RE(tmp); + IM(R0) = IM(tmp); + RE(ps->delay_Qmf[ps->delay_buf_index_delay[sb]][sb]) = RE(inputLeft); + IM(ps->delay_Qmf[ps->delay_buf_index_delay[sb]][sb]) = IM(inputLeft); + } else { + /* allpass filter */ + complex_t Phi_Fract; + + /* fetch parameters */ + if (gr < ps->num_hybrid_groups) + { + /* select data from the hybrid subbands */ + RE(tmp0) = RE(ps->delay_SubQmf[temp_delay][sb]); + IM(tmp0) = IM(ps->delay_SubQmf[temp_delay][sb]); + + RE(ps->delay_SubQmf[temp_delay][sb]) = RE(inputLeft); + IM(ps->delay_SubQmf[temp_delay][sb]) = IM(inputLeft); + + RE(Phi_Fract) = RE(Phi_Fract_SubQmf[sb]); + IM(Phi_Fract) = IM(Phi_Fract_SubQmf[sb]); + } else { + /* select data from the QMF subbands */ + RE(tmp0) = RE(ps->delay_Qmf[temp_delay][sb]); + IM(tmp0) = IM(ps->delay_Qmf[temp_delay][sb]); + + RE(ps->delay_Qmf[temp_delay][sb]) = RE(inputLeft); + IM(ps->delay_Qmf[temp_delay][sb]) = IM(inputLeft); + + RE(Phi_Fract) = RE(Phi_Fract_Qmf[sb]); + IM(Phi_Fract) = IM(Phi_Fract_Qmf[sb]); + } + + /* z^(-2) * Phi_Fract[k] */ + ComplexMult(&RE(tmp), &IM(tmp), RE(tmp0), IM(tmp0), RE(Phi_Fract), IM(Phi_Fract)); + + RE(R0) = RE(tmp); + IM(R0) = IM(tmp); + for (m = 0; m < NO_ALLPASS_LINKS; m++) + { + complex_t Q_Fract_allpass, tmp2; + + /* fetch parameters */ + if (gr < ps->num_hybrid_groups) + { + /* select data from the hybrid subbands */ + RE(tmp0) = RE(ps->delay_SubQmf_ser[m][temp_delay_ser[m]][sb]); + IM(tmp0) = IM(ps->delay_SubQmf_ser[m][temp_delay_ser[m]][sb]); + + if (ps->use34hybrid_bands) + { + RE(Q_Fract_allpass) = RE(Q_Fract_allpass_SubQmf34[sb][m]); + IM(Q_Fract_allpass) = IM(Q_Fract_allpass_SubQmf34[sb][m]); + } else { + RE(Q_Fract_allpass) = RE(Q_Fract_allpass_SubQmf20[sb][m]); + IM(Q_Fract_allpass) = IM(Q_Fract_allpass_SubQmf20[sb][m]); + } + } else { + /* select data from the QMF subbands */ + RE(tmp0) = RE(ps->delay_Qmf_ser[m][temp_delay_ser[m]][sb]); + IM(tmp0) = IM(ps->delay_Qmf_ser[m][temp_delay_ser[m]][sb]); + + RE(Q_Fract_allpass) = RE(Q_Fract_allpass_Qmf[sb][m]); + IM(Q_Fract_allpass) = IM(Q_Fract_allpass_Qmf[sb][m]); + } + + /* delay by a fraction */ + /* z^(-d(m)) * Q_Fract_allpass[k,m] */ + ComplexMult(&RE(tmp), &IM(tmp), RE(tmp0), IM(tmp0), RE(Q_Fract_allpass), IM(Q_Fract_allpass)); + + /* -a(m) * g_DecaySlope[k] */ + RE(tmp) += -MUL_F(g_DecaySlope_filt[m], RE(R0)); + IM(tmp) += -MUL_F(g_DecaySlope_filt[m], IM(R0)); + + /* -a(m) * g_DecaySlope[k] * Q_Fract_allpass[k,m] * z^(-d(m)) */ + RE(tmp2) = RE(R0) + MUL_F(g_DecaySlope_filt[m], RE(tmp)); + IM(tmp2) = IM(R0) + MUL_F(g_DecaySlope_filt[m], IM(tmp)); + + /* store sample */ + if (gr < ps->num_hybrid_groups) + { + RE(ps->delay_SubQmf_ser[m][temp_delay_ser[m]][sb]) = RE(tmp2); + IM(ps->delay_SubQmf_ser[m][temp_delay_ser[m]][sb]) = IM(tmp2); + } else { + RE(ps->delay_Qmf_ser[m][temp_delay_ser[m]][sb]) = RE(tmp2); + IM(ps->delay_Qmf_ser[m][temp_delay_ser[m]][sb]) = IM(tmp2); + } + + /* store for next iteration (or as output value if last iteration) */ + RE(R0) = RE(tmp); + IM(R0) = IM(tmp); + } + } + + /* select b(k) for reading the transient ratio */ + bk = (~NEGATE_IPD_MASK) & ps->map_group2bk[gr]; + + /* duck if a past transient is found */ + RE(R0) = MUL_R(G_TransientRatio[n][bk], RE(R0)); + IM(R0) = MUL_R(G_TransientRatio[n][bk], IM(R0)); + + if (gr < ps->num_hybrid_groups) + { + /* hybrid */ + QMF_RE(X_hybrid_right[n][sb]) = RE(R0); + QMF_IM(X_hybrid_right[n][sb]) = IM(R0); + } else { + /* QMF */ + QMF_RE(X_right[n][sb]) = RE(R0); + QMF_IM(X_right[n][sb]) = IM(R0); + } + + /* Update delay buffer index */ + if (++temp_delay >= 2) + { + temp_delay = 0; + } + + /* update delay indices */ + if (sb > ps->nr_allpass_bands && gr >= ps->num_hybrid_groups) + { + /* delay_D depends on the samplerate, it can hold the values 14 and 1 */ + if (++ps->delay_buf_index_delay[sb] >= ps->delay_D[sb]) + { + ps->delay_buf_index_delay[sb] = 0; + } + } + + for (m = 0; m < NO_ALLPASS_LINKS; m++) + { + if (++temp_delay_ser[m] >= ps->num_sample_delay_ser[m]) + { + temp_delay_ser[m] = 0; + } + } + } + } + } + + /* update delay indices */ + ps->saved_delay = temp_delay; + for (n = 0; n < NO_ALLPASS_LINKS; n++) + ps->delay_buf_index_ser[n] = temp_delay_ser[n]; +} + +#if 0 +#ifdef FIXED_POINT +#define step(shift) \ + if ((0x40000000l >> shift) + root <= value) \ + { \ + value -= (0x40000000l >> shift) + root; \ + root = (root >> 1) | (0x40000000l >> shift); \ + } else { \ + root = root >> 1; \ + } + +/* fixed point square root approximation */ +static real_t ps_sqrt(real_t value) +{ + real_t root = 0; + + step( 0); step( 2); step( 4); step( 6); + step( 8); step(10); step(12); step(14); + step(16); step(18); step(20); step(22); + step(24); step(26); step(28); step(30); + + if (root < value) + ++root; + + root <<= (REAL_BITS/2); + + return root; +} +#else +#define ps_sqrt(A) sqrt(A) +#endif +#endif + +static const real_t ipdopd_cos_tab[] = { + FRAC_CONST(1.000000000000000), + FRAC_CONST(0.707106781186548), + FRAC_CONST(0.000000000000000), + FRAC_CONST(-0.707106781186547), + FRAC_CONST(-1.000000000000000), + FRAC_CONST(-0.707106781186548), + FRAC_CONST(-0.000000000000000), + FRAC_CONST(0.707106781186547), + FRAC_CONST(1.000000000000000) +}; + +static const real_t ipdopd_sin_tab[] = { + FRAC_CONST(0.000000000000000), + FRAC_CONST(0.707106781186547), + FRAC_CONST(1.000000000000000), + FRAC_CONST(0.707106781186548), + FRAC_CONST(0.000000000000000), + FRAC_CONST(-0.707106781186547), + FRAC_CONST(-1.000000000000000), + FRAC_CONST(-0.707106781186548), + FRAC_CONST(-0.000000000000000) +}; + +static real_t magnitude_c(complex_t c) +{ +#ifdef FIXED_POINT +#define ps_abs(A) (((A) > 0) ? (A) : (-(A))) +#define ALPHA FRAC_CONST(0.948059448969) +#define BETA FRAC_CONST(0.392699081699) + + real_t abs_inphase = ps_abs(RE(c)); + real_t abs_quadrature = ps_abs(IM(c)); + + if (abs_inphase > abs_quadrature) { + return MUL_F(abs_inphase, ALPHA) + MUL_F(abs_quadrature, BETA); + } else { + return MUL_F(abs_quadrature, ALPHA) + MUL_F(abs_inphase, BETA); + } +#else + return sqrt(RE(c)*RE(c) + IM(c)*IM(c)); +#endif +} + +static void ps_mix_phase(ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64], + qmf_t X_hybrid_left[32][32], qmf_t X_hybrid_right[32][32]) +{ + uint8_t n; + uint8_t gr; + uint8_t bk = 0; + uint8_t sb, maxsb; + uint8_t env; + uint8_t nr_ipdopd_par; + complex_t h11, h12, h21, h22; // COEF + complex_t H11, H12, H21, H22; // COEF + complex_t deltaH11, deltaH12, deltaH21, deltaH22; // COEF + complex_t tempLeft, tempRight; // FRAC + complex_t phaseLeft, phaseRight; // FRAC + real_t L; + const real_t *sf_iid; + uint8_t no_iid_steps; + + if (ps->iid_mode >= 3) + { + no_iid_steps = 15; + sf_iid = sf_iid_fine; + } else { + no_iid_steps = 7; + sf_iid = sf_iid_normal; + } + + if (ps->ipd_mode == 0 || ps->ipd_mode == 3) + { + nr_ipdopd_par = 11; /* resolution */ + } else { + nr_ipdopd_par = ps->nr_ipdopd_par; + } + + for (gr = 0; gr < ps->num_groups; gr++) + { + bk = (~NEGATE_IPD_MASK) & ps->map_group2bk[gr]; + + /* use one channel per group in the subqmf domain */ + maxsb = (gr < ps->num_hybrid_groups) ? ps->group_border[gr] + 1 : ps->group_border[gr + 1]; + + for (env = 0; env < ps->num_env; env++) + { + uint8_t abs_iid = (uint8_t)abs(ps->iid_index[env][bk]); + /* index range is supposed to be -7...7 or -15...15 depending on iid_mode + (Table 8.24, ISO/IEC 14496-3:2005). + if it is outside these boundaries, this is most likely an error. sanitize + it and try to process further. */ + if (ps->iid_index[env][bk] < -no_iid_steps) { + fprintf(stderr, "Warning: invalid iid_index: %d < %d\n", ps->iid_index[env][bk], + -no_iid_steps); + ps->iid_index[env][bk] = -no_iid_steps; + abs_iid = no_iid_steps; + } else if (ps->iid_index[env][bk] > no_iid_steps) { + fprintf(stderr, "Warning: invalid iid_index: %d > %d\n", ps->iid_index[env][bk], + no_iid_steps); + ps->iid_index[env][bk] = no_iid_steps; + abs_iid = no_iid_steps; + } + if (ps->icc_index[env][bk] < 0) { + fprintf(stderr, "Warning: invalid icc_index: %d < 0\n", ps->icc_index[env][bk]); + ps->icc_index[env][bk] = 0; + } else if (ps->icc_index[env][bk] > 7) { + fprintf(stderr, "Warning: invalid icc_index: %d > 7\n", ps->icc_index[env][bk]); + ps->icc_index[env][bk] = 7; + } + + if (ps->icc_mode < 3) + { + /* type 'A' mixing as described in 8.6.4.6.2.1 */ + real_t c_1, c_2; // COEF + real_t cosa, sina; // COEF + real_t cosb, sinb; // COEF + real_t ab1, ab2; // COEF + real_t ab3, ab4; // COEF + + /* + c_1 = sqrt(2.0 / (1.0 + pow(10.0, quant_iid[no_iid_steps + iid_index] / 10.0))); + c_2 = sqrt(2.0 / (1.0 + pow(10.0, quant_iid[no_iid_steps - iid_index] / 10.0))); + alpha = 0.5 * acos(quant_rho[icc_index]); + beta = alpha * ( c_1 - c_2 ) / sqrt(2.0); + */ + + //printf("%d\n", ps->iid_index[env][bk]); + + /* calculate the scalefactors c_1 and c_2 from the intensity differences */ + c_1 = sf_iid[no_iid_steps + ps->iid_index[env][bk]]; + c_2 = sf_iid[no_iid_steps - ps->iid_index[env][bk]]; + + /* calculate alpha and beta using the ICC parameters */ + cosa = cos_alphas[ps->icc_index[env][bk]]; + sina = sin_alphas[ps->icc_index[env][bk]]; + + if (ps->iid_mode >= 3) + { + cosb = cos_betas_fine[abs_iid][ps->icc_index[env][bk]]; + sinb = sin_betas_fine[abs_iid][ps->icc_index[env][bk]]; + } else { + cosb = cos_betas_normal[abs_iid][ps->icc_index[env][bk]]; + sinb = sin_betas_normal[abs_iid][ps->icc_index[env][bk]]; + } + + ab1 = MUL_C(cosb, cosa); + ab2 = MUL_C(sinb, sina); + ab3 = MUL_C(sinb, cosa); + ab4 = MUL_C(cosb, sina); + + /* h_xy: COEF */ + RE(h11) = MUL_C(c_2, (ab1 - ab2)); + RE(h12) = MUL_C(c_1, (ab1 + ab2)); + RE(h21) = MUL_C(c_2, (ab3 + ab4)); + RE(h22) = MUL_C(c_1, (ab3 - ab4)); + } else { + /* type 'B' mixing as described in 8.6.4.6.2.2 */ + real_t sina, cosa; // COEF + real_t cosg, sing; // COEF + + /* + real_t c, rho, mu, alpha, gamma; + uint8_t i; + + i = ps->iid_index[env][bk]; + c = (real_t)pow(10.0, ((i)?(((i>0)?1:-1)*quant_iid[((i>0)?i:-i)-1]):0.)/20.0); + rho = quant_rho[ps->icc_index[env][bk]]; + + if (rho == 0.0f && c == 1.) + { + alpha = (real_t)M_PI/4.0f; + rho = 0.05f; + } else { + if (rho <= 0.05f) + { + rho = 0.05f; + } + alpha = 0.5f*(real_t)atan( (2.0f*c*rho) / (c*c-1.0f) ); + + if (alpha < 0.) + { + alpha += (real_t)M_PI/2.0f; + } + if (rho < 0.) + { + alpha += (real_t)M_PI; + } + } + mu = c+1.0f/c; + mu = 1+(4.0f*rho*rho-4.0f)/(mu*mu); + gamma = (real_t)atan(sqrt((1.0f-sqrt(mu))/(1.0f+sqrt(mu)))); + */ + + if (ps->iid_mode >= 3) + { + cosa = sincos_alphas_B_fine[no_iid_steps + ps->iid_index[env][bk]][ps->icc_index[env][bk]]; + sina = sincos_alphas_B_fine[30 - (no_iid_steps + ps->iid_index[env][bk])][ps->icc_index[env][bk]]; + cosg = cos_gammas_fine[abs_iid][ps->icc_index[env][bk]]; + sing = sin_gammas_fine[abs_iid][ps->icc_index[env][bk]]; + } else { + cosa = sincos_alphas_B_normal[no_iid_steps + ps->iid_index[env][bk]][ps->icc_index[env][bk]]; + sina = sincos_alphas_B_normal[14 - (no_iid_steps + ps->iid_index[env][bk])][ps->icc_index[env][bk]]; + cosg = cos_gammas_normal[abs_iid][ps->icc_index[env][bk]]; + sing = sin_gammas_normal[abs_iid][ps->icc_index[env][bk]]; + } + + RE(h11) = MUL_C(COEF_SQRT2, MUL_C(cosa, cosg)); + RE(h12) = MUL_C(COEF_SQRT2, MUL_C(sina, cosg)); + RE(h21) = MUL_C(COEF_SQRT2, MUL_C(-cosa, sing)); + RE(h22) = MUL_C(COEF_SQRT2, MUL_C(sina, sing)); + } + IM(h11) = IM(h12) = IM(h21) = IM(h22) = 0; + + /* calculate phase rotation parameters H_xy */ + /* note that the imaginary part of these parameters are only calculated when + IPD and OPD are enabled + */ + if ((ps->enable_ipdopd) && (bk < nr_ipdopd_par)) + { + int8_t i; + real_t xy, pq, xypq; // FRAC + + /* ringbuffer index */ + i = ps->phase_hist; + + /* previous value */ +#ifdef FIXED_POINT + /* divide by 4*2, shift right 3 bits; + extra halving to avoid overflows; it is ok, because result is normalized */ + RE(tempLeft) = RE(ps->ipd_prev[bk][i]) >> 3; + IM(tempLeft) = IM(ps->ipd_prev[bk][i]) >> 3; + RE(tempRight) = RE(ps->opd_prev[bk][i]) >> 3; + IM(tempRight) = IM(ps->opd_prev[bk][i]) >> 3; +#else + RE(tempLeft) = MUL_F(RE(ps->ipd_prev[bk][i]), FRAC_CONST(0.25)); + IM(tempLeft) = MUL_F(IM(ps->ipd_prev[bk][i]), FRAC_CONST(0.25)); + RE(tempRight) = MUL_F(RE(ps->opd_prev[bk][i]), FRAC_CONST(0.25)); + IM(tempRight) = MUL_F(IM(ps->opd_prev[bk][i]), FRAC_CONST(0.25)); +#endif + + /* save current value */ + RE(ps->ipd_prev[bk][i]) = ipdopd_cos_tab[abs(ps->ipd_index[env][bk])]; + IM(ps->ipd_prev[bk][i]) = ipdopd_sin_tab[abs(ps->ipd_index[env][bk])]; + RE(ps->opd_prev[bk][i]) = ipdopd_cos_tab[abs(ps->opd_index[env][bk])]; + IM(ps->opd_prev[bk][i]) = ipdopd_sin_tab[abs(ps->opd_index[env][bk])]; + + /* add current value */ +#ifdef FIXED_POINT + /* extra halving to avoid overflows */ + RE(tempLeft) += RE(ps->ipd_prev[bk][i]) >> 1; + IM(tempLeft) += IM(ps->ipd_prev[bk][i]) >> 1; + RE(tempRight) += RE(ps->opd_prev[bk][i]) >> 1; + IM(tempRight) += IM(ps->opd_prev[bk][i]) >> 1; +#else + RE(tempLeft) += RE(ps->ipd_prev[bk][i]); + IM(tempLeft) += IM(ps->ipd_prev[bk][i]); + RE(tempRight) += RE(ps->opd_prev[bk][i]); + IM(tempRight) += IM(ps->opd_prev[bk][i]); +#endif + + /* ringbuffer index */ + if (i == 0) + { + i = 2; + } + i--; + + /* get value before previous */ +#ifdef FIXED_POINT + /* dividing by 2*2, shift right 2 bits; extra halving to avoid overflows */ + RE(tempLeft) += (RE(ps->ipd_prev[bk][i]) >> 2); + IM(tempLeft) += (IM(ps->ipd_prev[bk][i]) >> 2); + RE(tempRight) += (RE(ps->opd_prev[bk][i]) >> 2); + IM(tempRight) += (IM(ps->opd_prev[bk][i]) >> 2); +#else + RE(tempLeft) += MUL_F(RE(ps->ipd_prev[bk][i]), FRAC_CONST(0.5)); + IM(tempLeft) += MUL_F(IM(ps->ipd_prev[bk][i]), FRAC_CONST(0.5)); + RE(tempRight) += MUL_F(RE(ps->opd_prev[bk][i]), FRAC_CONST(0.5)); + IM(tempRight) += MUL_F(IM(ps->opd_prev[bk][i]), FRAC_CONST(0.5)); +#endif + +#if 0 /* original code */ + ipd = (float)atan2(IM(tempLeft), RE(tempLeft)); + opd = (float)atan2(IM(tempRight), RE(tempRight)); + + /* phase rotation */ + RE(phaseLeft) = (float)cos(opd); + IM(phaseLeft) = (float)sin(opd); + opd -= ipd; + RE(phaseRight) = (float)cos(opd); + IM(phaseRight) = (float)sin(opd); +#else + + // x = IM(tempLeft) + // y = RE(tempLeft) + // p = IM(tempRight) + // q = RE(tempRight) + // cos(atan2(x,y)) = y/sqrt((x*x) + (y*y)) + // sin(atan2(x,y)) = x/sqrt((x*x) + (y*y)) + // cos(atan2(x,y)-atan2(p,q)) = (y*q + x*p) / ( sqrt((x*x) + (y*y)) * sqrt((p*p) + (q*q)) ); + // sin(atan2(x,y)-atan2(p,q)) = (x*q - y*p) / ( sqrt((x*x) + (y*y)) * sqrt((p*p) + (q*q)) ); + + xy = magnitude_c(tempRight); + pq = magnitude_c(tempLeft); + + if (xy != 0) + { + RE(phaseLeft) = DIV_F(RE(tempRight), xy); + IM(phaseLeft) = DIV_F(IM(tempRight), xy); + } else { + RE(phaseLeft) = 0; + IM(phaseLeft) = 0; + } + + xypq = MUL_F(xy, pq); + + if (xypq != 0) + { + real_t tmp1 = MUL_F(RE(tempRight), RE(tempLeft)) + MUL_F(IM(tempRight), IM(tempLeft)); + real_t tmp2 = MUL_F(IM(tempRight), RE(tempLeft)) - MUL_F(RE(tempRight), IM(tempLeft)); + + RE(phaseRight) = DIV_F(tmp1, xypq); + IM(phaseRight) = DIV_F(tmp2, xypq); + } else { + RE(phaseRight) = 0; + IM(phaseRight) = 0; + } + +#endif + + /* MUL_F(COEF, REAL) = COEF */ + IM(h11) = MUL_F(RE(h11), IM(phaseLeft)); + IM(h12) = MUL_F(RE(h12), IM(phaseRight)); + IM(h21) = MUL_F(RE(h21), IM(phaseLeft)); + IM(h22) = MUL_F(RE(h22), IM(phaseRight)); + + RE(h11) = MUL_F(RE(h11), RE(phaseLeft)); + RE(h12) = MUL_F(RE(h12), RE(phaseRight)); + RE(h21) = MUL_F(RE(h21), RE(phaseLeft)); + RE(h22) = MUL_F(RE(h22), RE(phaseRight)); + } + + /* length of the envelope n_e+1 - n_e (in time samples) */ + /* 0 < L <= 32: integer */ + L = (real_t)(ps->border_position[env + 1] - ps->border_position[env]); + + /* obtain final H_xy by means of linear interpolation */ + RE(deltaH11) = (RE(h11) - RE(ps->h11_prev[gr])) / L; + RE(deltaH12) = (RE(h12) - RE(ps->h12_prev[gr])) / L; + RE(deltaH21) = (RE(h21) - RE(ps->h21_prev[gr])) / L; + RE(deltaH22) = (RE(h22) - RE(ps->h22_prev[gr])) / L; + + RE(H11) = RE(ps->h11_prev[gr]); + RE(H12) = RE(ps->h12_prev[gr]); + RE(H21) = RE(ps->h21_prev[gr]); + RE(H22) = RE(ps->h22_prev[gr]); + IM(H11) = IM(H12) = IM(H21) = IM(H22) = 0; + + RE(ps->h11_prev[gr]) = RE(h11); + RE(ps->h12_prev[gr]) = RE(h12); + RE(ps->h21_prev[gr]) = RE(h21); + RE(ps->h22_prev[gr]) = RE(h22); + + /* only calculate imaginary part when needed */ + if ((ps->enable_ipdopd) && (bk < nr_ipdopd_par)) + { + /* obtain final H_xy by means of linear interpolation */ + IM(deltaH11) = (IM(h11) - IM(ps->h11_prev[gr])) / L; + IM(deltaH12) = (IM(h12) - IM(ps->h12_prev[gr])) / L; + IM(deltaH21) = (IM(h21) - IM(ps->h21_prev[gr])) / L; + IM(deltaH22) = (IM(h22) - IM(ps->h22_prev[gr])) / L; + + IM(H11) = IM(ps->h11_prev[gr]); + IM(H12) = IM(ps->h12_prev[gr]); + IM(H21) = IM(ps->h21_prev[gr]); + IM(H22) = IM(ps->h22_prev[gr]); + + if ((NEGATE_IPD_MASK & ps->map_group2bk[gr]) != 0) + { + IM(deltaH11) = -IM(deltaH11); + IM(deltaH12) = -IM(deltaH12); + IM(deltaH21) = -IM(deltaH21); + IM(deltaH22) = -IM(deltaH22); + + IM(H11) = -IM(H11); + IM(H12) = -IM(H12); + IM(H21) = -IM(H21); + IM(H22) = -IM(H22); + } + + IM(ps->h11_prev[gr]) = IM(h11); + IM(ps->h12_prev[gr]) = IM(h12); + IM(ps->h21_prev[gr]) = IM(h21); + IM(ps->h22_prev[gr]) = IM(h22); + } + + /* apply H_xy to the current envelope band of the decorrelated subband */ + for (n = ps->border_position[env]; n < ps->border_position[env + 1]; n++) + { + /* addition finalises the interpolation over every n */ + RE(H11) += RE(deltaH11); + RE(H12) += RE(deltaH12); + RE(H21) += RE(deltaH21); + RE(H22) += RE(deltaH22); + if ((ps->enable_ipdopd) && (bk < nr_ipdopd_par)) + { + IM(H11) += IM(deltaH11); + IM(H12) += IM(deltaH12); + IM(H21) += IM(deltaH21); + IM(H22) += IM(deltaH22); + } + + /* channel is an alias to the subband */ + for (sb = ps->group_border[gr]; sb < maxsb; sb++) + { + complex_t inLeft, inRight; // precision_of in(Left|Right) == precision_of X_(left|right) + + /* load decorrelated samples */ + if (gr < ps->num_hybrid_groups) + { + RE(inLeft) = RE(X_hybrid_left[n][sb]); + IM(inLeft) = IM(X_hybrid_left[n][sb]); + RE(inRight) = RE(X_hybrid_right[n][sb]); + IM(inRight) = IM(X_hybrid_right[n][sb]); + } else { + RE(inLeft) = RE(X_left[n][sb]); + IM(inLeft) = IM(X_left[n][sb]); + RE(inRight) = RE(X_right[n][sb]); + IM(inRight) = IM(X_right[n][sb]); + } + + /* precision_of temp(Left|Right) == precision_of X_(left|right) */ + + /* apply mixing */ + RE(tempLeft) = MUL_C(RE(H11), RE(inLeft)) + MUL_C(RE(H21), RE(inRight)); + IM(tempLeft) = MUL_C(RE(H11), IM(inLeft)) + MUL_C(RE(H21), IM(inRight)); + RE(tempRight) = MUL_C(RE(H12), RE(inLeft)) + MUL_C(RE(H22), RE(inRight)); + IM(tempRight) = MUL_C(RE(H12), IM(inLeft)) + MUL_C(RE(H22), IM(inRight)); + + /* only perform imaginary operations when needed */ + if ((ps->enable_ipdopd) && (bk < nr_ipdopd_par)) + { + /* apply rotation */ + RE(tempLeft) -= MUL_C(IM(H11), IM(inLeft)) + MUL_C(IM(H21), IM(inRight)); + IM(tempLeft) += MUL_C(IM(H11), RE(inLeft)) + MUL_C(IM(H21), RE(inRight)); + RE(tempRight) -= MUL_C(IM(H12), IM(inLeft)) + MUL_C(IM(H22), IM(inRight)); + IM(tempRight) += MUL_C(IM(H12), RE(inLeft)) + MUL_C(IM(H22), RE(inRight)); + } + + /* store final samples */ + if (gr < ps->num_hybrid_groups) + { + RE(X_hybrid_left[n][sb]) = RE(tempLeft); + IM(X_hybrid_left[n][sb]) = IM(tempLeft); + RE(X_hybrid_right[n][sb]) = RE(tempRight); + IM(X_hybrid_right[n][sb]) = IM(tempRight); + } else { + RE(X_left[n][sb]) = RE(tempLeft); + IM(X_left[n][sb]) = IM(tempLeft); + RE(X_right[n][sb]) = RE(tempRight); + IM(X_right[n][sb]) = IM(tempRight); + } + } + } + + /* shift phase smoother's circular buffer index */ + ps->phase_hist++; + if (ps->phase_hist == 2) + { + ps->phase_hist = 0; + } + } + } +} + +void ps_free(ps_info *ps) +{ + /* free hybrid filterbank structures */ + hybrid_free(ps->hyb); + + faad_free(ps); +} + +ps_info *ps_init(uint8_t sr_index, uint8_t numTimeSlotsRate) +{ + uint8_t i; + uint8_t short_delay_band; + + ps_info *ps = (ps_info*)faad_malloc(sizeof(ps_info)); + memset(ps, 0, sizeof(ps_info)); + + ps->hyb = hybrid_init(numTimeSlotsRate); + ps->numTimeSlotsRate = numTimeSlotsRate; + + ps->ps_data_available = 0; + + /* delay stuff*/ + ps->saved_delay = 0; + + for (i = 0; i < 64; i++) + { + ps->delay_buf_index_delay[i] = 0; + } + + for (i = 0; i < NO_ALLPASS_LINKS; i++) + { + ps->delay_buf_index_ser[i] = 0; +#ifdef PARAM_32KHZ + if (sr_index <= 5) /* >= 32 kHz*/ + { + ps->num_sample_delay_ser[i] = delay_length_d[1][i]; + } else { + ps->num_sample_delay_ser[i] = delay_length_d[0][i]; + } +#else + (void)sr_index; + /* THESE ARE CONSTANTS NOW */ + ps->num_sample_delay_ser[i] = delay_length_d[i]; +#endif + } + +#ifdef PARAM_32KHZ + if (sr_index <= 5) /* >= 32 kHz*/ + { + short_delay_band = 35; + ps->nr_allpass_bands = 22; + ps->alpha_decay = FRAC_CONST(0.76592833836465); + ps->alpha_smooth = FRAC_CONST(0.25); + } else { + short_delay_band = 64; + ps->nr_allpass_bands = 45; + ps->alpha_decay = FRAC_CONST(0.58664621951003); + ps->alpha_smooth = FRAC_CONST(0.6); + } +#else + /* THESE ARE CONSTANTS NOW */ + short_delay_band = 35; + ps->nr_allpass_bands = 22; + ps->alpha_decay = FRAC_CONST(0.76592833836465); + ps->alpha_smooth = FRAC_CONST(0.25); +#endif + + /* THESE ARE CONSTANT NOW IF PS IS INDEPENDANT OF SAMPLERATE */ + for (i = 0; i < short_delay_band; i++) + { + ps->delay_D[i] = 14; + } + for (i = short_delay_band; i < 64; i++) + { + ps->delay_D[i] = 1; + } + + /* mixing and phase */ + for (i = 0; i < 50; i++) + { + RE(ps->h11_prev[i]) = 1; + IM(ps->h11_prev[i]) = 1; + RE(ps->h12_prev[i]) = 1; + IM(ps->h12_prev[i]) = 1; + } + + ps->phase_hist = 0; + + for (i = 0; i < 20; i++) + { + RE(ps->ipd_prev[i][0]) = 0; + IM(ps->ipd_prev[i][0]) = 0; + RE(ps->ipd_prev[i][1]) = 0; + IM(ps->ipd_prev[i][1]) = 0; + RE(ps->opd_prev[i][0]) = 0; + IM(ps->opd_prev[i][0]) = 0; + RE(ps->opd_prev[i][1]) = 0; + IM(ps->opd_prev[i][1]) = 0; + } + + return ps; +} + +/* main Parametric Stereo decoding function */ +uint8_t ps_decode(ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64]) +{ + qmf_t X_hybrid_left[32][32] = {{{0}}}; + qmf_t X_hybrid_right[32][32] = {{{0}}}; + + /* delta decoding of the bitstream data */ + ps_data_decode(ps); + + /* set up some parameters depending on filterbank type */ + if (ps->use34hybrid_bands) + { + ps->group_border = (uint8_t*)group_border34; + ps->map_group2bk = (uint16_t*)map_group2bk34; + ps->num_groups = 32+18; + ps->num_hybrid_groups = 32; + ps->nr_par_bands = 34; + ps->decay_cutoff = 5; + } else { + ps->group_border = (uint8_t*)group_border20; + ps->map_group2bk = (uint16_t*)map_group2bk20; + ps->num_groups = 10+12; + ps->num_hybrid_groups = 10; + ps->nr_par_bands = 20; + ps->decay_cutoff = 3; + } + + /* Perform further analysis on the lowest subbands to get a higher + * frequency resolution + */ + hybrid_analysis((hyb_info*)ps->hyb, X_left, X_hybrid_left, + ps->use34hybrid_bands, ps->numTimeSlotsRate); + + /* decorrelate mono signal */ + ps_decorrelate(ps, X_left, X_right, X_hybrid_left, X_hybrid_right); + + /* apply mixing and phase parameters */ + ps_mix_phase(ps, X_left, X_right, X_hybrid_left, X_hybrid_right); + + /* hybrid synthesis, to rebuild the SBR QMF matrices */ + hybrid_synthesis((hyb_info*)ps->hyb, X_left, X_hybrid_left, + ps->use34hybrid_bands, ps->numTimeSlotsRate); + + hybrid_synthesis((hyb_info*)ps->hyb, X_right, X_hybrid_right, + ps->use34hybrid_bands, ps->numTimeSlotsRate); + + return 0; +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ps_dec.h b/third_party/faad_hdc/upstream/libfaad/ps_dec.h new file mode 100644 index 000000000..fb02a3d96 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ps_dec.h @@ -0,0 +1,155 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ps_dec.h,v 1.13 2009/01/26 22:32:31 menno Exp $ +**/ + +#ifndef __PS_DEC_H__ +#define __PS_DEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" + +#define EXTENSION_ID_PS 2 + +#define MAX_PS_ENVELOPES 5 +#define NO_ALLPASS_LINKS 3 + +typedef struct +{ + /* bitstream parameters */ + uint8_t enable_iid; + uint8_t enable_icc; + uint8_t enable_ext; + + uint8_t iid_mode; + uint8_t icc_mode; + uint8_t nr_iid_par; + uint8_t nr_ipdopd_par; + uint8_t nr_icc_par; + + uint8_t frame_class; + uint8_t num_env; + + uint8_t border_position[MAX_PS_ENVELOPES+1]; + + uint8_t iid_dt[MAX_PS_ENVELOPES]; + uint8_t icc_dt[MAX_PS_ENVELOPES]; + + uint8_t enable_ipdopd; + uint8_t ipd_mode; + uint8_t ipd_dt[MAX_PS_ENVELOPES]; + uint8_t opd_dt[MAX_PS_ENVELOPES]; + + /* indices */ + int8_t iid_index_prev[34]; + int8_t icc_index_prev[34]; + int8_t ipd_index_prev[17]; + int8_t opd_index_prev[17]; + int8_t iid_index[MAX_PS_ENVELOPES][34]; + int8_t icc_index[MAX_PS_ENVELOPES][34]; + int8_t ipd_index[MAX_PS_ENVELOPES][17]; + int8_t opd_index[MAX_PS_ENVELOPES][17]; + + int8_t ipd_index_1[17]; + int8_t opd_index_1[17]; + int8_t ipd_index_2[17]; + int8_t opd_index_2[17]; + + /* ps data was correctly read */ + uint8_t ps_data_available; + + /* a header has been read */ + uint8_t header_read; + + /**/ + uint8_t num_groups; + uint8_t num_hybrid_groups; + uint8_t nr_par_bands; + uint8_t nr_allpass_bands; + uint8_t decay_cutoff; + + /* filter delay handling */ + uint8_t saved_delay; + uint8_t delay_buf_index_ser[NO_ALLPASS_LINKS]; + uint8_t num_sample_delay_ser[NO_ALLPASS_LINKS]; + uint8_t delay_D[64]; + uint8_t delay_buf_index_delay[64]; + + /* mixing and phase */ + uint8_t phase_hist; + + /* hybrid filterbank parameters */ + uint8_t use34hybrid_bands; + uint8_t numTimeSlotsRate; + + uint8_t *group_border; + uint16_t *map_group2bk; + + complex_t delay_Qmf[14][64]; /* 14 samples delay max, 64 QMF channels */ + complex_t delay_SubQmf[2][32]; /* 2 samples delay max (SubQmf is always allpass filtered) */ + complex_t delay_Qmf_ser[NO_ALLPASS_LINKS][5][64]; /* 5 samples delay max (table 8.34), 64 QMF channels */ + complex_t delay_SubQmf_ser[NO_ALLPASS_LINKS][5][32]; /* 5 samples delay max (table 8.34) */ + + /* transients */ + real_t alpha_decay; + real_t alpha_smooth; + + real_t P_PeakDecayNrg[34]; + real_t P_prev[34]; + real_t P_SmoothPeakDecayDiffNrg_prev[34]; + + /* mixing and phase */ + complex_t h11_prev[50]; + complex_t h12_prev[50]; + complex_t h21_prev[50]; + complex_t h22_prev[50]; + complex_t ipd_prev[20][2]; + complex_t opd_prev[20][2]; + + /* hybrid filterbank parameters */ + void *hyb; +} ps_info; + +/* ps_syntax.c */ +uint16_t ps_data(ps_info *ps, bitfile *ld, uint8_t *header); + +/* ps_dec.c */ +ps_info *ps_init(uint8_t sr_index, uint8_t numTimeSlotsRate); +void ps_free(ps_info *ps); + +uint8_t ps_decode(ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64]); + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/ps_syntax.c b/third_party/faad_hdc/upstream/libfaad/ps_syntax.c new file mode 100644 index 000000000..09992a8d0 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ps_syntax.c @@ -0,0 +1,552 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ps_syntax.c,v 1.11 2007/11/01 12:33:33 menno Exp $ +**/ + +#include "common.h" + +#ifdef PS_DEC + +#include "bits.h" +#include "ps_dec.h" + +/* type definitaions */ +typedef const int8_t (*ps_huff_tab)[2]; + +/* static data tables */ +static const uint8_t nr_iid_par_tab[] = { + 10, 20, 34, 10, 20, 34, 0, 0 +}; +static const uint8_t nr_ipdopd_par_tab[] = { + 5, 11, 17, 5, 11, 17, 0, 0 +}; +static const uint8_t nr_icc_par_tab[] = { + 10, 20, 34, 10, 20, 34, 0, 0 +}; +static const uint8_t num_env_tab[][4] = { + { 0, 1, 2, 4 }, + { 1, 2, 3, 4 } +}; + +/* binary lookup huffman tables */ +static const int8_t f_huff_iid_def[][2] = { + { /*0*/ -31, 1 }, /* index 0: 1 bits: x */ + { 2, 3 }, /* index 1: 2 bits: 1x */ + { /*1*/ -30, /*-1*/ -32 }, /* index 2: 3 bits: 10x */ + { 4, 5 }, /* index 3: 3 bits: 11x */ + { /*2*/ -29, /*-2*/ -33 }, /* index 4: 4 bits: 110x */ + { 6, 7 }, /* index 5: 4 bits: 111x */ + { /*3*/ -28, /*-3*/ -34 }, /* index 6: 5 bits: 1110x */ + { 8, 9 }, /* index 7: 5 bits: 1111x */ + { /*-4*/ -35, /*4*/ -27 }, /* index 8: 6 bits: 11110x */ + { /*5*/ -26, 10 }, /* index 9: 6 bits: 11111x */ + { /*-5*/ -36, 11 }, /* index 10: 7 bits: 111111x */ + { /*6*/ -25, 12 }, /* index 11: 8 bits: 1111111x */ + { /*-6*/ -37, 13 }, /* index 12: 9 bits: 11111111x */ + { /*-7*/ -38, 14 }, /* index 13: 10 bits: 111111111x */ + { /*7*/ -24, 15 }, /* index 14: 11 bits: 1111111111x */ + { 16, 17 }, /* index 15: 12 bits: 11111111111x */ + { /*8*/ -23, /*-8*/ -39 }, /* index 16: 13 bits: 111111111110x */ + { 18, 19 }, /* index 17: 13 bits: 111111111111x */ + { /*9*/ -22, /*10*/ -21 }, /* index 18: 14 bits: 1111111111110x */ + { 20, 21 }, /* index 19: 14 bits: 1111111111111x */ + { /*-9*/ -40, /*11*/ -20 }, /* index 20: 15 bits: 11111111111110x */ + { 22, 23 }, /* index 21: 15 bits: 11111111111111x */ + { /*-10*/ -41, 24 }, /* index 22: 16 bits: 111111111111110x */ + { 25, 26 }, /* index 23: 16 bits: 111111111111111x */ + { /*-11*/ -42, /*-14*/ -45 }, /* index 24: 17 bits: 1111111111111101x */ + { /*-13*/ -44, /*-12*/ -43 }, /* index 25: 17 bits: 1111111111111110x */ + { /*12*/ -19, 27 }, /* index 26: 17 bits: 1111111111111111x */ + { /*13*/ -18, /*14*/ -17 } /* index 27: 18 bits: 11111111111111111x */ +}; + +static const int8_t t_huff_iid_def[][2] = { + { /*0*/ -31, 1 }, /* index 0: 1 bits: x */ + { /*-1*/ -32, 2 }, /* index 1: 2 bits: 1x */ + { /*1*/ -30, 3 }, /* index 2: 3 bits: 11x */ + { /*-2*/ -33, 4 }, /* index 3: 4 bits: 111x */ + { /*2*/ -29, 5 }, /* index 4: 5 bits: 1111x */ + { /*-3*/ -34, 6 }, /* index 5: 6 bits: 11111x */ + { /*3*/ -28, 7 }, /* index 6: 7 bits: 111111x */ + { /*-4*/ -35, 8 }, /* index 7: 8 bits: 1111111x */ + { /*4*/ -27, 9 }, /* index 8: 9 bits: 11111111x */ + { /*-5*/ -36, 10 }, /* index 9: 10 bits: 111111111x */ + { /*5*/ -26, 11 }, /* index 10: 11 bits: 1111111111x */ + { /*-6*/ -37, 12 }, /* index 11: 12 bits: 11111111111x */ + { /*6*/ -25, 13 }, /* index 12: 13 bits: 111111111111x */ + { /*7*/ -24, 14 }, /* index 13: 14 bits: 1111111111111x */ + { /*-7*/ -38, 15 }, /* index 14: 15 bits: 11111111111111x */ + { 16, 17 }, /* index 15: 16 bits: 111111111111111x */ + { /*8*/ -23, /*-8*/ -39 }, /* index 16: 17 bits: 1111111111111110x */ + { 18, 19 }, /* index 17: 17 bits: 1111111111111111x */ + { 20, 21 }, /* index 18: 18 bits: 11111111111111110x */ + { 22, 23 }, /* index 19: 18 bits: 11111111111111111x */ + { /*9*/ -22, /*-14*/ -45 }, /* index 20: 19 bits: 111111111111111100x */ + { /*-13*/ -44, /*-12*/ -43 }, /* index 21: 19 bits: 111111111111111101x */ + { 24, 25 }, /* index 22: 19 bits: 111111111111111110x */ + { 26, 27 }, /* index 23: 19 bits: 111111111111111111x */ + { /*-11*/ -42, /*-10*/ -41 }, /* index 24: 20 bits: 1111111111111111100x */ + { /*-9*/ -40, /*10*/ -21 }, /* index 25: 20 bits: 1111111111111111101x */ + { /*11*/ -20, /*12*/ -19 }, /* index 26: 20 bits: 1111111111111111110x */ + { /*13*/ -18, /*14*/ -17 } /* index 27: 20 bits: 1111111111111111111x */ +}; + +static const int8_t f_huff_iid_fine[][2] = { + { 1, /*0*/ -31 }, /* index 0: 1 bits: x */ + { 2, 3 }, /* index 1: 2 bits: 0x */ + { 4, /*-1*/ -32 }, /* index 2: 3 bits: 00x */ + { /*1*/ -30, 5 }, /* index 3: 3 bits: 01x */ + { /*-2*/ -33, /*2*/ -29 }, /* index 4: 4 bits: 000x */ + { 6, 7 }, /* index 5: 4 bits: 011x */ + { /*-3*/ -34, /*3*/ -28 }, /* index 6: 5 bits: 0110x */ + { 8, 9 }, /* index 7: 5 bits: 0111x */ + { /*-4*/ -35, /*4*/ -27 }, /* index 8: 6 bits: 01110x */ + { 10, 11 }, /* index 9: 6 bits: 01111x */ + { /*-5*/ -36, /*5*/ -26 }, /* index 10: 7 bits: 011110x */ + { 12, 13 }, /* index 11: 7 bits: 011111x */ + { /*-6*/ -37, /*6*/ -25 }, /* index 12: 8 bits: 0111110x */ + { 14, 15 }, /* index 13: 8 bits: 0111111x */ + { /*7*/ -24, 16 }, /* index 14: 9 bits: 01111110x */ + { 17, 18 }, /* index 15: 9 bits: 01111111x */ + { 19, /*-8*/ -39 }, /* index 16: 10 bits: 011111101x */ + { /*8*/ -23, 20 }, /* index 17: 10 bits: 011111110x */ + { 21, /*-7*/ -38 }, /* index 18: 10 bits: 011111111x */ + { /*10*/ -21, 22 }, /* index 19: 11 bits: 0111111010x */ + { 23, /*-9*/ -40 }, /* index 20: 11 bits: 0111111101x */ + { /*9*/ -22, 24 }, /* index 21: 11 bits: 0111111110x */ + { /*-11*/ -42, /*11*/ -20 }, /* index 22: 12 bits: 01111110101x */ + { 25, 26 }, /* index 23: 12 bits: 01111111010x */ + { 27, /*-10*/ -41 }, /* index 24: 12 bits: 01111111101x */ + { 28, /*-12*/ -43 }, /* index 25: 13 bits: 011111110100x */ + { /*12*/ -19, 29 }, /* index 26: 13 bits: 011111110101x */ + { 30, 31 }, /* index 27: 13 bits: 011111111010x */ + { 32, /*-14*/ -45 }, /* index 28: 14 bits: 0111111101000x */ + { /*14*/ -17, 33 }, /* index 29: 14 bits: 0111111101011x */ + { 34, /*-13*/ -44 }, /* index 30: 14 bits: 0111111110100x */ + { /*13*/ -18, 35 }, /* index 31: 14 bits: 0111111110101x */ + { 36, 37 }, /* index 32: 15 bits: 01111111010000x */ + { 38, /*-15*/ -46 }, /* index 33: 15 bits: 01111111010111x */ + { /*15*/ -16, 39 }, /* index 34: 15 bits: 01111111101000x */ + { 40, 41 }, /* index 35: 15 bits: 01111111101011x */ + { 42, 43 }, /* index 36: 16 bits: 011111110100000x */ + { /*-17*/ -48, /*17*/ -14 }, /* index 37: 16 bits: 011111110100001x */ + { 44, 45 }, /* index 38: 16 bits: 011111110101110x */ + { 46, 47 }, /* index 39: 16 bits: 011111111010001x */ + { 48, 49 }, /* index 40: 16 bits: 011111111010110x */ + { /*-16*/ -47, /*16*/ -15 }, /* index 41: 16 bits: 011111111010111x */ + { /*-21*/ -52, /*21*/ -10 }, /* index 42: 17 bits: 0111111101000000x */ + { /*-19*/ -50, /*19*/ -12 }, /* index 43: 17 bits: 0111111101000001x */ + { /*-18*/ -49, /*18*/ -13 }, /* index 44: 17 bits: 0111111101011100x */ + { 50, 51 }, /* index 45: 17 bits: 0111111101011101x */ + { 52, 53 }, /* index 46: 17 bits: 0111111110100010x */ + { 54, 55 }, /* index 47: 17 bits: 0111111110100011x */ + { 56, 57 }, /* index 48: 17 bits: 0111111110101100x */ + { 58, 59 }, /* index 49: 17 bits: 0111111110101101x */ + { /*-26*/ -57, /*-25*/ -56 }, /* index 50: 18 bits: 01111111010111010x */ + { /*-28*/ -59, /*-27*/ -58 }, /* index 51: 18 bits: 01111111010111011x */ + { /*-22*/ -53, /*22*/ -9 }, /* index 52: 18 bits: 01111111101000100x */ + { /*-24*/ -55, /*-23*/ -54 }, /* index 53: 18 bits: 01111111101000101x */ + { /*25*/ -6, /*26*/ -5 }, /* index 54: 18 bits: 01111111101000110x */ + { /*23*/ -8, /*24*/ -7 }, /* index 55: 18 bits: 01111111101000111x */ + { /*29*/ -2, /*30*/ -1 }, /* index 56: 18 bits: 01111111101011000x */ + { /*27*/ -4, /*28*/ -3 }, /* index 57: 18 bits: 01111111101011001x */ + { /*-30*/ -61, /*-29*/ -60 }, /* index 58: 18 bits: 01111111101011010x */ + { /*-20*/ -51, /*20*/ -11 } /* index 59: 18 bits: 01111111101011011x */ +}; + +static const int8_t t_huff_iid_fine[][2] = { + { 1, /*0*/ -31 }, /* index 0: 1 bits: x */ + { /*1*/ -30, 2 }, /* index 1: 2 bits: 0x */ + { 3, /*-1*/ -32 }, /* index 2: 3 bits: 01x */ + { 4, 5 }, /* index 3: 4 bits: 010x */ + { 6, 7 }, /* index 4: 5 bits: 0100x */ + { /*-2*/ -33, /*2*/ -29 }, /* index 5: 5 bits: 0101x */ + { 8, /*-3*/ -34 }, /* index 6: 6 bits: 01000x */ + { /*3*/ -28, 9 }, /* index 7: 6 bits: 01001x */ + { /*-4*/ -35, /*4*/ -27 }, /* index 8: 7 bits: 010000x */ + { 10, 11 }, /* index 9: 7 bits: 010011x */ + { /*5*/ -26, 12 }, /* index 10: 8 bits: 0100110x */ + { 13, 14 }, /* index 11: 8 bits: 0100111x */ + { /*-6*/ -37, /*6*/ -25 }, /* index 12: 9 bits: 01001101x */ + { 15, 16 }, /* index 13: 9 bits: 01001110x */ + { 17, /*-5*/ -36 }, /* index 14: 9 bits: 01001111x */ + { 18, /*-7*/ -38 }, /* index 15: 10 bits: 010011100x */ + { /*7*/ -24, 19 }, /* index 16: 10 bits: 010011101x */ + { 20, 21 }, /* index 17: 10 bits: 010011110x */ + { /*9*/ -22, 22 }, /* index 18: 11 bits: 0100111000x */ + { 23, 24 }, /* index 19: 11 bits: 0100111011x */ + { /*-8*/ -39, /*8*/ -23 }, /* index 20: 11 bits: 0100111100x */ + { 25, 26 }, /* index 21: 11 bits: 0100111101x */ + { /*11*/ -20, 27 }, /* index 22: 12 bits: 01001110001x */ + { 28, 29 }, /* index 23: 12 bits: 01001110110x */ + { /*-10*/ -41, /*10*/ -21 }, /* index 24: 12 bits: 01001110111x */ + { 30, 31 }, /* index 25: 12 bits: 01001111010x */ + { 32, /*-9*/ -40 }, /* index 26: 12 bits: 01001111011x */ + { 33, /*-13*/ -44 }, /* index 27: 13 bits: 010011100011x */ + { /*13*/ -18, 34 }, /* index 28: 13 bits: 010011101100x */ + { 35, 36 }, /* index 29: 13 bits: 010011101101x */ + { 37, /*-12*/ -43 }, /* index 30: 13 bits: 010011110100x */ + { /*12*/ -19, 38 }, /* index 31: 13 bits: 010011110101x */ + { 39, /*-11*/ -42 }, /* index 32: 13 bits: 010011110110x */ + { 40, 41 }, /* index 33: 14 bits: 0100111000110x */ + { 42, 43 }, /* index 34: 14 bits: 0100111011001x */ + { 44, 45 }, /* index 35: 14 bits: 0100111011010x */ + { 46, /*-15*/ -46 }, /* index 36: 14 bits: 0100111011011x */ + { /*15*/ -16, 47 }, /* index 37: 14 bits: 0100111101000x */ + { /*-14*/ -45, /*14*/ -17 }, /* index 38: 14 bits: 0100111101011x */ + { 48, 49 }, /* index 39: 14 bits: 0100111101100x */ + { /*-21*/ -52, /*-20*/ -51 }, /* index 40: 15 bits: 01001110001100x */ + { /*18*/ -13, /*19*/ -12 }, /* index 41: 15 bits: 01001110001101x */ + { /*-19*/ -50, /*-18*/ -49 }, /* index 42: 15 bits: 01001110110010x */ + { 50, 51 }, /* index 43: 15 bits: 01001110110011x */ + { 52, 53 }, /* index 44: 15 bits: 01001110110100x */ + { 54, 55 }, /* index 45: 15 bits: 01001110110101x */ + { 56, /*-17*/ -48 }, /* index 46: 15 bits: 01001110110110x */ + { /*17*/ -14, 57 }, /* index 47: 15 bits: 01001111010001x */ + { 58, /*-16*/ -47 }, /* index 48: 15 bits: 01001111011000x */ + { /*16*/ -15, 59 }, /* index 49: 15 bits: 01001111011001x */ + { /*-26*/ -57, /*26*/ -5 }, /* index 50: 16 bits: 010011101100110x */ + { /*-28*/ -59, /*-27*/ -58 }, /* index 51: 16 bits: 010011101100111x */ + { /*29*/ -2, /*30*/ -1 }, /* index 52: 16 bits: 010011101101000x */ + { /*27*/ -4, /*28*/ -3 }, /* index 53: 16 bits: 010011101101001x */ + { /*-30*/ -61, /*-29*/ -60 }, /* index 54: 16 bits: 010011101101010x */ + { /*-25*/ -56, /*25*/ -6 }, /* index 55: 16 bits: 010011101101011x */ + { /*-24*/ -55, /*24*/ -7 }, /* index 56: 16 bits: 010011101101100x */ + { /*-23*/ -54, /*23*/ -8 }, /* index 57: 16 bits: 010011110100011x */ + { /*-22*/ -53, /*22*/ -9 }, /* index 58: 16 bits: 010011110110000x */ + { /*20*/ -11, /*21*/ -10 } /* index 59: 16 bits: 010011110110011x */ +}; + +static const int8_t f_huff_icc[][2] = { + { /*0*/ -31, 1 }, /* index 0: 1 bits: x */ + { /*1*/ -30, 2 }, /* index 1: 2 bits: 1x */ + { /*-1*/ -32, 3 }, /* index 2: 3 bits: 11x */ + { /*2*/ -29, 4 }, /* index 3: 4 bits: 111x */ + { /*-2*/ -33, 5 }, /* index 4: 5 bits: 1111x */ + { /*3*/ -28, 6 }, /* index 5: 6 bits: 11111x */ + { /*-3*/ -34, 7 }, /* index 6: 7 bits: 111111x */ + { /*4*/ -27, 8 }, /* index 7: 8 bits: 1111111x */ + { /*5*/ -26, 9 }, /* index 8: 9 bits: 11111111x */ + { /*-4*/ -35, 10 }, /* index 9: 10 bits: 111111111x */ + { /*6*/ -25, 11 }, /* index 10: 11 bits: 1111111111x */ + { /*-5*/ -36, 12 }, /* index 11: 12 bits: 11111111111x */ + { /*7*/ -24, 13 }, /* index 12: 13 bits: 111111111111x */ + { /*-6*/ -37, /*-7*/ -38 } /* index 13: 14 bits: 1111111111111x */ +}; + +static const int8_t t_huff_icc[][2] = { + { /*0*/ -31, 1 }, /* index 0: 1 bits: x */ + { /*1*/ -30, 2 }, /* index 1: 2 bits: 1x */ + { /*-1*/ -32, 3 }, /* index 2: 3 bits: 11x */ + { /*2*/ -29, 4 }, /* index 3: 4 bits: 111x */ + { /*-2*/ -33, 5 }, /* index 4: 5 bits: 1111x */ + { /*3*/ -28, 6 }, /* index 5: 6 bits: 11111x */ + { /*-3*/ -34, 7 }, /* index 6: 7 bits: 111111x */ + { /*4*/ -27, 8 }, /* index 7: 8 bits: 1111111x */ + { /*-4*/ -35, 9 }, /* index 8: 9 bits: 11111111x */ + { /*5*/ -26, 10 }, /* index 9: 10 bits: 111111111x */ + { /*-5*/ -36, 11 }, /* index 10: 11 bits: 1111111111x */ + { /*6*/ -25, 12 }, /* index 11: 12 bits: 11111111111x */ + { /*-6*/ -37, 13 }, /* index 12: 13 bits: 111111111111x */ + { /*-7*/ -38, /*7*/ -24 } /* index 13: 14 bits: 1111111111111x */ +}; + +static const int8_t f_huff_ipd[][2] = { + { 1, /*0*/ -31 }, /* index 0: 1 bits: x */ + { 2, 3 }, /* index 1: 2 bits: 0x */ + { /*1*/ -30, 4 }, /* index 2: 3 bits: 00x */ + { 5, 6 }, /* index 3: 3 bits: 01x */ + { /*4*/ -27, /*5*/ -26 }, /* index 4: 4 bits: 001x */ + { /*3*/ -28, /*6*/ -25 }, /* index 5: 4 bits: 010x */ + { /*2*/ -29, /*7*/ -24 } /* index 6: 4 bits: 011x */ +}; + +static const int8_t t_huff_ipd[][2] = { + { 1, /*0*/ -31 }, /* index 0: 1 bits: x */ + { 2, 3 }, /* index 1: 2 bits: 0x */ + { 4, 5 }, /* index 2: 3 bits: 00x */ + { /*1*/ -30, /*7*/ -24 }, /* index 3: 3 bits: 01x */ + { /*5*/ -26, 6 }, /* index 4: 4 bits: 000x */ + { /*2*/ -29, /*6*/ -25 }, /* index 5: 4 bits: 001x */ + { /*4*/ -27, /*3*/ -28 } /* index 6: 5 bits: 0001x */ +}; + +static const int8_t f_huff_opd[][2] = { + { 1, /*0*/ -31 }, /* index 0: 1 bits: x */ + { 2, 3 }, /* index 1: 2 bits: 0x */ + { /*7*/ -24, /*1*/ -30 }, /* index 2: 3 bits: 00x */ + { 4, 5 }, /* index 3: 3 bits: 01x */ + { /*3*/ -28, /*6*/ -25 }, /* index 4: 4 bits: 010x */ + { /*2*/ -29, 6 }, /* index 5: 4 bits: 011x */ + { /*5*/ -26, /*4*/ -27 } /* index 6: 5 bits: 0111x */ +}; + +static const int8_t t_huff_opd[][2] = { + { 1, /*0*/ -31 }, /* index 0: 1 bits: x */ + { 2, 3 }, /* index 1: 2 bits: 0x */ + { 4, 5 }, /* index 2: 3 bits: 00x */ + { /*1*/ -30, /*7*/ -24 }, /* index 3: 3 bits: 01x */ + { /*5*/ -26, /*2*/ -29 }, /* index 4: 4 bits: 000x */ + { /*6*/ -25, 6 }, /* index 5: 4 bits: 001x */ + { /*4*/ -27, /*3*/ -28 } /* index 6: 5 bits: 0011x */ +}; + +/* static function declarations */ +static uint16_t ps_extension(ps_info *ps, bitfile *ld, + const uint8_t ps_extension_id, + const uint16_t num_bits_left); +static void huff_data(bitfile *ld, const uint8_t dt, const uint8_t nr_par, + ps_huff_tab t_huff, ps_huff_tab f_huff, int8_t *par); +static INLINE int8_t ps_huff_dec(bitfile *ld, ps_huff_tab t_huff); + + +uint16_t ps_data(ps_info *ps, bitfile *ld, uint8_t *header) +{ + uint8_t tmp, n; + uint16_t bits = (uint16_t)faad_get_processed_bits(ld); + + *header = 0; + + /* check for new PS header */ + if (faad_get1bit(ld + DEBUGVAR(1,1000,"ps_data(): enable_ps_header"))) + { + *header = 1; + + ps->header_read = 1; + + ps->use34hybrid_bands = 0; + + /* Inter-channel Intensity Difference (IID) parameters enabled */ + ps->enable_iid = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,1001,"ps_data(): enable_iid")); + + if (ps->enable_iid) + { + ps->iid_mode = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,1002,"ps_data(): iid_mode")); + + ps->nr_iid_par = nr_iid_par_tab[ps->iid_mode]; + ps->nr_ipdopd_par = nr_ipdopd_par_tab[ps->iid_mode]; + + if (ps->iid_mode == 2 || ps->iid_mode == 5) + ps->use34hybrid_bands = 1; + + /* IPD freq res equal to IID freq res */ + ps->ipd_mode = ps->iid_mode; + } + + /* Inter-channel Coherence (ICC) parameters enabled */ + ps->enable_icc = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,1003,"ps_data(): enable_icc")); + + if (ps->enable_icc) + { + ps->icc_mode = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,1004,"ps_data(): icc_mode")); + + ps->nr_icc_par = nr_icc_par_tab[ps->icc_mode]; + + if (ps->icc_mode == 2 || ps->icc_mode == 5) + ps->use34hybrid_bands = 1; + } + + /* PS extension layer enabled */ + ps->enable_ext = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,1005,"ps_data(): enable_ext")); + } + + /* we are here, but no header has been read yet */ + if (ps->header_read == 0) + { + ps->ps_data_available = 0; + return 1; + } + + ps->frame_class = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,1006,"ps_data(): frame_class")); + tmp = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,1007,"ps_data(): num_env_idx")); + + ps->num_env = num_env_tab[ps->frame_class][tmp]; + + if (ps->frame_class) + { + for (n = 1; n < ps->num_env+1; n++) + { + ps->border_position[n] = (uint8_t)faad_getbits(ld, 5 + DEBUGVAR(1,1008,"ps_data(): border_position")) + 1; + } + } + + if (ps->enable_iid) + { + for (n = 0; n < ps->num_env; n++) + { + ps->iid_dt[n] = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,1009,"ps_data(): iid_dt")); + + /* iid_data */ + if (ps->iid_mode < 3) + { + huff_data(ld, ps->iid_dt[n], ps->nr_iid_par, t_huff_iid_def, + f_huff_iid_def, ps->iid_index[n]); + } else { + huff_data(ld, ps->iid_dt[n], ps->nr_iid_par, t_huff_iid_fine, + f_huff_iid_fine, ps->iid_index[n]); + } + } + } + + if (ps->enable_icc) + { + for (n = 0; n < ps->num_env; n++) + { + ps->icc_dt[n] = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,1010,"ps_data(): icc_dt")); + + /* icc_data */ + huff_data(ld, ps->icc_dt[n], ps->nr_icc_par, t_huff_icc, + f_huff_icc, ps->icc_index[n]); + } + } + + if (ps->enable_ext) + { + uint16_t num_bits_left; + uint16_t cnt = (uint16_t)faad_getbits(ld, 4 + DEBUGVAR(1,1011,"ps_data(): ps_extension_size")); + if (cnt == 15) + { + cnt += (uint16_t)faad_getbits(ld, 8 + DEBUGVAR(1,1012,"ps_data(): esc_count")); + } + + num_bits_left = 8 * cnt; + while (num_bits_left > 7) + { + uint8_t ps_extension_id = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,1013,"ps_data(): ps_extension_size")); + + num_bits_left -= 2; + num_bits_left -= ps_extension(ps, ld, ps_extension_id, num_bits_left); + } + + faad_getbits(ld, num_bits_left + DEBUGVAR(1,1014,"ps_data(): fill_bits")); + } + + bits = (uint16_t)faad_get_processed_bits(ld) - bits; + + ps->ps_data_available = 1; + + return bits; +} + +static uint16_t ps_extension(ps_info *ps, bitfile *ld, + const uint8_t ps_extension_id, + const uint16_t num_bits_left) +{ + uint8_t n; + uint16_t bits = (uint16_t)faad_get_processed_bits(ld); + (void)num_bits_left; /* TODO: remove parameter, or actually use it. */ + + if (ps_extension_id == 0) + { + ps->enable_ipdopd = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,1015,"ps_extension(): enable_ipdopd")); + + if (ps->enable_ipdopd) + { + for (n = 0; n < ps->num_env; n++) + { + ps->ipd_dt[n] = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,1016,"ps_extension(): ipd_dt")); + + /* ipd_data */ + huff_data(ld, ps->ipd_dt[n], ps->nr_ipdopd_par, t_huff_ipd, + f_huff_ipd, ps->ipd_index[n]); + + ps->opd_dt[n] = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,1017,"ps_extension(): opd_dt")); + + /* opd_data */ + huff_data(ld, ps->opd_dt[n], ps->nr_ipdopd_par, t_huff_opd, + f_huff_opd, ps->opd_index[n]); + } + } + faad_get1bit(ld + DEBUGVAR(1,1018,"ps_extension(): reserved_ps")); + } + + /* return number of bits read */ + bits = (uint16_t)faad_get_processed_bits(ld) - bits; + + return bits; +} + +/* read huffman data coded in either the frequency or the time direction */ +static void huff_data(bitfile *ld, const uint8_t dt, const uint8_t nr_par, + ps_huff_tab t_huff, ps_huff_tab f_huff, int8_t *par) +{ + uint8_t n; + + if (dt) + { + /* coded in time direction */ + for (n = 0; n < nr_par; n++) + { + par[n] = ps_huff_dec(ld, t_huff); + } + } else { + /* coded in frequency direction */ + par[0] = ps_huff_dec(ld, f_huff); + + for (n = 1; n < nr_par; n++) + { + par[n] = ps_huff_dec(ld, f_huff); + } + } +} + +/* binary search huffman decoding */ +static INLINE int8_t ps_huff_dec(bitfile *ld, ps_huff_tab t_huff) +{ + uint8_t bit; + int8_t index = 0; + + while (index >= 0) + { + bit = (uint8_t)faad_get1bit(ld); + index = t_huff[index][bit]; + } + + return index + 31; +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ps_tables.h b/third_party/faad_hdc/upstream/libfaad/ps_tables.h new file mode 100644 index 000000000..3a9aaf58d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ps_tables.h @@ -0,0 +1,550 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ps_tables.h,v 1.8 2007/11/01 12:33:33 menno Exp $ +**/ + +#ifndef __PS_TABLES_H__ +#define __PS_TABLES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4305) +#pragma warning(disable:4244) +#endif + +#if 0 +#if 0 +float f_center_20[12] = { + 0.5/4, 1.5/4, 2.5/4, 3.5/4, + 4.5/4*0, 5.5/4*0, -1.5/4, -0.5/4, + 3.5/2, 2.5/2, 4.5/2, 5.5/2 +}; +#else +float f_center_20[12] = { + 0.5/8, 1.5/8, 2.5/8, 3.5/8, + 4.5/8*0, 5.5/8*0, -1.5/8, -0.5/8, + 3.5/4, 2.5/4, 4.5/4, 5.5/4 +}; +#endif + +float f_center_34[32] = { + 1/12, 3/12, 5/12, 7/12, + 9/12, 11/12, 13/12, 15/12, + 17/12, -5/12, -3/12, -1/12, + 17/8, 19/8, 5/8, 7/8, + 9/8, 11/8, 13/8, 15/8, + 9/4, 11/4, 13/4, 7/4, + 17/4, 11/4, 13/4, 15/4, + 17/4, 19/4, 21/4, 15/4 +}; + +static const real_t frac_delay_q[] = { + FRAC_CONST(0.43), + FRAC_CONST(0.75), + FRAC_CONST(0.347) +}; +#endif + +/* RE(ps->Phi_Fract_Qmf[j]) = (float)cos(M_PI*(j+0.5)*(0.39)); */ +/* IM(ps->Phi_Fract_Qmf[j]) = (float)sin(M_PI*(j+0.5)*(0.39)); */ +static const complex_t Phi_Fract_Qmf[] = { + { FRAC_CONST(0.8181497455), FRAC_CONST(0.5750052333) }, + { FRAC_CONST(-0.2638730407), FRAC_CONST(0.9645574093) }, + { FRAC_CONST(-0.9969173074), FRAC_CONST(0.0784590989) }, + { FRAC_CONST(-0.4115143716), FRAC_CONST(-0.9114032984) }, + { FRAC_CONST(0.7181262970), FRAC_CONST(-0.6959127784) }, + { FRAC_CONST(0.8980275989), FRAC_CONST(0.4399391711) }, + { FRAC_CONST(-0.1097343117), FRAC_CONST(0.9939609766) }, + { FRAC_CONST(-0.9723699093), FRAC_CONST(0.2334453613) }, + { FRAC_CONST(-0.5490227938), FRAC_CONST(-0.8358073831) }, + { FRAC_CONST(0.6004202366), FRAC_CONST(-0.7996846437) }, + { FRAC_CONST(0.9557930231), FRAC_CONST(0.2940403223) }, + { FRAC_CONST(0.0471064523), FRAC_CONST(0.9988898635) }, + { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, + { FRAC_CONST(-0.6730124950), FRAC_CONST(-0.7396311164) }, + { FRAC_CONST(0.4679298103), FRAC_CONST(-0.8837656379) }, + { FRAC_CONST(0.9900236726), FRAC_CONST(0.1409012377) }, + { FRAC_CONST(0.2027872950), FRAC_CONST(0.9792228341) }, + { FRAC_CONST(-0.8526401520), FRAC_CONST(0.5224985480) }, + { FRAC_CONST(-0.7804304361), FRAC_CONST(-0.6252426505) }, + { FRAC_CONST(0.3239174187), FRAC_CONST(-0.9460853338) }, + { FRAC_CONST(0.9998766184), FRAC_CONST(-0.0157073177) }, + { FRAC_CONST(0.3534748554), FRAC_CONST(0.9354440570) }, + { FRAC_CONST(-0.7604059577), FRAC_CONST(0.6494480371) }, + { FRAC_CONST(-0.8686315417), FRAC_CONST(-0.4954586625) }, + { FRAC_CONST(0.1719291061), FRAC_CONST(-0.9851093292) }, + { FRAC_CONST(0.9851093292), FRAC_CONST(-0.1719291061) }, + { FRAC_CONST(0.4954586625), FRAC_CONST(0.8686315417) }, + { FRAC_CONST(-0.6494480371), FRAC_CONST(0.7604059577) }, + { FRAC_CONST(-0.9354440570), FRAC_CONST(-0.3534748554) }, + { FRAC_CONST(0.0157073177), FRAC_CONST(-0.9998766184) }, + { FRAC_CONST(0.9460853338), FRAC_CONST(-0.3239174187) }, + { FRAC_CONST(0.6252426505), FRAC_CONST(0.7804304361) }, + { FRAC_CONST(-0.5224985480), FRAC_CONST(0.8526401520) }, + { FRAC_CONST(-0.9792228341), FRAC_CONST(-0.2027872950) }, + { FRAC_CONST(-0.1409012377), FRAC_CONST(-0.9900236726) }, + { FRAC_CONST(0.8837656379), FRAC_CONST(-0.4679298103) }, + { FRAC_CONST(0.7396311164), FRAC_CONST(0.6730124950) }, + { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, + { FRAC_CONST(-0.9988898635), FRAC_CONST(-0.0471064523) }, + { FRAC_CONST(-0.2940403223), FRAC_CONST(-0.9557930231) }, + { FRAC_CONST(0.7996846437), FRAC_CONST(-0.6004202366) }, + { FRAC_CONST(0.8358073831), FRAC_CONST(0.5490227938) }, + { FRAC_CONST(-0.2334453613), FRAC_CONST(0.9723699093) }, + { FRAC_CONST(-0.9939609766), FRAC_CONST(0.1097343117) }, + { FRAC_CONST(-0.4399391711), FRAC_CONST(-0.8980275989) }, + { FRAC_CONST(0.6959127784), FRAC_CONST(-0.7181262970) }, + { FRAC_CONST(0.9114032984), FRAC_CONST(0.4115143716) }, + { FRAC_CONST(-0.0784590989), FRAC_CONST(0.9969173074) }, + { FRAC_CONST(-0.9645574093), FRAC_CONST(0.2638730407) }, + { FRAC_CONST(-0.5750052333), FRAC_CONST(-0.8181497455) }, + { FRAC_CONST(0.5750052333), FRAC_CONST(-0.8181497455) }, + { FRAC_CONST(0.9645574093), FRAC_CONST(0.2638730407) }, + { FRAC_CONST(0.0784590989), FRAC_CONST(0.9969173074) }, + { FRAC_CONST(-0.9114032984), FRAC_CONST(0.4115143716) }, + { FRAC_CONST(-0.6959127784), FRAC_CONST(-0.7181262970) }, + { FRAC_CONST(0.4399391711), FRAC_CONST(-0.8980275989) }, + { FRAC_CONST(0.9939609766), FRAC_CONST(0.1097343117) }, + { FRAC_CONST(0.2334453613), FRAC_CONST(0.9723699093) }, + { FRAC_CONST(-0.8358073831), FRAC_CONST(0.5490227938) }, + { FRAC_CONST(-0.7996846437), FRAC_CONST(-0.6004202366) }, + { FRAC_CONST(0.2940403223), FRAC_CONST(-0.9557930231) }, + { FRAC_CONST(0.9988898635), FRAC_CONST(-0.0471064523) }, + { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, + { FRAC_CONST(-0.7396311164), FRAC_CONST(0.6730124950) } +}; + +/* RE(Phi_Fract_SubQmf20[j]) = (float)cos(M_PI*f_center_20[j]*0.39); */ +/* IM(Phi_Fract_SubQmf20[j]) = (float)sin(M_PI*f_center_20[j]*0.39); */ +static const complex_t Phi_Fract_SubQmf20[] = { + { FRAC_CONST(0.9882950187), FRAC_CONST(0.1525546312) }, + { FRAC_CONST(0.8962930441), FRAC_CONST(0.4434623122) }, + { FRAC_CONST(0.7208535671), FRAC_CONST(0.6930873394) }, + { FRAC_CONST(0.4783087075), FRAC_CONST(0.8781917691) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(0.8962930441), FRAC_CONST(-0.4434623122) }, + { FRAC_CONST(0.9882950187), FRAC_CONST(-0.1525546312) }, + { FRAC_CONST(-0.5424415469), FRAC_CONST(0.8400935531) }, + { FRAC_CONST(0.0392598175), FRAC_CONST(0.9992290139) }, + { FRAC_CONST(-0.9268565774), FRAC_CONST(0.3754155636) }, + { FRAC_CONST(-0.9741733670), FRAC_CONST(-0.2258012742) } +}; + +/* RE(Phi_Fract_SubQmf34[j]) = (float)cos(M_PI*f_center_34[j]*0.39); */ +/* IM(Phi_Fract_SubQmf34[j]) = (float)sin(M_PI*f_center_34[j]*0.39); */ +static const complex_t Phi_Fract_SubQmf34[] = { + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(0.3387379348), FRAC_CONST(0.9408807755) }, + { FRAC_CONST(0.3387379348), FRAC_CONST(0.9408807755) }, + { FRAC_CONST(0.3387379348), FRAC_CONST(0.9408807755) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(-0.7705132365), FRAC_CONST(0.6374239922) }, + { FRAC_CONST(-0.7705132365), FRAC_CONST(0.6374239922) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, + { FRAC_CONST(0.3387379348), FRAC_CONST(0.9408807755) }, + { FRAC_CONST(0.3387379348), FRAC_CONST(0.9408807755) }, + { FRAC_CONST(0.3387379348), FRAC_CONST(0.9408807755) }, + { FRAC_CONST(0.3387379348), FRAC_CONST(0.9408807755) }, + { FRAC_CONST(-0.7705132365), FRAC_CONST(0.6374239922) }, + { FRAC_CONST(-0.7705132365), FRAC_CONST(0.6374239922) }, + { FRAC_CONST(-0.8607420325), FRAC_CONST(-0.5090414286) }, + { FRAC_CONST(0.3387379348), FRAC_CONST(0.9408807755) }, + { FRAC_CONST(0.1873813123), FRAC_CONST(-0.9822872281) }, + { FRAC_CONST(-0.7705132365), FRAC_CONST(0.6374239922) }, + { FRAC_CONST(-0.8607420325), FRAC_CONST(-0.5090414286) }, + { FRAC_CONST(-0.8607420325), FRAC_CONST(-0.5090414286) }, + { FRAC_CONST(0.1873813123), FRAC_CONST(-0.9822872281) }, + { FRAC_CONST(0.1873813123), FRAC_CONST(-0.9822872281) }, + { FRAC_CONST(0.9876883626), FRAC_CONST(-0.1564344615) }, + { FRAC_CONST(-0.8607420325), FRAC_CONST(-0.5090414286) } +}; + +/* RE(Q_Fract_allpass_Qmf[j][i]) = (float)cos(M_PI*(j+0.5)*(frac_delay_q[i])); */ +/* IM(Q_Fract_allpass_Qmf[j][i]) = (float)sin(M_PI*(j+0.5)*(frac_delay_q[i])); */ +static const complex_t Q_Fract_allpass_Qmf[][3] = { + { { FRAC_CONST(0.7804303765), FRAC_CONST(0.6252426505) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.8550928831), FRAC_CONST(0.5184748173) } }, + { { FRAC_CONST(-0.4399392009), FRAC_CONST(0.8980275393) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.0643581524), FRAC_CONST(0.9979268909) } }, + { { FRAC_CONST(-0.9723699093), FRAC_CONST(-0.2334454209) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.9146071672), FRAC_CONST(0.4043435752) } }, + { { FRAC_CONST(0.0157073960), FRAC_CONST(-0.9998766184) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.7814115286), FRAC_CONST(-0.6240159869) } }, + { { FRAC_CONST(0.9792228341), FRAC_CONST(-0.2027871907) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.1920081824), FRAC_CONST(-0.9813933372) } }, + { { FRAC_CONST(0.4115142524), FRAC_CONST(0.9114032984) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.9589683414), FRAC_CONST(-0.2835132182) } }, + { { FRAC_CONST(-0.7996847630), FRAC_CONST(0.6004201174) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.6947838664), FRAC_CONST(0.7192186117) } }, + { { FRAC_CONST(-0.7604058385), FRAC_CONST(-0.6494481564) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.3164770305), FRAC_CONST(0.9486001730) } }, + { { FRAC_CONST(0.4679299891), FRAC_CONST(-0.8837655187) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.9874414206), FRAC_CONST(0.1579856575) } }, + { { FRAC_CONST(0.9645573497), FRAC_CONST(0.2638732493) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.5966450572), FRAC_CONST(-0.8025052547) } }, + { { FRAC_CONST(-0.0471066870), FRAC_CONST(0.9988898635) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.4357025325), FRAC_CONST(-0.9000906944) } }, + { { FRAC_CONST(-0.9851093888), FRAC_CONST(0.1719288528) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.9995546937), FRAC_CONST(-0.0298405960) } }, + { { FRAC_CONST(-0.3826831877), FRAC_CONST(-0.9238796234) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.4886211455), FRAC_CONST(0.8724960685) } }, + { { FRAC_CONST(0.8181498647), FRAC_CONST(-0.5750049949) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.5477093458), FRAC_CONST(0.8366686702) } }, + { { FRAC_CONST(0.7396308780), FRAC_CONST(0.6730127335) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.9951074123), FRAC_CONST(-0.0987988561) } }, + { { FRAC_CONST(-0.4954589605), FRAC_CONST(0.8686313629) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.3725017905), FRAC_CONST(-0.9280315042) } }, + { { FRAC_CONST(-0.9557929039), FRAC_CONST(-0.2940406799) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.6506417990), FRAC_CONST(-0.7593847513) } }, + { { FRAC_CONST(0.0784594864), FRAC_CONST(-0.9969173074) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.9741733670), FRAC_CONST(0.2258014232) } }, + { { FRAC_CONST(0.9900237322), FRAC_CONST(-0.1409008205) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.2502108514), FRAC_CONST(0.9681913853) } }, + { { FRAC_CONST(0.3534744382), FRAC_CONST(0.9354441762) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.7427945137), FRAC_CONST(0.6695194840) } }, + { { FRAC_CONST(-0.8358076215), FRAC_CONST(0.5490224361) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.9370992780), FRAC_CONST(-0.3490629196) } }, + { { FRAC_CONST(-0.7181259394), FRAC_CONST(-0.6959131360) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.1237744763), FRAC_CONST(-0.9923103452) } }, + { { FRAC_CONST(0.5224990249), FRAC_CONST(-0.8526399136) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.8226406574), FRAC_CONST(-0.5685616732) } }, + { { FRAC_CONST(0.9460852146), FRAC_CONST(0.3239179254) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.8844994903), FRAC_CONST(0.4665412009) } }, + { { FRAC_CONST(-0.1097348556), FRAC_CONST(0.9939609170) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.0047125919), FRAC_CONST(0.9999889135) } }, + { { FRAC_CONST(-0.9939610362), FRAC_CONST(0.1097337380) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.8888573647), FRAC_CONST(0.4581840038) } }, + { { FRAC_CONST(-0.3239168525), FRAC_CONST(-0.9460855722) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.8172453642), FRAC_CONST(-0.5762898922) } }, + { { FRAC_CONST(0.8526405096), FRAC_CONST(-0.5224980116) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.1331215799), FRAC_CONST(-0.9910997152) } }, + { { FRAC_CONST(0.6959123611), FRAC_CONST(0.7181267142) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.9403476119), FRAC_CONST(-0.3402152061) } }, + { { FRAC_CONST(-0.5490233898), FRAC_CONST(0.8358070254) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.7364512086), FRAC_CONST(0.6764906645) } }, + { { FRAC_CONST(-0.9354437590), FRAC_CONST(-0.3534754813) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.2593250275), FRAC_CONST(0.9657900929) } }, + { { FRAC_CONST(0.1409019381), FRAC_CONST(-0.9900235534) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.9762582779), FRAC_CONST(0.2166097313) } }, + { { FRAC_CONST(0.9969173670), FRAC_CONST(-0.0784583688) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.6434556246), FRAC_CONST(-0.7654833794) } }, + { { FRAC_CONST(0.2940396070), FRAC_CONST(0.9557932615) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.3812320232), FRAC_CONST(-0.9244794250) } }, + { { FRAC_CONST(-0.8686318994), FRAC_CONST(0.4954580069) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.9959943891), FRAC_CONST(-0.0894154981) } }, + { { FRAC_CONST(-0.6730118990), FRAC_CONST(-0.7396316528) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.5397993922), FRAC_CONST(0.8417937160) } }, + { { FRAC_CONST(0.5750059485), FRAC_CONST(-0.8181492686) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.4968227744), FRAC_CONST(0.8678520322) } }, + { { FRAC_CONST(0.9238792062), FRAC_CONST(0.3826842010) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.9992290139), FRAC_CONST(-0.0392601527) } }, + { { FRAC_CONST(-0.1719299555), FRAC_CONST(0.9851091504) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.4271997511), FRAC_CONST(-0.9041572809) } }, + { { FRAC_CONST(-0.9988899231), FRAC_CONST(0.0471055657) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.6041822433), FRAC_CONST(-0.7968461514) } }, + { { FRAC_CONST(-0.2638721764), FRAC_CONST(-0.9645576477) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.9859085083), FRAC_CONST(0.1672853529) } }, + { { FRAC_CONST(0.8837660551), FRAC_CONST(-0.4679289758) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.3075223565), FRAC_CONST(0.9515408874) } }, + { { FRAC_CONST(0.6494473219), FRAC_CONST(0.7604066133) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.7015317082), FRAC_CONST(0.7126382589) } }, + { { FRAC_CONST(-0.6004210114), FRAC_CONST(0.7996840477) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.9562535882), FRAC_CONST(-0.2925389707) } }, + { { FRAC_CONST(-0.9114028811), FRAC_CONST(-0.4115152657) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.1827499419), FRAC_CONST(-0.9831594229) } }, + { { FRAC_CONST(0.2027882934), FRAC_CONST(-0.9792225957) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.7872582674), FRAC_CONST(-0.6166234016) } }, + { { FRAC_CONST(0.9998766780), FRAC_CONST(-0.0157062728) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.9107555747), FRAC_CONST(0.4129458666) } }, + { { FRAC_CONST(0.2334443331), FRAC_CONST(0.9723701477) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.0549497530), FRAC_CONST(0.9984891415) } }, + { { FRAC_CONST(-0.8980280757), FRAC_CONST(0.4399381876) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.8599416018), FRAC_CONST(0.5103924870) } }, + { { FRAC_CONST(-0.6252418160), FRAC_CONST(-0.7804310918) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(-0.8501682281), FRAC_CONST(-0.5265110731) } }, + { { FRAC_CONST(0.6252435446), FRAC_CONST(-0.7804297209) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.0737608299), FRAC_CONST(-0.9972759485) } }, + { { FRAC_CONST(0.8980270624), FRAC_CONST(0.4399402142) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.9183775187), FRAC_CONST(-0.3957053721) } }, + { { FRAC_CONST(-0.2334465086), FRAC_CONST(0.9723696709) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.7754954696), FRAC_CONST(0.6313531399) } }, + { { FRAC_CONST(-0.9998766184), FRAC_CONST(-0.0157085191) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.2012493610), FRAC_CONST(0.9795400500) } }, + { { FRAC_CONST(-0.2027861029), FRAC_CONST(-0.9792230725) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.9615978599), FRAC_CONST(0.2744622827) } }, + { { FRAC_CONST(0.9114037752), FRAC_CONST(-0.4115132093) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.6879743338), FRAC_CONST(-0.7257350087) } }, + { { FRAC_CONST(0.6004192233), FRAC_CONST(0.7996854186) }, { FRAC_CONST(0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(0.3254036009), FRAC_CONST(-0.9455752373) } }, + { { FRAC_CONST(-0.6494490504), FRAC_CONST(0.7604051232) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.9888865948), FRAC_CONST(-0.1486719251) } }, + { { FRAC_CONST(-0.8837650418), FRAC_CONST(-0.4679309726) }, { FRAC_CONST(0.9238795042), FRAC_CONST(-0.3826834261) }, { FRAC_CONST(0.5890548825), FRAC_CONST(0.8080930114) } }, + { { FRAC_CONST(0.2638743520), FRAC_CONST(-0.9645570517) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(0.9238795042) }, { FRAC_CONST(-0.4441666007), FRAC_CONST(0.8959442377) } }, + { { FRAC_CONST(0.9988898039), FRAC_CONST(0.0471078083) }, { FRAC_CONST(-0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(-0.9997915030), FRAC_CONST(0.0204183888) } }, + { { FRAC_CONST(0.1719277352), FRAC_CONST(0.9851095676) }, { FRAC_CONST(0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(-0.4803760946), FRAC_CONST(-0.8770626187) } }, + { { FRAC_CONST(-0.9238800406), FRAC_CONST(0.3826821446) }, { FRAC_CONST(-0.9238795042), FRAC_CONST(0.3826834261) }, { FRAC_CONST(0.5555707216), FRAC_CONST(-0.8314692974) } }, + { { FRAC_CONST(-0.5750041008), FRAC_CONST(-0.8181505203) }, { FRAC_CONST(0.3826834261), FRAC_CONST(-0.9238795042) }, { FRAC_CONST(0.9941320419), FRAC_CONST(0.1081734300) } } +}; + +/* RE(Q_Fract_allpass_SubQmf20[j][i]) = (float)cos(M_PI*f_center_20[j]*frac_delay_q[i]); */ +/* IM(Q_Fract_allpass_SubQmf20[j][i]) = (float)sin(M_PI*f_center_20[j]*frac_delay_q[i]); */ +static const complex_t Q_Fract_allpass_SubQmf20[][3] = { + { { FRAC_CONST(0.9857769012), FRAC_CONST(0.1680592746) }, { FRAC_CONST(0.9569403529), FRAC_CONST(0.2902846634) }, { FRAC_CONST(0.9907300472), FRAC_CONST(0.1358452588) } }, + { { FRAC_CONST(0.8744080663), FRAC_CONST(0.4851911962) }, { FRAC_CONST(0.6343932748), FRAC_CONST(0.7730104327) }, { FRAC_CONST(0.9175986052), FRAC_CONST(0.3975082636) } }, + { { FRAC_CONST(0.6642524004), FRAC_CONST(0.7475083470) }, { FRAC_CONST(0.0980171412), FRAC_CONST(0.9951847196) }, { FRAC_CONST(0.7767338753), FRAC_CONST(0.6298289299) } }, + { { FRAC_CONST(0.3790524006), FRAC_CONST(0.9253752232) }, { FRAC_CONST(-0.4713967443), FRAC_CONST(0.8819212914) }, { FRAC_CONST(0.5785340071), FRAC_CONST(0.8156582713) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(0.8744080663), FRAC_CONST(-0.4851911962) }, { FRAC_CONST(0.6343932748), FRAC_CONST(-0.7730104327) }, { FRAC_CONST(0.9175986052), FRAC_CONST(-0.3975082636) } }, + { { FRAC_CONST(0.9857769012), FRAC_CONST(-0.1680592746) }, { FRAC_CONST(0.9569403529), FRAC_CONST(-0.2902846634) }, { FRAC_CONST(0.9907300472), FRAC_CONST(-0.1358452588) } }, + { { FRAC_CONST(-0.7126385570), FRAC_CONST(0.7015314102) }, { FRAC_CONST(-0.5555702448), FRAC_CONST(-0.8314695954) }, { FRAC_CONST(-0.3305967748), FRAC_CONST(0.9437720776) } }, + { { FRAC_CONST(-0.1175374240), FRAC_CONST(0.9930684566) }, { FRAC_CONST(-0.9807852507), FRAC_CONST(0.1950903237) }, { FRAC_CONST(0.2066311091), FRAC_CONST(0.9784189463) } }, + { { FRAC_CONST(-0.9947921634), FRAC_CONST(0.1019244045) }, { FRAC_CONST(0.5555702448), FRAC_CONST(-0.8314695954) }, { FRAC_CONST(-0.7720130086), FRAC_CONST(0.6356067061) } }, + { { FRAC_CONST(-0.8400934935), FRAC_CONST(-0.5424416065) }, { FRAC_CONST(0.9807852507), FRAC_CONST(0.1950903237) }, { FRAC_CONST(-0.9896889329), FRAC_CONST(0.1432335079) } } +}; + +/* RE(Q_Fract_allpass_SubQmf34[j][i]) = (float)cos(M_PI*f_center_34[j]*frac_delay_q[i]); */ +/* IM(Q_Fract_allpass_SubQmf34[j][i]) = (float)sin(M_PI*f_center_34[j]*frac_delay_q[i]); */ +static const complex_t Q_Fract_allpass_SubQmf34[][3] = { + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(0.2181432247), FRAC_CONST(0.9759167433) }, { FRAC_CONST(-0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(0.4623677433), FRAC_CONST(0.8866882324) } }, + { { FRAC_CONST(0.2181432247), FRAC_CONST(0.9759167433) }, { FRAC_CONST(-0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(0.4623677433), FRAC_CONST(0.8866882324) } }, + { { FRAC_CONST(0.2181432247), FRAC_CONST(0.9759167433) }, { FRAC_CONST(-0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(0.4623677433), FRAC_CONST(0.8866882324) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(-0.9048270583), FRAC_CONST(0.4257792532) }, { FRAC_CONST(-0.0000000000), FRAC_CONST(-1.0000000000) }, { FRAC_CONST(-0.5724321604), FRAC_CONST(0.8199520707) } }, + { { FRAC_CONST(-0.9048270583), FRAC_CONST(0.4257792532) }, { FRAC_CONST(-0.0000000000), FRAC_CONST(-1.0000000000) }, { FRAC_CONST(-0.5724321604), FRAC_CONST(0.8199520707) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(1.0000000000), FRAC_CONST(0.0000000000) } }, + { { FRAC_CONST(0.2181432247), FRAC_CONST(0.9759167433) }, { FRAC_CONST(-0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(0.4623677433), FRAC_CONST(0.8866882324) } }, + { { FRAC_CONST(0.2181432247), FRAC_CONST(0.9759167433) }, { FRAC_CONST(-0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(0.4623677433), FRAC_CONST(0.8866882324) } }, + { { FRAC_CONST(0.2181432247), FRAC_CONST(0.9759167433) }, { FRAC_CONST(-0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(0.4623677433), FRAC_CONST(0.8866882324) } }, + { { FRAC_CONST(0.2181432247), FRAC_CONST(0.9759167433) }, { FRAC_CONST(-0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(0.4623677433), FRAC_CONST(0.8866882324) } }, + { { FRAC_CONST(-0.9048270583), FRAC_CONST(0.4257792532) }, { FRAC_CONST(-0.0000000000), FRAC_CONST(-1.0000000000) }, { FRAC_CONST(-0.5724321604), FRAC_CONST(0.8199520707) } }, + { { FRAC_CONST(-0.9048270583), FRAC_CONST(0.4257792532) }, { FRAC_CONST(-0.0000000000), FRAC_CONST(-1.0000000000) }, { FRAC_CONST(-0.5724321604), FRAC_CONST(0.8199520707) } }, + { { FRAC_CONST(-0.6129069924), FRAC_CONST(-0.7901550531) }, { FRAC_CONST(0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(-0.9917160273), FRAC_CONST(-0.1284494549) } }, + { { FRAC_CONST(0.2181432247), FRAC_CONST(0.9759167433) }, { FRAC_CONST(-0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(0.4623677433), FRAC_CONST(0.8866882324) } }, + { { FRAC_CONST(0.6374240518), FRAC_CONST(-0.7705131769) }, { FRAC_CONST(-1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(-0.3446428776), FRAC_CONST(-0.9387338758) } }, + { { FRAC_CONST(-0.9048270583), FRAC_CONST(0.4257792532) }, { FRAC_CONST(-0.0000000000), FRAC_CONST(-1.0000000000) }, { FRAC_CONST(-0.5724321604), FRAC_CONST(0.8199520707) } }, + { { FRAC_CONST(-0.6129069924), FRAC_CONST(-0.7901550531) }, { FRAC_CONST(0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(-0.9917160273), FRAC_CONST(-0.1284494549) } }, + { { FRAC_CONST(-0.6129069924), FRAC_CONST(-0.7901550531) }, { FRAC_CONST(0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(-0.9917160273), FRAC_CONST(-0.1284494549) } }, + { { FRAC_CONST(0.6374240518), FRAC_CONST(-0.7705131769) }, { FRAC_CONST(-1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(-0.3446428776), FRAC_CONST(-0.9387338758) } }, + { { FRAC_CONST(0.6374240518), FRAC_CONST(-0.7705131769) }, { FRAC_CONST(-1.0000000000), FRAC_CONST(0.0000000000) }, { FRAC_CONST(-0.3446428776), FRAC_CONST(-0.9387338758) } }, + { { FRAC_CONST(0.8910064697), FRAC_CONST(0.4539906085) }, { FRAC_CONST(0.7071067691), FRAC_CONST(-0.7071067691) }, { FRAC_CONST(0.6730125546), FRAC_CONST(-0.7396310568) } }, + { { FRAC_CONST(-0.6129069924), FRAC_CONST(-0.7901550531) }, { FRAC_CONST(0.7071067691), FRAC_CONST(0.7071067691) }, { FRAC_CONST(-0.9917160273), FRAC_CONST(-0.1284494549) } } +}; + +#if 0 +static float quant_rho[8] = +{ + FRAC_CONST(1.0), FRAC_CONST(0.937), FRAC_CONST(0.84118), FRAC_CONST(0.60092), + FRAC_CONST(0.36764), FRAC_CONST(0.0), FRAC_CONST(-0.589), FRAC_CONST(-1.0) +}; + +static const uint8_t quant_iid_normal[7] = +{ + 2, 4, 7, 10, 14, 18, 25 +}; + +static const uint8_t quant_iid_fine[15] = +{ + 2, 4, 6, 8, 10, 13, 16, 19, 22, 25, 30, 35, 40, 45, 50 +}; +#endif + +static const real_t cos_alphas[] = { + COEF_CONST(1.0000000000), COEF_CONST(0.9841239700), COEF_CONST(0.9594738210), + COEF_CONST(0.8946843079), COEF_CONST(0.8269340931), COEF_CONST(0.7071067812), + COEF_CONST(0.4533210856), COEF_CONST(0.0000000000) +}; + +static const real_t sin_alphas[] = { + COEF_CONST(0.0000000000), COEF_CONST(0.1774824264), COEF_CONST(0.2817977763), + COEF_CONST(0.4466989918), COEF_CONST(0.5622988580), COEF_CONST(0.7071067812), + COEF_CONST(0.8913472911), COEF_CONST(1.0000000000) +}; + +static const real_t cos_betas_normal[][8] = { + { COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9995871699), COEF_CONST(0.9989419133), COEF_CONST(0.9972204583), COEF_CONST(0.9953790839), COEF_CONST(0.9920112747), COEF_CONST(0.9843408180), COEF_CONST(0.9681727381) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9984497744), COEF_CONST(0.9960279377), COEF_CONST(0.9895738413), COEF_CONST(0.9826814632), COEF_CONST(0.9701058164), COEF_CONST(0.9416098832), COEF_CONST(0.8822105900) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9959398908), COEF_CONST(0.9896038018), COEF_CONST(0.9727589768), COEF_CONST(0.9548355329), COEF_CONST(0.9223070404), COEF_CONST(0.8494349490), COEF_CONST(0.7013005535) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9932417400), COEF_CONST(0.9827071856), COEF_CONST(0.9547730996), COEF_CONST(0.9251668930), COEF_CONST(0.8717461589), COEF_CONST(0.7535520592), COEF_CONST(0.5198827312) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9902068095), COEF_CONST(0.9749613872), COEF_CONST(0.9346538534), COEF_CONST(0.8921231300), COEF_CONST(0.8158851259), COEF_CONST(0.6495964302), COEF_CONST(0.3313370772) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9880510933), COEF_CONST(0.9694670261), COEF_CONST(0.9204347876), COEF_CONST(0.8688622825), COEF_CONST(0.7768516704), COEF_CONST(0.5782161800), COEF_CONST(0.2069970356) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9858996945), COEF_CONST(0.9639898866), COEF_CONST(0.9063034786), COEF_CONST(0.8458214608), COEF_CONST(0.7384262300), COEF_CONST(0.5089811277), COEF_CONST(0.0905465944) } +}; + +static const real_t sin_betas_normal[][8] = { + { COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.0287313368), COEF_CONST(-0.0459897147), COEF_CONST(-0.0745074328), COEF_CONST(-0.0960233266), COEF_CONST(-0.1261492408), COEF_CONST(-0.1762757894), COEF_CONST(-0.2502829383) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.0556601118), COEF_CONST(-0.0890412670), COEF_CONST(-0.1440264301), COEF_CONST(-0.1853028382), COEF_CONST(-0.2426823129), COEF_CONST(-0.3367058477), COEF_CONST(-0.4708550466) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.0900207420), COEF_CONST(-0.1438204281), COEF_CONST(-0.2318188366), COEF_CONST(-0.2971348264), COEF_CONST(-0.3864579191), COEF_CONST(-0.5276933461), COEF_CONST(-0.7128657193) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1160639735), COEF_CONST(-0.1851663774), COEF_CONST(-0.2973353800), COEF_CONST(-0.3795605619), COEF_CONST(-0.4899577884), COEF_CONST(-0.6573882369), COEF_CONST(-0.8542376401) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1396082894), COEF_CONST(-0.2223742196), COEF_CONST(-0.3555589603), COEF_CONST(-0.4517923427), COEF_CONST(-0.5782140273), COEF_CONST(-0.7602792104), COEF_CONST(-0.9435124489) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1541266914), COEF_CONST(-0.2452217065), COEF_CONST(-0.3908961522), COEF_CONST(-0.4950538699), COEF_CONST(-0.6296836366), COEF_CONST(-0.8158836002), COEF_CONST(-0.9783415698) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1673373610), COEF_CONST(-0.2659389001), COEF_CONST(-0.4226275012), COEF_CONST(-0.5334660781), COEF_CONST(-0.6743342664), COEF_CONST(-0.8607776784), COEF_CONST(-0.9958922202) } +}; + +static const real_t cos_betas_fine[][8] = { + { COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000), COEF_CONST(1.0000000000) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9995871699), COEF_CONST(0.9989419133), COEF_CONST(0.9972204583), COEF_CONST(0.9953790839), COEF_CONST(0.9920112747), COEF_CONST(0.9843408180), COEF_CONST(0.9681727381) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9984497744), COEF_CONST(0.9960279377), COEF_CONST(0.9895738413), COEF_CONST(0.9826814632), COEF_CONST(0.9701058164), COEF_CONST(0.9416098832), COEF_CONST(0.8822105900) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9968361371), COEF_CONST(0.9918968104), COEF_CONST(0.9787540479), COEF_CONST(0.9647515190), COEF_CONST(0.9392903010), COEF_CONST(0.8820167114), COEF_CONST(0.7645325390) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9950262915), COEF_CONST(0.9872675041), COEF_CONST(0.9666584578), COEF_CONST(0.9447588606), COEF_CONST(0.9050918405), COEF_CONST(0.8165997379), COEF_CONST(0.6383824796) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9932417400), COEF_CONST(0.9827071856), COEF_CONST(0.9547730996), COEF_CONST(0.9251668930), COEF_CONST(0.8717461589), COEF_CONST(0.7535520592), COEF_CONST(0.5198827312) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9908827998), COEF_CONST(0.9766855904), COEF_CONST(0.9391249214), COEF_CONST(0.8994531782), COEF_CONST(0.8282352693), COEF_CONST(0.6723983174), COEF_CONST(0.3719473225) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9890240165), COEF_CONST(0.9719459866), COEF_CONST(0.9268448110), COEF_CONST(0.8793388536), COEF_CONST(0.7944023271), COEF_CONST(0.6101812098), COEF_CONST(0.2621501145) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9876350461), COEF_CONST(0.9684073447), COEF_CONST(0.9176973944), COEF_CONST(0.8643930070), COEF_CONST(0.7693796058), COEF_CONST(0.5646720713), COEF_CONST(0.1838899556) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9866247085), COEF_CONST(0.9658349704), COEF_CONST(0.9110590761), COEF_CONST(0.8535668048), COEF_CONST(0.7513165426), COEF_CONST(0.5320914819), COEF_CONST(0.1289530943) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9858996945), COEF_CONST(0.9639898866), COEF_CONST(0.9063034786), COEF_CONST(0.8458214608), COEF_CONST(0.7384262300), COEF_CONST(0.5089811277), COEF_CONST(0.0905465944) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9851245614), COEF_CONST(0.9620180268), COEF_CONST(0.9012265590), COEF_CONST(0.8375623272), COEF_CONST(0.7247108045), COEF_CONST(0.4845204297), COEF_CONST(0.0504115003) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9846869856), COEF_CONST(0.9609052357), COEF_CONST(0.8983639533), COEF_CONST(0.8329098386), COEF_CONST(0.7169983441), COEF_CONST(0.4708245354), COEF_CONST(0.0281732509) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9844406325), COEF_CONST(0.9602788522), COEF_CONST(0.8967533934), COEF_CONST(0.8302936455), COEF_CONST(0.7126658102), COEF_CONST(0.4631492839), COEF_CONST(0.0157851140) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9843020502), COEF_CONST(0.9599265269), COEF_CONST(0.8958477331), COEF_CONST(0.8288229094), COEF_CONST(0.7102315840), COEF_CONST(0.4588429315), COEF_CONST(0.0088578059) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9842241136), COEF_CONST(0.9597283916), COEF_CONST(0.8953385094), COEF_CONST(0.8279961409), COEF_CONST(0.7088635748), COEF_CONST(0.4564246834), COEF_CONST(0.0049751355) } +}; + +static const real_t sin_betas_fine[][8] = { + { COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000), COEF_CONST(0.0000000000) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.0287313368), COEF_CONST(-0.0459897147), COEF_CONST(-0.0745074328), COEF_CONST(-0.0960233266), COEF_CONST(-0.1261492408), COEF_CONST(-0.1762757894), COEF_CONST(-0.2502829383) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.0556601118), COEF_CONST(-0.0890412670), COEF_CONST(-0.1440264301), COEF_CONST(-0.1853028382), COEF_CONST(-0.2426823129), COEF_CONST(-0.3367058477), COEF_CONST(-0.4708550466) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.0794840594), COEF_CONST(-0.1270461238), COEF_CONST(-0.2050378347), COEF_CONST(-0.2631625097), COEF_CONST(-0.3431234916), COEF_CONST(-0.4712181245), COEF_CONST(-0.6445851354) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.0996126459), COEF_CONST(-0.1590687758), COEF_CONST(-0.2560691819), COEF_CONST(-0.3277662204), COEF_CONST(-0.4252161335), COEF_CONST(-0.5772043556), COEF_CONST(-0.7697193058) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1160639735), COEF_CONST(-0.1851663774), COEF_CONST(-0.2973353800), COEF_CONST(-0.3795605619), COEF_CONST(-0.4899577884), COEF_CONST(-0.6573882369), COEF_CONST(-0.8542376401) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1347266752), COEF_CONST(-0.2146747714), COEF_CONST(-0.3435758752), COEF_CONST(-0.4370171396), COEF_CONST(-0.5603805303), COEF_CONST(-0.7401895046), COEF_CONST(-0.9282538388) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1477548470), COEF_CONST(-0.2352041647), COEF_CONST(-0.3754446647), COEF_CONST(-0.4761965776), COEF_CONST(-0.6073919186), COEF_CONST(-0.7922618830), COEF_CONST(-0.9650271071) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1567705832), COEF_CONST(-0.2493736450), COEF_CONST(-0.3972801182), COEF_CONST(-0.5028167951), COEF_CONST(-0.6387918458), COEF_CONST(-0.8253153651), COEF_CONST(-0.9829468369) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1630082348), COEF_CONST(-0.2591578860), COEF_CONST(-0.4122758299), COEF_CONST(-0.5209834064), COEF_CONST(-0.6599420072), COEF_CONST(-0.8466868694), COEF_CONST(-0.9916506943) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1673373610), COEF_CONST(-0.2659389001), COEF_CONST(-0.4226275012), COEF_CONST(-0.5334660781), COEF_CONST(-0.6743342664), COEF_CONST(-0.8607776784), COEF_CONST(-0.9958922202) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1718417832), COEF_CONST(-0.2729859267), COEF_CONST(-0.4333482310), COEF_CONST(-0.5463417868), COEF_CONST(-0.6890531546), COEF_CONST(-0.8747799456), COEF_CONST(-0.9987285320) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1743316967), COEF_CONST(-0.2768774604), COEF_CONST(-0.4392518725), COEF_CONST(-0.5534087104), COEF_CONST(-0.6970748701), COEF_CONST(-0.8822268738), COEF_CONST(-0.9996030552) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1757175038), COEF_CONST(-0.2790421580), COEF_CONST(-0.4425306221), COEF_CONST(-0.5573261722), COEF_CONST(-0.7015037013), COEF_CONST(-0.8862802834), COEF_CONST(-0.9998754073) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1764921355), COEF_CONST(-0.2802517850), COEF_CONST(-0.4443611583), COEF_CONST(-0.5595110229), COEF_CONST(-0.7039681080), COEF_CONST(-0.8885173967), COEF_CONST(-0.9999607689) }, + { COEF_CONST(0.0000000000), COEF_CONST(-0.1769262394), COEF_CONST(-0.2809295540), COEF_CONST(-0.4453862969), COEF_CONST(-0.5607337966), COEF_CONST(-0.7053456119), COEF_CONST(-0.8897620516), COEF_CONST(-0.9999876239) } +}; + +static const real_t sincos_alphas_B_normal[][8] = { + { COEF_CONST(0.0561454100), COEF_CONST(0.0526385859), COEF_CONST(0.0472937334), COEF_CONST(0.0338410641), COEF_CONST(0.0207261065), COEF_CONST(0.0028205635), COEF_CONST(0.0028205635), COEF_CONST(0.0028205635) }, + { COEF_CONST(0.1249065138), COEF_CONST(0.1173697697), COEF_CONST(0.1057888284), COEF_CONST(0.0761985131), COEF_CONST(0.0468732723), COEF_CONST(0.0063956103), COEF_CONST(0.0063956103), COEF_CONST(0.0063956103) }, + { COEF_CONST(0.1956693050), COEF_CONST(0.1846090179), COEF_CONST(0.1673645109), COEF_CONST(0.1220621836), COEF_CONST(0.0757362479), COEF_CONST(0.0103882630), COEF_CONST(0.0103882630), COEF_CONST(0.0103882630) }, + { COEF_CONST(0.3015113269), COEF_CONST(0.2870525790), COEF_CONST(0.2637738799), COEF_CONST(0.1984573949), COEF_CONST(0.1260749909), COEF_CONST(0.0175600126), COEF_CONST(0.0175600126), COEF_CONST(0.0175600126) }, + { COEF_CONST(0.4078449476), COEF_CONST(0.3929852420), COEF_CONST(0.3680589270), COEF_CONST(0.2911029124), COEF_CONST(0.1934512363), COEF_CONST(0.0278686716), COEF_CONST(0.0278686716), COEF_CONST(0.0278686716) }, + { COEF_CONST(0.5336171261), COEF_CONST(0.5226637762), COEF_CONST(0.5033652606), COEF_CONST(0.4349162672), COEF_CONST(0.3224682122), COEF_CONST(0.0521999036), COEF_CONST(0.0521999036), COEF_CONST(0.0521999036) }, + { COEF_CONST(0.6219832023), COEF_CONST(0.6161847276), COEF_CONST(0.6057251063), COEF_CONST(0.5654342668), COEF_CONST(0.4826149915), COEF_CONST(0.1058044758), COEF_CONST(0.1058044758), COEF_CONST(0.1058044758) }, + { COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657) }, + { COEF_CONST(0.7830305572), COEF_CONST(0.7876016373), COEF_CONST(0.7956739618), COEF_CONST(0.8247933372), COEF_CONST(0.8758325942), COEF_CONST(0.9943869542), COEF_CONST(0.9943869542), COEF_CONST(0.9943869542) }, + { COEF_CONST(0.8457261833), COEF_CONST(0.8525388778), COEF_CONST(0.8640737401), COEF_CONST(0.9004708933), COEF_CONST(0.9465802987), COEF_CONST(0.9986366532), COEF_CONST(0.9986366532), COEF_CONST(0.9986366532) }, + { COEF_CONST(0.9130511848), COEF_CONST(0.9195447612), COEF_CONST(0.9298024282), COEF_CONST(0.9566917233), COEF_CONST(0.9811098801), COEF_CONST(0.9996115928), COEF_CONST(0.9996115928), COEF_CONST(0.9996115928) }, + { COEF_CONST(0.9534625907), COEF_CONST(0.9579148236), COEF_CONST(0.9645845234), COEF_CONST(0.9801095128), COEF_CONST(0.9920207064), COEF_CONST(0.9998458099), COEF_CONST(0.9998458099), COEF_CONST(0.9998458099) }, + { COEF_CONST(0.9806699215), COEF_CONST(0.9828120260), COEF_CONST(0.9858950861), COEF_CONST(0.9925224431), COEF_CONST(0.9971278825), COEF_CONST(0.9999460406), COEF_CONST(0.9999460406), COEF_CONST(0.9999460406) }, + { COEF_CONST(0.9921685024), COEF_CONST(0.9930882705), COEF_CONST(0.9943886135), COEF_CONST(0.9970926648), COEF_CONST(0.9989008403), COEF_CONST(0.9999795479), COEF_CONST(0.9999795479), COEF_CONST(0.9999795479) }, + { COEF_CONST(0.9984226014), COEF_CONST(0.9986136287), COEF_CONST(0.9988810254), COEF_CONST(0.9994272242), COEF_CONST(0.9997851906), COEF_CONST(0.9999960221), COEF_CONST(0.9999960221), COEF_CONST(0.9999960221) } +}; + +static const real_t sincos_alphas_B_fine[][8] = { + { COEF_CONST(0.0031622158), COEF_CONST(0.0029630181), COEF_CONST(0.0026599892), COEF_CONST(0.0019002704), COEF_CONST(0.0011626042), COEF_CONST(0.0001580278), COEF_CONST(0.0001580278), COEF_CONST(0.0001580278) }, + { COEF_CONST(0.0056232673), COEF_CONST(0.0052689825), COEF_CONST(0.0047302825), COEF_CONST(0.0033791756), COEF_CONST(0.0020674015), COEF_CONST(0.0002811710), COEF_CONST(0.0002811710), COEF_CONST(0.0002811710) }, + { COEF_CONST(0.0099994225), COEF_CONST(0.0093696693), COEF_CONST(0.0084117414), COEF_CONST(0.0060093796), COEF_CONST(0.0036766009), COEF_CONST(0.0005000392), COEF_CONST(0.0005000392), COEF_CONST(0.0005000392) }, + { COEF_CONST(0.0177799194), COEF_CONST(0.0166607102), COEF_CONST(0.0149581377), COEF_CONST(0.0106875809), COEF_CONST(0.0065392545), COEF_CONST(0.0008893767), COEF_CONST(0.0008893767), COEF_CONST(0.0008893767) }, + { COEF_CONST(0.0316069684), COEF_CONST(0.0296211579), COEF_CONST(0.0265987295), COEF_CONST(0.0190113813), COEF_CONST(0.0116349973), COEF_CONST(0.0015826974), COEF_CONST(0.0015826974), COEF_CONST(0.0015826974) }, + { COEF_CONST(0.0561454100), COEF_CONST(0.0526385859), COEF_CONST(0.0472937334), COEF_CONST(0.0338410641), COEF_CONST(0.0207261065), COEF_CONST(0.0028205635), COEF_CONST(0.0028205635), COEF_CONST(0.0028205635) }, + { COEF_CONST(0.0791834041), COEF_CONST(0.0742798103), COEF_CONST(0.0667907269), COEF_CONST(0.0478705292), COEF_CONST(0.0293500747), COEF_CONST(0.0039966755), COEF_CONST(0.0039966755), COEF_CONST(0.0039966755) }, + { COEF_CONST(0.1115021177), COEF_CONST(0.1047141985), COEF_CONST(0.0943053154), COEF_CONST(0.0678120561), COEF_CONST(0.0416669150), COEF_CONST(0.0056813213), COEF_CONST(0.0056813213), COEF_CONST(0.0056813213) }, + { COEF_CONST(0.1565355066), COEF_CONST(0.1473258371), COEF_CONST(0.1330924027), COEF_CONST(0.0963282233), COEF_CONST(0.0594509113), COEF_CONST(0.0081277946), COEF_CONST(0.0081277946), COEF_CONST(0.0081277946) }, + { COEF_CONST(0.2184643682), COEF_CONST(0.2064579524), COEF_CONST(0.1876265439), COEF_CONST(0.1375744167), COEF_CONST(0.0856896681), COEF_CONST(0.0117817338), COEF_CONST(0.0117817338), COEF_CONST(0.0117817338) }, + { COEF_CONST(0.3015113269), COEF_CONST(0.2870525790), COEF_CONST(0.2637738799), COEF_CONST(0.1984573949), COEF_CONST(0.1260749909), COEF_CONST(0.0175600126), COEF_CONST(0.0175600126), COEF_CONST(0.0175600126) }, + { COEF_CONST(0.3698741335), COEF_CONST(0.3547727297), COEF_CONST(0.3298252076), COEF_CONST(0.2556265829), COEF_CONST(0.1665990017), COEF_CONST(0.0236344541), COEF_CONST(0.0236344541), COEF_CONST(0.0236344541) }, + { COEF_CONST(0.4480623975), COEF_CONST(0.4339410024), COEF_CONST(0.4098613774), COEF_CONST(0.3322709108), COEF_CONST(0.2266784729), COEF_CONST(0.0334094131), COEF_CONST(0.0334094131), COEF_CONST(0.0334094131) }, + { COEF_CONST(0.5336171261), COEF_CONST(0.5226637762), COEF_CONST(0.5033652606), COEF_CONST(0.4349162672), COEF_CONST(0.3224682122), COEF_CONST(0.0521999036), COEF_CONST(0.0521999036), COEF_CONST(0.0521999036) }, + { COEF_CONST(0.6219832023), COEF_CONST(0.6161847276), COEF_CONST(0.6057251063), COEF_CONST(0.5654342668), COEF_CONST(0.4826149915), COEF_CONST(0.1058044758), COEF_CONST(0.1058044758), COEF_CONST(0.1058044758) }, + { COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657), COEF_CONST(0.7071067657) }, + { COEF_CONST(0.7830305572), COEF_CONST(0.7876016373), COEF_CONST(0.7956739618), COEF_CONST(0.8247933372), COEF_CONST(0.8758325942), COEF_CONST(0.9943869542), COEF_CONST(0.9943869542), COEF_CONST(0.9943869542) }, + { COEF_CONST(0.8457261833), COEF_CONST(0.8525388778), COEF_CONST(0.8640737401), COEF_CONST(0.9004708933), COEF_CONST(0.9465802987), COEF_CONST(0.9986366532), COEF_CONST(0.9986366532), COEF_CONST(0.9986366532) }, + { COEF_CONST(0.8940022267), COEF_CONST(0.9009412572), COEF_CONST(0.9121477564), COEF_CONST(0.9431839770), COEF_CONST(0.9739696219), COEF_CONST(0.9994417480), COEF_CONST(0.9994417480), COEF_CONST(0.9994417480) }, + { COEF_CONST(0.9290818561), COEF_CONST(0.9349525662), COEF_CONST(0.9440420138), COEF_CONST(0.9667755833), COEF_CONST(0.9860247275), COEF_CONST(0.9997206664), COEF_CONST(0.9997206664), COEF_CONST(0.9997206664) }, + { COEF_CONST(0.9534625907), COEF_CONST(0.9579148236), COEF_CONST(0.9645845234), COEF_CONST(0.9801095128), COEF_CONST(0.9920207064), COEF_CONST(0.9998458099), COEF_CONST(0.9998458099), COEF_CONST(0.9998458099) }, + { COEF_CONST(0.9758449068), COEF_CONST(0.9784554646), COEF_CONST(0.9822404252), COEF_CONST(0.9904914275), COEF_CONST(0.9963218730), COEF_CONST(0.9999305926), COEF_CONST(0.9999305926), COEF_CONST(0.9999305926) }, + { COEF_CONST(0.9876723320), COEF_CONST(0.9890880155), COEF_CONST(0.9911036356), COEF_CONST(0.9953496173), COEF_CONST(0.9982312259), COEF_CONST(0.9999669685), COEF_CONST(0.9999669685), COEF_CONST(0.9999669685) }, + { COEF_CONST(0.9937641889), COEF_CONST(0.9945023501), COEF_CONST(0.9955433130), COEF_CONST(0.9976981117), COEF_CONST(0.9991315558), COEF_CONST(0.9999838610), COEF_CONST(0.9999838610), COEF_CONST(0.9999838610) }, + { COEF_CONST(0.9968600642), COEF_CONST(0.9972374385), COEF_CONST(0.9977670024), COEF_CONST(0.9988535464), COEF_CONST(0.9995691924), COEF_CONST(0.9999920129), COEF_CONST(0.9999920129), COEF_CONST(0.9999920129) }, + { COEF_CONST(0.9984226014), COEF_CONST(0.9986136287), COEF_CONST(0.9988810254), COEF_CONST(0.9994272242), COEF_CONST(0.9997851906), COEF_CONST(0.9999960221), COEF_CONST(0.9999960221), COEF_CONST(0.9999960221) }, + { COEF_CONST(0.9995003746), COEF_CONST(0.9995611974), COEF_CONST(0.9996461891), COEF_CONST(0.9998192657), COEF_CONST(0.9999323103), COEF_CONST(0.9999987475), COEF_CONST(0.9999987475), COEF_CONST(0.9999987475) }, + { COEF_CONST(0.9998419236), COEF_CONST(0.9998611991), COEF_CONST(0.9998881193), COEF_CONST(0.9999428861), COEF_CONST(0.9999786185), COEF_CONST(0.9999996045), COEF_CONST(0.9999996045), COEF_CONST(0.9999996045) }, + { COEF_CONST(0.9999500038), COEF_CONST(0.9999561034), COEF_CONST(0.9999646206), COEF_CONST(0.9999819429), COEF_CONST(0.9999932409), COEF_CONST(0.9999998750), COEF_CONST(0.9999998750), COEF_CONST(0.9999998750) }, + { COEF_CONST(0.9999841890), COEF_CONST(0.9999861183), COEF_CONST(0.9999888121), COEF_CONST(0.9999942902), COEF_CONST(0.9999978628), COEF_CONST(0.9999999605), COEF_CONST(0.9999999605), COEF_CONST(0.9999999605) }, + { COEF_CONST(0.9999950000), COEF_CONST(0.9999956102), COEF_CONST(0.9999964621), COEF_CONST(0.9999981945), COEF_CONST(0.9999993242), COEF_CONST(0.9999999875), COEF_CONST(0.9999999875), COEF_CONST(0.9999999875) } +}; + +static const real_t cos_gammas_normal[][8] = { + { COEF_CONST(1.0000000000), COEF_CONST(0.9841239707), COEF_CONST(0.9594738226), COEF_CONST(0.8946843024), COEF_CONST(0.8269341029), COEF_CONST(0.7245688486), COEF_CONST(0.7245688486), COEF_CONST(0.7245688486) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9849690570), COEF_CONST(0.9617776789), COEF_CONST(0.9020941550), COEF_CONST(0.8436830391), COEF_CONST(0.7846832804), COEF_CONST(0.7846832804), COEF_CONST(0.7846832804) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9871656089), COEF_CONST(0.9676774734), COEF_CONST(0.9199102884), COEF_CONST(0.8785067015), COEF_CONST(0.8464232214), COEF_CONST(0.8464232214), COEF_CONST(0.8464232214) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9913533967), COEF_CONST(0.9786000177), COEF_CONST(0.9496063381), COEF_CONST(0.9277157252), COEF_CONST(0.9133354077), COEF_CONST(0.9133354077), COEF_CONST(0.9133354077) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9948924435), COEF_CONST(0.9875319180), COEF_CONST(0.9716329849), COEF_CONST(0.9604805241), COEF_CONST(0.9535949574), COEF_CONST(0.9535949574), COEF_CONST(0.9535949574) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9977406278), COEF_CONST(0.9945423840), COEF_CONST(0.9878736667), COEF_CONST(0.9833980494), COEF_CONST(0.9807207440), COEF_CONST(0.9807207440), COEF_CONST(0.9807207440) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9990607067), COEF_CONST(0.9977417734), COEF_CONST(0.9950323970), COEF_CONST(0.9932453273), COEF_CONST(0.9921884740), COEF_CONST(0.9921884740), COEF_CONST(0.9921884740) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9998081748), COEF_CONST(0.9995400312), COEF_CONST(0.9989936459), COEF_CONST(0.9986365356), COEF_CONST(0.9984265591), COEF_CONST(0.9984265591), COEF_CONST(0.9984265591) } +}; + +static const real_t cos_gammas_fine[][8] = { + { COEF_CONST(1.0000000000), COEF_CONST(0.9841239707), COEF_CONST(0.9594738226), COEF_CONST(0.8946843024), COEF_CONST(0.8269341029), COEF_CONST(0.7245688486), COEF_CONST(0.7245688486), COEF_CONST(0.7245688486) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9849690570), COEF_CONST(0.9617776789), COEF_CONST(0.9020941550), COEF_CONST(0.8436830391), COEF_CONST(0.7846832804), COEF_CONST(0.7846832804), COEF_CONST(0.7846832804) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9871656089), COEF_CONST(0.9676774734), COEF_CONST(0.9199102884), COEF_CONST(0.8785067015), COEF_CONST(0.8464232214), COEF_CONST(0.8464232214), COEF_CONST(0.8464232214) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9899597309), COEF_CONST(0.9750098690), COEF_CONST(0.9402333855), COEF_CONST(0.9129698759), COEF_CONST(0.8943765944), COEF_CONST(0.8943765944), COEF_CONST(0.8943765944) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9926607607), COEF_CONST(0.9819295710), COEF_CONST(0.9580160104), COEF_CONST(0.9404993670), COEF_CONST(0.9293004472), COEF_CONST(0.9293004472), COEF_CONST(0.9293004472) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9948924435), COEF_CONST(0.9875319180), COEF_CONST(0.9716329849), COEF_CONST(0.9604805241), COEF_CONST(0.9535949574), COEF_CONST(0.9535949574), COEF_CONST(0.9535949574) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9972074644), COEF_CONST(0.9932414270), COEF_CONST(0.9849197629), COEF_CONST(0.9792926592), COEF_CONST(0.9759092525), COEF_CONST(0.9759092525), COEF_CONST(0.9759092525) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9985361982), COEF_CONST(0.9964742028), COEF_CONST(0.9922136306), COEF_CONST(0.9893845420), COEF_CONST(0.9877041371), COEF_CONST(0.9877041371), COEF_CONST(0.9877041371) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9992494366), COEF_CONST(0.9981967170), COEF_CONST(0.9960386625), COEF_CONST(0.9946185834), COEF_CONST(0.9937800239), COEF_CONST(0.9937800239), COEF_CONST(0.9937800239) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9996194722), COEF_CONST(0.9990869422), COEF_CONST(0.9979996269), COEF_CONST(0.9972873651), COEF_CONST(0.9968679747), COEF_CONST(0.9968679747), COEF_CONST(0.9968679747) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9998081748), COEF_CONST(0.9995400312), COEF_CONST(0.9989936459), COEF_CONST(0.9986365356), COEF_CONST(0.9984265591), COEF_CONST(0.9984265591), COEF_CONST(0.9984265591) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9999390971), COEF_CONST(0.9998540271), COEF_CONST(0.9996809352), COEF_CONST(0.9995679735), COEF_CONST(0.9995016284), COEF_CONST(0.9995016284), COEF_CONST(0.9995016284) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9999807170), COEF_CONST(0.9999537862), COEF_CONST(0.9998990191), COEF_CONST(0.9998632947), COEF_CONST(0.9998423208), COEF_CONST(0.9998423208), COEF_CONST(0.9998423208) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9999938979), COEF_CONST(0.9999853814), COEF_CONST(0.9999680568), COEF_CONST(0.9999567596), COEF_CONST(0.9999501270), COEF_CONST(0.9999501270), COEF_CONST(0.9999501270) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9999980703), COEF_CONST(0.9999953731), COEF_CONST(0.9999898968), COEF_CONST(0.9999863277), COEF_CONST(0.9999842265), COEF_CONST(0.9999842265), COEF_CONST(0.9999842265) }, + { COEF_CONST(1.0000000000), COEF_CONST(0.9999993891), COEF_CONST(0.9999985397), COEF_CONST(0.9999968037), COEF_CONST(0.9999956786), COEF_CONST(0.9999950155), COEF_CONST(0.9999950155), COEF_CONST(0.9999950155) } +}; + +static const real_t sin_gammas_normal[][8] = { + { COEF_CONST(0.0000000000), COEF_CONST(0.1774824223), COEF_CONST(0.2817977711), COEF_CONST(0.4466990028), COEF_CONST(0.5622988435), COEF_CONST(0.6892024258), COEF_CONST(0.6892024258), COEF_CONST(0.6892024258) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.1727308798), COEF_CONST(0.2738315110), COEF_CONST(0.4315392630), COEF_CONST(0.5368416242), COEF_CONST(0.6198968861), COEF_CONST(0.6198968861), COEF_CONST(0.6198968861) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.1596999079), COEF_CONST(0.2521910140), COEF_CONST(0.3921288836), COEF_CONST(0.4777300236), COEF_CONST(0.5325107795), COEF_CONST(0.5325107795), COEF_CONST(0.5325107795) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.1312190642), COEF_CONST(0.2057717310), COEF_CONST(0.3134450552), COEF_CONST(0.3732874674), COEF_CONST(0.4072080955), COEF_CONST(0.4072080955), COEF_CONST(0.4072080955) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.1009407043), COEF_CONST(0.1574189028), COEF_CONST(0.2364938532), COEF_CONST(0.2783471983), COEF_CONST(0.3010924396), COEF_CONST(0.3010924396), COEF_CONST(0.3010924396) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0671836269), COEF_CONST(0.1043333428), COEF_CONST(0.1552598422), COEF_CONST(0.1814615013), COEF_CONST(0.1954144885), COEF_CONST(0.1954144885), COEF_CONST(0.1954144885) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0433324862), COEF_CONST(0.0671666110), COEF_CONST(0.0995516398), COEF_CONST(0.1160332699), COEF_CONST(0.1247478739), COEF_CONST(0.1247478739), COEF_CONST(0.1247478739) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0195860576), COEF_CONST(0.0303269852), COEF_CONST(0.0448519274), COEF_CONST(0.0522022017), COEF_CONST(0.0560750040), COEF_CONST(0.0560750040), COEF_CONST(0.0560750040) } +}; + +static const real_t sin_gammas_fine[][8] = { + { COEF_CONST(0.0000000000), COEF_CONST(0.1774824223), COEF_CONST(0.2817977711), COEF_CONST(0.4466990028), COEF_CONST(0.5622988435), COEF_CONST(0.6892024258), COEF_CONST(0.6892024258), COEF_CONST(0.6892024258) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.1727308798), COEF_CONST(0.2738315110), COEF_CONST(0.4315392630), COEF_CONST(0.5368416242), COEF_CONST(0.6198968861), COEF_CONST(0.6198968861), COEF_CONST(0.6198968861) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.1596999079), COEF_CONST(0.2521910140), COEF_CONST(0.3921288836), COEF_CONST(0.4777300236), COEF_CONST(0.5325107795), COEF_CONST(0.5325107795), COEF_CONST(0.5325107795) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.1413496768), COEF_CONST(0.2221615526), COEF_CONST(0.3405307340), COEF_CONST(0.4080269669), COEF_CONST(0.4473147744), COEF_CONST(0.4473147744), COEF_CONST(0.4473147744) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.1209322714), COEF_CONST(0.1892467110), COEF_CONST(0.2867147079), COEF_CONST(0.3397954394), COEF_CONST(0.3693246252), COEF_CONST(0.3693246252), COEF_CONST(0.3693246252) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.1009407043), COEF_CONST(0.1574189028), COEF_CONST(0.2364938532), COEF_CONST(0.2783471983), COEF_CONST(0.3010924396), COEF_CONST(0.3010924396), COEF_CONST(0.3010924396) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0746811420), COEF_CONST(0.1160666523), COEF_CONST(0.1730117353), COEF_CONST(0.2024497161), COEF_CONST(0.2181768341), COEF_CONST(0.2181768341), COEF_CONST(0.2181768341) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0540875291), COEF_CONST(0.0838997203), COEF_CONST(0.1245476266), COEF_CONST(0.1453211203), COEF_CONST(0.1563346972), COEF_CONST(0.1563346972), COEF_CONST(0.1563346972) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0387371058), COEF_CONST(0.0600276114), COEF_CONST(0.0889212171), COEF_CONST(0.1036044086), COEF_CONST(0.1113609634), COEF_CONST(0.1113609634), COEF_CONST(0.1113609634) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0275846110), COEF_CONST(0.0427233177), COEF_CONST(0.0632198125), COEF_CONST(0.0736064637), COEF_CONST(0.0790837596), COEF_CONST(0.0790837596), COEF_CONST(0.0790837596) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0195860576), COEF_CONST(0.0303269852), COEF_CONST(0.0448519274), COEF_CONST(0.0522022017), COEF_CONST(0.0560750040), COEF_CONST(0.0560750040), COEF_CONST(0.0560750040) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0110363955), COEF_CONST(0.0170857974), COEF_CONST(0.0252592108), COEF_CONST(0.0293916021), COEF_CONST(0.0315673054), COEF_CONST(0.0315673054), COEF_CONST(0.0315673054) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0062101284), COEF_CONST(0.0096138203), COEF_CONST(0.0142109649), COEF_CONST(0.0165345659), COEF_CONST(0.0177576316), COEF_CONST(0.0177576316), COEF_CONST(0.0177576316) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0034934509), COEF_CONST(0.0054071189), COEF_CONST(0.0079928316), COEF_CONST(0.0092994041), COEF_CONST(0.0099871631), COEF_CONST(0.0099871631), COEF_CONST(0.0099871631) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0019645397), COEF_CONST(0.0030419905), COEF_CONST(0.0044951511), COEF_CONST(0.0052291853), COEF_CONST(0.0056166498), COEF_CONST(0.0056166498), COEF_CONST(0.0056166498) }, + { COEF_CONST(0.0000000000), COEF_CONST(0.0011053943), COEF_CONST(0.0017089869), COEF_CONST(0.0025283670), COEF_CONST(0.0029398552), COEF_CONST(0.0031573685), COEF_CONST(0.0031573685), COEF_CONST(0.0031573685) } +}; + +static const real_t sf_iid_normal[] = { + COEF_CONST(1.4119827747), COEF_CONST(1.4031381607), COEF_CONST(1.3868767023), + COEF_CONST(1.3483997583), COEF_CONST(1.2912493944), COEF_CONST(1.1960374117), + COEF_CONST(1.1073724031), COEF_CONST(1.0000000000), COEF_CONST(0.8796171546), + COEF_CONST(0.7546485662), COEF_CONST(0.5767799020), COEF_CONST(0.4264014363), + COEF_CONST(0.2767182887), COEF_CONST(0.1766446233), COEF_CONST(0.0794016272) +}; + +static const real_t sf_iid_fine[] = { + COEF_CONST(1.4142065048), COEF_CONST(1.4141912460), COEF_CONST(1.4141428471), + COEF_CONST(1.4139900208), COEF_CONST(1.4135069847), COEF_CONST(1.4119827747), + COEF_CONST(1.4097729921), COEF_CONST(1.4053947926), COEF_CONST(1.3967796564), + COEF_CONST(1.3800530434), COEF_CONST(1.3483997583), COEF_CONST(1.3139201403), + COEF_CONST(1.2643101215), COEF_CONST(1.1960374117), COEF_CONST(1.1073724031), + COEF_CONST(1.0000000000), COEF_CONST(0.8796171546), COEF_CONST(0.7546485662), + COEF_CONST(0.6336560845), COEF_CONST(0.5230810642), COEF_CONST(0.4264014363), + COEF_CONST(0.3089554012), COEF_CONST(0.2213746458), COEF_CONST(0.1576878875), + COEF_CONST(0.1119822487), COEF_CONST(0.0794016272), COEF_CONST(0.0446990170), + COEF_CONST(0.0251446925), COEF_CONST(0.0141414283), COEF_CONST(0.0079525812), + COEF_CONST(0.0044721137) +}; + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/pulse.c b/third_party/faad_hdc/upstream/libfaad/pulse.c new file mode 100644 index 000000000..8d875e9bf --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/pulse.c @@ -0,0 +1,58 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: pulse.c,v 1.21 2007/11/01 12:33:34 menno Exp $ +**/ +#include "common.h" +#include "structs.h" + +#include "syntax.h" +#include "pulse.h" + +uint8_t pulse_decode(ic_stream *ics, int16_t *spec_data, uint16_t framelen) +{ + uint8_t i; + uint16_t k; + pulse_info *pul = &(ics->pul); + + k = min(ics->swb_offset[pul->pulse_start_sfb], ics->swb_offset_max); + + for (i = 0; i <= pul->number_pulse; i++) + { + k += pul->pulse_offset[i]; + + if (k >= framelen) + return 15; /* should not be possible */ + + if (spec_data[k] > 0) + spec_data[k] += pul->pulse_amp[i]; + else + spec_data[k] -= pul->pulse_amp[i]; + } + + return 0; +} diff --git a/third_party/faad_hdc/upstream/libfaad/pulse.h b/third_party/faad_hdc/upstream/libfaad/pulse.h new file mode 100644 index 000000000..5a689019d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/pulse.h @@ -0,0 +1,43 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: pulse.h,v 1.20 2007/11/01 12:33:34 menno Exp $ +**/ + +#ifndef __PULSE_H__ +#define __PULSE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t pulse_decode(ic_stream *ics, int16_t *spec_coef, uint16_t framelen); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/rvlc.c b/third_party/faad_hdc/upstream/libfaad/rvlc.c new file mode 100644 index 000000000..da7b8709e --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/rvlc.c @@ -0,0 +1,551 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: rvlc.c,v 1.21 2007/11/01 12:33:34 menno Exp $ +**/ + +/* RVLC scalefactor decoding + * + * RVLC works like this: + * 1. Only symmetric huffman codewords are used + * 2. Total length of the scalefactor data is stored in the bitsream + * 3. Scalefactors are DPCM coded + * 4. Next to the starting value for DPCM the ending value is also stored + * + * With all this it is possible to read the scalefactor data from 2 sides. + * If there is a bit error in the scalefactor data it is possible to start + * decoding from the other end of the data, to find all but 1 scalefactor. + */ + +#include "common.h" +#include "structs.h" + +#include + +#include "syntax.h" +#include "bits.h" +#include "rvlc.h" + + +#ifdef ERROR_RESILIENCE + +//#define PRINT_RVLC + +/* static function declarations */ +static uint8_t rvlc_decode_sf_forward(ic_stream *ics, + bitfile *ld_sf, + bitfile *ld_esc, + uint8_t *is_used); +#if 0 +static uint8_t rvlc_decode_sf_reverse(ic_stream *ics, + bitfile *ld_sf, + bitfile *ld_esc, + uint8_t is_used); +#endif +static int8_t rvlc_huffman_sf(bitfile *ld_sf, bitfile *ld_esc /*, + int8_t direction*/); +static int8_t rvlc_huffman_esc(bitfile *ld_esc /*, int8_t direction*/); + + +uint8_t rvlc_scale_factor_data(ic_stream *ics, bitfile *ld) +{ + uint8_t bits = 9; + + ics->sf_concealment = faad_get1bit(ld + DEBUGVAR(1,149,"rvlc_scale_factor_data(): sf_concealment")); + ics->rev_global_gain = (uint8_t)faad_getbits(ld, 8 + DEBUGVAR(1,150,"rvlc_scale_factor_data(): rev_global_gain")); + + if (ics->window_sequence == EIGHT_SHORT_SEQUENCE) + bits = 11; + + /* the number of bits used for the huffman codewords */ + ics->length_of_rvlc_sf = (uint16_t)faad_getbits(ld, bits + DEBUGVAR(1,151,"rvlc_scale_factor_data(): length_of_rvlc_sf")); + + if (ics->noise_used) + { + ics->dpcm_noise_nrg = (uint16_t)faad_getbits(ld, 9 + DEBUGVAR(1,152,"rvlc_scale_factor_data(): dpcm_noise_nrg")); + + ics->length_of_rvlc_sf -= 9; + } + + ics->sf_escapes_present = faad_get1bit(ld + DEBUGVAR(1,153,"rvlc_scale_factor_data(): sf_escapes_present")); + + if (ics->sf_escapes_present) + { + ics->length_of_rvlc_escapes = (uint8_t)faad_getbits(ld, 8 + DEBUGVAR(1,154,"rvlc_scale_factor_data(): length_of_rvlc_escapes")); + } + + if (ics->noise_used) + { + ics->dpcm_noise_last_position = (uint16_t)faad_getbits(ld, 9 + DEBUGVAR(1,155,"rvlc_scale_factor_data(): dpcm_noise_last_position")); + } + + return 0; +} + +uint8_t rvlc_decode_scale_factors(ic_stream *ics, bitfile *ld) +{ + uint8_t result; + uint8_t intensity_used = 0; + uint8_t *rvlc_sf_buffer = NULL; + uint8_t *rvlc_esc_buffer = NULL; + bitfile ld_rvlc_sf = {0}, ld_rvlc_esc = {0}; +// bitfile ld_rvlc_sf_rev, ld_rvlc_esc_rev; + + if (ics->length_of_rvlc_sf > 0) + { + /* We read length_of_rvlc_sf bits here to put it in a + seperate bitfile. + */ + rvlc_sf_buffer = faad_getbitbuffer(ld, ics->length_of_rvlc_sf + DEBUGVAR(1,156,"rvlc_decode_scale_factors(): bitbuffer: length_of_rvlc_sf")); + + faad_initbits(&ld_rvlc_sf, (void*)rvlc_sf_buffer, bit2byte(ics->length_of_rvlc_sf)); +// faad_initbits_rev(&ld_rvlc_sf_rev, (void*)rvlc_sf_buffer, +// ics->length_of_rvlc_sf); + } + if ((ics->length_of_rvlc_sf == 0) || (ld_rvlc_sf.error != 0)) { + memset(&ld_rvlc_sf, 0, sizeof(ld_rvlc_sf)); + ld_rvlc_sf.error = 1; + } + + if (ics->sf_escapes_present) + { + /* We read length_of_rvlc_escapes bits here to put it in a + seperate bitfile. + */ + rvlc_esc_buffer = faad_getbitbuffer(ld, ics->length_of_rvlc_escapes + DEBUGVAR(1,157,"rvlc_decode_scale_factors(): bitbuffer: length_of_rvlc_escapes")); + + faad_initbits(&ld_rvlc_esc, (void*)rvlc_esc_buffer, bit2byte(ics->length_of_rvlc_escapes)); +// faad_initbits_rev(&ld_rvlc_esc_rev, (void*)rvlc_esc_buffer, +// ics->length_of_rvlc_escapes); + } + if (!ics->sf_escapes_present || (ld_rvlc_esc.error != 0)) { + memset(&ld_rvlc_esc, 0, sizeof(ld_rvlc_esc)); + ld_rvlc_esc.error = 1; + } + + /* decode the rvlc scale factors and escapes */ + result = rvlc_decode_sf_forward(ics, &ld_rvlc_sf, + &ld_rvlc_esc, &intensity_used); +// result = rvlc_decode_sf_reverse(ics, &ld_rvlc_sf_rev, +// &ld_rvlc_esc_rev, intensity_used); + + + if (rvlc_esc_buffer) faad_free(rvlc_esc_buffer); + if (rvlc_sf_buffer) faad_free(rvlc_sf_buffer); + + if (ics->length_of_rvlc_sf > 0) + faad_endbits(&ld_rvlc_sf); + if (ics->sf_escapes_present) + faad_endbits(&ld_rvlc_esc); + + return result; +} + +static uint8_t rvlc_decode_sf_forward(ic_stream *ics, bitfile *ld_sf, bitfile *ld_esc, + uint8_t *intensity_used) +{ + int8_t g, sfb; + int8_t t = 0; + int8_t error = ld_sf->error | ld_esc->error; + int8_t noise_pcm_flag = 1; + + int16_t scale_factor = ics->global_gain; + int16_t is_position = 0; + int16_t noise_energy = ics->global_gain - 90 - 256; + int16_t scale_factor_max = 255; +#ifdef FIXED_POINT + /* TODO: consider rolling out to regular build. */ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* The value is inexact, adjusted to current fuzzer findings. */ + scale_factor_max = 165; +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#endif // FIXED_POINT + +#ifdef PRINT_RVLC + printf("\nglobal_gain: %d\n", ics->global_gain); +#endif + + for (g = 0; g < ics->num_window_groups; g++) + { + for (sfb = 0; sfb < ics->max_sfb; sfb++) + { + if (error) + { + ics->scale_factors[g][sfb] = 0; + } else { + switch (ics->sfb_cb[g][sfb]) + { + case ZERO_HCB: /* zero book */ + ics->scale_factors[g][sfb] = 0; + break; + case INTENSITY_HCB: /* intensity books */ + case INTENSITY_HCB2: + + *intensity_used = 1; + + /* decode intensity position */ + t = rvlc_huffman_sf(ld_sf, ld_esc /*, +1*/); + + is_position += t; + ics->scale_factors[g][sfb] = is_position; + + break; + case NOISE_HCB: /* noise books */ + + /* decode noise energy */ + if (noise_pcm_flag) + { + int16_t n = ics->dpcm_noise_nrg; + noise_pcm_flag = 0; + noise_energy += n; + } else { + t = rvlc_huffman_sf(ld_sf, ld_esc /*, +1*/); + noise_energy += t; + } + + ics->scale_factors[g][sfb] = noise_energy; + + break; + default: /* spectral books */ + + /* decode scale factor */ + t = rvlc_huffman_sf(ld_sf, ld_esc /*, +1*/); + + scale_factor += t; + if (scale_factor < 0 || scale_factor > 255) + return 4; + + ics->scale_factors[g][sfb] = min(scale_factor, scale_factor_max); + + break; + } +#ifdef PRINT_RVLC + printf("%3d:%4d%4d\n", sfb, ics->sfb_cb[g][sfb], + ics->scale_factors[g][sfb]); +#endif + if (t == 99) + { + error = 1; + } + } + } + } +#ifdef PRINT_RVLC + printf("\n\n"); +#endif + + return 0; +} + +#if 0 // not used right now, doesn't work correctly yet +static uint8_t rvlc_decode_sf_reverse(ic_stream *ics, bitfile *ld_sf, bitfile *ld_esc, + uint8_t intensity_used) +{ + int8_t g, sfb; + int8_t t = 0; + int8_t error = 0; + int8_t noise_pcm_flag = 1, is_pcm_flag = 1, sf_pcm_flag = 1; + + int16_t scale_factor = ics->rev_global_gain; + int16_t is_position = 0; + int16_t noise_energy = ics->rev_global_gain; + +#ifdef PRINT_RVLC + printf("\nrev_global_gain: %d\n", ics->rev_global_gain); +#endif + + if (intensity_used) + { + is_position = rvlc_huffman_sf(ld_sf, ld_esc, -1); +#ifdef PRINT_RVLC + printf("is_position: %d\n", is_position); +#endif + } + + for (g = ics->num_window_groups-1; g >= 0; g--) + { + for (sfb = ics->max_sfb-1; sfb >= 0; sfb--) + { + if (error) + { + ics->scale_factors[g][sfb] = 0; + } else { + switch (ics->sfb_cb[g][sfb]) + { + case ZERO_HCB: /* zero book */ + ics->scale_factors[g][sfb] = 0; + break; + case INTENSITY_HCB: /* intensity books */ + case INTENSITY_HCB2: + + if (is_pcm_flag) + { + is_pcm_flag = 0; + ics->scale_factors[g][sfb] = is_position; + } else { + t = rvlc_huffman_sf(ld_sf, ld_esc, -1); + is_position -= t; + + ics->scale_factors[g][sfb] = (uint8_t)is_position; + } + break; + case NOISE_HCB: /* noise books */ + + /* decode noise energy */ + if (noise_pcm_flag) + { + noise_pcm_flag = 0; + noise_energy = ics->dpcm_noise_last_position; + } else { + t = rvlc_huffman_sf(ld_sf, ld_esc, -1); + noise_energy -= t; + } + + ics->scale_factors[g][sfb] = (uint8_t)noise_energy; + break; + default: /* spectral books */ + + if (sf_pcm_flag || (sfb == 0)) + { + sf_pcm_flag = 0; + if (sfb == 0) + scale_factor = ics->global_gain; + } else { + /* decode scale factor */ + t = rvlc_huffman_sf(ld_sf, ld_esc, -1); + scale_factor -= t; + } + + if (scale_factor < 0) + return 4; + + ics->scale_factors[g][sfb] = (uint8_t)scale_factor; + break; + } +#ifdef PRINT_RVLC + printf("%3d:%4d%4d\n", sfb, ics->sfb_cb[g][sfb], + ics->scale_factors[g][sfb]); +#endif + if (t == 99) + { + error = 1; + } + } + } + } + +#ifdef PRINT_RVLC + printf("\n\n"); +#endif + + return 0; +} +#endif + +/* index == 99 means not allowed codeword */ +static rvlc_huff_table book_rvlc[] = { + /*index length codeword */ + { 0, 1, 0 }, /* 0 */ + { -1, 3, 5 }, /* 101 */ + { 1, 3, 7 }, /* 111 */ + { -2, 4, 9 }, /* 1001 */ + { -3, 5, 17 }, /* 10001 */ + { 2, 5, 27 }, /* 11011 */ + { -4, 6, 33 }, /* 100001 */ + { 99, 6, 50 }, /* 110010 */ + { 3, 6, 51 }, /* 110011 */ + { 99, 6, 52 }, /* 110100 */ + { -7, 7, 65 }, /* 1000001 */ + { 99, 7, 96 }, /* 1100000 */ + { 99, 7, 98 }, /* 1100010 */ + { 7, 7, 99 }, /* 1100011 */ + { 4, 7, 107 }, /* 1101011 */ + { -5, 8, 129 }, /* 10000001 */ + { 99, 8, 194 }, /* 11000010 */ + { 5, 8, 195 }, /* 11000011 */ + { 99, 8, 212 }, /* 11010100 */ + { 99, 9, 256 }, /* 100000000 */ + { -6, 9, 257 }, /* 100000001 */ + { 99, 9, 426 }, /* 110101010 */ + { 6, 9, 427 }, /* 110101011 */ + { 99, 10, 0 } /* Shouldn't come this far */ +}; + +static rvlc_huff_table book_escape[] = { + /*index length codeword */ + { 1, 2, 0 }, + { 0, 2, 2 }, + { 3, 3, 2 }, + { 2, 3, 6 }, + { 4, 4, 14 }, + { 7, 5, 13 }, + { 6, 5, 15 }, + { 5, 5, 31 }, + { 11, 6, 24 }, + { 10, 6, 25 }, + { 9, 6, 29 }, + { 8, 6, 61 }, + { 13, 7, 56 }, + { 12, 7, 120 }, + { 15, 8, 114 }, + { 14, 8, 242 }, + { 17, 9, 230 }, + { 16, 9, 486 }, + { 19, 10, 463 }, + { 18, 10, 974 }, + { 22, 11, 925 }, + { 20, 11, 1950 }, + { 21, 11, 1951 }, + { 23, 12, 1848 }, + { 25, 13, 3698 }, + { 24, 14, 7399 }, + { 26, 15, 14797 }, + { 49, 19, 236736 }, + { 50, 19, 236737 }, + { 51, 19, 236738 }, + { 52, 19, 236739 }, + { 53, 19, 236740 }, + { 27, 20, 473482 }, + { 28, 20, 473483 }, + { 29, 20, 473484 }, + { 30, 20, 473485 }, + { 31, 20, 473486 }, + { 32, 20, 473487 }, + { 33, 20, 473488 }, + { 34, 20, 473489 }, + { 35, 20, 473490 }, + { 36, 20, 473491 }, + { 37, 20, 473492 }, + { 38, 20, 473493 }, + { 39, 20, 473494 }, + { 40, 20, 473495 }, + { 41, 20, 473496 }, + { 42, 20, 473497 }, + { 43, 20, 473498 }, + { 44, 20, 473499 }, + { 45, 20, 473500 }, + { 46, 20, 473501 }, + { 47, 20, 473502 }, + { 48, 20, 473503 }, + { 99, 21, 0 } /* Shouldn't come this far */ +}; + +static int8_t rvlc_huffman_sf(bitfile *ld_sf, bitfile *ld_esc /*, + int8_t direction*/) +{ + uint16_t i, j; + int16_t index; + uint32_t cw; + rvlc_huff_table *h = book_rvlc; + int8_t direction = +1; + + i = h->len; + if (direction > 0) + cw = faad_getbits(ld_sf, i DEBUGVAR(1,0,"")); + else + cw = 0 /* faad_getbits_rev(ld_sf, i DEBUGVAR(1,0,"")) */; + + while ((cw != h->cw) + && (i < 10)) + { + h++; + j = h->len-i; + i += j; + cw <<= j; + if (direction > 0) + cw |= faad_getbits(ld_sf, j DEBUGVAR(1,0,"")); + else + cw |= 0 /* faad_getbits_rev(ld_sf, j DEBUGVAR(1,0,"")) */; + } + + index = h->index; + + if (index == +ESC_VAL) + { + int8_t esc = rvlc_huffman_esc(ld_esc /*, direction*/); + if (esc == 99) + return 99; + index += esc; +#ifdef PRINT_RVLC + printf("esc: %d - ", esc); +#endif + } + if (index == -ESC_VAL) + { + int8_t esc = rvlc_huffman_esc(ld_esc /*, direction*/); + if (esc == 99) + return 99; + index -= esc; +#ifdef PRINT_RVLC + printf("esc: %d - ", esc); +#endif + } + + return (int8_t)index; +} + +static int8_t rvlc_huffman_esc(bitfile *ld /*, + int8_t direction*/) +{ + uint16_t i, j; + uint32_t cw; + rvlc_huff_table *h = book_escape; + int8_t direction = +1; + + i = h->len; + if (direction > 0) + cw = faad_getbits(ld, i DEBUGVAR(1,0,"")); + else + cw = 0 /* faad_getbits_rev(ld, i DEBUGVAR(1,0,"")) */; + + while ((cw != h->cw) + && (i < 21)) + { + h++; + j = h->len-i; + i += j; + cw <<= j; + if (direction > 0) + cw |= faad_getbits(ld, j DEBUGVAR(1,0,"")); + else + cw |= 0 /* faad_getbits_rev(ld, j DEBUGVAR(1,0,"")) */; + } + + return (int8_t)h->index; +} + +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/rvlc.h b/third_party/faad_hdc/upstream/libfaad/rvlc.h new file mode 100644 index 000000000..127c0a96e --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/rvlc.h @@ -0,0 +1,56 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: rvlc.h,v 1.17 2007/11/01 12:33:34 menno Exp $ +**/ + +#ifndef __RVLC_SCF_H__ +#define __RVLC_SCF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + int16_t index; + uint16_t len; + uint32_t cw; +} rvlc_huff_table; + + +#define ESC_VAL 7 + + +uint8_t rvlc_scale_factor_data(ic_stream *ics, bitfile *ld); +uint8_t rvlc_decode_scale_factors(ic_stream *ics, bitfile *ld); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_dct.c b/third_party/faad_hdc/upstream/libfaad/sbr_dct.c new file mode 100644 index 000000000..39c02065d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_dct.c @@ -0,0 +1,2279 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_dct.c,v 1.20 2007/11/01 12:33:34 menno Exp $ +**/ + + +/* Most of the DCT/DST codes here are generated using Spiral which is GPL + * For more info see: http://www.spiral.net/ + */ + +#include "common.h" + +#ifdef SBR_DEC + +#ifdef _MSC_VER +#pragma warning(disable:4305) +#pragma warning(disable:4244) +#endif + + +#include "sbr_dct.h" + +void DCT4_32(real_t *y, real_t *x) +{ + real_t f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10; + real_t f11, f12, f13, f14, f15, f16, f17, f18, f19, f20; + real_t f21, f22, f23, f24, f25, f26, f27, f28, f29, f30; + real_t f31, f32, f33, f34, f35, f36, f37, f38, f39, f40; + real_t f41, f42, f43, f44, f45, f46, f47, f48, f49, f50; + real_t f51, f52, f53, f54, f55, f56, f57, f58, f59, f60; + real_t f61, f62, f63, f64, f65, f66, f67, f68, f69, f70; + real_t f71, f72, f73, f74, f75, f76, f77, f78, f79, f80; + real_t f81, f82, f83, f84, f85, f86, f87, f88, f89, f90; + real_t f91, f92, f93, f94, f95, f96, f97, f98, f99, f100; + real_t f101, f102, f103, f104, f105, f106, f107, f108, f109, f110; + real_t f111, f112, f113, f114, f115, f116, f117, f118, f119, f120; + real_t f121, f122, f123, f124, f125, f126, f127, f128, f129, f130; + real_t f131, f132, f133, f134, f135, f136, f137, f138, f139, f140; + real_t f141, f142, f143, f144, f145, f146, f147, f148, f149, f150; + real_t f151, f152, f153, f154, f155, f156, f157, f158, f159, f160; + real_t f161, f162, f163, f164, f165, f166, f167, f168, f169, f170; + real_t f171, f172, f173, f174, f175, f176, f177, f178, f179, f180; + real_t f181, f182, f183, f184, f185, f186, f187, f188, f189, f190; + real_t f191, f192, f193, f194, f195, f196, f197, f198, f199, f200; + real_t f201, f202, f203, f204, f205, f206, f207, f208, f209, f210; + real_t f211, f212, f213, f214, f215, f216, f217, f218, f219, f220; + real_t f221, f222, f223, f224, f225, f226, f227, f228, f229, f230; + real_t f231, f232, f233, f234, f235, f236, f237, f238, f239, f240; + real_t f241, f242, f243, f244, f245, f246, f247, f248, f249, f250; + real_t f251, f252, f253, f254, f255, f256, f257, f258, f259, f260; + real_t f261, f262, f263, f264, f265, f266, f267, f268, f269, f270; + real_t f271, f272, f273, f274, f275, f276, f277, f278, f279, f280; + real_t f281, f282, f283, f284, f285, f286, f287, f288, f289, f290; + real_t f291, f292, f293, f294, f295, f296, f297, f298, f299, f300; + real_t f301, f302, f303, f304, f305, f306, f307, f310, f311, f312; + real_t f313, f316, f317, f318, f319, f322, f323, f324, f325, f328; + real_t f329, f330, f331, f334, f335, f336, f337, f340, f341, f342; + real_t f343, f346, f347, f348, f349, f352, f353, f354, f355, f358; + real_t f359, f360, f361, f364, f365, f366, f367, f370, f371, f372; + real_t f373, f376, f377, f378, f379, f382, f383, f384, f385, f388; + real_t f389, f390, f391, f394, f395, f396, f397; + + f0 = x[15] - x[16]; + f1 = x[15] + x[16]; + f2 = MUL_F(FRAC_CONST(0.7071067811865476), f1); + f3 = MUL_F(FRAC_CONST(0.7071067811865476), f0); + f4 = x[8] - x[23]; + f5 = x[8] + x[23]; + f6 = MUL_F(FRAC_CONST(0.7071067811865476), f5); + f7 = MUL_F(FRAC_CONST(0.7071067811865476), f4); + f8 = x[12] - x[19]; + f9 = x[12] + x[19]; + f10 = MUL_F(FRAC_CONST(0.7071067811865476), f9); + f11 = MUL_F(FRAC_CONST(0.7071067811865476), f8); + f12 = x[11] - x[20]; + f13 = x[11] + x[20]; + f14 = MUL_F(FRAC_CONST(0.7071067811865476), f13); + f15 = MUL_F(FRAC_CONST(0.7071067811865476), f12); + f16 = x[14] - x[17]; + f17 = x[14] + x[17]; + f18 = MUL_F(FRAC_CONST(0.7071067811865476), f17); + f19 = MUL_F(FRAC_CONST(0.7071067811865476), f16); + f20 = x[9] - x[22]; + f21 = x[9] + x[22]; + f22 = MUL_F(FRAC_CONST(0.7071067811865476), f21); + f23 = MUL_F(FRAC_CONST(0.7071067811865476), f20); + f24 = x[13] - x[18]; + f25 = x[13] + x[18]; + f26 = MUL_F(FRAC_CONST(0.7071067811865476), f25); + f27 = MUL_F(FRAC_CONST(0.7071067811865476), f24); + f28 = x[10] - x[21]; + f29 = x[10] + x[21]; + f30 = MUL_F(FRAC_CONST(0.7071067811865476), f29); + f31 = MUL_F(FRAC_CONST(0.7071067811865476), f28); + f32 = x[0] - f2; + f33 = x[0] + f2; + f34 = x[31] - f3; + f35 = x[31] + f3; + f36 = x[7] - f6; + f37 = x[7] + f6; + f38 = x[24] - f7; + f39 = x[24] + f7; + f40 = x[3] - f10; + f41 = x[3] + f10; + f42 = x[28] - f11; + f43 = x[28] + f11; + f44 = x[4] - f14; + f45 = x[4] + f14; + f46 = x[27] - f15; + f47 = x[27] + f15; + f48 = x[1] - f18; + f49 = x[1] + f18; + f50 = x[30] - f19; + f51 = x[30] + f19; + f52 = x[6] - f22; + f53 = x[6] + f22; + f54 = x[25] - f23; + f55 = x[25] + f23; + f56 = x[2] - f26; + f57 = x[2] + f26; + f58 = x[29] - f27; + f59 = x[29] + f27; + f60 = x[5] - f30; + f61 = x[5] + f30; + f62 = x[26] - f31; + f63 = x[26] + f31; + f64 = f39 + f37; + f65 = MUL_F(FRAC_CONST(-0.5411961001461969), f39); + f66 = MUL_F(FRAC_CONST(0.9238795325112867), f64); + f67 = MUL_C(COEF_CONST(1.3065629648763766), f37); + f68 = f65 + f66; + f69 = f67 - f66; + f70 = f38 + f36; + f71 = MUL_C(COEF_CONST(1.3065629648763770), f38); + f72 = MUL_F(FRAC_CONST(-0.3826834323650904), f70); + f73 = MUL_F(FRAC_CONST(0.5411961001461961), f36); + f74 = f71 + f72; + f75 = f73 - f72; + f76 = f47 + f45; + f77 = MUL_F(FRAC_CONST(-0.5411961001461969), f47); + f78 = MUL_F(FRAC_CONST(0.9238795325112867), f76); + f79 = MUL_C(COEF_CONST(1.3065629648763766), f45); + f80 = f77 + f78; + f81 = f79 - f78; + f82 = f46 + f44; + f83 = MUL_C(COEF_CONST(1.3065629648763770), f46); + f84 = MUL_F(FRAC_CONST(-0.3826834323650904), f82); + f85 = MUL_F(FRAC_CONST(0.5411961001461961), f44); + f86 = f83 + f84; + f87 = f85 - f84; + f88 = f55 + f53; + f89 = MUL_F(FRAC_CONST(-0.5411961001461969), f55); + f90 = MUL_F(FRAC_CONST(0.9238795325112867), f88); + f91 = MUL_C(COEF_CONST(1.3065629648763766), f53); + f92 = f89 + f90; + f93 = f91 - f90; + f94 = f54 + f52; + f95 = MUL_C(COEF_CONST(1.3065629648763770), f54); + f96 = MUL_F(FRAC_CONST(-0.3826834323650904), f94); + f97 = MUL_F(FRAC_CONST(0.5411961001461961), f52); + f98 = f95 + f96; + f99 = f97 - f96; + f100 = f63 + f61; + f101 = MUL_F(FRAC_CONST(-0.5411961001461969), f63); + f102 = MUL_F(FRAC_CONST(0.9238795325112867), f100); + f103 = MUL_C(COEF_CONST(1.3065629648763766), f61); + f104 = f101 + f102; + f105 = f103 - f102; + f106 = f62 + f60; + f107 = MUL_C(COEF_CONST(1.3065629648763770), f62); + f108 = MUL_F(FRAC_CONST(-0.3826834323650904), f106); + f109 = MUL_F(FRAC_CONST(0.5411961001461961), f60); + f110 = f107 + f108; + f111 = f109 - f108; + f112 = f33 - f68; + f113 = f33 + f68; + f114 = f35 - f69; + f115 = f35 + f69; + f116 = f32 - f74; + f117 = f32 + f74; + f118 = f34 - f75; + f119 = f34 + f75; + f120 = f41 - f80; + f121 = f41 + f80; + f122 = f43 - f81; + f123 = f43 + f81; + f124 = f40 - f86; + f125 = f40 + f86; + f126 = f42 - f87; + f127 = f42 + f87; + f128 = f49 - f92; + f129 = f49 + f92; + f130 = f51 - f93; + f131 = f51 + f93; + f132 = f48 - f98; + f133 = f48 + f98; + f134 = f50 - f99; + f135 = f50 + f99; + f136 = f57 - f104; + f137 = f57 + f104; + f138 = f59 - f105; + f139 = f59 + f105; + f140 = f56 - f110; + f141 = f56 + f110; + f142 = f58 - f111; + f143 = f58 + f111; + f144 = f123 + f121; + f145 = MUL_F(FRAC_CONST(-0.7856949583871021), f123); + f146 = MUL_F(FRAC_CONST(0.9807852804032304), f144); + f147 = MUL_C(COEF_CONST(1.1758756024193588), f121); + f148 = f145 + f146; + f149 = f147 - f146; + f150 = f127 + f125; + f151 = MUL_F(FRAC_CONST(0.2758993792829431), f127); + f152 = MUL_F(FRAC_CONST(0.5555702330196022), f150); + f153 = MUL_C(COEF_CONST(1.3870398453221475), f125); + f154 = f151 + f152; + f155 = f153 - f152; + f156 = f122 + f120; + f157 = MUL_C(COEF_CONST(1.1758756024193591), f122); + f158 = MUL_F(FRAC_CONST(-0.1950903220161287), f156); + f159 = MUL_F(FRAC_CONST(0.7856949583871016), f120); + f160 = f157 + f158; + f161 = f159 - f158; + f162 = f126 + f124; + f163 = MUL_C(COEF_CONST(1.3870398453221473), f126); + f164 = MUL_F(FRAC_CONST(-0.8314696123025455), f162); + f165 = MUL_F(FRAC_CONST(-0.2758993792829436), f124); + f166 = f163 + f164; + f167 = f165 - f164; + f168 = f139 + f137; + f169 = MUL_F(FRAC_CONST(-0.7856949583871021), f139); + f170 = MUL_F(FRAC_CONST(0.9807852804032304), f168); + f171 = MUL_C(COEF_CONST(1.1758756024193588), f137); + f172 = f169 + f170; + f173 = f171 - f170; + f174 = f143 + f141; + f175 = MUL_F(FRAC_CONST(0.2758993792829431), f143); + f176 = MUL_F(FRAC_CONST(0.5555702330196022), f174); + f177 = MUL_C(COEF_CONST(1.3870398453221475), f141); + f178 = f175 + f176; + f179 = f177 - f176; + f180 = f138 + f136; + f181 = MUL_C(COEF_CONST(1.1758756024193591), f138); + f182 = MUL_F(FRAC_CONST(-0.1950903220161287), f180); + f183 = MUL_F(FRAC_CONST(0.7856949583871016), f136); + f184 = f181 + f182; + f185 = f183 - f182; + f186 = f142 + f140; + f187 = MUL_C(COEF_CONST(1.3870398453221473), f142); + f188 = MUL_F(FRAC_CONST(-0.8314696123025455), f186); + f189 = MUL_F(FRAC_CONST(-0.2758993792829436), f140); + f190 = f187 + f188; + f191 = f189 - f188; + f192 = f113 - f148; + f193 = f113 + f148; + f194 = f115 - f149; + f195 = f115 + f149; + f196 = f117 - f154; + f197 = f117 + f154; + f198 = f119 - f155; + f199 = f119 + f155; + f200 = f112 - f160; + f201 = f112 + f160; + f202 = f114 - f161; + f203 = f114 + f161; + f204 = f116 - f166; + f205 = f116 + f166; + f206 = f118 - f167; + f207 = f118 + f167; + f208 = f129 - f172; + f209 = f129 + f172; + f210 = f131 - f173; + f211 = f131 + f173; + f212 = f133 - f178; + f213 = f133 + f178; + f214 = f135 - f179; + f215 = f135 + f179; + f216 = f128 - f184; + f217 = f128 + f184; + f218 = f130 - f185; + f219 = f130 + f185; + f220 = f132 - f190; + f221 = f132 + f190; + f222 = f134 - f191; + f223 = f134 + f191; + f224 = f211 + f209; + f225 = MUL_F(FRAC_CONST(-0.8971675863426361), f211); + f226 = MUL_F(FRAC_CONST(0.9951847266721968), f224); + f227 = MUL_C(COEF_CONST(1.0932018670017576), f209); + f228 = f225 + f226; + f229 = f227 - f226; + f230 = f215 + f213; + f231 = MUL_F(FRAC_CONST(-0.4105245275223571), f215); + f232 = MUL_F(FRAC_CONST(0.8819212643483549), f230); + f233 = MUL_C(COEF_CONST(1.3533180011743529), f213); + f234 = f231 + f232; + f235 = f233 - f232; + f236 = f219 + f217; + f237 = MUL_F(FRAC_CONST(0.1386171691990915), f219); + f238 = MUL_F(FRAC_CONST(0.6343932841636455), f236); + f239 = MUL_C(COEF_CONST(1.4074037375263826), f217); + f240 = f237 + f238; + f241 = f239 - f238; + f242 = f223 + f221; + f243 = MUL_F(FRAC_CONST(0.6666556584777466), f223); + f244 = MUL_F(FRAC_CONST(0.2902846772544623), f242); + f245 = MUL_C(COEF_CONST(1.2472250129866711), f221); + f246 = f243 + f244; + f247 = f245 - f244; + f248 = f210 + f208; + f249 = MUL_C(COEF_CONST(1.0932018670017574), f210); + f250 = MUL_F(FRAC_CONST(-0.0980171403295605), f248); + f251 = MUL_F(FRAC_CONST(0.8971675863426364), f208); + f252 = f249 + f250; + f253 = f251 - f250; + f254 = f214 + f212; + f255 = MUL_C(COEF_CONST(1.3533180011743529), f214); + f256 = MUL_F(FRAC_CONST(-0.4713967368259979), f254); + f257 = MUL_F(FRAC_CONST(0.4105245275223569), f212); + f258 = f255 + f256; + f259 = f257 - f256; + f260 = f218 + f216; + f261 = MUL_C(COEF_CONST(1.4074037375263826), f218); + f262 = MUL_F(FRAC_CONST(-0.7730104533627369), f260); + f263 = MUL_F(FRAC_CONST(-0.1386171691990913), f216); + f264 = f261 + f262; + f265 = f263 - f262; + f266 = f222 + f220; + f267 = MUL_C(COEF_CONST(1.2472250129866711), f222); + f268 = MUL_F(FRAC_CONST(-0.9569403357322089), f266); + f269 = MUL_F(FRAC_CONST(-0.6666556584777469), f220); + f270 = f267 + f268; + f271 = f269 - f268; + f272 = f193 - f228; + f273 = f193 + f228; + f274 = f195 - f229; + f275 = f195 + f229; + f276 = f197 - f234; + f277 = f197 + f234; + f278 = f199 - f235; + f279 = f199 + f235; + f280 = f201 - f240; + f281 = f201 + f240; + f282 = f203 - f241; + f283 = f203 + f241; + f284 = f205 - f246; + f285 = f205 + f246; + f286 = f207 - f247; + f287 = f207 + f247; + f288 = f192 - f252; + f289 = f192 + f252; + f290 = f194 - f253; + f291 = f194 + f253; + f292 = f196 - f258; + f293 = f196 + f258; + f294 = f198 - f259; + f295 = f198 + f259; + f296 = f200 - f264; + f297 = f200 + f264; + f298 = f202 - f265; + f299 = f202 + f265; + f300 = f204 - f270; + f301 = f204 + f270; + f302 = f206 - f271; + f303 = f206 + f271; + f304 = f275 + f273; + f305 = MUL_F(FRAC_CONST(-0.9751575901732920), f275); + f306 = MUL_F(FRAC_CONST(0.9996988186962043), f304); + f307 = MUL_C(COEF_CONST(1.0242400472191164), f273); + y[0] = f305 + f306; + y[31] = f307 - f306; + f310 = f279 + f277; + f311 = MUL_F(FRAC_CONST(-0.8700688593994936), f279); + f312 = MUL_F(FRAC_CONST(0.9924795345987100), f310); + f313 = MUL_C(COEF_CONST(1.1148902097979263), f277); + y[2] = f311 + f312; + y[29] = f313 - f312; + f316 = f283 + f281; + f317 = MUL_F(FRAC_CONST(-0.7566008898816587), f283); + f318 = MUL_F(FRAC_CONST(0.9757021300385286), f316); + f319 = MUL_C(COEF_CONST(1.1948033701953984), f281); + y[4] = f317 + f318; + y[27] = f319 - f318; + f322 = f287 + f285; + f323 = MUL_F(FRAC_CONST(-0.6358464401941451), f287); + f324 = MUL_F(FRAC_CONST(0.9495281805930367), f322); + f325 = MUL_C(COEF_CONST(1.2632099209919283), f285); + y[6] = f323 + f324; + y[25] = f325 - f324; + f328 = f291 + f289; + f329 = MUL_F(FRAC_CONST(-0.5089684416985408), f291); + f330 = MUL_F(FRAC_CONST(0.9142097557035307), f328); + f331 = MUL_C(COEF_CONST(1.3194510697085207), f289); + y[8] = f329 + f330; + y[23] = f331 - f330; + f334 = f295 + f293; + f335 = MUL_F(FRAC_CONST(-0.3771887988789273), f295); + f336 = MUL_F(FRAC_CONST(0.8700869911087114), f334); + f337 = MUL_C(COEF_CONST(1.3629851833384954), f293); + y[10] = f335 + f336; + y[21] = f337 - f336; + f340 = f299 + f297; + f341 = MUL_F(FRAC_CONST(-0.2417766217337384), f299); + f342 = MUL_F(FRAC_CONST(0.8175848131515837), f340); + f343 = MUL_C(COEF_CONST(1.3933930045694289), f297); + y[12] = f341 + f342; + y[19] = f343 - f342; + f346 = f303 + f301; + f347 = MUL_F(FRAC_CONST(-0.1040360035527077), f303); + f348 = MUL_F(FRAC_CONST(0.7572088465064845), f346); + f349 = MUL_C(COEF_CONST(1.4103816894602612), f301); + y[14] = f347 + f348; + y[17] = f349 - f348; + f352 = f274 + f272; + f353 = MUL_F(FRAC_CONST(0.0347065382144002), f274); + f354 = MUL_F(FRAC_CONST(0.6895405447370668), f352); + f355 = MUL_C(COEF_CONST(1.4137876276885337), f272); + y[16] = f353 + f354; + y[15] = f355 - f354; + f358 = f278 + f276; + f359 = MUL_F(FRAC_CONST(0.1731148370459795), f278); + f360 = MUL_F(FRAC_CONST(0.6152315905806268), f358); + f361 = MUL_C(COEF_CONST(1.4035780182072330), f276); + y[18] = f359 + f360; + y[13] = f361 - f360; + f364 = f282 + f280; + f365 = MUL_F(FRAC_CONST(0.3098559453626100), f282); + f366 = MUL_F(FRAC_CONST(0.5349976198870972), f364); + f367 = MUL_C(COEF_CONST(1.3798511851368043), f280); + y[20] = f365 + f366; + y[11] = f367 - f366; + f370 = f286 + f284; + f371 = MUL_F(FRAC_CONST(0.4436129715409088), f286); + f372 = MUL_F(FRAC_CONST(0.4496113296546065), f370); + f373 = MUL_C(COEF_CONST(1.3428356308501219), f284); + y[22] = f371 + f372; + y[9] = f373 - f372; + f376 = f290 + f288; + f377 = MUL_F(FRAC_CONST(0.5730977622997509), f290); + f378 = MUL_F(FRAC_CONST(0.3598950365349881), f376); + f379 = MUL_C(COEF_CONST(1.2928878353697271), f288); + y[24] = f377 + f378; + y[7] = f379 - f378; + f382 = f294 + f292; + f383 = MUL_F(FRAC_CONST(0.6970633083205415), f294); + f384 = MUL_F(FRAC_CONST(0.2667127574748984), f382); + f385 = MUL_C(COEF_CONST(1.2304888232703382), f292); + y[26] = f383 + f384; + y[5] = f385 - f384; + f388 = f298 + f296; + f389 = MUL_F(FRAC_CONST(0.8143157536286401), f298); + f390 = MUL_F(FRAC_CONST(0.1709618887603012), f388); + f391 = MUL_C(COEF_CONST(1.1562395311492424), f296); + y[28] = f389 + f390; + y[3] = f391 - f390; + f394 = f302 + f300; + f395 = MUL_F(FRAC_CONST(0.9237258930790228), f302); + f396 = MUL_F(FRAC_CONST(0.0735645635996674), f394); + f397 = MUL_C(COEF_CONST(1.0708550202783576), f300); + y[30] = f395 + f396; + y[1] = f397 - f396; +} + +void DST4_32(real_t *y, real_t *x) +{ + real_t f0, f1, f2, f3, f4, f5, f6, f7, f8, f9; + real_t f10, f11, f12, f13, f14, f15, f16, f17, f18, f19; + real_t f20, f21, f22, f23, f24, f25, f26, f27, f28, f29; + real_t f30, f31, f32, f33, f34, f35, f36, f37, f38, f39; + real_t f40, f41, f42, f43, f44, f45, f46, f47, f48, f49; + real_t f50, f51, f52, f53, f54, f55, f56, f57, f58, f59; + real_t f60, f61, f62, f63, f64, f65, f66, f67, f68, f69; + real_t f70, f71, f72, f73, f74, f75, f76, f77, f78, f79; + real_t f80, f81, f82, f83, f84, f85, f86, f87, f88, f89; + real_t f90, f91, f92, f93, f94, f95, f96, f97, f98, f99; + real_t f100, f101, f102, f103, f104, f105, f106, f107, f108, f109; + real_t f110, f111, f112, f113, f114, f115, f116, f117, f118, f119; + real_t f120, f121, f122, f123, f124, f125, f126, f127, f128, f129; + real_t f130, f131, f132, f133, f134, f135, f136, f137, f138, f139; + real_t f140, f141, f142, f143, f144, f145, f146, f147, f148, f149; + real_t f150, f151, f152, f153, f154, f155, f156, f157, f158, f159; + real_t f160, f161, f162, f163, f164, f165, f166, f167, f168, f169; + real_t f170, f171, f172, f173, f174, f175, f176, f177, f178, f179; + real_t f180, f181, f182, f183, f184, f185, f186, f187, f188, f189; + real_t f190, f191, f192, f193, f194, f195, f196, f197, f198, f199; + real_t f200, f201, f202, f203, f204, f205, f206, f207, f208, f209; + real_t f210, f211, f212, f213, f214, f215, f216, f217, f218, f219; + real_t f220, f221, f222, f223, f224, f225, f226, f227, f228, f229; + real_t f230, f231, f232, f233, f234, f235, f236, f237, f238, f239; + real_t f240, f241, f242, f243, f244, f245, f246, f247, f248, f249; + real_t f250, f251, f252, f253, f254, f255, f256, f257, f258, f259; + real_t f260, f261, f262, f263, f264, f265, f266, f267, f268, f269; + real_t f270, f271, f272, f273, f274, f275, f276, f277, f278, f279; + real_t f280, f281, f282, f283, f284, f285, f286, f287, f288, f289; + real_t f290, f291, f292, f293, f294, f295, f296, f297, f298, f299; + real_t f300, f301, f302, f303, f304, f305, f306, f307, f308, f309; + real_t f310, f311, f312, f313, f314, f315, f316, f317, f318, f319; + real_t f320, f321, f322, f323, f324, f325, f326, f327, f328, f329; + real_t f330, f331, f332, f333, f334, f335; + + f0 = x[0] - x[1]; + f1 = x[2] - x[1]; + f2 = x[2] - x[3]; + f3 = x[4] - x[3]; + f4 = x[4] - x[5]; + f5 = x[6] - x[5]; + f6 = x[6] - x[7]; + f7 = x[8] - x[7]; + f8 = x[8] - x[9]; + f9 = x[10] - x[9]; + f10 = x[10] - x[11]; + f11 = x[12] - x[11]; + f12 = x[12] - x[13]; + f13 = x[14] - x[13]; + f14 = x[14] - x[15]; + f15 = x[16] - x[15]; + f16 = x[16] - x[17]; + f17 = x[18] - x[17]; + f18 = x[18] - x[19]; + f19 = x[20] - x[19]; + f20 = x[20] - x[21]; + f21 = x[22] - x[21]; + f22 = x[22] - x[23]; + f23 = x[24] - x[23]; + f24 = x[24] - x[25]; + f25 = x[26] - x[25]; + f26 = x[26] - x[27]; + f27 = x[28] - x[27]; + f28 = x[28] - x[29]; + f29 = x[30] - x[29]; + f30 = x[30] - x[31]; + f31 = MUL_F(FRAC_CONST(0.7071067811865476), f15); + f32 = x[0] - f31; + f33 = x[0] + f31; + f34 = f7 + f23; + f35 = MUL_C(COEF_CONST(1.3065629648763766), f7); + f36 = MUL_F(FRAC_CONST(-0.9238795325112866), f34); + f37 = MUL_F(FRAC_CONST(-0.5411961001461967), f23); + f38 = f35 + f36; + f39 = f37 - f36; + f40 = f33 - f39; + f41 = f33 + f39; + f42 = f32 - f38; + f43 = f32 + f38; + f44 = f11 - f19; + f45 = f11 + f19; + f46 = MUL_F(FRAC_CONST(0.7071067811865476), f45); + f47 = f3 - f46; + f48 = f3 + f46; + f49 = MUL_F(FRAC_CONST(0.7071067811865476), f44); + f50 = f49 - f27; + f51 = f49 + f27; + f52 = f51 + f48; + f53 = MUL_F(FRAC_CONST(-0.7856949583871021), f51); + f54 = MUL_F(FRAC_CONST(0.9807852804032304), f52); + f55 = MUL_C(COEF_CONST(1.1758756024193588), f48); + f56 = f53 + f54; + f57 = f55 - f54; + f58 = f50 + f47; + f59 = MUL_F(FRAC_CONST(-0.2758993792829430), f50); + f60 = MUL_F(FRAC_CONST(0.8314696123025452), f58); + f61 = MUL_C(COEF_CONST(1.3870398453221475), f47); + f62 = f59 + f60; + f63 = f61 - f60; + f64 = f41 - f56; + f65 = f41 + f56; + f66 = f43 - f62; + f67 = f43 + f62; + f68 = f42 - f63; + f69 = f42 + f63; + f70 = f40 - f57; + f71 = f40 + f57; + f72 = f5 - f9; + f73 = f5 + f9; + f74 = f13 - f17; + f75 = f13 + f17; + f76 = f21 - f25; + f77 = f21 + f25; + f78 = MUL_F(FRAC_CONST(0.7071067811865476), f75); + f79 = f1 - f78; + f80 = f1 + f78; + f81 = f73 + f77; + f82 = MUL_C(COEF_CONST(1.3065629648763766), f73); + f83 = MUL_F(FRAC_CONST(-0.9238795325112866), f81); + f84 = MUL_F(FRAC_CONST(-0.5411961001461967), f77); + f85 = f82 + f83; + f86 = f84 - f83; + f87 = f80 - f86; + f88 = f80 + f86; + f89 = f79 - f85; + f90 = f79 + f85; + f91 = MUL_F(FRAC_CONST(0.7071067811865476), f74); + f92 = f29 - f91; + f93 = f29 + f91; + f94 = f76 + f72; + f95 = MUL_C(COEF_CONST(1.3065629648763766), f76); + f96 = MUL_F(FRAC_CONST(-0.9238795325112866), f94); + f97 = MUL_F(FRAC_CONST(-0.5411961001461967), f72); + f98 = f95 + f96; + f99 = f97 - f96; + f100 = f93 - f99; + f101 = f93 + f99; + f102 = f92 - f98; + f103 = f92 + f98; + f104 = f101 + f88; + f105 = MUL_F(FRAC_CONST(-0.8971675863426361), f101); + f106 = MUL_F(FRAC_CONST(0.9951847266721968), f104); + f107 = MUL_C(COEF_CONST(1.0932018670017576), f88); + f108 = f105 + f106; + f109 = f107 - f106; + f110 = f90 - f103; + f111 = MUL_F(FRAC_CONST(-0.6666556584777466), f103); + f112 = MUL_F(FRAC_CONST(0.9569403357322089), f110); + f113 = MUL_C(COEF_CONST(1.2472250129866713), f90); + f114 = f112 - f111; + f115 = f113 - f112; + f116 = f102 + f89; + f117 = MUL_F(FRAC_CONST(-0.4105245275223571), f102); + f118 = MUL_F(FRAC_CONST(0.8819212643483549), f116); + f119 = MUL_C(COEF_CONST(1.3533180011743529), f89); + f120 = f117 + f118; + f121 = f119 - f118; + f122 = f87 - f100; + f123 = MUL_F(FRAC_CONST(-0.1386171691990915), f100); + f124 = MUL_F(FRAC_CONST(0.7730104533627370), f122); + f125 = MUL_C(COEF_CONST(1.4074037375263826), f87); + f126 = f124 - f123; + f127 = f125 - f124; + f128 = f65 - f108; + f129 = f65 + f108; + f130 = f67 - f114; + f131 = f67 + f114; + f132 = f69 - f120; + f133 = f69 + f120; + f134 = f71 - f126; + f135 = f71 + f126; + f136 = f70 - f127; + f137 = f70 + f127; + f138 = f68 - f121; + f139 = f68 + f121; + f140 = f66 - f115; + f141 = f66 + f115; + f142 = f64 - f109; + f143 = f64 + f109; + f144 = f0 + f30; + f145 = MUL_C(COEF_CONST(1.0478631305325901), f0); + f146 = MUL_F(FRAC_CONST(-0.9987954562051724), f144); + f147 = MUL_F(FRAC_CONST(-0.9497277818777548), f30); + f148 = f145 + f146; + f149 = f147 - f146; + f150 = f4 + f26; + f151 = MUL_C(COEF_CONST(1.2130114330978077), f4); + f152 = MUL_F(FRAC_CONST(-0.9700312531945440), f150); + f153 = MUL_F(FRAC_CONST(-0.7270510732912803), f26); + f154 = f151 + f152; + f155 = f153 - f152; + f156 = f8 + f22; + f157 = MUL_C(COEF_CONST(1.3315443865537255), f8); + f158 = MUL_F(FRAC_CONST(-0.9039892931234433), f156); + f159 = MUL_F(FRAC_CONST(-0.4764341996931612), f22); + f160 = f157 + f158; + f161 = f159 - f158; + f162 = f12 + f18; + f163 = MUL_C(COEF_CONST(1.3989068359730781), f12); + f164 = MUL_F(FRAC_CONST(-0.8032075314806453), f162); + f165 = MUL_F(FRAC_CONST(-0.2075082269882124), f18); + f166 = f163 + f164; + f167 = f165 - f164; + f168 = f16 + f14; + f169 = MUL_C(COEF_CONST(1.4125100802019777), f16); + f170 = MUL_F(FRAC_CONST(-0.6715589548470187), f168); + f171 = MUL_F(FRAC_CONST(0.0693921705079402), f14); + f172 = f169 + f170; + f173 = f171 - f170; + f174 = f20 + f10; + f175 = MUL_C(COEF_CONST(1.3718313541934939), f20); + f176 = MUL_F(FRAC_CONST(-0.5141027441932219), f174); + f177 = MUL_F(FRAC_CONST(0.3436258658070501), f10); + f178 = f175 + f176; + f179 = f177 - f176; + f180 = f24 + f6; + f181 = MUL_C(COEF_CONST(1.2784339185752409), f24); + f182 = MUL_F(FRAC_CONST(-0.3368898533922200), f180); + f183 = MUL_F(FRAC_CONST(0.6046542117908008), f6); + f184 = f181 + f182; + f185 = f183 - f182; + f186 = f28 + f2; + f187 = MUL_C(COEF_CONST(1.1359069844201433), f28); + f188 = MUL_F(FRAC_CONST(-0.1467304744553624), f186); + f189 = MUL_F(FRAC_CONST(0.8424460355094185), f2); + f190 = f187 + f188; + f191 = f189 - f188; + f192 = f149 - f173; + f193 = f149 + f173; + f194 = f148 - f172; + f195 = f148 + f172; + f196 = f155 - f179; + f197 = f155 + f179; + f198 = f154 - f178; + f199 = f154 + f178; + f200 = f161 - f185; + f201 = f161 + f185; + f202 = f160 - f184; + f203 = f160 + f184; + f204 = f167 - f191; + f205 = f167 + f191; + f206 = f166 - f190; + f207 = f166 + f190; + f208 = f192 + f194; + f209 = MUL_C(COEF_CONST(1.1758756024193588), f192); + f210 = MUL_F(FRAC_CONST(-0.9807852804032304), f208); + f211 = MUL_F(FRAC_CONST(-0.7856949583871021), f194); + f212 = f209 + f210; + f213 = f211 - f210; + f214 = f196 + f198; + f215 = MUL_C(COEF_CONST(1.3870398453221475), f196); + f216 = MUL_F(FRAC_CONST(-0.5555702330196022), f214); + f217 = MUL_F(FRAC_CONST(0.2758993792829431), f198); + f218 = f215 + f216; + f219 = f217 - f216; + f220 = f200 + f202; + f221 = MUL_F(FRAC_CONST(0.7856949583871022), f200); + f222 = MUL_F(FRAC_CONST(0.1950903220161283), f220); + f223 = MUL_C(COEF_CONST(1.1758756024193586), f202); + f224 = f221 + f222; + f225 = f223 - f222; + f226 = f204 + f206; + f227 = MUL_F(FRAC_CONST(-0.2758993792829430), f204); + f228 = MUL_F(FRAC_CONST(0.8314696123025452), f226); + f229 = MUL_C(COEF_CONST(1.3870398453221475), f206); + f230 = f227 + f228; + f231 = f229 - f228; + f232 = f193 - f201; + f233 = f193 + f201; + f234 = f195 - f203; + f235 = f195 + f203; + f236 = f197 - f205; + f237 = f197 + f205; + f238 = f199 - f207; + f239 = f199 + f207; + f240 = f213 - f225; + f241 = f213 + f225; + f242 = f212 - f224; + f243 = f212 + f224; + f244 = f219 - f231; + f245 = f219 + f231; + f246 = f218 - f230; + f247 = f218 + f230; + f248 = f232 + f234; + f249 = MUL_C(COEF_CONST(1.3065629648763766), f232); + f250 = MUL_F(FRAC_CONST(-0.9238795325112866), f248); + f251 = MUL_F(FRAC_CONST(-0.5411961001461967), f234); + f252 = f249 + f250; + f253 = f251 - f250; + f254 = f236 + f238; + f255 = MUL_F(FRAC_CONST(0.5411961001461969), f236); + f256 = MUL_F(FRAC_CONST(0.3826834323650898), f254); + f257 = MUL_C(COEF_CONST(1.3065629648763766), f238); + f258 = f255 + f256; + f259 = f257 - f256; + f260 = f240 + f242; + f261 = MUL_C(COEF_CONST(1.3065629648763766), f240); + f262 = MUL_F(FRAC_CONST(-0.9238795325112866), f260); + f263 = MUL_F(FRAC_CONST(-0.5411961001461967), f242); + f264 = f261 + f262; + f265 = f263 - f262; + f266 = f244 + f246; + f267 = MUL_F(FRAC_CONST(0.5411961001461969), f244); + f268 = MUL_F(FRAC_CONST(0.3826834323650898), f266); + f269 = MUL_C(COEF_CONST(1.3065629648763766), f246); + f270 = f267 + f268; + f271 = f269 - f268; + f272 = f233 - f237; + f273 = f233 + f237; + f274 = f235 - f239; + f275 = f235 + f239; + f276 = f253 - f259; + f277 = f253 + f259; + f278 = f252 - f258; + f279 = f252 + f258; + f280 = f241 - f245; + f281 = f241 + f245; + f282 = f243 - f247; + f283 = f243 + f247; + f284 = f265 - f271; + f285 = f265 + f271; + f286 = f264 - f270; + f287 = f264 + f270; + f288 = f272 - f274; + f289 = f272 + f274; + f290 = MUL_F(FRAC_CONST(0.7071067811865474), f288); + f291 = MUL_F(FRAC_CONST(0.7071067811865474), f289); + f292 = f276 - f278; + f293 = f276 + f278; + f294 = MUL_F(FRAC_CONST(0.7071067811865474), f292); + f295 = MUL_F(FRAC_CONST(0.7071067811865474), f293); + f296 = f280 - f282; + f297 = f280 + f282; + f298 = MUL_F(FRAC_CONST(0.7071067811865474), f296); + f299 = MUL_F(FRAC_CONST(0.7071067811865474), f297); + f300 = f284 - f286; + f301 = f284 + f286; + f302 = MUL_F(FRAC_CONST(0.7071067811865474), f300); + f303 = MUL_F(FRAC_CONST(0.7071067811865474), f301); + f304 = f129 - f273; + f305 = f129 + f273; + f306 = f131 - f281; + f307 = f131 + f281; + f308 = f133 - f285; + f309 = f133 + f285; + f310 = f135 - f277; + f311 = f135 + f277; + f312 = f137 - f295; + f313 = f137 + f295; + f314 = f139 - f303; + f315 = f139 + f303; + f316 = f141 - f299; + f317 = f141 + f299; + f318 = f143 - f291; + f319 = f143 + f291; + f320 = f142 - f290; + f321 = f142 + f290; + f322 = f140 - f298; + f323 = f140 + f298; + f324 = f138 - f302; + f325 = f138 + f302; + f326 = f136 - f294; + f327 = f136 + f294; + f328 = f134 - f279; + f329 = f134 + f279; + f330 = f132 - f287; + f331 = f132 + f287; + f332 = f130 - f283; + f333 = f130 + f283; + f334 = f128 - f275; + f335 = f128 + f275; + y[31] = MUL_F(FRAC_CONST(0.5001506360206510), f305); + y[30] = MUL_F(FRAC_CONST(0.5013584524464084), f307); + y[29] = MUL_F(FRAC_CONST(0.5037887256810443), f309); + y[28] = MUL_F(FRAC_CONST(0.5074711720725553), f311); + y[27] = MUL_F(FRAC_CONST(0.5124514794082247), f313); + y[26] = MUL_F(FRAC_CONST(0.5187927131053328), f315); + y[25] = MUL_F(FRAC_CONST(0.5265773151542700), f317); + y[24] = MUL_F(FRAC_CONST(0.5359098169079920), f319); + y[23] = MUL_F(FRAC_CONST(0.5469204379855088), f321); + y[22] = MUL_F(FRAC_CONST(0.5597698129470802), f323); + y[21] = MUL_F(FRAC_CONST(0.5746551840326600), f325); + y[20] = MUL_F(FRAC_CONST(0.5918185358574165), f327); + y[19] = MUL_F(FRAC_CONST(0.6115573478825099), f329); + y[18] = MUL_F(FRAC_CONST(0.6342389366884031), f331); + y[17] = MUL_F(FRAC_CONST(0.6603198078137061), f333); + y[16] = MUL_F(FRAC_CONST(0.6903721282002123), f335); + y[15] = MUL_F(FRAC_CONST(0.7251205223771985), f334); + y[14] = MUL_F(FRAC_CONST(0.7654941649730891), f332); + y[13] = MUL_F(FRAC_CONST(0.8127020908144905), f330); + y[12] = MUL_F(FRAC_CONST(0.8683447152233481), f328); + y[11] = MUL_F(FRAC_CONST(0.9345835970364075), f326); + y[10] = MUL_C(COEF_CONST(1.0144082649970547), f324); + y[9] = MUL_C(COEF_CONST(1.1120716205797176), f322); + y[8] = MUL_C(COEF_CONST(1.2338327379765710), f320); + y[7] = MUL_C(COEF_CONST(1.3892939586328277), f318); + y[6] = MUL_C(COEF_CONST(1.5939722833856311), f316); + y[5] = MUL_C(COEF_CONST(1.8746759800084078), f314); + y[4] = MUL_C(COEF_CONST(2.2820500680051619), f312); + y[3] = MUL_C(COEF_CONST(2.9246284281582162), f310); + y[2] = MUL_C(COEF_CONST(4.0846110781292477), f308); + y[1] = MUL_C(COEF_CONST(6.7967507116736332), f306); + y[0] = MUL_R(REAL_CONST(20.3738781672314530), f304); +} + +#ifdef SBR_LOW_POWER + +void DCT2_16_unscaled(real_t *y, real_t *x) +{ + real_t f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10; + real_t f11, f12, f13, f14, f15, f16, f17, f18, f19, f20; + real_t f21, f22, f23, f24, f25, f26, f27, f28, f31, f32; + real_t f33, f34, f37, f38, f39, f40, f41, f42, f43, f44; + real_t f45, f46, f47, f48, f49, f51, f53, f54, f57, f58; + real_t f59, f60, f61, f62, f63, f64, f65, f66, f67, f68; + real_t f69, f70, f71, f72, f73, f74, f75, f76, f77, f78; + real_t f79, f80, f81, f82, f83, f84, f85, f86, f87, f88; + real_t f89, f90, f91, f92, f95, f96, f97, f98, f101, f102; + real_t f103, f104, f107, f108, f109, f110; + + f0 = x[0] - x[15]; + f1 = x[0] + x[15]; + f2 = x[1] - x[14]; + f3 = x[1] + x[14]; + f4 = x[2] - x[13]; + f5 = x[2] + x[13]; + f6 = x[3] - x[12]; + f7 = x[3] + x[12]; + f8 = x[4] - x[11]; + f9 = x[4] + x[11]; + f10 = x[5] - x[10]; + f11 = x[5] + x[10]; + f12 = x[6] - x[9]; + f13 = x[6] + x[9]; + f14 = x[7] - x[8]; + f15 = x[7] + x[8]; + f16 = f1 - f15; + f17 = f1 + f15; + f18 = f3 - f13; + f19 = f3 + f13; + f20 = f5 - f11; + f21 = f5 + f11; + f22 = f7 - f9; + f23 = f7 + f9; + f24 = f17 - f23; + f25 = f17 + f23; + f26 = f19 - f21; + f27 = f19 + f21; + f28 = f25 - f27; + y[0] = f25 + f27; + y[8] = MUL_F(f28, FRAC_CONST(0.7071067811865476)); + f31 = f24 + f26; + f32 = MUL_C(f24, COEF_CONST(1.3065629648763766)); + f33 = MUL_F(f31, FRAC_CONST(-0.9238795325112866)); + f34 = MUL_F(f26, FRAC_CONST(-0.5411961001461967)); + y[12] = f32 + f33; + y[4] = f34 - f33; + f37 = f16 + f22; + f38 = MUL_C(f16, COEF_CONST(1.1758756024193588)); + f39 = MUL_F(f37, FRAC_CONST(-0.9807852804032304)); + f40 = MUL_F(f22, FRAC_CONST(-0.7856949583871021)); + f41 = f38 + f39; + f42 = f40 - f39; + f43 = f18 + f20; + f44 = MUL_C(f18, COEF_CONST(1.3870398453221473)); + f45 = MUL_F(f43, FRAC_CONST(-0.8314696123025455)); + f46 = MUL_F(f20, FRAC_CONST(-0.2758993792829436)); + f47 = f44 + f45; + f48 = f46 - f45; + f49 = f42 - f48; + y[2] = f42 + f48; + f51 = MUL_F(f49, FRAC_CONST(0.7071067811865476)); + y[14] = f41 - f47; + f53 = f41 + f47; + f54 = MUL_F(f53, FRAC_CONST(0.7071067811865476)); + y[10] = f51 - f54; + y[6] = f51 + f54; + f57 = f2 - f4; + f58 = f2 + f4; + f59 = f6 - f8; + f60 = f6 + f8; + f61 = f10 - f12; + f62 = f10 + f12; + f63 = MUL_F(f60, FRAC_CONST(0.7071067811865476)); + f64 = f0 - f63; + f65 = f0 + f63; + f66 = f58 + f62; + f67 = MUL_C(f58, COEF_CONST(1.3065629648763766)); + f68 = MUL_F(f66, FRAC_CONST(-0.9238795325112866)); + f69 = MUL_F(f62, FRAC_CONST(-0.5411961001461967)); + f70 = f67 + f68; + f71 = f69 - f68; + f72 = f65 - f71; + f73 = f65 + f71; + f74 = f64 - f70; + f75 = f64 + f70; + f76 = MUL_F(f59, FRAC_CONST(0.7071067811865476)); + f77 = f14 - f76; + f78 = f14 + f76; + f79 = f61 + f57; + f80 = MUL_C(f61, COEF_CONST(1.3065629648763766)); + f81 = MUL_F(f79, FRAC_CONST(-0.9238795325112866)); + f82 = MUL_F(f57, FRAC_CONST(-0.5411961001461967)); + f83 = f80 + f81; + f84 = f82 - f81; + f85 = f78 - f84; + f86 = f78 + f84; + f87 = f77 - f83; + f88 = f77 + f83; + f89 = f86 + f73; + f90 = MUL_F(f86, FRAC_CONST(-0.8971675863426361)); + f91 = MUL_F(f89, FRAC_CONST(0.9951847266721968)); + f92 = MUL_C(f73, COEF_CONST(1.0932018670017576)); + y[1] = f90 + f91; + y[15] = f92 - f91; + f95 = f75 - f88; + f96 = MUL_F(f88, FRAC_CONST(-0.6666556584777466)); + f97 = MUL_F(f95, FRAC_CONST(0.9569403357322089)); + f98 = MUL_C(f75, COEF_CONST(1.2472250129866713)); + y[3] = f97 - f96; + y[13] = f98 - f97; + f101 = f87 + f74; + f102 = MUL_F(f87, FRAC_CONST(-0.4105245275223571)); + f103 = MUL_F(f101, FRAC_CONST(0.8819212643483549)); + f104 = MUL_C(f74, COEF_CONST(1.3533180011743529)); + y[5] = f102 + f103; + y[11] = f104 - f103; + f107 = f72 - f85; + f108 = MUL_F(f85, FRAC_CONST(-0.1386171691990915)); + f109 = MUL_F(f107, FRAC_CONST(0.7730104533627370)); + f110 = MUL_C(f72, COEF_CONST(1.4074037375263826)); + y[7] = f109 - f108; + y[9] = f110 - f109; +} + +void DCT4_16(real_t *y, real_t *x) +{ + real_t f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10; + real_t f11, f12, f13, f14, f15, f16, f17, f18, f19, f20; + real_t f21, f22, f23, f24, f25, f26, f27, f28, f29, f30; + real_t f31, f32, f33, f34, f35, f36, f37, f38, f39, f40; + real_t f41, f42, f43, f44, f45, f46, f47, f48, f49, f50; + real_t f51, f52, f53, f54, f55, f56, f57, f58, f59, f60; + real_t f61, f62, f63, f64, f65, f66, f67, f68, f69, f70; + real_t f71, f72, f73, f74, f75, f76, f77, f78, f79, f80; + real_t f81, f82, f83, f84, f85, f86, f87, f88, f89, f90; + real_t f91, f92, f93, f94, f95, f96, f97, f98, f99, f100; + real_t f101, f102, f103, f104, f105, f106, f107, f108, f109, f110; + real_t f111, f112, f113, f114, f115, f116, f117, f118, f119, f120; + real_t f121, f122, f123, f124, f125, f126, f127, f128, f130, f132; + real_t f134, f136, f138, f140, f142, f144, f145, f148, f149, f152; + real_t f153, f156, f157; + + f0 = x[0] + x[15]; + f1 = MUL_C(COEF_CONST(1.0478631305325901), x[0]); + f2 = MUL_F(FRAC_CONST(-0.9987954562051724), f0); + f3 = MUL_F(FRAC_CONST(-0.9497277818777548), x[15]); + f4 = f1 + f2; + f5 = f3 - f2; + f6 = x[2] + x[13]; + f7 = MUL_C(COEF_CONST(1.2130114330978077), x[2]); + f8 = MUL_F(FRAC_CONST(-0.9700312531945440), f6); + f9 = MUL_F(FRAC_CONST(-0.7270510732912803), x[13]); + f10 = f7 + f8; + f11 = f9 - f8; + f12 = x[4] + x[11]; + f13 = MUL_C(COEF_CONST(1.3315443865537255), x[4]); + f14 = MUL_F(FRAC_CONST(-0.9039892931234433), f12); + f15 = MUL_F(FRAC_CONST(-0.4764341996931612), x[11]); + f16 = f13 + f14; + f17 = f15 - f14; + f18 = x[6] + x[9]; + f19 = MUL_C(COEF_CONST(1.3989068359730781), x[6]); + f20 = MUL_F(FRAC_CONST(-0.8032075314806453), f18); + f21 = MUL_F(FRAC_CONST(-0.2075082269882124), x[9]); + f22 = f19 + f20; + f23 = f21 - f20; + f24 = x[8] + x[7]; + f25 = MUL_C(COEF_CONST(1.4125100802019777), x[8]); + f26 = MUL_F(FRAC_CONST(-0.6715589548470187), f24); + f27 = MUL_F(FRAC_CONST(0.0693921705079402), x[7]); + f28 = f25 + f26; + f29 = f27 - f26; + f30 = x[10] + x[5]; + f31 = MUL_C(COEF_CONST(1.3718313541934939), x[10]); + f32 = MUL_F(FRAC_CONST(-0.5141027441932219), f30); + f33 = MUL_F(FRAC_CONST(0.3436258658070501), x[5]); + f34 = f31 + f32; + f35 = f33 - f32; + f36 = x[12] + x[3]; + f37 = MUL_C(COEF_CONST(1.2784339185752409), x[12]); + f38 = MUL_F(FRAC_CONST(-0.3368898533922200), f36); + f39 = MUL_F(FRAC_CONST(0.6046542117908008), x[3]); + f40 = f37 + f38; + f41 = f39 - f38; + f42 = x[14] + x[1]; + f43 = MUL_C(COEF_CONST(1.1359069844201433), x[14]); + f44 = MUL_F(FRAC_CONST(-0.1467304744553624), f42); + f45 = MUL_F(FRAC_CONST(0.8424460355094185), x[1]); + f46 = f43 + f44; + f47 = f45 - f44; + f48 = f5 - f29; + f49 = f5 + f29; + f50 = f4 - f28; + f51 = f4 + f28; + f52 = f11 - f35; + f53 = f11 + f35; + f54 = f10 - f34; + f55 = f10 + f34; + f56 = f17 - f41; + f57 = f17 + f41; + f58 = f16 - f40; + f59 = f16 + f40; + f60 = f23 - f47; + f61 = f23 + f47; + f62 = f22 - f46; + f63 = f22 + f46; + f64 = f48 + f50; + f65 = MUL_C(COEF_CONST(1.1758756024193588), f48); + f66 = MUL_F(FRAC_CONST(-0.9807852804032304), f64); + f67 = MUL_F(FRAC_CONST(-0.7856949583871021), f50); + f68 = f65 + f66; + f69 = f67 - f66; + f70 = f52 + f54; + f71 = MUL_C(COEF_CONST(1.3870398453221475), f52); + f72 = MUL_F(FRAC_CONST(-0.5555702330196022), f70); + f73 = MUL_F(FRAC_CONST(0.2758993792829431), f54); + f74 = f71 + f72; + f75 = f73 - f72; + f76 = f56 + f58; + f77 = MUL_F(FRAC_CONST(0.7856949583871022), f56); + f78 = MUL_F(FRAC_CONST(0.1950903220161283), f76); + f79 = MUL_C(COEF_CONST(1.1758756024193586), f58); + f80 = f77 + f78; + f81 = f79 - f78; + f82 = f60 + f62; + f83 = MUL_F(FRAC_CONST(-0.2758993792829430), f60); + f84 = MUL_F(FRAC_CONST(0.8314696123025452), f82); + f85 = MUL_C(COEF_CONST(1.3870398453221475), f62); + f86 = f83 + f84; + f87 = f85 - f84; + f88 = f49 - f57; + f89 = f49 + f57; + f90 = f51 - f59; + f91 = f51 + f59; + f92 = f53 - f61; + f93 = f53 + f61; + f94 = f55 - f63; + f95 = f55 + f63; + f96 = f69 - f81; + f97 = f69 + f81; + f98 = f68 - f80; + f99 = f68 + f80; + f100 = f75 - f87; + f101 = f75 + f87; + f102 = f74 - f86; + f103 = f74 + f86; + f104 = f88 + f90; + f105 = MUL_C(COEF_CONST(1.3065629648763766), f88); + f106 = MUL_F(FRAC_CONST(-0.9238795325112866), f104); + f107 = MUL_F(FRAC_CONST(-0.5411961001461967), f90); + f108 = f105 + f106; + f109 = f107 - f106; + f110 = f92 + f94; + f111 = MUL_F(FRAC_CONST(0.5411961001461969), f92); + f112 = MUL_F(FRAC_CONST(0.3826834323650898), f110); + f113 = MUL_C(COEF_CONST(1.3065629648763766), f94); + f114 = f111 + f112; + f115 = f113 - f112; + f116 = f96 + f98; + f117 = MUL_C(COEF_CONST(1.3065629648763766), f96); + f118 = MUL_F(FRAC_CONST(-0.9238795325112866), f116); + f119 = MUL_F(FRAC_CONST(-0.5411961001461967), f98); + f120 = f117 + f118; + f121 = f119 - f118; + f122 = f100 + f102; + f123 = MUL_F(FRAC_CONST(0.5411961001461969), f100); + f124 = MUL_F(FRAC_CONST(0.3826834323650898), f122); + f125 = MUL_C(COEF_CONST(1.3065629648763766), f102); + f126 = f123 + f124; + f127 = f125 - f124; + f128 = f89 - f93; + y[0] = f89 + f93; + f130 = f91 - f95; + y[15] = f91 + f95; + f132 = f109 - f115; + y[3] = f109 + f115; + f134 = f108 - f114; + y[12] = f108 + f114; + f136 = f97 - f101; + y[1] = f97 + f101; + f138 = f99 - f103; + y[14] = f99 + f103; + f140 = f121 - f127; + y[2] = f121 + f127; + f142 = f120 - f126; + y[13] = f120 + f126; + f144 = f128 - f130; + f145 = f128 + f130; + y[8] = MUL_F(FRAC_CONST(0.7071067811865474), f144); + y[7] = MUL_F(FRAC_CONST(0.7071067811865474), f145); + f148 = f132 - f134; + f149 = f132 + f134; + y[11] = MUL_F(FRAC_CONST(0.7071067811865474), f148); + y[4] = MUL_F(FRAC_CONST(0.7071067811865474), f149); + f152 = f136 - f138; + f153 = f136 + f138; + y[9] = MUL_F(FRAC_CONST(0.7071067811865474), f152); + y[6] = MUL_F(FRAC_CONST(0.7071067811865474), f153); + f156 = f140 - f142; + f157 = f140 + f142; + y[10] = MUL_F(FRAC_CONST(0.7071067811865474), f156); + y[5] = MUL_F(FRAC_CONST(0.7071067811865474), f157); +} + +void DCT3_32_unscaled(real_t *y, real_t *x) +{ + real_t f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10; + real_t f11, f12, f13, f14, f15, f16, f17, f18, f19, f20; + real_t f21, f22, f23, f24, f25, f26, f27, f28, f29, f30; + real_t f31, f32, f33, f34, f35, f36, f37, f38, f39, f40; + real_t f41, f42, f43, f44, f45, f46, f47, f48, f49, f50; + real_t f51, f52, f53, f54, f55, f56, f57, f58, f59, f60; + real_t f61, f62, f63, f64, f65, f66, f67, f68, f69, f70; + real_t f71, f72, f73, f74, f75, f76, f77, f78, f79, f80; + real_t f81, f82, f83, f84, f85, f86, f87, f88, f89, f90; + real_t f91, f92, f93, f94, f95, f96, f97, f98, f99, f100; + real_t f101, f102, f103, f104, f105, f106, f107, f108, f109, f110; + real_t f111, f112, f113, f114, f115, f116, f117, f118, f119, f120; + real_t f121, f122, f123, f124, f125, f126, f127, f128, f129, f130; + real_t f131, f132, f133, f134, f135, f136, f137, f138, f139, f140; + real_t f141, f142, f143, f144, f145, f146, f147, f148, f149, f150; + real_t f151, f152, f153, f154, f155, f156, f157, f158, f159, f160; + real_t f161, f162, f163, f164, f165, f166, f167, f168, f169, f170; + real_t f171, f172, f173, f174, f175, f176, f177, f178, f179, f180; + real_t f181, f182, f183, f184, f185, f186, f187, f188, f189, f190; + real_t f191, f192, f193, f194, f195, f196, f197, f198, f199, f200; + real_t f201, f202, f203, f204, f205, f206, f207, f208, f209, f210; + real_t f211, f212, f213, f214, f215, f216, f217, f218, f219, f220; + real_t f221, f222, f223, f224, f225, f226, f227, f228, f229, f230; + real_t f231, f232, f233, f234, f235, f236, f237, f238, f239, f240; + real_t f241, f242, f243, f244, f245, f246, f247, f248, f249, f250; + real_t f251, f252, f253, f254, f255, f256, f257, f258, f259, f260; + real_t f261, f262, f263, f264, f265, f266, f267, f268, f269, f270; + real_t f271, f272; + + f0 = MUL_F(x[16], FRAC_CONST(0.7071067811865476)); + f1 = x[0] - f0; + f2 = x[0] + f0; + f3 = x[8] + x[24]; + f4 = MUL_C(x[8], COEF_CONST(1.3065629648763766)); + f5 = MUL_F(f3, FRAC_CONST((-0.9238795325112866))); + f6 = MUL_F(x[24], FRAC_CONST((-0.5411961001461967))); + f7 = f4 + f5; + f8 = f6 - f5; + f9 = f2 - f8; + f10 = f2 + f8; + f11 = f1 - f7; + f12 = f1 + f7; + f13 = x[4] + x[28]; + f14 = MUL_C(x[4], COEF_CONST(1.1758756024193588)); + f15 = MUL_F(f13, FRAC_CONST((-0.9807852804032304))); + f16 = MUL_F(x[28], FRAC_CONST((-0.7856949583871021))); + f17 = f14 + f15; + f18 = f16 - f15; + f19 = x[12] + x[20]; + f20 = MUL_C(x[12], COEF_CONST(1.3870398453221473)); + f21 = MUL_F(f19, FRAC_CONST((-0.8314696123025455))); + f22 = MUL_F(x[20], FRAC_CONST((-0.2758993792829436))); + f23 = f20 + f21; + f24 = f22 - f21; + f25 = f18 - f24; + f26 = f18 + f24; + f27 = MUL_F(f25, FRAC_CONST(0.7071067811865476)); + f28 = f17 - f23; + f29 = f17 + f23; + f30 = MUL_F(f29, FRAC_CONST(0.7071067811865476)); + f31 = f27 - f30; + f32 = f27 + f30; + f33 = f10 - f26; + f34 = f10 + f26; + f35 = f12 - f32; + f36 = f12 + f32; + f37 = f11 - f31; + f38 = f11 + f31; + f39 = f9 - f28; + f40 = f9 + f28; + f41 = x[2] + x[30]; + f42 = MUL_C(x[2], COEF_CONST(1.0932018670017569)); + f43 = MUL_F(f41, FRAC_CONST((-0.9951847266721969))); + f44 = MUL_F(x[30], FRAC_CONST((-0.8971675863426368))); + f45 = f42 + f43; + f46 = f44 - f43; + f47 = x[6] + x[26]; + f48 = MUL_C(x[6], COEF_CONST(1.2472250129866711)); + f49 = MUL_F(f47, FRAC_CONST((-0.9569403357322089))); + f50 = MUL_F(x[26], FRAC_CONST((-0.6666556584777469))); + f51 = f48 + f49; + f52 = f50 - f49; + f53 = x[10] + x[22]; + f54 = MUL_C(x[10], COEF_CONST(1.3533180011743526)); + f55 = MUL_F(f53, FRAC_CONST((-0.8819212643483551))); + f56 = MUL_F(x[22], FRAC_CONST((-0.4105245275223575))); + f57 = f54 + f55; + f58 = f56 - f55; + f59 = x[14] + x[18]; + f60 = MUL_C(x[14], COEF_CONST(1.4074037375263826)); + f61 = MUL_F(f59, FRAC_CONST((-0.7730104533627369))); + f62 = MUL_F(x[18], FRAC_CONST((-0.1386171691990913))); + f63 = f60 + f61; + f64 = f62 - f61; + f65 = f46 - f64; + f66 = f46 + f64; + f67 = f52 - f58; + f68 = f52 + f58; + f69 = f66 - f68; + f70 = f66 + f68; + f71 = MUL_F(f69, FRAC_CONST(0.7071067811865476)); + f72 = f65 + f67; + f73 = MUL_C(f65, COEF_CONST(1.3065629648763766)); + f74 = MUL_F(f72, FRAC_CONST((-0.9238795325112866))); + f75 = MUL_F(f67, FRAC_CONST((-0.5411961001461967))); + f76 = f73 + f74; + f77 = f75 - f74; + f78 = f45 - f63; + f79 = f45 + f63; + f80 = f51 - f57; + f81 = f51 + f57; + f82 = f79 + f81; + f83 = MUL_C(f79, COEF_CONST(1.3065629648763770)); + f84 = MUL_F(f82, FRAC_CONST((-0.3826834323650904))); + f85 = MUL_F(f81, FRAC_CONST(0.5411961001461961)); + f86 = f83 + f84; + f87 = f85 - f84; + f88 = f78 - f80; + f89 = f78 + f80; + f90 = MUL_F(f89, FRAC_CONST(0.7071067811865476)); + f91 = f77 - f87; + f92 = f77 + f87; + f93 = f71 - f90; + f94 = f71 + f90; + f95 = f76 - f86; + f96 = f76 + f86; + f97 = f34 - f70; + f98 = f34 + f70; + f99 = f36 - f92; + f100 = f36 + f92; + f101 = f38 - f91; + f102 = f38 + f91; + f103 = f40 - f94; + f104 = f40 + f94; + f105 = f39 - f93; + f106 = f39 + f93; + f107 = f37 - f96; + f108 = f37 + f96; + f109 = f35 - f95; + f110 = f35 + f95; + f111 = f33 - f88; + f112 = f33 + f88; + f113 = x[1] + x[31]; + f114 = MUL_C(x[1], COEF_CONST(1.0478631305325901)); + f115 = MUL_F(f113, FRAC_CONST((-0.9987954562051724))); + f116 = MUL_F(x[31], FRAC_CONST((-0.9497277818777548))); + f117 = f114 + f115; + f118 = f116 - f115; + f119 = x[5] + x[27]; + f120 = MUL_C(x[5], COEF_CONST(1.2130114330978077)); + f121 = MUL_F(f119, FRAC_CONST((-0.9700312531945440))); + f122 = MUL_F(x[27], FRAC_CONST((-0.7270510732912803))); + f123 = f120 + f121; + f124 = f122 - f121; + f125 = x[9] + x[23]; + f126 = MUL_C(x[9], COEF_CONST(1.3315443865537255)); + f127 = MUL_F(f125, FRAC_CONST((-0.9039892931234433))); + f128 = MUL_F(x[23], FRAC_CONST((-0.4764341996931612))); + f129 = f126 + f127; + f130 = f128 - f127; + f131 = x[13] + x[19]; + f132 = MUL_C(x[13], COEF_CONST(1.3989068359730781)); + f133 = MUL_F(f131, FRAC_CONST((-0.8032075314806453))); + f134 = MUL_F(x[19], FRAC_CONST((-0.2075082269882124))); + f135 = f132 + f133; + f136 = f134 - f133; + f137 = x[17] + x[15]; + f138 = MUL_C(x[17], COEF_CONST(1.4125100802019777)); + f139 = MUL_F(f137, FRAC_CONST((-0.6715589548470187))); + f140 = MUL_F(x[15], FRAC_CONST(0.0693921705079402)); + f141 = f138 + f139; + f142 = f140 - f139; + f143 = x[21] + x[11]; + f144 = MUL_C(x[21], COEF_CONST(1.3718313541934939)); + f145 = MUL_F(f143, FRAC_CONST((-0.5141027441932219))); + f146 = MUL_F(x[11], FRAC_CONST(0.3436258658070501)); + f147 = f144 + f145; + f148 = f146 - f145; + f149 = x[25] + x[7]; + f150 = MUL_C(x[25], COEF_CONST(1.2784339185752409)); + f151 = MUL_F(f149, FRAC_CONST((-0.3368898533922200))); + f152 = MUL_F(x[7], FRAC_CONST(0.6046542117908008)); + f153 = f150 + f151; + f154 = f152 - f151; + f155 = x[29] + x[3]; + f156 = MUL_C(x[29], COEF_CONST(1.1359069844201433)); + f157 = MUL_F(f155, FRAC_CONST((-0.1467304744553624))); + f158 = MUL_F(x[3], FRAC_CONST(0.8424460355094185)); + f159 = f156 + f157; + f160 = f158 - f157; + f161 = f118 - f142; + f162 = f118 + f142; + f163 = f117 - f141; + f164 = f117 + f141; + f165 = f124 - f148; + f166 = f124 + f148; + f167 = f123 - f147; + f168 = f123 + f147; + f169 = f130 - f154; + f170 = f130 + f154; + f171 = f129 - f153; + f172 = f129 + f153; + f173 = f136 - f160; + f174 = f136 + f160; + f175 = f135 - f159; + f176 = f135 + f159; + f177 = f161 + f163; + f178 = MUL_C(f161, COEF_CONST(1.1758756024193588)); + f179 = MUL_F(f177, FRAC_CONST((-0.9807852804032304))); + f180 = MUL_F(f163, FRAC_CONST((-0.7856949583871021))); + f181 = f178 + f179; + f182 = f180 - f179; + f183 = f165 + f167; + f184 = MUL_C(f165, COEF_CONST(1.3870398453221475)); + f185 = MUL_F(f183, FRAC_CONST((-0.5555702330196022))); + f186 = MUL_F(f167, FRAC_CONST(0.2758993792829431)); + f187 = f184 + f185; + f188 = f186 - f185; + f189 = f169 + f171; + f190 = MUL_F(f169, FRAC_CONST(0.7856949583871022)); + f191 = MUL_F(f189, FRAC_CONST(0.1950903220161283)); + f192 = MUL_C(f171, COEF_CONST(1.1758756024193586)); + f193 = f190 + f191; + f194 = f192 - f191; + f195 = f173 + f175; + f196 = MUL_F(f173, FRAC_CONST((-0.2758993792829430))); + f197 = MUL_F(f195, FRAC_CONST(0.8314696123025452)); + f198 = MUL_C(f175, COEF_CONST(1.3870398453221475)); + f199 = f196 + f197; + f200 = f198 - f197; + f201 = f162 - f170; + f202 = f162 + f170; + f203 = f164 - f172; + f204 = f164 + f172; + f205 = f166 - f174; + f206 = f166 + f174; + f207 = f168 - f176; + f208 = f168 + f176; + f209 = f182 - f194; + f210 = f182 + f194; + f211 = f181 - f193; + f212 = f181 + f193; + f213 = f188 - f200; + f214 = f188 + f200; + f215 = f187 - f199; + f216 = f187 + f199; + f217 = f201 + f203; + f218 = MUL_C(f201, COEF_CONST(1.3065629648763766)); + f219 = MUL_F(f217, FRAC_CONST((-0.9238795325112866))); + f220 = MUL_F(f203, FRAC_CONST((-0.5411961001461967))); + f221 = f218 + f219; + f222 = f220 - f219; + f223 = f205 + f207; + f224 = MUL_F(f205, FRAC_CONST(0.5411961001461969)); + f225 = MUL_F(f223, FRAC_CONST(0.3826834323650898)); + f226 = MUL_C(f207, COEF_CONST(1.3065629648763766)); + f227 = f224 + f225; + f228 = f226 - f225; + f229 = f209 + f211; + f230 = MUL_C(f209, COEF_CONST(1.3065629648763766)); + f231 = MUL_F(f229, FRAC_CONST((-0.9238795325112866))); + f232 = MUL_F(f211, FRAC_CONST((-0.5411961001461967))); + f233 = f230 + f231; + f234 = f232 - f231; + f235 = f213 + f215; + f236 = MUL_F(f213, FRAC_CONST(0.5411961001461969)); + f237 = MUL_F(f235, FRAC_CONST(0.3826834323650898)); + f238 = MUL_C(f215, COEF_CONST(1.3065629648763766)); + f239 = f236 + f237; + f240 = f238 - f237; + f241 = f202 - f206; + f242 = f202 + f206; + f243 = f204 - f208; + f244 = f204 + f208; + f245 = f222 - f228; + f246 = f222 + f228; + f247 = f221 - f227; + f248 = f221 + f227; + f249 = f210 - f214; + f250 = f210 + f214; + f251 = f212 - f216; + f252 = f212 + f216; + f253 = f234 - f240; + f254 = f234 + f240; + f255 = f233 - f239; + f256 = f233 + f239; + f257 = f241 - f243; + f258 = f241 + f243; + f259 = MUL_F(f257, FRAC_CONST(0.7071067811865474)); + f260 = MUL_F(f258, FRAC_CONST(0.7071067811865474)); + f261 = f245 - f247; + f262 = f245 + f247; + f263 = MUL_F(f261, FRAC_CONST(0.7071067811865474)); + f264 = MUL_F(f262, FRAC_CONST(0.7071067811865474)); + f265 = f249 - f251; + f266 = f249 + f251; + f267 = MUL_F(f265, FRAC_CONST(0.7071067811865474)); + f268 = MUL_F(f266, FRAC_CONST(0.7071067811865474)); + f269 = f253 - f255; + f270 = f253 + f255; + f271 = MUL_F(f269, FRAC_CONST(0.7071067811865474)); + f272 = MUL_F(f270, FRAC_CONST(0.7071067811865474)); + y[31] = f98 - f242; + y[0] = f98 + f242; + y[30] = f100 - f250; + y[1] = f100 + f250; + y[29] = f102 - f254; + y[2] = f102 + f254; + y[28] = f104 - f246; + y[3] = f104 + f246; + y[27] = f106 - f264; + y[4] = f106 + f264; + y[26] = f108 - f272; + y[5] = f108 + f272; + y[25] = f110 - f268; + y[6] = f110 + f268; + y[24] = f112 - f260; + y[7] = f112 + f260; + y[23] = f111 - f259; + y[8] = f111 + f259; + y[22] = f109 - f267; + y[9] = f109 + f267; + y[21] = f107 - f271; + y[10] = f107 + f271; + y[20] = f105 - f263; + y[11] = f105 + f263; + y[19] = f103 - f248; + y[12] = f103 + f248; + y[18] = f101 - f256; + y[13] = f101 + f256; + y[17] = f99 - f252; + y[14] = f99 + f252; + y[16] = f97 - f244; + y[15] = f97 + f244; +} + +void DCT2_32_unscaled(real_t *y, real_t *x) +{ + real_t f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10; + real_t f11, f12, f13, f14, f15, f16, f17, f18, f19, f20; + real_t f21, f22, f23, f24, f25, f26, f27, f28, f29, f30; + real_t f31, f32, f33, f34, f35, f36, f37, f38, f39, f40; + real_t f41, f42, f43, f44, f45, f46, f47, f48, f49, f50; + real_t f51, f52, f53, f54, f55, f56, f57, f58, f59, f60; + real_t f63, f64, f65, f66, f69, f70, f71, f72, f73, f74; + real_t f75, f76, f77, f78, f79, f80, f81, f83, f85, f86; + real_t f89, f90, f91, f92, f93, f94, f95, f96, f97, f98; + real_t f99, f100, f101, f102, f103, f104, f105, f106, f107, f108; + real_t f109, f110, f111, f112, f113, f114, f115, f116, f117, f118; + real_t f119, f120, f121, f122, f123, f124, f127, f128, f129, f130; + real_t f133, f134, f135, f136, f139, f140, f141, f142, f145, f146; + real_t f147, f148, f149, f150, f151, f152, f153, f154, f155, f156; + real_t f157, f158, f159, f160, f161, f162, f163, f164, f165, f166; + real_t f167, f168, f169, f170, f171, f172, f173, f174, f175, f176; + real_t f177, f178, f179, f180, f181, f182, f183, f184, f185, f186; + real_t f187, f188, f189, f190, f191, f192, f193, f194, f195, f196; + real_t f197, f198, f199, f200, f201, f202, f203, f204, f205, f206; + real_t f207, f208, f209, f210, f211, f212, f213, f214, f215, f216; + real_t f217, f218, f219, f220, f221, f222, f223, f224, f225, f226; + real_t f227, f228, f229, f230, f231, f232, f233, f234, f235, f236; + real_t f237, f238, f239, f240, f241, f242, f243, f244, f247, f248; + real_t f249, f250, f253, f254, f255, f256, f259, f260, f261, f262; + real_t f265, f266, f267, f268, f271, f272, f273, f274, f277, f278; + real_t f279, f280, f283, f284, f285, f286; + + f0 = x[0] - x[31]; + f1 = x[0] + x[31]; + f2 = x[1] - x[30]; + f3 = x[1] + x[30]; + f4 = x[2] - x[29]; + f5 = x[2] + x[29]; + f6 = x[3] - x[28]; + f7 = x[3] + x[28]; + f8 = x[4] - x[27]; + f9 = x[4] + x[27]; + f10 = x[5] - x[26]; + f11 = x[5] + x[26]; + f12 = x[6] - x[25]; + f13 = x[6] + x[25]; + f14 = x[7] - x[24]; + f15 = x[7] + x[24]; + f16 = x[8] - x[23]; + f17 = x[8] + x[23]; + f18 = x[9] - x[22]; + f19 = x[9] + x[22]; + f20 = x[10] - x[21]; + f21 = x[10] + x[21]; + f22 = x[11] - x[20]; + f23 = x[11] + x[20]; + f24 = x[12] - x[19]; + f25 = x[12] + x[19]; + f26 = x[13] - x[18]; + f27 = x[13] + x[18]; + f28 = x[14] - x[17]; + f29 = x[14] + x[17]; + f30 = x[15] - x[16]; + f31 = x[15] + x[16]; + f32 = f1 - f31; + f33 = f1 + f31; + f34 = f3 - f29; + f35 = f3 + f29; + f36 = f5 - f27; + f37 = f5 + f27; + f38 = f7 - f25; + f39 = f7 + f25; + f40 = f9 - f23; + f41 = f9 + f23; + f42 = f11 - f21; + f43 = f11 + f21; + f44 = f13 - f19; + f45 = f13 + f19; + f46 = f15 - f17; + f47 = f15 + f17; + f48 = f33 - f47; + f49 = f33 + f47; + f50 = f35 - f45; + f51 = f35 + f45; + f52 = f37 - f43; + f53 = f37 + f43; + f54 = f39 - f41; + f55 = f39 + f41; + f56 = f49 - f55; + f57 = f49 + f55; + f58 = f51 - f53; + f59 = f51 + f53; + f60 = f57 - f59; + y[0] = f57 + f59; + y[16] = MUL_F(FRAC_CONST(0.7071067811865476), f60); + f63 = f56 + f58; + f64 = MUL_C(COEF_CONST(1.3065629648763766), f56); + f65 = MUL_F(FRAC_CONST(-0.9238795325112866), f63); + f66 = MUL_F(FRAC_CONST(-0.5411961001461967), f58); + y[24] = f64 + f65; + y[8] = f66 - f65; + f69 = f48 + f54; + f70 = MUL_C(COEF_CONST(1.1758756024193588), f48); + f71 = MUL_F(FRAC_CONST(-0.9807852804032304), f69); + f72 = MUL_F(FRAC_CONST(-0.7856949583871021), f54); + f73 = f70 + f71; + f74 = f72 - f71; + f75 = f50 + f52; + f76 = MUL_C(COEF_CONST(1.3870398453221473), f50); + f77 = MUL_F(FRAC_CONST(-0.8314696123025455), f75); + f78 = MUL_F(FRAC_CONST(-0.2758993792829436), f52); + f79 = f76 + f77; + f80 = f78 - f77; + f81 = f74 - f80; + y[4] = f74 + f80; + f83 = MUL_F(FRAC_CONST(0.7071067811865476), f81); + y[28] = f73 - f79; + f85 = f73 + f79; + f86 = MUL_F(FRAC_CONST(0.7071067811865476), f85); + y[20] = f83 - f86; + y[12] = f83 + f86; + f89 = f34 - f36; + f90 = f34 + f36; + f91 = f38 - f40; + f92 = f38 + f40; + f93 = f42 - f44; + f94 = f42 + f44; + f95 = MUL_F(FRAC_CONST(0.7071067811865476), f92); + f96 = f32 - f95; + f97 = f32 + f95; + f98 = f90 + f94; + f99 = MUL_C(COEF_CONST(1.3065629648763766), f90); + f100 = MUL_F(FRAC_CONST(-0.9238795325112866), f98); + f101 = MUL_F(FRAC_CONST(-0.5411961001461967), f94); + f102 = f99 + f100; + f103 = f101 - f100; + f104 = f97 - f103; + f105 = f97 + f103; + f106 = f96 - f102; + f107 = f96 + f102; + f108 = MUL_F(FRAC_CONST(0.7071067811865476), f91); + f109 = f46 - f108; + f110 = f46 + f108; + f111 = f93 + f89; + f112 = MUL_C(COEF_CONST(1.3065629648763766), f93); + f113 = MUL_F(FRAC_CONST(-0.9238795325112866), f111); + f114 = MUL_F(FRAC_CONST(-0.5411961001461967), f89); + f115 = f112 + f113; + f116 = f114 - f113; + f117 = f110 - f116; + f118 = f110 + f116; + f119 = f109 - f115; + f120 = f109 + f115; + f121 = f118 + f105; + f122 = MUL_F(FRAC_CONST(-0.8971675863426361), f118); + f123 = MUL_F(FRAC_CONST(0.9951847266721968), f121); + f124 = MUL_C(COEF_CONST(1.0932018670017576), f105); + y[2] = f122 + f123; + y[30] = f124 - f123; + f127 = f107 - f120; + f128 = MUL_F(FRAC_CONST(-0.6666556584777466), f120); + f129 = MUL_F(FRAC_CONST(0.9569403357322089), f127); + f130 = MUL_C(COEF_CONST(1.2472250129866713), f107); + y[6] = f129 - f128; + y[26] = f130 - f129; + f133 = f119 + f106; + f134 = MUL_F(FRAC_CONST(-0.4105245275223571), f119); + f135 = MUL_F(FRAC_CONST(0.8819212643483549), f133); + f136 = MUL_C(COEF_CONST(1.3533180011743529), f106); + y[10] = f134 + f135; + y[22] = f136 - f135; + f139 = f104 - f117; + f140 = MUL_F(FRAC_CONST(-0.1386171691990915), f117); + f141 = MUL_F(FRAC_CONST(0.7730104533627370), f139); + f142 = MUL_C(COEF_CONST(1.4074037375263826), f104); + y[14] = f141 - f140; + y[18] = f142 - f141; + f145 = f2 - f4; + f146 = f2 + f4; + f147 = f6 - f8; + f148 = f6 + f8; + f149 = f10 - f12; + f150 = f10 + f12; + f151 = f14 - f16; + f152 = f14 + f16; + f153 = f18 - f20; + f154 = f18 + f20; + f155 = f22 - f24; + f156 = f22 + f24; + f157 = f26 - f28; + f158 = f26 + f28; + f159 = MUL_F(FRAC_CONST(0.7071067811865476), f152); + f160 = f0 - f159; + f161 = f0 + f159; + f162 = f148 + f156; + f163 = MUL_C(COEF_CONST(1.3065629648763766), f148); + f164 = MUL_F(FRAC_CONST(-0.9238795325112866), f162); + f165 = MUL_F(FRAC_CONST(-0.5411961001461967), f156); + f166 = f163 + f164; + f167 = f165 - f164; + f168 = f161 - f167; + f169 = f161 + f167; + f170 = f160 - f166; + f171 = f160 + f166; + f172 = f146 + f158; + f173 = MUL_C(COEF_CONST(1.1758756024193588), f146); + f174 = MUL_F(FRAC_CONST(-0.9807852804032304), f172); + f175 = MUL_F(FRAC_CONST(-0.7856949583871021), f158); + f176 = f173 + f174; + f177 = f175 - f174; + f178 = f150 + f154; + f179 = MUL_C(COEF_CONST(1.3870398453221473), f150); + f180 = MUL_F(FRAC_CONST(-0.8314696123025455), f178); + f181 = MUL_F(FRAC_CONST(-0.2758993792829436), f154); + f182 = f179 + f180; + f183 = f181 - f180; + f184 = f177 - f183; + f185 = f177 + f183; + f186 = MUL_F(FRAC_CONST(0.7071067811865476), f184); + f187 = f176 - f182; + f188 = f176 + f182; + f189 = MUL_F(FRAC_CONST(0.7071067811865476), f188); + f190 = f186 - f189; + f191 = f186 + f189; + f192 = f169 - f185; + f193 = f169 + f185; + f194 = f171 - f191; + f195 = f171 + f191; + f196 = f170 - f190; + f197 = f170 + f190; + f198 = f168 - f187; + f199 = f168 + f187; + f200 = MUL_F(FRAC_CONST(0.7071067811865476), f151); + f201 = f30 - f200; + f202 = f30 + f200; + f203 = f155 + f147; + f204 = MUL_C(COEF_CONST(1.3065629648763766), f155); + f205 = MUL_F(FRAC_CONST(-0.9238795325112866), f203); + f206 = MUL_F(FRAC_CONST(-0.5411961001461967), f147); + f207 = f204 + f205; + f208 = f206 - f205; + f209 = f202 - f208; + f210 = f202 + f208; + f211 = f201 - f207; + f212 = f201 + f207; + f213 = f157 + f145; + f214 = MUL_C(COEF_CONST(1.1758756024193588), f157); + f215 = MUL_F(FRAC_CONST(-0.9807852804032304), f213); + f216 = MUL_F(FRAC_CONST(-0.7856949583871021), f145); + f217 = f214 + f215; + f218 = f216 - f215; + f219 = f153 + f149; + f220 = MUL_C(COEF_CONST(1.3870398453221473), f153); + f221 = MUL_F(FRAC_CONST(-0.8314696123025455), f219); + f222 = MUL_F(FRAC_CONST(-0.2758993792829436), f149); + f223 = f220 + f221; + f224 = f222 - f221; + f225 = f218 - f224; + f226 = f218 + f224; + f227 = MUL_F(FRAC_CONST(0.7071067811865476), f225); + f228 = f217 - f223; + f229 = f217 + f223; + f230 = MUL_F(FRAC_CONST(0.7071067811865476), f229); + f231 = f227 - f230; + f232 = f227 + f230; + f233 = f210 - f226; + f234 = f210 + f226; + f235 = f212 - f232; + f236 = f212 + f232; + f237 = f211 - f231; + f238 = f211 + f231; + f239 = f209 - f228; + f240 = f209 + f228; + f241 = f234 + f193; + f242 = MUL_F(FRAC_CONST(-0.9497277818777543), f234); + f243 = MUL_F(FRAC_CONST(0.9987954562051724), f241); + f244 = MUL_C(COEF_CONST(1.0478631305325905), f193); + y[1] = f242 + f243; + y[31] = f244 - f243; + f247 = f195 - f236; + f248 = MUL_F(FRAC_CONST(-0.8424460355094192), f236); + f249 = MUL_F(FRAC_CONST(0.9891765099647810), f247); + f250 = MUL_C(COEF_CONST(1.1359069844201428), f195); + y[3] = f249 - f248; + y[29] = f250 - f249; + f253 = f238 + f197; + f254 = MUL_F(FRAC_CONST(-0.7270510732912801), f238); + f255 = MUL_F(FRAC_CONST(0.9700312531945440), f253); + f256 = MUL_C(COEF_CONST(1.2130114330978079), f197); + y[5] = f254 + f255; + y[27] = f256 - f255; + f259 = f199 - f240; + f260 = MUL_F(FRAC_CONST(-0.6046542117908007), f240); + f261 = MUL_F(FRAC_CONST(0.9415440651830208), f259); + f262 = MUL_C(COEF_CONST(1.2784339185752409), f199); + y[7] = f261 - f260; + y[25] = f262 - f261; + f265 = f239 + f198; + f266 = MUL_F(FRAC_CONST(-0.4764341996931611), f239); + f267 = MUL_F(FRAC_CONST(0.9039892931234433), f265); + f268 = MUL_C(COEF_CONST(1.3315443865537255), f198); + y[9] = f266 + f267; + y[23] = f268 - f267; + f271 = f196 - f237; + f272 = MUL_F(FRAC_CONST(-0.3436258658070505), f237); + f273 = MUL_F(FRAC_CONST(0.8577286100002721), f271); + f274 = MUL_C(COEF_CONST(1.3718313541934939), f196); + y[11] = f273 - f272; + y[21] = f274 - f273; + f277 = f235 + f194; + f278 = MUL_F(FRAC_CONST(-0.2075082269882114), f235); + f279 = MUL_F(FRAC_CONST(0.8032075314806448), f277); + f280 = MUL_C(COEF_CONST(1.3989068359730783), f194); + y[13] = f278 + f279; + y[19] = f280 - f279; + f283 = f192 - f233; + f284 = MUL_F(FRAC_CONST(-0.0693921705079408), f233); + f285 = MUL_F(FRAC_CONST(0.7409511253549591), f283); + f286 = MUL_C(COEF_CONST(1.4125100802019774), f192); + y[15] = f285 - f284; + y[17] = f286 - f285; +} + +#else + + +#define n 32 +#define log2n 5 + +// w_array_real[i] = cos(2*M_PI*i/32) +static const real_t w_array_real[] = { + FRAC_CONST(1.000000000000000), FRAC_CONST(0.980785279337272), + FRAC_CONST(0.923879528329380), FRAC_CONST(0.831469603195765), + FRAC_CONST(0.707106765732237), FRAC_CONST(0.555570210304169), + FRAC_CONST(0.382683402077046), FRAC_CONST(0.195090284503576), + FRAC_CONST(0.000000000000000), FRAC_CONST(-0.195090370246552), + FRAC_CONST(-0.382683482845162), FRAC_CONST(-0.555570282993553), + FRAC_CONST(-0.707106827549476), FRAC_CONST(-0.831469651765257), + FRAC_CONST(-0.923879561784627), FRAC_CONST(-0.980785296392607) +}; + +// w_array_imag[i] = sin(-2*M_PI*i/32) +static const real_t w_array_imag[] = { + FRAC_CONST(0.000000000000000), FRAC_CONST(-0.195090327375064), + FRAC_CONST(-0.382683442461104), FRAC_CONST(-0.555570246648862), + FRAC_CONST(-0.707106796640858), FRAC_CONST(-0.831469627480512), + FRAC_CONST(-0.923879545057005), FRAC_CONST(-0.980785287864940), + FRAC_CONST(-1.000000000000000), FRAC_CONST(-0.980785270809601), + FRAC_CONST(-0.923879511601754), FRAC_CONST(-0.831469578911016), + FRAC_CONST(-0.707106734823616), FRAC_CONST(-0.555570173959476), + FRAC_CONST(-0.382683361692986), FRAC_CONST(-0.195090241632088) +}; + +// FFT decimation in frequency +// 4*16*2+16=128+16=144 multiplications +// 6*16*2+10*8+4*16*2=192+80+128=400 additions +static void fft_dif(real_t * Real, real_t * Imag) +{ + real_t w_real, w_imag; // For faster access + real_t point1_real, point1_imag, point2_real, point2_imag; // For faster access + uint32_t j, i, i2, w_index; // Counters + + // First 2 stages of 32 point FFT decimation in frequency + // 4*16*2=64*2=128 multiplications + // 6*16*2=96*2=192 additions + // Stage 1 of 32 point FFT decimation in frequency + for (i = 0; i < 16; i++) + { + point1_real = Real[i]; + point1_imag = Imag[i]; + i2 = i+16; + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + w_real = w_array_real[i]; + w_imag = w_array_imag[i]; + + // temp1 = x[i] - x[i2] + point1_real -= point2_real; + point1_imag -= point2_imag; + + // x[i1] = x[i] + x[i2] + Real[i] += point2_real; + Imag[i] += point2_imag; + + // x[i2] = (x[i] - x[i2]) * w + Real[i2] = (MUL_F(point1_real,w_real) - MUL_F(point1_imag,w_imag)); + Imag[i2] = (MUL_F(point1_real,w_imag) + MUL_F(point1_imag,w_real)); + } + // Stage 2 of 32 point FFT decimation in frequency + for (j = 0, w_index = 0; j < 8; j++, w_index += 2) + { + w_real = w_array_real[w_index]; + w_imag = w_array_imag[w_index]; + + i = j; + point1_real = Real[i]; + point1_imag = Imag[i]; + i2 = i+8; + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + // temp1 = x[i] - x[i2] + point1_real -= point2_real; + point1_imag -= point2_imag; + + // x[i1] = x[i] + x[i2] + Real[i] += point2_real; + Imag[i] += point2_imag; + + // x[i2] = (x[i] - x[i2]) * w + Real[i2] = (MUL_F(point1_real,w_real) - MUL_F(point1_imag,w_imag)); + Imag[i2] = (MUL_F(point1_real,w_imag) + MUL_F(point1_imag,w_real)); + + i = j+16; + point1_real = Real[i]; + point1_imag = Imag[i]; + i2 = i+8; + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + // temp1 = x[i] - x[i2] + point1_real -= point2_real; + point1_imag -= point2_imag; + + // x[i1] = x[i] + x[i2] + Real[i] += point2_real; + Imag[i] += point2_imag; + + // x[i2] = (x[i] - x[i2]) * w + Real[i2] = (MUL_F(point1_real,w_real) - MUL_F(point1_imag,w_imag)); + Imag[i2] = (MUL_F(point1_real,w_imag) + MUL_F(point1_imag,w_real)); + } + + // Stage 3 of 32 point FFT decimation in frequency + // 2*4*2=16 multiplications + // 4*4*2+6*4*2=10*8=80 additions + for (i = 0; i < n; i += 8) + { + i2 = i+4; + point1_real = Real[i]; + point1_imag = Imag[i]; + + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + // out[i1] = point1 + point2 + Real[i] += point2_real; + Imag[i] += point2_imag; + + // out[i2] = point1 - point2 + Real[i2] = point1_real - point2_real; + Imag[i2] = point1_imag - point2_imag; + } + w_real = w_array_real[4]; // = sqrt(2)/2 + // w_imag = -w_real; // = w_array_imag[4]; // = -sqrt(2)/2 + for (i = 1; i < n; i += 8) + { + i2 = i+4; + point1_real = Real[i]; + point1_imag = Imag[i]; + + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + // temp1 = x[i] - x[i2] + point1_real -= point2_real; + point1_imag -= point2_imag; + + // x[i1] = x[i] + x[i2] + Real[i] += point2_real; + Imag[i] += point2_imag; + + // x[i2] = (x[i] - x[i2]) * w + Real[i2] = MUL_F(point1_real+point1_imag, w_real); + Imag[i2] = MUL_F(point1_imag-point1_real, w_real); + } + for (i = 2; i < n; i += 8) + { + i2 = i+4; + point1_real = Real[i]; + point1_imag = Imag[i]; + + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + // x[i] = x[i] + x[i2] + Real[i] += point2_real; + Imag[i] += point2_imag; + + // x[i2] = (x[i] - x[i2]) * (-i) + Real[i2] = point1_imag - point2_imag; + Imag[i2] = point2_real - point1_real; + } + w_real = w_array_real[12]; // = -sqrt(2)/2 + // w_imag = w_real; // = w_array_imag[12]; // = -sqrt(2)/2 + for (i = 3; i < n; i += 8) + { + i2 = i+4; + point1_real = Real[i]; + point1_imag = Imag[i]; + + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + // temp1 = x[i] - x[i2] + point1_real -= point2_real; + point1_imag -= point2_imag; + + // x[i1] = x[i] + x[i2] + Real[i] += point2_real; + Imag[i] += point2_imag; + + // x[i2] = (x[i] - x[i2]) * w + Real[i2] = MUL_F(point1_real-point1_imag, w_real); + Imag[i2] = MUL_F(point1_real+point1_imag, w_real); + } + + + // Stage 4 of 32 point FFT decimation in frequency (no multiplications) + // 16*4=64 additions + for (i = 0; i < n; i += 4) + { + i2 = i+2; + point1_real = Real[i]; + point1_imag = Imag[i]; + + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + // x[i1] = x[i] + x[i2] + Real[i] += point2_real; + Imag[i] += point2_imag; + + // x[i2] = x[i] - x[i2] + Real[i2] = point1_real - point2_real; + Imag[i2] = point1_imag - point2_imag; + } + for (i = 1; i < n; i += 4) + { + i2 = i+2; + point1_real = Real[i]; + point1_imag = Imag[i]; + + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + // x[i] = x[i] + x[i2] + Real[i] += point2_real; + Imag[i] += point2_imag; + + // x[i2] = (x[i] - x[i2]) * (-i) + Real[i2] = point1_imag - point2_imag; + Imag[i2] = point2_real - point1_real; + } + + // Stage 5 of 32 point FFT decimation in frequency (no multiplications) + // 16*4=64 additions + for (i = 0; i < n; i += 2) + { + i2 = i+1; + point1_real = Real[i]; + point1_imag = Imag[i]; + + point2_real = Real[i2]; + point2_imag = Imag[i2]; + + // out[i1] = point1 + point2 + Real[i] += point2_real; + Imag[i] += point2_imag; + + // out[i2] = point1 - point2 + Real[i2] = point1_real - point2_real; + Imag[i2] = point1_imag - point2_imag; + } + +#ifdef REORDER_IN_FFT + FFTReorder(Real, Imag); +#endif // #ifdef REORDER_IN_FFT +} +#undef n +#undef log2n + +static const real_t dct4_64_tab[] = { + COEF_CONST(0.999924719333649), COEF_CONST(0.998118102550507), + COEF_CONST(0.993906974792480), COEF_CONST(0.987301409244537), + COEF_CONST(0.978317379951477), COEF_CONST(0.966976463794708), + COEF_CONST(0.953306019306183), COEF_CONST(0.937339007854462), + COEF_CONST(0.919113874435425), COEF_CONST(0.898674488067627), + COEF_CONST(0.876070082187653), COEF_CONST(0.851355195045471), + COEF_CONST(0.824589252471924), COEF_CONST(0.795836925506592), + COEF_CONST(0.765167236328125), COEF_CONST(0.732654273509979), + COEF_CONST(0.698376238346100), COEF_CONST(0.662415742874146), + COEF_CONST(0.624859452247620), COEF_CONST(0.585797846317291), + COEF_CONST(0.545324981212616), COEF_CONST(0.503538429737091), + COEF_CONST(0.460538715124130), COEF_CONST(0.416429549455643), + COEF_CONST(0.371317148208618), COEF_CONST(0.325310230255127), + COEF_CONST(0.278519600629807), COEF_CONST(0.231058135628700), + COEF_CONST(0.183039888739586), COEF_CONST(0.134580686688423), + COEF_CONST(0.085797272622585), COEF_CONST(0.036807164549828), + COEF_CONST(-1.012196302413940), COEF_CONST(-1.059438824653626), + COEF_CONST(-1.104129195213318), COEF_CONST(-1.146159529685974), + COEF_CONST(-1.185428738594055), COEF_CONST(-1.221842169761658), + COEF_CONST(-1.255311965942383), COEF_CONST(-1.285757660865784), + COEF_CONST(-1.313105940818787), COEF_CONST(-1.337290763854981), + COEF_CONST(-1.358253836631775), COEF_CONST(-1.375944852828980), + COEF_CONST(-1.390321016311646), COEF_CONST(-1.401347875595093), + COEF_CONST(-1.408998727798462), COEF_CONST(-1.413255214691162), + COEF_CONST(-1.414107084274292), COEF_CONST(-1.411552190780640), + COEF_CONST(-1.405596733093262), COEF_CONST(-1.396255016326904), + COEF_CONST(-1.383549690246582), COEF_CONST(-1.367511272430420), + COEF_CONST(-1.348178386688232), COEF_CONST(-1.325597524642944), + COEF_CONST(-1.299823284149170), COEF_CONST(-1.270917654037476), + COEF_CONST(-1.238950133323669), COEF_CONST(-1.203998088836670), + COEF_CONST(-1.166145324707031), COEF_CONST(-1.125483393669128), + COEF_CONST(-1.082109928131104), COEF_CONST(-1.036129593849182), + COEF_CONST(-0.987653195858002), COEF_CONST(-0.936797380447388), + COEF_CONST(-0.883684754371643), COEF_CONST(-0.828443288803101), + COEF_CONST(-0.771206021308899), COEF_CONST(-0.712110757827759), + COEF_CONST(-0.651300072669983), COEF_CONST(-0.588920354843140), + COEF_CONST(-0.525121808052063), COEF_CONST(-0.460058242082596), + COEF_CONST(-0.393886327743530), COEF_CONST(-0.326765477657318), + COEF_CONST(-0.258857429027557), COEF_CONST(-0.190325915813446), + COEF_CONST(-0.121335685253143), COEF_CONST(-0.052053272724152), + COEF_CONST(0.017354607582092), COEF_CONST(0.086720645427704), + COEF_CONST(0.155877828598022), COEF_CONST(0.224659323692322), + COEF_CONST(0.292899727821350), COEF_CONST(0.360434412956238), + COEF_CONST(0.427100926637650), COEF_CONST(0.492738455533981), + COEF_CONST(0.557188928127289), COEF_CONST(0.620297133922577), + COEF_CONST(0.681910991668701), COEF_CONST(0.741881847381592), + COEF_CONST(0.800065577030182), COEF_CONST(0.856321990489960), + COEF_CONST(0.910515367984772), COEF_CONST(0.962515234947205), + COEF_CONST(1.000000000000000), COEF_CONST(0.998795449733734), + COEF_CONST(0.995184719562531), COEF_CONST(0.989176511764526), + COEF_CONST(0.980785250663757), COEF_CONST(0.970031261444092), + COEF_CONST(0.956940352916718), COEF_CONST(0.941544055938721), + COEF_CONST(0.923879504203796), COEF_CONST(0.903989315032959), + COEF_CONST(0.881921231746674), COEF_CONST(0.857728600502014), + COEF_CONST(0.831469595432281), COEF_CONST(0.803207516670227), + COEF_CONST(0.773010432720184), COEF_CONST(0.740951120853424), + COEF_CONST(0.707106769084930), COEF_CONST(0.671558916568756), + COEF_CONST(0.634393274784088), COEF_CONST(0.595699310302734), + COEF_CONST(0.555570185184479), COEF_CONST(0.514102697372437), + COEF_CONST(0.471396654844284), COEF_CONST(0.427555114030838), + COEF_CONST(0.382683426141739), COEF_CONST(0.336889833211899), + COEF_CONST(0.290284633636475), COEF_CONST(0.242980122566223), + COEF_CONST(0.195090234279633), COEF_CONST(0.146730497479439), + COEF_CONST(0.098017133772373), COEF_CONST(0.049067649990320), + COEF_CONST(-1.000000000000000), COEF_CONST(-1.047863125801086), + COEF_CONST(-1.093201875686646), COEF_CONST(-1.135906934738159), + COEF_CONST(-1.175875544548035), COEF_CONST(-1.213011503219605), + COEF_CONST(-1.247225046157837), COEF_CONST(-1.278433918952942), + COEF_CONST(-1.306562900543213), COEF_CONST(-1.331544399261475), + COEF_CONST(-1.353317975997925), COEF_CONST(-1.371831417083740), + COEF_CONST(-1.387039899826050), COEF_CONST(-1.398906826972961), + COEF_CONST(-1.407403707504273), COEF_CONST(-1.412510156631470), + COEF_CONST(0), COEF_CONST(-1.412510156631470), + COEF_CONST(-1.407403707504273), COEF_CONST(-1.398906826972961), + COEF_CONST(-1.387039899826050), COEF_CONST(-1.371831417083740), + COEF_CONST(-1.353317975997925), COEF_CONST(-1.331544399261475), + COEF_CONST(-1.306562900543213), COEF_CONST(-1.278433918952942), + COEF_CONST(-1.247225046157837), COEF_CONST(-1.213011384010315), + COEF_CONST(-1.175875544548035), COEF_CONST(-1.135907053947449), + COEF_CONST(-1.093201875686646), COEF_CONST(-1.047863125801086), + COEF_CONST(-1.000000000000000), COEF_CONST(-0.949727773666382), + COEF_CONST(-0.897167563438416), COEF_CONST(-0.842446029186249), + COEF_CONST(-0.785694956779480), COEF_CONST(-0.727051079273224), + COEF_CONST(-0.666655659675598), COEF_CONST(-0.604654192924500), + COEF_CONST(-0.541196048259735), COEF_CONST(-0.476434230804443), + COEF_CONST(-0.410524487495422), COEF_CONST(-0.343625843524933), + COEF_CONST(-0.275899350643158), COEF_CONST(-0.207508206367493), + COEF_CONST(-0.138617098331451), COEF_CONST(-0.069392144680023), + COEF_CONST(0), COEF_CONST(0.069392263889313), + COEF_CONST(0.138617157936096), COEF_CONST(0.207508206367493), + COEF_CONST(0.275899469852448), COEF_CONST(0.343625962734222), + COEF_CONST(0.410524636507034), COEF_CONST(0.476434201002121), + COEF_CONST(0.541196107864380), COEF_CONST(0.604654192924500), + COEF_CONST(0.666655719280243), COEF_CONST(0.727051138877869), + COEF_CONST(0.785695075988770), COEF_CONST(0.842446029186249), + COEF_CONST(0.897167563438416), COEF_CONST(0.949727773666382) +}; + +/* size 64 only! */ +void dct4_kernel(real_t * in_real, real_t * in_imag, real_t * out_real, real_t * out_imag) +{ + // Tables with bit reverse values for 5 bits, bit reverse of i at i-th position + const uint8_t bit_rev_tab[32] = { 0,16,8,24,4,20,12,28,2,18,10,26,6,22,14,30,1,17,9,25,5,21,13,29,3,19,11,27,7,23,15,31 }; + uint32_t i, i_rev; + + /* Step 2: modulate */ + // 3*32=96 multiplications + // 3*32=96 additions + for (i = 0; i < 32; i++) + { + real_t x_re, x_im, tmp; + x_re = in_real[i]; + x_im = in_imag[i]; + tmp = MUL_C(x_re + x_im, dct4_64_tab[i]); + in_real[i] = MUL_C(x_im, dct4_64_tab[i + 64]) + tmp; + in_imag[i] = MUL_C(x_re, dct4_64_tab[i + 32]) + tmp; + } + + /* Step 3: FFT, but with output in bit reverse order */ + fft_dif(in_real, in_imag); + + /* Step 4: modulate + bitreverse reordering */ + // 3*31+2=95 multiplications + // 3*31+2=95 additions + for (i = 0; i < 16; i++) + { + real_t x_re, x_im, tmp; + i_rev = bit_rev_tab[i]; + x_re = in_real[i_rev]; + x_im = in_imag[i_rev]; + + tmp = MUL_C(x_re + x_im, dct4_64_tab[i + 3*32]); + out_real[i] = MUL_C(x_im, dct4_64_tab[i + 5*32]) + tmp; + out_imag[i] = MUL_C(x_re, dct4_64_tab[i + 4*32]) + tmp; + } + // i = 16, i_rev = 1 = rev(16); + out_imag[16] = MUL_C(in_imag[1] - in_real[1], dct4_64_tab[16 + 3*32]); + out_real[16] = MUL_C(in_real[1] + in_imag[1], dct4_64_tab[16 + 3*32]); + for (i = 17; i < 32; i++) + { + real_t x_re, x_im, tmp; + i_rev = bit_rev_tab[i]; + x_re = in_real[i_rev]; + x_im = in_imag[i_rev]; + tmp = MUL_C(x_re + x_im, dct4_64_tab[i + 3*32]); + out_real[i] = MUL_C(x_im, dct4_64_tab[i + 5*32]) + tmp; + out_imag[i] = MUL_C(x_re, dct4_64_tab[i + 4*32]) + tmp; + } + +} + +#endif + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_dct.h b/third_party/faad_hdc/upstream/libfaad/sbr_dct.h new file mode 100644 index 000000000..861ec5197 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_dct.h @@ -0,0 +1,52 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_dct.h,v 1.19 2007/11/01 12:33:34 menno Exp $ +**/ + +#ifndef __SBR_DCT_H__ +#define __SBR_DCT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void dct4_kernel(real_t * in_real, real_t * in_imag, real_t * out_real, real_t * out_imag); + +void DCT3_32_unscaled(real_t *y, real_t *x); +void DCT4_32(real_t *y, real_t *x); +void DST4_32(real_t *y, real_t *x); +void DCT2_32_unscaled(real_t *y, real_t *x); +void DCT4_16(real_t *y, real_t *x); +void DCT2_16_unscaled(real_t *y, real_t *x); + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_dec.c b/third_party/faad_hdc/upstream/libfaad/sbr_dec.c new file mode 100644 index 000000000..99463aa11 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_dec.c @@ -0,0 +1,706 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_dec.c,v 1.44 2009/01/26 22:32:31 menno Exp $ +**/ + + +#include "common.h" +#include "structs.h" + +#ifdef SBR_DEC + +#include + +#include "syntax.h" +#include "bits.h" +#include "sbr_syntax.h" +#include "sbr_qmf.h" +#include "sbr_hfgen.h" +#include "sbr_hfadj.h" + + +/* static function declarations */ +static uint8_t sbr_save_prev_data(sbr_info *sbr, uint8_t ch); +static void sbr_save_matrix(sbr_info *sbr, uint8_t ch); + +#define INVALID ((uint8_t)-1) + +sbr_info *sbrDecodeInit(uint16_t framelength, uint8_t id_aac, + uint32_t sample_rate, uint8_t downSampledSBR +#ifdef DRM + , uint8_t IsDRM +#endif + ) +{ + sbr_info *sbr = faad_malloc(sizeof(sbr_info)); + memset(sbr, 0, sizeof(sbr_info)); + + /* save id of the parent element */ + sbr->id_aac = id_aac; + sbr->sample_rate = sample_rate; + + sbr->bs_freq_scale = 2; + sbr->bs_alter_scale = 1; + sbr->bs_noise_bands = 2; + sbr->bs_limiter_bands = 2; + sbr->bs_limiter_gains = 2; + sbr->bs_interpol_freq = 1; + sbr->bs_smoothing_mode = 1; + sbr->bs_start_freq = 5; + sbr->bs_amp_res = 1; + sbr->bs_samplerate_mode = 1; + sbr->prevEnvIsShort[0] = -1; + sbr->prevEnvIsShort[1] = -1; + sbr->header_count = 0; + sbr->Reset = 1; + +#ifdef DRM + sbr->Is_DRM_SBR = IsDRM; +#endif + sbr->tHFGen = T_HFGEN; + sbr->tHFAdj = T_HFADJ; + + sbr->bsco = 0; + sbr->bsco_prev = 0; + sbr->M_prev = 0; + sbr->frame_len = framelength; + + /* force sbr reset */ + sbr->bs_start_freq_prev = INVALID; + + if (framelength == 960) + { + sbr->numTimeSlotsRate = RATE * NO_TIME_SLOTS_960; + sbr->numTimeSlots = NO_TIME_SLOTS_960; + } + else if (framelength == 1024) + { + sbr->numTimeSlotsRate = RATE * NO_TIME_SLOTS; + sbr->numTimeSlots = NO_TIME_SLOTS; + } + else + { + faad_free(sbr); + return NULL; + } + + sbr->GQ_ringbuf_index[0] = 0; + sbr->GQ_ringbuf_index[1] = 0; + + if (id_aac == ID_CPE) + { + /* stereo */ + uint8_t j; + sbr->qmfa[0] = qmfa_init(32); + sbr->qmfa[1] = qmfa_init(32); + sbr->qmfs[0] = qmfs_init((downSampledSBR)?32:64); + sbr->qmfs[1] = qmfs_init((downSampledSBR)?32:64); + + for (j = 0; j < 5; j++) + { + sbr->G_temp_prev[0][j] = faad_malloc(64*sizeof(real_t)); + sbr->G_temp_prev[1][j] = faad_malloc(64*sizeof(real_t)); + sbr->Q_temp_prev[0][j] = faad_malloc(64*sizeof(real_t)); + sbr->Q_temp_prev[1][j] = faad_malloc(64*sizeof(real_t)); + } + + memset(sbr->Xsbr[0], 0, (sbr->numTimeSlotsRate+sbr->tHFGen)*64 * sizeof(qmf_t)); + memset(sbr->Xsbr[1], 0, (sbr->numTimeSlotsRate+sbr->tHFGen)*64 * sizeof(qmf_t)); + } else { + /* mono */ + uint8_t j; + sbr->qmfa[0] = qmfa_init(32); + sbr->qmfs[0] = qmfs_init((downSampledSBR)?32:64); + sbr->qmfs[1] = NULL; + + for (j = 0; j < 5; j++) + { + sbr->G_temp_prev[0][j] = faad_malloc(64*sizeof(real_t)); + sbr->Q_temp_prev[0][j] = faad_malloc(64*sizeof(real_t)); + } + + memset(sbr->Xsbr[0], 0, (sbr->numTimeSlotsRate+sbr->tHFGen)*64 * sizeof(qmf_t)); + } + + return sbr; +} + +void sbrDecodeEnd(sbr_info *sbr) +{ + uint8_t j; + + if (sbr) + { + qmfa_end(sbr->qmfa[0]); + qmfs_end(sbr->qmfs[0]); + if (sbr->qmfs[1] != NULL) + { + qmfa_end(sbr->qmfa[1]); + qmfs_end(sbr->qmfs[1]); + } + + for (j = 0; j < 5; j++) + { + if (sbr->G_temp_prev[0][j]) faad_free(sbr->G_temp_prev[0][j]); + if (sbr->Q_temp_prev[0][j]) faad_free(sbr->Q_temp_prev[0][j]); + if (sbr->G_temp_prev[1][j]) faad_free(sbr->G_temp_prev[1][j]); + if (sbr->Q_temp_prev[1][j]) faad_free(sbr->Q_temp_prev[1][j]); + } + +#ifdef PS_DEC + if (sbr->ps != NULL) + ps_free(sbr->ps); +#endif + +#ifdef DRM_PS + if (sbr->drm_ps != NULL) + drm_ps_free(sbr->drm_ps); +#endif + + faad_free(sbr); + } +} + +void sbrReset(sbr_info *sbr) +{ + uint8_t j; + if (sbr->qmfa[0] != NULL) + memset(sbr->qmfa[0]->x, 0, 2 * sbr->qmfa[0]->channels * 10 * sizeof(real_t)); + if (sbr->qmfa[1] != NULL) + memset(sbr->qmfa[1]->x, 0, 2 * sbr->qmfa[1]->channels * 10 * sizeof(real_t)); + if (sbr->qmfs[0] != NULL) + memset(sbr->qmfs[0]->v, 0, 2 * sbr->qmfs[0]->channels * 20 * sizeof(real_t)); + if (sbr->qmfs[1] != NULL) + memset(sbr->qmfs[1]->v, 0, 2 * sbr->qmfs[1]->channels * 20 * sizeof(real_t)); + + for (j = 0; j < 5; j++) + { + if (sbr->G_temp_prev[0][j] != NULL) + memset(sbr->G_temp_prev[0][j], 0, 64*sizeof(real_t)); + if (sbr->G_temp_prev[1][j] != NULL) + memset(sbr->G_temp_prev[1][j], 0, 64*sizeof(real_t)); + if (sbr->Q_temp_prev[0][j] != NULL) + memset(sbr->Q_temp_prev[0][j], 0, 64*sizeof(real_t)); + if (sbr->Q_temp_prev[1][j] != NULL) + memset(sbr->Q_temp_prev[1][j], 0, 64*sizeof(real_t)); + } + + memset(sbr->Xsbr[0], 0, (sbr->numTimeSlotsRate+sbr->tHFGen)*64 * sizeof(qmf_t)); + memset(sbr->Xsbr[1], 0, (sbr->numTimeSlotsRate+sbr->tHFGen)*64 * sizeof(qmf_t)); + + sbr->GQ_ringbuf_index[0] = 0; + sbr->GQ_ringbuf_index[1] = 0; + sbr->header_count = 0; + sbr->Reset = 1; + + sbr->L_E_prev[0] = 0; + sbr->L_E_prev[1] = 0; + sbr->bs_freq_scale = 2; + sbr->bs_alter_scale = 1; + sbr->bs_noise_bands = 2; + sbr->bs_limiter_bands = 2; + sbr->bs_limiter_gains = 2; + sbr->bs_interpol_freq = 1; + sbr->bs_smoothing_mode = 1; + sbr->bs_start_freq = 5; + sbr->bs_amp_res = 1; + sbr->bs_samplerate_mode = 1; + sbr->prevEnvIsShort[0] = -1; + sbr->prevEnvIsShort[1] = -1; + sbr->bsco = 0; + sbr->bsco_prev = 0; + sbr->M_prev = 0; + sbr->bs_start_freq_prev = INVALID; + + sbr->f_prev[0] = 0; + sbr->f_prev[1] = 0; + for (j = 0; j < MAX_M; j++) + { + sbr->E_prev[0][j] = 0; + sbr->Q_prev[0][j] = 0; + sbr->E_prev[1][j] = 0; + sbr->Q_prev[1][j] = 0; + sbr->bs_add_harmonic_prev[0][j] = 0; + sbr->bs_add_harmonic_prev[1][j] = 0; + } + sbr->bs_add_harmonic_flag_prev[0] = 0; + sbr->bs_add_harmonic_flag_prev[1] = 0; +} + +static uint8_t sbr_save_prev_data(sbr_info *sbr, uint8_t ch) +{ + uint8_t i; + + /* save data for next frame */ + sbr->kx_prev = sbr->kx; + sbr->M_prev = sbr->M; + sbr->bsco_prev = sbr->bsco; + + sbr->L_E_prev[ch] = sbr->L_E[ch]; + + /* sbr->L_E[ch] can become 0 on files with bit errors */ + if (sbr->L_E[ch] <= 0) + return 19; + + sbr->f_prev[ch] = sbr->f[ch][sbr->L_E[ch] - 1]; + for (i = 0; i < MAX_M; i++) + { + sbr->E_prev[ch][i] = sbr->E[ch][i][sbr->L_E[ch] - 1]; + sbr->Q_prev[ch][i] = sbr->Q[ch][i][sbr->L_Q[ch] - 1]; + } + + for (i = 0; i < MAX_M; i++) + { + sbr->bs_add_harmonic_prev[ch][i] = sbr->bs_add_harmonic[ch][i]; + } + sbr->bs_add_harmonic_flag_prev[ch] = sbr->bs_add_harmonic_flag[ch]; + + if (sbr->l_A[ch] == sbr->L_E[ch]) + sbr->prevEnvIsShort[ch] = 0; + else + sbr->prevEnvIsShort[ch] = -1; + + return 0; +} + +static void sbr_save_matrix(sbr_info *sbr, uint8_t ch) +{ + uint8_t i; + + for (i = 0; i < sbr->tHFGen; i++) + { + memmove(sbr->Xsbr[ch][i], sbr->Xsbr[ch][i+sbr->numTimeSlotsRate], 64 * sizeof(qmf_t)); + } + for (i = sbr->tHFGen; i < MAX_NTSRHFG; i++) + { + memset(sbr->Xsbr[ch][i], 0, 64 * sizeof(qmf_t)); + } +} + +static uint8_t sbr_process_channel(sbr_info *sbr, real_t *channel_buf, qmf_t X[MAX_NTSR][64], + uint8_t ch, uint8_t dont_process, + const uint8_t downSampledSBR) +{ + int16_t k, l; + uint8_t ret = 0; + (void)downSampledSBR; /* TODO: remove parameter? */ + +#ifdef SBR_LOW_POWER + ALIGN real_t deg[64]; +#endif + +#ifdef DRM + if (sbr->Is_DRM_SBR) + { + sbr->bsco = (uint8_t)max((int32_t)sbr->maxAACLine*32/(int32_t)sbr->frame_len - (int32_t)sbr->kx, 0); + } else { +#endif + sbr->bsco = 0; +#ifdef DRM + } +#endif + + +//#define PRE_QMF_PRINT +#ifdef PRE_QMF_PRINT + { + int i; + for (i = 0; i < 1024; i++) + { + printf("%d\n", channel_buf[i]); + } + } +#endif + + + /* subband analysis */ + if (dont_process) + sbr_qmf_analysis_32(sbr, sbr->qmfa[ch], channel_buf, sbr->Xsbr[ch], sbr->tHFGen, 32); + else + sbr_qmf_analysis_32(sbr, sbr->qmfa[ch], channel_buf, sbr->Xsbr[ch], sbr->tHFGen, sbr->kx); + + if (!dont_process) + { +#if 1 + /* insert high frequencies here */ + /* hf generation using patching */ + hf_generation(sbr, sbr->Xsbr[ch], sbr->Xsbr[ch] +#ifdef SBR_LOW_POWER + ,deg +#endif + ,ch); +#endif + +#if 0 //def SBR_LOW_POWER + for (l = sbr->t_E[ch][0]; l < sbr->t_E[ch][sbr->L_E[ch]]; l++) + { + for (k = 0; k < sbr->kx; k++) + { + QMF_RE(sbr->Xsbr[ch][sbr->tHFAdj + l][k]) = 0; + } + } +#endif + +#if 1 + /* hf adjustment */ + ret = hf_adjustment(sbr, sbr->Xsbr[ch] +#ifdef SBR_LOW_POWER + ,deg +#endif + ,ch); +#endif + if (ret > 0) + { + dont_process = 1; + } + } + + if ((sbr->just_seeked != 0) || dont_process) + { + for (l = 0; l < sbr->numTimeSlotsRate; l++) + { + for (k = 0; k < 32; k++) + { + QMF_RE(X[l][k]) = QMF_RE(sbr->Xsbr[ch][l + sbr->tHFAdj][k]); +#ifndef SBR_LOW_POWER + QMF_IM(X[l][k]) = QMF_IM(sbr->Xsbr[ch][l + sbr->tHFAdj][k]); +#endif + } + for (k = 32; k < 64; k++) + { + QMF_RE(X[l][k]) = 0; +#ifndef SBR_LOW_POWER + QMF_IM(X[l][k]) = 0; +#endif + } + } + } else { + for (l = 0; l < sbr->numTimeSlotsRate; l++) + { + uint8_t kx_band, M_band, bsco_band; + + if (l < sbr->t_E[ch][0]) + { + kx_band = sbr->kx_prev; + M_band = sbr->M_prev; + bsco_band = sbr->bsco_prev; + } else { + kx_band = sbr->kx; + M_band = sbr->M; + bsco_band = sbr->bsco; + } + +#ifndef SBR_LOW_POWER + for (k = 0; k < kx_band + bsco_band; k++) + { + QMF_RE(X[l][k]) = QMF_RE(sbr->Xsbr[ch][l + sbr->tHFAdj][k]); + QMF_IM(X[l][k]) = QMF_IM(sbr->Xsbr[ch][l + sbr->tHFAdj][k]); + } + for (k = kx_band + bsco_band; k < kx_band + M_band; k++) + { + QMF_RE(X[l][k]) = QMF_RE(sbr->Xsbr[ch][l + sbr->tHFAdj][k]); + QMF_IM(X[l][k]) = QMF_IM(sbr->Xsbr[ch][l + sbr->tHFAdj][k]); + } + for (k = max(kx_band + bsco_band, kx_band + M_band); k < 64; k++) + { + QMF_RE(X[l][k]) = 0; + QMF_IM(X[l][k]) = 0; + } +#else + for (k = 0; k < kx_band + bsco_band; k++) + { + QMF_RE(X[l][k]) = QMF_RE(sbr->Xsbr[ch][l + sbr->tHFAdj][k]); + } + for (k = kx_band + bsco_band; k < min(kx_band + M_band, 63); k++) + { + QMF_RE(X[l][k]) = QMF_RE(sbr->Xsbr[ch][l + sbr->tHFAdj][k]); + } + for (k = max(kx_band + bsco_band, kx_band + M_band); k < 64; k++) + { + QMF_RE(X[l][k]) = 0; + } + QMF_RE(X[l][kx_band - 1 + bsco_band]) += + QMF_RE(sbr->Xsbr[ch][l + sbr->tHFAdj][kx_band - 1 + bsco_band]); +#endif + } + } + + return ret; +} + +uint8_t sbrDecodeCoupleFrame(sbr_info *sbr, real_t *left_chan, real_t *right_chan, + const uint8_t just_seeked, const uint8_t downSampledSBR) +{ + uint8_t dont_process = 0; + uint8_t ret = 0; + ALIGN qmf_t X[MAX_NTSRHFG][64]; + + if (sbr == NULL) + return 20; + + /* case can occur due to bit errors */ + if (sbr->id_aac != ID_CPE) + return 21; + + if (sbr->ret || (sbr->header_count == 0)) + { + /* don't process just upsample */ + dont_process = 1; + + /* Re-activate reset for next frame */ + if (sbr->ret && sbr->Reset) + sbr->bs_start_freq_prev = INVALID; + } + + if (just_seeked) + { + sbr->just_seeked = 1; + } else { + sbr->just_seeked = 0; + } + + sbr->ret += sbr_process_channel(sbr, left_chan, X, 0, dont_process, downSampledSBR); + /* subband synthesis */ + if (downSampledSBR) + { + sbr_qmf_synthesis_32(sbr, sbr->qmfs[0], X, left_chan); + } else { + sbr_qmf_synthesis_64(sbr, sbr->qmfs[0], X, left_chan); + } + + sbr->ret += sbr_process_channel(sbr, right_chan, X, 1, dont_process, downSampledSBR); + /* subband synthesis */ + if (downSampledSBR) + { + sbr_qmf_synthesis_32(sbr, sbr->qmfs[1], X, right_chan); + } else { + sbr_qmf_synthesis_64(sbr, sbr->qmfs[1], X, right_chan); + } + + if (sbr->bs_header_flag) + sbr->just_seeked = 0; + + if (sbr->header_count != 0 && sbr->ret == 0) + { + ret = sbr_save_prev_data(sbr, 0); + if (ret) return ret; + ret = sbr_save_prev_data(sbr, 1); + if (ret) return ret; + } + + sbr_save_matrix(sbr, 0); + sbr_save_matrix(sbr, 1); + + sbr->frame++; + +//#define POST_QMF_PRINT +#ifdef POST_QMF_PRINT + { + int i; + for (i = 0; i < 2048; i++) + { + printf("%d\n", left_chan[i]); + } + for (i = 0; i < 2048; i++) + { + printf("%d\n", right_chan[i]); + } + } +#endif + + return 0; +} + +uint8_t sbrDecodeSingleFrame(sbr_info *sbr, real_t *channel, + const uint8_t just_seeked, const uint8_t downSampledSBR) +{ + uint8_t dont_process = 0; + uint8_t ret = 0; + ALIGN qmf_t X[MAX_NTSRHFG][64]; + + if (sbr == NULL) + return 20; + + /* case can occur due to bit errors */ + if (sbr->id_aac != ID_SCE && sbr->id_aac != ID_LFE) + return 21; + + if (sbr->ret || (sbr->header_count == 0)) + { + /* don't process just upsample */ + dont_process = 1; + + /* Re-activate reset for next frame */ + if (sbr->ret && sbr->Reset) + sbr->bs_start_freq_prev = INVALID; + } + + if (just_seeked) + { + sbr->just_seeked = 1; + } else { + sbr->just_seeked = 0; + } + + sbr->ret += sbr_process_channel(sbr, channel, X, 0, dont_process, downSampledSBR); + /* subband synthesis */ + if (downSampledSBR) + { + sbr_qmf_synthesis_32(sbr, sbr->qmfs[0], X, channel); + } else { + sbr_qmf_synthesis_64(sbr, sbr->qmfs[0], X, channel); + } + + if (sbr->bs_header_flag) + sbr->just_seeked = 0; + + if (sbr->header_count != 0 && sbr->ret == 0) + { + ret = sbr_save_prev_data(sbr, 0); + if (ret) return ret; + } + + sbr_save_matrix(sbr, 0); + + sbr->frame++; + +//#define POST_QMF_PRINT +#ifdef POST_QMF_PRINT + { + int i; + for (i = 0; i < 2048; i++) + { + printf("%d\n", channel[i]); + } + } +#endif + + return 0; +} + +#if (defined(PS_DEC) || defined(DRM_PS)) +uint8_t sbrDecodeSingleFramePS(sbr_info *sbr, real_t *left_channel, real_t *right_channel, + const uint8_t just_seeked, const uint8_t downSampledSBR) +{ + uint8_t l, k; + uint8_t dont_process = 0; + uint8_t ret = 0; + ALIGN qmf_t X_left[MAX_NTSRHFG][64] = {{{0}}}; + ALIGN qmf_t X_right[MAX_NTSRHFG][64] = {{{0}}}; /* must set this to 0 */ + + if (sbr == NULL) + return 20; + + /* case can occur due to bit errors */ + if (sbr->id_aac != ID_SCE && sbr->id_aac != ID_LFE) + return 21; + + if (sbr->ret || (sbr->header_count == 0)) + { + /* don't process just upsample */ + dont_process = 1; + + /* Re-activate reset for next frame */ + if (sbr->ret && sbr->Reset) + sbr->bs_start_freq_prev = INVALID; + } + + if (just_seeked) + { + sbr->just_seeked = 1; + } else { + sbr->just_seeked = 0; + } + + if (sbr->qmfs[1] == NULL) + { + sbr->qmfs[1] = qmfs_init((downSampledSBR)?32:64); + } + + sbr->ret += sbr_process_channel(sbr, left_channel, X_left, 0, dont_process, downSampledSBR); + + /* copy some extra data for PS */ + for (l = sbr->numTimeSlotsRate; l < sbr->numTimeSlotsRate + 6; l++) + { + for (k = 0; k < 5; k++) + { + QMF_RE(X_left[l][k]) = QMF_RE(sbr->Xsbr[0][sbr->tHFAdj+l][k]); + QMF_IM(X_left[l][k]) = QMF_IM(sbr->Xsbr[0][sbr->tHFAdj+l][k]); + } + } + + /* perform parametric stereo */ +#ifdef DRM_PS +#ifdef HDC + if (sbr->Is_DRM_SBR || sbr->Is_HDC_SBR) +#else + if (sbr->Is_DRM_SBR) +#endif + { +#ifdef HDC + drm_ps_decode(sbr->drm_ps, (sbr->ret > 0), X_left, X_right, sbr->Is_HDC_SBR); +#else + drm_ps_decode(sbr->drm_ps, (sbr->ret > 0), X_left, X_right, 0); +#endif + } else { +#endif +#ifdef PS_DEC + ps_decode(sbr->ps, X_left, X_right); +#endif +#ifdef DRM_PS + } +#endif + + /* subband synthesis */ + if (downSampledSBR) + { + sbr_qmf_synthesis_32(sbr, sbr->qmfs[0], X_left, left_channel); + sbr_qmf_synthesis_32(sbr, sbr->qmfs[1], X_right, right_channel); + } else { + sbr_qmf_synthesis_64(sbr, sbr->qmfs[0], X_left, left_channel); + sbr_qmf_synthesis_64(sbr, sbr->qmfs[1], X_right, right_channel); + } + + if (sbr->bs_header_flag) + sbr->just_seeked = 0; + + if (sbr->header_count != 0 && sbr->ret == 0) + { + ret = sbr_save_prev_data(sbr, 0); + if (ret) return ret; + } + + sbr_save_matrix(sbr, 0); + + sbr->frame++; + + return 0; +} +#endif + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_dec.h b/third_party/faad_hdc/upstream/libfaad/sbr_dec.h new file mode 100644 index 000000000..ce85feb4a --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_dec.h @@ -0,0 +1,262 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_dec.h,v 1.39 2007/11/01 12:33:34 menno Exp $ +**/ + +#ifndef __SBR_DEC_H__ +#define __SBR_DEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PS_DEC +#include "ps_dec.h" +#endif +#ifdef DRM_PS +#include "drm_dec.h" +#endif + +/* MAX_NTSRHFG: maximum of number_time_slots * rate + HFGen. 16*2+8 */ +#define MAX_NTSRHFG 40 +#define MAX_NTSR 32 /* max number_time_slots * rate, ok for DRM and not DRM mode */ + +/* MAX_M: maximum value for M */ +#define MAX_M 49 +/* MAX_L_E: maximum value for L_E */ +#define MAX_L_E 5 + +typedef struct { + real_t *x; + int16_t x_index; + uint8_t channels; +} qmfa_info; + +typedef struct { + real_t *v; + int16_t v_index; + uint8_t channels; +} qmfs_info; + +typedef struct +{ + uint32_t sample_rate; + uint32_t maxAACLine; + + uint8_t rate; + uint8_t just_seeked; + uint8_t ret; + + uint8_t amp_res[2]; + + uint8_t k0; + uint8_t kx; + uint8_t M; + uint8_t N_master; + uint8_t N_high; + uint8_t N_low; + uint8_t N_Q; + uint8_t N_L[4]; + uint8_t n[2]; + + uint8_t f_master[64]; + uint8_t f_table_res[2][64]; + uint8_t f_table_noise[64]; + uint8_t f_table_lim[4][64]; +#ifdef SBR_LOW_POWER + uint8_t f_group[5][64]; + uint8_t N_G[5]; +#endif + + uint8_t table_map_k_to_g[64]; + + uint8_t abs_bord_lead[2]; + uint8_t abs_bord_trail[2]; + uint8_t n_rel_lead[2]; + uint8_t n_rel_trail[2]; + + uint8_t L_E[2]; + uint8_t L_E_prev[2]; + uint8_t L_Q[2]; + + uint8_t t_E[2][MAX_L_E+1]; + uint8_t t_Q[2][3]; + uint8_t f[2][MAX_L_E+1]; + uint8_t f_prev[2]; + + real_t *G_temp_prev[2][5]; + real_t *Q_temp_prev[2][5]; + + int16_t E[2][64][MAX_L_E]; + int16_t E_prev[2][64]; +#ifndef FIXED_POINT + real_t E_orig[2][64][MAX_L_E]; +#endif + real_t E_curr[2][64][MAX_L_E]; + int32_t Q[2][64][2]; +#ifndef FIXED_POINT + real_t Q_div[2][64][2]; + real_t Q_div2[2][64][2]; +#endif + int32_t Q_prev[2][64]; + + int8_t l_A[2]; + int8_t l_A_prev[2]; + + uint8_t bs_invf_mode[2][MAX_L_E]; + uint8_t bs_invf_mode_prev[2][MAX_L_E]; + real_t bwArray[2][64]; + real_t bwArray_prev[2][64]; + + uint8_t noPatches; + uint8_t patchNoSubbands[64]; + uint8_t patchStartSubband[64]; + + uint8_t bs_add_harmonic[2][64]; + uint8_t bs_add_harmonic_prev[2][64]; + + int8_t GQ_ringbuf_index[2]; + + uint16_t index_noise_prev[2]; + uint8_t psi_is_prev[2]; + + uint8_t bs_start_freq_prev; + uint8_t bs_stop_freq_prev; + uint8_t bs_xover_band_prev; + uint8_t bs_freq_scale_prev; + uint8_t bs_alter_scale_prev; + uint8_t bs_noise_bands_prev; + + int8_t prevEnvIsShort[2]; + + int8_t kx_prev; + uint8_t bsco; + uint8_t bsco_prev; + uint8_t M_prev; + uint16_t frame_len; + + uint32_t frame; + uint32_t header_count; + + qmfa_info *qmfa[2]; + qmfs_info *qmfs[2]; + + qmf_t Xsbr[2][MAX_NTSRHFG][64]; + +#if defined(DRM) && defined(DRM_PS) + drm_ps_info *drm_ps; +#endif + +#ifdef PS_DEC + ps_info *ps; +#endif + +#ifdef HDC + uint8_t Is_HDC_SBR; +#endif + + uint8_t numTimeSlotsRate; + uint8_t numTimeSlots; + uint8_t tHFGen; + uint8_t tHFAdj; + +#if (defined(PS_DEC) || defined(DRM_PS)) + uint8_t ps_used; + uint8_t psResetFlag; +#endif + + /* to get it compiling */ + /* we'll see during the coding of all the tools, whether + these are all used or not. + */ + uint8_t bs_header_flag; + uint8_t bs_crc_flag; + uint16_t bs_sbr_crc_bits; + uint8_t bs_protocol_version; + uint8_t bs_amp_res; + uint8_t bs_start_freq; + uint8_t bs_stop_freq; + uint8_t bs_xover_band; + uint8_t bs_freq_scale; + uint8_t bs_alter_scale; + uint8_t bs_noise_bands; + uint8_t bs_limiter_bands; + uint8_t bs_limiter_gains; + uint8_t bs_interpol_freq; + uint8_t bs_smoothing_mode; + uint8_t bs_samplerate_mode; + uint8_t bs_add_harmonic_flag[2]; + uint8_t bs_add_harmonic_flag_prev[2]; + uint8_t bs_extended_data; + uint8_t bs_extension_id; + uint8_t bs_extension_data; + uint8_t bs_coupling; + uint8_t bs_frame_class[2]; + uint8_t bs_rel_bord[2][9]; + uint8_t bs_rel_bord_0[2][9]; + uint8_t bs_rel_bord_1[2][9]; + uint8_t bs_pointer[2]; + uint8_t bs_abs_bord_0[2]; + uint8_t bs_abs_bord_1[2]; + uint8_t bs_num_rel_0[2]; + uint8_t bs_num_rel_1[2]; + uint8_t bs_df_env[2][9]; + uint8_t bs_df_noise[2][3]; + + uint8_t Reset; + uint8_t id_aac; + +#ifdef DRM + uint8_t Is_DRM_SBR; +#endif +} sbr_info; + +sbr_info *sbrDecodeInit(uint16_t framelength, uint8_t id_aac, + uint32_t sample_rate, uint8_t downSampledSBR +#ifdef DRM + , uint8_t IsDRM +#endif + ); +void sbrDecodeEnd(sbr_info *sbr); +void sbrReset(sbr_info *sbr); + +uint8_t sbrDecodeCoupleFrame(sbr_info *sbr, real_t *left_chan, real_t *right_chan, + const uint8_t just_seeked, const uint8_t downSampledSBR); +uint8_t sbrDecodeSingleFrame(sbr_info *sbr, real_t *channel, + const uint8_t just_seeked, const uint8_t downSampledSBR); +#if (defined(PS_DEC) || defined(DRM_PS)) +uint8_t sbrDecodeSingleFramePS(sbr_info *sbr, real_t *left_channel, real_t *right_channel, + const uint8_t just_seeked, const uint8_t downSampledSBR); +#endif + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_e_nf.c b/third_party/faad_hdc/upstream/libfaad/sbr_e_nf.c new file mode 100644 index 000000000..279aefd26 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_e_nf.c @@ -0,0 +1,510 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_e_nf.c,v 1.22 2008/03/23 23:03:29 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#ifdef SBR_DEC + +#include + +#include "sbr_syntax.h" +#include "sbr_e_nf.h" + +void extract_envelope_data(sbr_info *sbr, uint8_t ch) +{ + uint8_t l, k; + + for (l = 0; l < sbr->L_E[ch]; l++) + { + if (sbr->bs_df_env[ch][l] == 0) + { + for (k = 1; k < sbr->n[sbr->f[ch][l]]; k++) + { + sbr->E[ch][k][l] = sbr->E[ch][k - 1][l] + sbr->E[ch][k][l]; + if (sbr->E[ch][k][l] < 0) + sbr->E[ch][k][l] = 0; + } + + } else { /* bs_df_env == 1 */ + + uint8_t g = (l == 0) ? sbr->f_prev[ch] : sbr->f[ch][l-1]; + int16_t E_prev; + + if (sbr->f[ch][l] == g) + { + for (k = 0; k < sbr->n[sbr->f[ch][l]]; k++) + { + if (l == 0) + E_prev = sbr->E_prev[ch][k]; + else + E_prev = sbr->E[ch][k][l - 1]; + + sbr->E[ch][k][l] = E_prev + sbr->E[ch][k][l]; + } + + } else if ((g == 1) && (sbr->f[ch][l] == 0)) { + uint8_t i; + + for (k = 0; k < sbr->n[sbr->f[ch][l]]; k++) + { + for (i = 0; i < sbr->N_high; i++) + { + if (sbr->f_table_res[HI_RES][i] == sbr->f_table_res[LO_RES][k]) + { + if (l == 0) + E_prev = sbr->E_prev[ch][i]; + else + E_prev = sbr->E[ch][i][l - 1]; + + sbr->E[ch][k][l] = E_prev + sbr->E[ch][k][l]; + } + } + } + + } else if ((g == 0) && (sbr->f[ch][l] == 1)) { + uint8_t i; + + for (k = 0; k < sbr->n[sbr->f[ch][l]]; k++) + { + for (i = 0; i < sbr->N_low; i++) + { + if ((sbr->f_table_res[LO_RES][i] <= sbr->f_table_res[HI_RES][k]) && + (sbr->f_table_res[HI_RES][k] < sbr->f_table_res[LO_RES][i + 1])) + { + if (l == 0) + E_prev = sbr->E_prev[ch][i]; + else + E_prev = sbr->E[ch][i][l - 1]; + + sbr->E[ch][k][l] = E_prev + sbr->E[ch][k][l]; + } + } + } + } + } + } +} + +void extract_noise_floor_data(sbr_info *sbr, uint8_t ch) +{ + uint8_t l, k; + + for (l = 0; l < sbr->L_Q[ch]; l++) + { + if (sbr->bs_df_noise[ch][l] == 0) + { + for (k = 1; k < sbr->N_Q; k++) + { + sbr->Q[ch][k][l] = sbr->Q[ch][k][l] + sbr->Q[ch][k-1][l]; + } + } else { + if (l == 0) + { + for (k = 0; k < sbr->N_Q; k++) + { + sbr->Q[ch][k][l] = sbr->Q_prev[ch][k] + sbr->Q[ch][k][0]; + } + } else { + for (k = 0; k < sbr->N_Q; k++) + { + sbr->Q[ch][k][l] = sbr->Q[ch][k][l - 1] + sbr->Q[ch][k][l]; + } + } + } + } +} + +#ifndef FIXED_POINT + +/* table for Q_div values when no coupling */ +static const real_t Q_div_tab[31] = { + FRAC_CONST(0.0153846), FRAC_CONST(0.030303), + FRAC_CONST(0.0588235), FRAC_CONST(0.111111), + FRAC_CONST(0.2), FRAC_CONST(0.333333), + FRAC_CONST(0.5), FRAC_CONST(0.666667), + FRAC_CONST(0.8), FRAC_CONST(0.888889), + FRAC_CONST(0.941176), FRAC_CONST(0.969697), + FRAC_CONST(0.984615), FRAC_CONST(0.992248), + FRAC_CONST(0.996109), FRAC_CONST(0.998051), + FRAC_CONST(0.999024), FRAC_CONST(0.999512), + FRAC_CONST(0.999756), FRAC_CONST(0.999878), + FRAC_CONST(0.999939), FRAC_CONST(0.999969), + FRAC_CONST(0.999985), FRAC_CONST(0.999992), + FRAC_CONST(0.999996), FRAC_CONST(0.999998), + FRAC_CONST(0.999999), FRAC_CONST(1), + FRAC_CONST(1), FRAC_CONST(1), + FRAC_CONST(1) +}; + +static const real_t Q_div_tab_left[31][13] = { + { FRAC_CONST(0.969704), FRAC_CONST(0.888985), FRAC_CONST(0.667532), FRAC_CONST(0.336788), FRAC_CONST(0.117241), FRAC_CONST(0.037594), FRAC_CONST(0.0153846), FRAC_CONST(0.00967118), FRAC_CONST(0.00823245), FRAC_CONST(0.00787211), FRAC_CONST(0.00778198), FRAC_CONST(0.00775945), FRAC_CONST(0.00775382) }, + { FRAC_CONST(0.984619), FRAC_CONST(0.94123), FRAC_CONST(0.800623), FRAC_CONST(0.503876), FRAC_CONST(0.209877), FRAC_CONST(0.0724638), FRAC_CONST(0.030303), FRAC_CONST(0.0191571), FRAC_CONST(0.0163305), FRAC_CONST(0.0156212), FRAC_CONST(0.0154438), FRAC_CONST(0.0153994), FRAC_CONST(0.0153883) }, + { FRAC_CONST(0.99225), FRAC_CONST(0.969726), FRAC_CONST(0.889273), FRAC_CONST(0.670103), FRAC_CONST(0.346939), FRAC_CONST(0.135135), FRAC_CONST(0.0588235), FRAC_CONST(0.037594), FRAC_CONST(0.0321361), FRAC_CONST(0.0307619), FRAC_CONST(0.0304178), FRAC_CONST(0.0303317), FRAC_CONST(0.0303102) }, + { FRAC_CONST(0.99611), FRAC_CONST(0.98463), FRAC_CONST(0.941392), FRAC_CONST(0.802469), FRAC_CONST(0.515152), FRAC_CONST(0.238095), FRAC_CONST(0.111111), FRAC_CONST(0.0724638), FRAC_CONST(0.0622711), FRAC_CONST(0.0596878), FRAC_CONST(0.0590397), FRAC_CONST(0.0588776), FRAC_CONST(0.058837) }, + { FRAC_CONST(0.998051), FRAC_CONST(0.992256), FRAC_CONST(0.969811), FRAC_CONST(0.890411), FRAC_CONST(0.68), FRAC_CONST(0.384615), FRAC_CONST(0.2), FRAC_CONST(0.135135), FRAC_CONST(0.117241), FRAC_CONST(0.112652), FRAC_CONST(0.111497), FRAC_CONST(0.111208), FRAC_CONST(0.111135) }, + { FRAC_CONST(0.999025), FRAC_CONST(0.996113), FRAC_CONST(0.984674), FRAC_CONST(0.942029), FRAC_CONST(0.809524), FRAC_CONST(0.555556), FRAC_CONST(0.333333), FRAC_CONST(0.238095), FRAC_CONST(0.209877), FRAC_CONST(0.202492), FRAC_CONST(0.200625), FRAC_CONST(0.200156), FRAC_CONST(0.200039) }, + { FRAC_CONST(0.999512), FRAC_CONST(0.998053), FRAC_CONST(0.992278), FRAC_CONST(0.970149), FRAC_CONST(0.894737), FRAC_CONST(0.714286), FRAC_CONST(0.5), FRAC_CONST(0.384615), FRAC_CONST(0.346939), FRAC_CONST(0.336788), FRAC_CONST(0.3342), FRAC_CONST(0.33355), FRAC_CONST(0.333388) }, + { FRAC_CONST(0.999756), FRAC_CONST(0.999025), FRAC_CONST(0.996124), FRAC_CONST(0.984848), FRAC_CONST(0.944444), FRAC_CONST(0.833333), FRAC_CONST(0.666667), FRAC_CONST(0.555556), FRAC_CONST(0.515152), FRAC_CONST(0.503876), FRAC_CONST(0.500975), FRAC_CONST(0.500244), FRAC_CONST(0.500061) }, + { FRAC_CONST(0.999878), FRAC_CONST(0.999512), FRAC_CONST(0.998058), FRAC_CONST(0.992366), FRAC_CONST(0.971429), FRAC_CONST(0.909091), FRAC_CONST(0.8), FRAC_CONST(0.714286), FRAC_CONST(0.68), FRAC_CONST(0.670103), FRAC_CONST(0.667532), FRAC_CONST(0.666884), FRAC_CONST(0.666721) }, + { FRAC_CONST(0.999939), FRAC_CONST(0.999756), FRAC_CONST(0.999028), FRAC_CONST(0.996169), FRAC_CONST(0.985507), FRAC_CONST(0.952381), FRAC_CONST(0.888889), FRAC_CONST(0.833333), FRAC_CONST(0.809524), FRAC_CONST(0.802469), FRAC_CONST(0.800623), FRAC_CONST(0.800156), FRAC_CONST(0.800039) }, + { FRAC_CONST(0.999969), FRAC_CONST(0.999878), FRAC_CONST(0.999514), FRAC_CONST(0.998081), FRAC_CONST(0.992701), FRAC_CONST(0.97561), FRAC_CONST(0.941176), FRAC_CONST(0.909091), FRAC_CONST(0.894737), FRAC_CONST(0.890411), FRAC_CONST(0.889273), FRAC_CONST(0.888985), FRAC_CONST(0.888913) }, + { FRAC_CONST(0.999985), FRAC_CONST(0.999939), FRAC_CONST(0.999757), FRAC_CONST(0.999039), FRAC_CONST(0.996337), FRAC_CONST(0.987654), FRAC_CONST(0.969697), FRAC_CONST(0.952381), FRAC_CONST(0.944444), FRAC_CONST(0.942029), FRAC_CONST(0.941392), FRAC_CONST(0.94123), FRAC_CONST(0.94119) }, + { FRAC_CONST(0.999992), FRAC_CONST(0.99997), FRAC_CONST(0.999878), FRAC_CONST(0.999519), FRAC_CONST(0.998165), FRAC_CONST(0.993789), FRAC_CONST(0.984615), FRAC_CONST(0.97561), FRAC_CONST(0.971429), FRAC_CONST(0.970149), FRAC_CONST(0.969811), FRAC_CONST(0.969726), FRAC_CONST(0.969704) }, + { FRAC_CONST(0.999996), FRAC_CONST(0.999985), FRAC_CONST(0.999939), FRAC_CONST(0.99976), FRAC_CONST(0.999082), FRAC_CONST(0.996885), FRAC_CONST(0.992248), FRAC_CONST(0.987654), FRAC_CONST(0.985507), FRAC_CONST(0.984848), FRAC_CONST(0.984674), FRAC_CONST(0.98463), FRAC_CONST(0.984619) }, + { FRAC_CONST(0.999998), FRAC_CONST(0.999992), FRAC_CONST(0.99997), FRAC_CONST(0.99988), FRAC_CONST(0.999541), FRAC_CONST(0.99844), FRAC_CONST(0.996109), FRAC_CONST(0.993789), FRAC_CONST(0.992701), FRAC_CONST(0.992366), FRAC_CONST(0.992278), FRAC_CONST(0.992256), FRAC_CONST(0.99225) }, + { FRAC_CONST(0.999999), FRAC_CONST(0.999996), FRAC_CONST(0.999985), FRAC_CONST(0.99994), FRAC_CONST(0.99977), FRAC_CONST(0.999219), FRAC_CONST(0.998051), FRAC_CONST(0.996885), FRAC_CONST(0.996337), FRAC_CONST(0.996169), FRAC_CONST(0.996124), FRAC_CONST(0.996113), FRAC_CONST(0.99611) }, + { FRAC_CONST(1), FRAC_CONST(0.999998), FRAC_CONST(0.999992), FRAC_CONST(0.99997), FRAC_CONST(0.999885), FRAC_CONST(0.99961), FRAC_CONST(0.999024), FRAC_CONST(0.99844), FRAC_CONST(0.998165), FRAC_CONST(0.998081), FRAC_CONST(0.998058), FRAC_CONST(0.998053), FRAC_CONST(0.998051) }, + { FRAC_CONST(1), FRAC_CONST(0.999999), FRAC_CONST(0.999996), FRAC_CONST(0.999985), FRAC_CONST(0.999943), FRAC_CONST(0.999805), FRAC_CONST(0.999512), FRAC_CONST(0.999219), FRAC_CONST(0.999082), FRAC_CONST(0.999039), FRAC_CONST(0.999028), FRAC_CONST(0.999025), FRAC_CONST(0.999025) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999998), FRAC_CONST(0.999992), FRAC_CONST(0.999971), FRAC_CONST(0.999902), FRAC_CONST(0.999756), FRAC_CONST(0.99961), FRAC_CONST(0.999541), FRAC_CONST(0.999519), FRAC_CONST(0.999514), FRAC_CONST(0.999512), FRAC_CONST(0.999512) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999999), FRAC_CONST(0.999996), FRAC_CONST(0.999986), FRAC_CONST(0.999951), FRAC_CONST(0.999878), FRAC_CONST(0.999805), FRAC_CONST(0.99977), FRAC_CONST(0.99976), FRAC_CONST(0.999757), FRAC_CONST(0.999756), FRAC_CONST(0.999756) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999998), FRAC_CONST(0.999993), FRAC_CONST(0.999976), FRAC_CONST(0.999939), FRAC_CONST(0.999902), FRAC_CONST(0.999885), FRAC_CONST(0.99988), FRAC_CONST(0.999878), FRAC_CONST(0.999878), FRAC_CONST(0.999878) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999999), FRAC_CONST(0.999996), FRAC_CONST(0.999988), FRAC_CONST(0.999969), FRAC_CONST(0.999951), FRAC_CONST(0.999943), FRAC_CONST(0.99994), FRAC_CONST(0.999939), FRAC_CONST(0.999939), FRAC_CONST(0.999939) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999998), FRAC_CONST(0.999994), FRAC_CONST(0.999985), FRAC_CONST(0.999976), FRAC_CONST(0.999971), FRAC_CONST(0.99997), FRAC_CONST(0.99997), FRAC_CONST(0.99997), FRAC_CONST(0.999969) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999999), FRAC_CONST(0.999997), FRAC_CONST(0.999992), FRAC_CONST(0.999988), FRAC_CONST(0.999986), FRAC_CONST(0.999985), FRAC_CONST(0.999985), FRAC_CONST(0.999985), FRAC_CONST(0.999985) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999998), FRAC_CONST(0.999996), FRAC_CONST(0.999994), FRAC_CONST(0.999993), FRAC_CONST(0.999992), FRAC_CONST(0.999992), FRAC_CONST(0.999992), FRAC_CONST(0.999992) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999999), FRAC_CONST(0.999998), FRAC_CONST(0.999997), FRAC_CONST(0.999996), FRAC_CONST(0.999996), FRAC_CONST(0.999996), FRAC_CONST(0.999996), FRAC_CONST(0.999996) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999999), FRAC_CONST(0.999998), FRAC_CONST(0.999998), FRAC_CONST(0.999998), FRAC_CONST(0.999998), FRAC_CONST(0.999998), FRAC_CONST(0.999998) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.999999), FRAC_CONST(0.999999), FRAC_CONST(0.999999), FRAC_CONST(0.999999), FRAC_CONST(0.999999), FRAC_CONST(0.999999) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) } +}; + +static const real_t Q_div_tab_right[31][13] = { + { FRAC_CONST(0.00775382), FRAC_CONST(0.00775945), FRAC_CONST(0.00778198), FRAC_CONST(0.00787211), FRAC_CONST(0.00823245), FRAC_CONST(0.00967118), FRAC_CONST(0.0153846), FRAC_CONST(0.037594), FRAC_CONST(0.117241), FRAC_CONST(0.336788), FRAC_CONST(0.667532), FRAC_CONST(0.888985), FRAC_CONST(0.969704) }, + { FRAC_CONST(0.0153883), FRAC_CONST(0.0153994), FRAC_CONST(0.0154438), FRAC_CONST(0.0156212), FRAC_CONST(0.0163305), FRAC_CONST(0.0191571), FRAC_CONST(0.030303), FRAC_CONST(0.0724638), FRAC_CONST(0.209877), FRAC_CONST(0.503876), FRAC_CONST(0.800623), FRAC_CONST(0.94123), FRAC_CONST(0.984619) }, + { FRAC_CONST(0.0303102), FRAC_CONST(0.0303317), FRAC_CONST(0.0304178), FRAC_CONST(0.0307619), FRAC_CONST(0.0321361), FRAC_CONST(0.037594), FRAC_CONST(0.0588235), FRAC_CONST(0.135135), FRAC_CONST(0.346939), FRAC_CONST(0.670103), FRAC_CONST(0.889273), FRAC_CONST(0.969726), FRAC_CONST(0.99225) }, + { FRAC_CONST(0.058837), FRAC_CONST(0.0588776), FRAC_CONST(0.0590397), FRAC_CONST(0.0596878), FRAC_CONST(0.0622711), FRAC_CONST(0.0724638), FRAC_CONST(0.111111), FRAC_CONST(0.238095), FRAC_CONST(0.515152), FRAC_CONST(0.802469), FRAC_CONST(0.941392), FRAC_CONST(0.98463), FRAC_CONST(0.99611) }, + { FRAC_CONST(0.111135), FRAC_CONST(0.111208), FRAC_CONST(0.111497), FRAC_CONST(0.112652), FRAC_CONST(0.117241), FRAC_CONST(0.135135), FRAC_CONST(0.2), FRAC_CONST(0.384615), FRAC_CONST(0.68), FRAC_CONST(0.890411), FRAC_CONST(0.969811), FRAC_CONST(0.992256), FRAC_CONST(0.998051) }, + { FRAC_CONST(0.200039), FRAC_CONST(0.200156), FRAC_CONST(0.200625), FRAC_CONST(0.202492), FRAC_CONST(0.209877), FRAC_CONST(0.238095), FRAC_CONST(0.333333), FRAC_CONST(0.555556), FRAC_CONST(0.809524), FRAC_CONST(0.942029), FRAC_CONST(0.984674), FRAC_CONST(0.996113), FRAC_CONST(0.999025) }, + { FRAC_CONST(0.333388), FRAC_CONST(0.33355), FRAC_CONST(0.3342), FRAC_CONST(0.336788), FRAC_CONST(0.346939), FRAC_CONST(0.384615), FRAC_CONST(0.5), FRAC_CONST(0.714286), FRAC_CONST(0.894737), FRAC_CONST(0.970149), FRAC_CONST(0.992278), FRAC_CONST(0.998053), FRAC_CONST(0.999512) }, + { FRAC_CONST(0.500061), FRAC_CONST(0.500244), FRAC_CONST(0.500975), FRAC_CONST(0.503876), FRAC_CONST(0.515152), FRAC_CONST(0.555556), FRAC_CONST(0.666667), FRAC_CONST(0.833333), FRAC_CONST(0.944444), FRAC_CONST(0.984848), FRAC_CONST(0.996124), FRAC_CONST(0.999025), FRAC_CONST(0.999756) }, + { FRAC_CONST(0.666721), FRAC_CONST(0.666884), FRAC_CONST(0.667532), FRAC_CONST(0.670103), FRAC_CONST(0.68), FRAC_CONST(0.714286), FRAC_CONST(0.8), FRAC_CONST(0.909091), FRAC_CONST(0.971429), FRAC_CONST(0.992366), FRAC_CONST(0.998058), FRAC_CONST(0.999512), FRAC_CONST(0.999878) }, + { FRAC_CONST(0.800039), FRAC_CONST(0.800156), FRAC_CONST(0.800623), FRAC_CONST(0.802469), FRAC_CONST(0.809524), FRAC_CONST(0.833333), FRAC_CONST(0.888889), FRAC_CONST(0.952381), FRAC_CONST(0.985507), FRAC_CONST(0.996169), FRAC_CONST(0.999028), FRAC_CONST(0.999756), FRAC_CONST(0.999939) }, + { FRAC_CONST(0.888913), FRAC_CONST(0.888985), FRAC_CONST(0.889273), FRAC_CONST(0.890411), FRAC_CONST(0.894737), FRAC_CONST(0.909091), FRAC_CONST(0.941176), FRAC_CONST(0.97561), FRAC_CONST(0.992701), FRAC_CONST(0.998081), FRAC_CONST(0.999514), FRAC_CONST(0.999878), FRAC_CONST(0.999969) }, + { FRAC_CONST(0.94119), FRAC_CONST(0.94123), FRAC_CONST(0.941392), FRAC_CONST(0.942029), FRAC_CONST(0.944444), FRAC_CONST(0.952381), FRAC_CONST(0.969697), FRAC_CONST(0.987654), FRAC_CONST(0.996337), FRAC_CONST(0.999039), FRAC_CONST(0.999757), FRAC_CONST(0.999939), FRAC_CONST(0.999985) }, + { FRAC_CONST(0.969704), FRAC_CONST(0.969726), FRAC_CONST(0.969811), FRAC_CONST(0.970149), FRAC_CONST(0.971429), FRAC_CONST(0.97561), FRAC_CONST(0.984615), FRAC_CONST(0.993789), FRAC_CONST(0.998165), FRAC_CONST(0.999519), FRAC_CONST(0.999878), FRAC_CONST(0.99997), FRAC_CONST(0.999992) }, + { FRAC_CONST(0.984619), FRAC_CONST(0.98463), FRAC_CONST(0.984674), FRAC_CONST(0.984848), FRAC_CONST(0.985507), FRAC_CONST(0.987654), FRAC_CONST(0.992248), FRAC_CONST(0.996885), FRAC_CONST(0.999082), FRAC_CONST(0.99976), FRAC_CONST(0.999939), FRAC_CONST(0.999985), FRAC_CONST(0.999996) }, + { FRAC_CONST(0.99225), FRAC_CONST(0.992256), FRAC_CONST(0.992278), FRAC_CONST(0.992366), FRAC_CONST(0.992701), FRAC_CONST(0.993789), FRAC_CONST(0.996109), FRAC_CONST(0.99844), FRAC_CONST(0.999541), FRAC_CONST(0.99988), FRAC_CONST(0.99997), FRAC_CONST(0.999992), FRAC_CONST(0.999998) }, + { FRAC_CONST(0.99611), FRAC_CONST(0.996113), FRAC_CONST(0.996124), FRAC_CONST(0.996169), FRAC_CONST(0.996337), FRAC_CONST(0.996885), FRAC_CONST(0.998051), FRAC_CONST(0.999219), FRAC_CONST(0.99977), FRAC_CONST(0.99994), FRAC_CONST(0.999985), FRAC_CONST(0.999996), FRAC_CONST(0.999999) }, + { FRAC_CONST(0.998051), FRAC_CONST(0.998053), FRAC_CONST(0.998058), FRAC_CONST(0.998081), FRAC_CONST(0.998165), FRAC_CONST(0.99844), FRAC_CONST(0.999024), FRAC_CONST(0.99961), FRAC_CONST(0.999885), FRAC_CONST(0.99997), FRAC_CONST(0.999992), FRAC_CONST(0.999998), FRAC_CONST(1) }, + { FRAC_CONST(0.999025), FRAC_CONST(0.999025), FRAC_CONST(0.999028), FRAC_CONST(0.999039), FRAC_CONST(0.999082), FRAC_CONST(0.999219), FRAC_CONST(0.999512), FRAC_CONST(0.999805), FRAC_CONST(0.999943), FRAC_CONST(0.999985), FRAC_CONST(0.999996), FRAC_CONST(0.999999), FRAC_CONST(1) }, + { FRAC_CONST(0.999512), FRAC_CONST(0.999512), FRAC_CONST(0.999514), FRAC_CONST(0.999519), FRAC_CONST(0.999541), FRAC_CONST(0.99961), FRAC_CONST(0.999756), FRAC_CONST(0.999902), FRAC_CONST(0.999971), FRAC_CONST(0.999992), FRAC_CONST(0.999998), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.999756), FRAC_CONST(0.999756), FRAC_CONST(0.999757), FRAC_CONST(0.99976), FRAC_CONST(0.99977), FRAC_CONST(0.999805), FRAC_CONST(0.999878), FRAC_CONST(0.999951), FRAC_CONST(0.999986), FRAC_CONST(0.999996), FRAC_CONST(0.999999), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.999878), FRAC_CONST(0.999878), FRAC_CONST(0.999878), FRAC_CONST(0.99988), FRAC_CONST(0.999885), FRAC_CONST(0.999902), FRAC_CONST(0.999939), FRAC_CONST(0.999976), FRAC_CONST(0.999993), FRAC_CONST(0.999998), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.999939), FRAC_CONST(0.999939), FRAC_CONST(0.999939), FRAC_CONST(0.99994), FRAC_CONST(0.999943), FRAC_CONST(0.999951), FRAC_CONST(0.999969), FRAC_CONST(0.999988), FRAC_CONST(0.999996), FRAC_CONST(0.999999), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.999969), FRAC_CONST(0.99997), FRAC_CONST(0.99997), FRAC_CONST(0.99997), FRAC_CONST(0.999971), FRAC_CONST(0.999976), FRAC_CONST(0.999985), FRAC_CONST(0.999994), FRAC_CONST(0.999998), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.999985), FRAC_CONST(0.999985), FRAC_CONST(0.999985), FRAC_CONST(0.999985), FRAC_CONST(0.999986), FRAC_CONST(0.999988), FRAC_CONST(0.999992), FRAC_CONST(0.999997), FRAC_CONST(0.999999), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.999992), FRAC_CONST(0.999992), FRAC_CONST(0.999992), FRAC_CONST(0.999992), FRAC_CONST(0.999993), FRAC_CONST(0.999994), FRAC_CONST(0.999996), FRAC_CONST(0.999998), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.999996), FRAC_CONST(0.999996), FRAC_CONST(0.999996), FRAC_CONST(0.999996), FRAC_CONST(0.999996), FRAC_CONST(0.999997), FRAC_CONST(0.999998), FRAC_CONST(0.999999), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.999998), FRAC_CONST(0.999998), FRAC_CONST(0.999998), FRAC_CONST(0.999998), FRAC_CONST(0.999998), FRAC_CONST(0.999998), FRAC_CONST(0.999999), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(0.999999), FRAC_CONST(0.999999), FRAC_CONST(0.999999), FRAC_CONST(0.999999), FRAC_CONST(0.999999), FRAC_CONST(0.999999), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) }, + { FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1) } +}; + +/* calculates 1/(1+Q) */ +/* [0..1] */ +static real_t calc_Q_div(sbr_info *sbr, uint8_t ch, uint8_t m, uint8_t l) +{ + if (sbr->bs_coupling) + { + /* left channel */ + if ((sbr->Q[0][m][l] < 0 || sbr->Q[0][m][l] > 30) || + (sbr->Q[1][m][l] < 0 || sbr->Q[1][m][l] > 24 /* 2*panOffset(1) */)) + { + return 0; + } else { + /* the pan parameter is always even */ + if (ch == 0) + { + return Q_div_tab_left[sbr->Q[0][m][l]][sbr->Q[1][m][l] >> 1]; + } else { + return Q_div_tab_right[sbr->Q[0][m][l]][sbr->Q[1][m][l] >> 1]; + } + } + } else { + /* no coupling */ + if (sbr->Q[ch][m][l] < 0 || sbr->Q[ch][m][l] > 30) + { + return 0; + } else { + return Q_div_tab[sbr->Q[ch][m][l]]; + } + } +} + +/* table for Q_div2 values when no coupling */ +static const real_t Q_div2_tab[31] = { + FRAC_CONST(0.984615), FRAC_CONST(0.969697), + FRAC_CONST(0.941176), FRAC_CONST(0.888889), + FRAC_CONST(0.8), FRAC_CONST(0.666667), + FRAC_CONST(0.5), FRAC_CONST(0.333333), + FRAC_CONST(0.2), FRAC_CONST(0.111111), + FRAC_CONST(0.0588235), FRAC_CONST(0.030303), + FRAC_CONST(0.0153846), FRAC_CONST(0.00775194), + FRAC_CONST(0.00389105), FRAC_CONST(0.00194932), + FRAC_CONST(0.00097561), FRAC_CONST(0.000488043), + FRAC_CONST(0.000244081), FRAC_CONST(0.000122055), + FRAC_CONST(6.10314E-005), FRAC_CONST(3.05166E-005), + FRAC_CONST(1.52586E-005), FRAC_CONST(7.62934E-006), + FRAC_CONST(3.81468E-006), FRAC_CONST(1.90734E-006), + FRAC_CONST(9.53673E-007), FRAC_CONST(4.76837E-007), + FRAC_CONST(2.38419E-007), FRAC_CONST(1.19209E-007), + FRAC_CONST(5.96046E-008) +}; + +static const real_t Q_div2_tab_left[31][13] = { + { FRAC_CONST(0.0302959), FRAC_CONST(0.111015), FRAC_CONST(0.332468), FRAC_CONST(0.663212), FRAC_CONST(0.882759), FRAC_CONST(0.962406), FRAC_CONST(0.984615), FRAC_CONST(0.990329), FRAC_CONST(0.991768), FRAC_CONST(0.992128), FRAC_CONST(0.992218), FRAC_CONST(0.992241), FRAC_CONST(0.992246) }, + { FRAC_CONST(0.0153809), FRAC_CONST(0.0587695), FRAC_CONST(0.199377), FRAC_CONST(0.496124), FRAC_CONST(0.790123), FRAC_CONST(0.927536), FRAC_CONST(0.969697), FRAC_CONST(0.980843), FRAC_CONST(0.98367), FRAC_CONST(0.984379), FRAC_CONST(0.984556), FRAC_CONST(0.984601), FRAC_CONST(0.984612) }, + { FRAC_CONST(0.00775006), FRAC_CONST(0.0302744), FRAC_CONST(0.110727), FRAC_CONST(0.329897), FRAC_CONST(0.653061), FRAC_CONST(0.864865), FRAC_CONST(0.941176), FRAC_CONST(0.962406), FRAC_CONST(0.967864), FRAC_CONST(0.969238), FRAC_CONST(0.969582), FRAC_CONST(0.969668), FRAC_CONST(0.96969) }, + { FRAC_CONST(0.0038901), FRAC_CONST(0.0153698), FRAC_CONST(0.0586081), FRAC_CONST(0.197531), FRAC_CONST(0.484848), FRAC_CONST(0.761905), FRAC_CONST(0.888889), FRAC_CONST(0.927536), FRAC_CONST(0.937729), FRAC_CONST(0.940312), FRAC_CONST(0.94096), FRAC_CONST(0.941122), FRAC_CONST(0.941163) }, + { FRAC_CONST(0.00194884), FRAC_CONST(0.00774443), FRAC_CONST(0.0301887), FRAC_CONST(0.109589), FRAC_CONST(0.32), FRAC_CONST(0.615385), FRAC_CONST(0.8), FRAC_CONST(0.864865), FRAC_CONST(0.882759), FRAC_CONST(0.887348), FRAC_CONST(0.888503), FRAC_CONST(0.888792), FRAC_CONST(0.888865) }, + { FRAC_CONST(0.000975372), FRAC_CONST(0.00388727), FRAC_CONST(0.0153257), FRAC_CONST(0.057971), FRAC_CONST(0.190476), FRAC_CONST(0.444444), FRAC_CONST(0.666667), FRAC_CONST(0.761905), FRAC_CONST(0.790123), FRAC_CONST(0.797508), FRAC_CONST(0.799375), FRAC_CONST(0.799844), FRAC_CONST(0.799961) }, + { FRAC_CONST(0.000487924), FRAC_CONST(0.00194742), FRAC_CONST(0.00772201), FRAC_CONST(0.0298507), FRAC_CONST(0.105263), FRAC_CONST(0.285714), FRAC_CONST(0.5), FRAC_CONST(0.615385), FRAC_CONST(0.653061), FRAC_CONST(0.663212), FRAC_CONST(0.6658), FRAC_CONST(0.66645), FRAC_CONST(0.666612) }, + { FRAC_CONST(0.000244021), FRAC_CONST(0.000974659), FRAC_CONST(0.00387597), FRAC_CONST(0.0151515), FRAC_CONST(0.0555556), FRAC_CONST(0.166667), FRAC_CONST(0.333333), FRAC_CONST(0.444444), FRAC_CONST(0.484848), FRAC_CONST(0.496124), FRAC_CONST(0.499025), FRAC_CONST(0.499756), FRAC_CONST(0.499939) }, + { FRAC_CONST(0.000122026), FRAC_CONST(0.000487567), FRAC_CONST(0.00194175), FRAC_CONST(0.00763359), FRAC_CONST(0.0285714), FRAC_CONST(0.0909091), FRAC_CONST(0.2), FRAC_CONST(0.285714), FRAC_CONST(0.32), FRAC_CONST(0.329897), FRAC_CONST(0.332468), FRAC_CONST(0.333116), FRAC_CONST(0.333279) }, + { FRAC_CONST(6.10165E-005), FRAC_CONST(0.000243843), FRAC_CONST(0.000971817), FRAC_CONST(0.00383142), FRAC_CONST(0.0144928), FRAC_CONST(0.047619), FRAC_CONST(0.111111), FRAC_CONST(0.166667), FRAC_CONST(0.190476), FRAC_CONST(0.197531), FRAC_CONST(0.199377), FRAC_CONST(0.199844), FRAC_CONST(0.199961) }, + { FRAC_CONST(3.05092E-005), FRAC_CONST(0.000121936), FRAC_CONST(0.000486145), FRAC_CONST(0.00191939), FRAC_CONST(0.00729927), FRAC_CONST(0.0243902), FRAC_CONST(0.0588235), FRAC_CONST(0.0909091), FRAC_CONST(0.105263), FRAC_CONST(0.109589), FRAC_CONST(0.110727), FRAC_CONST(0.111015), FRAC_CONST(0.111087) }, + { FRAC_CONST(1.52548E-005), FRAC_CONST(6.09719E-005), FRAC_CONST(0.000243132), FRAC_CONST(0.000960615), FRAC_CONST(0.003663), FRAC_CONST(0.0123457), FRAC_CONST(0.030303), FRAC_CONST(0.047619), FRAC_CONST(0.0555556), FRAC_CONST(0.057971), FRAC_CONST(0.0586081), FRAC_CONST(0.0587695), FRAC_CONST(0.05881) }, + { FRAC_CONST(7.62747E-006), FRAC_CONST(3.04869E-005), FRAC_CONST(0.000121581), FRAC_CONST(0.000480538), FRAC_CONST(0.00183486), FRAC_CONST(0.00621118), FRAC_CONST(0.0153846), FRAC_CONST(0.0243902), FRAC_CONST(0.0285714), FRAC_CONST(0.0298507), FRAC_CONST(0.0301887), FRAC_CONST(0.0302744), FRAC_CONST(0.0302959) }, + { FRAC_CONST(3.81375E-006), FRAC_CONST(1.52437E-005), FRAC_CONST(6.0794E-005), FRAC_CONST(0.000240327), FRAC_CONST(0.000918274), FRAC_CONST(0.00311526), FRAC_CONST(0.00775194), FRAC_CONST(0.0123457), FRAC_CONST(0.0144928), FRAC_CONST(0.0151515), FRAC_CONST(0.0153257), FRAC_CONST(0.0153698), FRAC_CONST(0.0153809) }, + { FRAC_CONST(1.90688E-006), FRAC_CONST(7.62189E-006), FRAC_CONST(3.03979E-005), FRAC_CONST(0.000120178), FRAC_CONST(0.000459348), FRAC_CONST(0.00156006), FRAC_CONST(0.00389105), FRAC_CONST(0.00621118), FRAC_CONST(0.00729927), FRAC_CONST(0.00763359), FRAC_CONST(0.00772201), FRAC_CONST(0.00774443), FRAC_CONST(0.00775006) }, + { FRAC_CONST(9.53441E-007), FRAC_CONST(3.81096E-006), FRAC_CONST(1.51992E-005), FRAC_CONST(6.00925E-005), FRAC_CONST(0.000229727), FRAC_CONST(0.00078064), FRAC_CONST(0.00194932), FRAC_CONST(0.00311526), FRAC_CONST(0.003663), FRAC_CONST(0.00383142), FRAC_CONST(0.00387597), FRAC_CONST(0.00388727), FRAC_CONST(0.0038901) }, + { FRAC_CONST(4.76721E-007), FRAC_CONST(1.90548E-006), FRAC_CONST(7.59965E-006), FRAC_CONST(3.00472E-005), FRAC_CONST(0.000114877), FRAC_CONST(0.000390472), FRAC_CONST(0.00097561), FRAC_CONST(0.00156006), FRAC_CONST(0.00183486), FRAC_CONST(0.00191939), FRAC_CONST(0.00194175), FRAC_CONST(0.00194742), FRAC_CONST(0.00194884) }, + { FRAC_CONST(2.3836E-007), FRAC_CONST(9.52743E-007), FRAC_CONST(3.79984E-006), FRAC_CONST(1.50238E-005), FRAC_CONST(5.74416E-005), FRAC_CONST(0.000195274), FRAC_CONST(0.000488043), FRAC_CONST(0.00078064), FRAC_CONST(0.000918274), FRAC_CONST(0.000960615), FRAC_CONST(0.000971817), FRAC_CONST(0.000974659), FRAC_CONST(0.000975372) }, + { FRAC_CONST(1.1918E-007), FRAC_CONST(4.76372E-007), FRAC_CONST(1.89992E-006), FRAC_CONST(7.51196E-006), FRAC_CONST(2.87216E-005), FRAC_CONST(9.76467E-005), FRAC_CONST(0.000244081), FRAC_CONST(0.000390472), FRAC_CONST(0.000459348), FRAC_CONST(0.000480538), FRAC_CONST(0.000486145), FRAC_CONST(0.000487567), FRAC_CONST(0.000487924) }, + { FRAC_CONST(5.95901E-008), FRAC_CONST(2.38186E-007), FRAC_CONST(9.49963E-007), FRAC_CONST(3.756E-006), FRAC_CONST(1.4361E-005), FRAC_CONST(4.88257E-005), FRAC_CONST(0.000122055), FRAC_CONST(0.000195274), FRAC_CONST(0.000229727), FRAC_CONST(0.000240327), FRAC_CONST(0.000243132), FRAC_CONST(0.000243843), FRAC_CONST(0.000244021) }, + { FRAC_CONST(2.9795E-008), FRAC_CONST(1.19093E-007), FRAC_CONST(4.74982E-007), FRAC_CONST(1.878E-006), FRAC_CONST(7.18056E-006), FRAC_CONST(2.44135E-005), FRAC_CONST(6.10314E-005), FRAC_CONST(9.76467E-005), FRAC_CONST(0.000114877), FRAC_CONST(0.000120178), FRAC_CONST(0.000121581), FRAC_CONST(0.000121936), FRAC_CONST(0.000122026) }, + { FRAC_CONST(1.48975E-008), FRAC_CONST(5.95465E-008), FRAC_CONST(2.37491E-007), FRAC_CONST(9.39002E-007), FRAC_CONST(3.59029E-006), FRAC_CONST(1.22069E-005), FRAC_CONST(3.05166E-005), FRAC_CONST(4.88257E-005), FRAC_CONST(5.74416E-005), FRAC_CONST(6.00925E-005), FRAC_CONST(6.0794E-005), FRAC_CONST(6.09719E-005), FRAC_CONST(6.10165E-005) }, + { FRAC_CONST(7.44876E-009), FRAC_CONST(2.97732E-008), FRAC_CONST(1.18745E-007), FRAC_CONST(4.69501E-007), FRAC_CONST(1.79515E-006), FRAC_CONST(6.10348E-006), FRAC_CONST(1.52586E-005), FRAC_CONST(2.44135E-005), FRAC_CONST(2.87216E-005), FRAC_CONST(3.00472E-005), FRAC_CONST(3.03979E-005), FRAC_CONST(3.04869E-005), FRAC_CONST(3.05092E-005) }, + { FRAC_CONST(3.72438E-009), FRAC_CONST(1.48866E-008), FRAC_CONST(5.93727E-008), FRAC_CONST(2.34751E-007), FRAC_CONST(8.97575E-007), FRAC_CONST(3.05175E-006), FRAC_CONST(7.62934E-006), FRAC_CONST(1.22069E-005), FRAC_CONST(1.4361E-005), FRAC_CONST(1.50238E-005), FRAC_CONST(1.51992E-005), FRAC_CONST(1.52437E-005), FRAC_CONST(1.52548E-005) }, + { FRAC_CONST(1.86219E-009), FRAC_CONST(7.44331E-009), FRAC_CONST(2.96864E-008), FRAC_CONST(1.17375E-007), FRAC_CONST(4.48788E-007), FRAC_CONST(1.52588E-006), FRAC_CONST(3.81468E-006), FRAC_CONST(6.10348E-006), FRAC_CONST(7.18056E-006), FRAC_CONST(7.51196E-006), FRAC_CONST(7.59965E-006), FRAC_CONST(7.62189E-006), FRAC_CONST(7.62747E-006) }, + { FRAC_CONST(9.31095E-010), FRAC_CONST(3.72166E-009), FRAC_CONST(1.48432E-008), FRAC_CONST(5.86876E-008), FRAC_CONST(2.24394E-007), FRAC_CONST(7.62939E-007), FRAC_CONST(1.90734E-006), FRAC_CONST(3.05175E-006), FRAC_CONST(3.59029E-006), FRAC_CONST(3.756E-006), FRAC_CONST(3.79984E-006), FRAC_CONST(3.81096E-006), FRAC_CONST(3.81375E-006) }, + { FRAC_CONST(4.65548E-010), FRAC_CONST(1.86083E-009), FRAC_CONST(7.42159E-009), FRAC_CONST(2.93438E-008), FRAC_CONST(1.12197E-007), FRAC_CONST(3.8147E-007), FRAC_CONST(9.53673E-007), FRAC_CONST(1.52588E-006), FRAC_CONST(1.79515E-006), FRAC_CONST(1.878E-006), FRAC_CONST(1.89992E-006), FRAC_CONST(1.90548E-006), FRAC_CONST(1.90688E-006) }, + { FRAC_CONST(2.32774E-010), FRAC_CONST(9.30414E-010), FRAC_CONST(3.71079E-009), FRAC_CONST(1.46719E-008), FRAC_CONST(5.60985E-008), FRAC_CONST(1.90735E-007), FRAC_CONST(4.76837E-007), FRAC_CONST(7.62939E-007), FRAC_CONST(8.97575E-007), FRAC_CONST(9.39002E-007), FRAC_CONST(9.49963E-007), FRAC_CONST(9.52743E-007), FRAC_CONST(9.53441E-007) }, + { FRAC_CONST(1.16387E-010), FRAC_CONST(4.65207E-010), FRAC_CONST(1.8554E-009), FRAC_CONST(7.33596E-009), FRAC_CONST(2.80492E-008), FRAC_CONST(9.53674E-008), FRAC_CONST(2.38419E-007), FRAC_CONST(3.8147E-007), FRAC_CONST(4.48788E-007), FRAC_CONST(4.69501E-007), FRAC_CONST(4.74982E-007), FRAC_CONST(4.76372E-007), FRAC_CONST(4.76721E-007) }, + { FRAC_CONST(5.81935E-011), FRAC_CONST(2.32603E-010), FRAC_CONST(9.27699E-010), FRAC_CONST(3.66798E-009), FRAC_CONST(1.40246E-008), FRAC_CONST(4.76837E-008), FRAC_CONST(1.19209E-007), FRAC_CONST(1.90735E-007), FRAC_CONST(2.24394E-007), FRAC_CONST(2.34751E-007), FRAC_CONST(2.37491E-007), FRAC_CONST(2.38186E-007), FRAC_CONST(2.3836E-007) }, + { FRAC_CONST(2.90967E-011), FRAC_CONST(1.16302E-010), FRAC_CONST(4.63849E-010), FRAC_CONST(1.83399E-009), FRAC_CONST(7.01231E-009), FRAC_CONST(2.38419E-008), FRAC_CONST(5.96046E-008), FRAC_CONST(9.53674E-008), FRAC_CONST(1.12197E-007), FRAC_CONST(1.17375E-007), FRAC_CONST(1.18745E-007), FRAC_CONST(1.19093E-007), FRAC_CONST(1.1918E-007) } +}; + +static const real_t Q_div2_tab_right[31][13] = { + { FRAC_CONST(0.992246), FRAC_CONST(0.992241), FRAC_CONST(0.992218), FRAC_CONST(0.992128), FRAC_CONST(0.991768), FRAC_CONST(0.990329), FRAC_CONST(0.984615), FRAC_CONST(0.962406), FRAC_CONST(0.882759), FRAC_CONST(0.663212), FRAC_CONST(0.332468), FRAC_CONST(0.111015), FRAC_CONST(0.0302959) }, + { FRAC_CONST(0.984612), FRAC_CONST(0.984601), FRAC_CONST(0.984556), FRAC_CONST(0.984379), FRAC_CONST(0.98367), FRAC_CONST(0.980843), FRAC_CONST(0.969697), FRAC_CONST(0.927536), FRAC_CONST(0.790123), FRAC_CONST(0.496124), FRAC_CONST(0.199377), FRAC_CONST(0.0587695), FRAC_CONST(0.0153809) }, + { FRAC_CONST(0.96969), FRAC_CONST(0.969668), FRAC_CONST(0.969582), FRAC_CONST(0.969238), FRAC_CONST(0.967864), FRAC_CONST(0.962406), FRAC_CONST(0.941176), FRAC_CONST(0.864865), FRAC_CONST(0.653061), FRAC_CONST(0.329897), FRAC_CONST(0.110727), FRAC_CONST(0.0302744), FRAC_CONST(0.00775006) }, + { FRAC_CONST(0.941163), FRAC_CONST(0.941122), FRAC_CONST(0.94096), FRAC_CONST(0.940312), FRAC_CONST(0.937729), FRAC_CONST(0.927536), FRAC_CONST(0.888889), FRAC_CONST(0.761905), FRAC_CONST(0.484848), FRAC_CONST(0.197531), FRAC_CONST(0.0586081), FRAC_CONST(0.0153698), FRAC_CONST(0.0038901) }, + { FRAC_CONST(0.888865), FRAC_CONST(0.888792), FRAC_CONST(0.888503), FRAC_CONST(0.887348), FRAC_CONST(0.882759), FRAC_CONST(0.864865), FRAC_CONST(0.8), FRAC_CONST(0.615385), FRAC_CONST(0.32), FRAC_CONST(0.109589), FRAC_CONST(0.0301887), FRAC_CONST(0.00774443), FRAC_CONST(0.00194884) }, + { FRAC_CONST(0.799961), FRAC_CONST(0.799844), FRAC_CONST(0.799375), FRAC_CONST(0.797508), FRAC_CONST(0.790123), FRAC_CONST(0.761905), FRAC_CONST(0.666667), FRAC_CONST(0.444444), FRAC_CONST(0.190476), FRAC_CONST(0.057971), FRAC_CONST(0.0153257), FRAC_CONST(0.00388727), FRAC_CONST(0.000975372) }, + { FRAC_CONST(0.666612), FRAC_CONST(0.66645), FRAC_CONST(0.6658), FRAC_CONST(0.663212), FRAC_CONST(0.653061), FRAC_CONST(0.615385), FRAC_CONST(0.5), FRAC_CONST(0.285714), FRAC_CONST(0.105263), FRAC_CONST(0.0298507), FRAC_CONST(0.00772201), FRAC_CONST(0.00194742), FRAC_CONST(0.000487924) }, + { FRAC_CONST(0.499939), FRAC_CONST(0.499756), FRAC_CONST(0.499025), FRAC_CONST(0.496124), FRAC_CONST(0.484848), FRAC_CONST(0.444444), FRAC_CONST(0.333333), FRAC_CONST(0.166667), FRAC_CONST(0.0555556), FRAC_CONST(0.0151515), FRAC_CONST(0.00387597), FRAC_CONST(0.000974659), FRAC_CONST(0.000244021) }, + { FRAC_CONST(0.333279), FRAC_CONST(0.333116), FRAC_CONST(0.332468), FRAC_CONST(0.329897), FRAC_CONST(0.32), FRAC_CONST(0.285714), FRAC_CONST(0.2), FRAC_CONST(0.0909091), FRAC_CONST(0.0285714), FRAC_CONST(0.00763359), FRAC_CONST(0.00194175), FRAC_CONST(0.000487567), FRAC_CONST(0.000122026) }, + { FRAC_CONST(0.199961), FRAC_CONST(0.199844), FRAC_CONST(0.199377), FRAC_CONST(0.197531), FRAC_CONST(0.190476), FRAC_CONST(0.166667), FRAC_CONST(0.111111), FRAC_CONST(0.047619), FRAC_CONST(0.0144928), FRAC_CONST(0.00383142), FRAC_CONST(0.000971817), FRAC_CONST(0.000243843), FRAC_CONST(6.10165E-005) }, + { FRAC_CONST(0.111087), FRAC_CONST(0.111015), FRAC_CONST(0.110727), FRAC_CONST(0.109589), FRAC_CONST(0.105263), FRAC_CONST(0.0909091), FRAC_CONST(0.0588235), FRAC_CONST(0.0243902), FRAC_CONST(0.00729927), FRAC_CONST(0.00191939), FRAC_CONST(0.000486145), FRAC_CONST(0.000121936), FRAC_CONST(3.05092E-005) }, + { FRAC_CONST(0.05881), FRAC_CONST(0.0587695), FRAC_CONST(0.0586081), FRAC_CONST(0.057971), FRAC_CONST(0.0555556), FRAC_CONST(0.047619), FRAC_CONST(0.030303), FRAC_CONST(0.0123457), FRAC_CONST(0.003663), FRAC_CONST(0.000960615), FRAC_CONST(0.000243132), FRAC_CONST(6.09719E-005), FRAC_CONST(1.52548E-005) }, + { FRAC_CONST(0.0302959), FRAC_CONST(0.0302744), FRAC_CONST(0.0301887), FRAC_CONST(0.0298507), FRAC_CONST(0.0285714), FRAC_CONST(0.0243902), FRAC_CONST(0.0153846), FRAC_CONST(0.00621118), FRAC_CONST(0.00183486), FRAC_CONST(0.000480538), FRAC_CONST(0.000121581), FRAC_CONST(3.04869E-005), FRAC_CONST(7.62747E-006) }, + { FRAC_CONST(0.0153809), FRAC_CONST(0.0153698), FRAC_CONST(0.0153257), FRAC_CONST(0.0151515), FRAC_CONST(0.0144928), FRAC_CONST(0.0123457), FRAC_CONST(0.00775194), FRAC_CONST(0.00311526), FRAC_CONST(0.000918274), FRAC_CONST(0.000240327), FRAC_CONST(6.0794E-005), FRAC_CONST(1.52437E-005), FRAC_CONST(3.81375E-006) }, + { FRAC_CONST(0.00775006), FRAC_CONST(0.00774443), FRAC_CONST(0.00772201), FRAC_CONST(0.00763359), FRAC_CONST(0.00729927), FRAC_CONST(0.00621118), FRAC_CONST(0.00389105), FRAC_CONST(0.00156006), FRAC_CONST(0.000459348), FRAC_CONST(0.000120178), FRAC_CONST(3.03979E-005), FRAC_CONST(7.62189E-006), FRAC_CONST(1.90688E-006) }, + { FRAC_CONST(0.0038901), FRAC_CONST(0.00388727), FRAC_CONST(0.00387597), FRAC_CONST(0.00383142), FRAC_CONST(0.003663), FRAC_CONST(0.00311526), FRAC_CONST(0.00194932), FRAC_CONST(0.00078064), FRAC_CONST(0.000229727), FRAC_CONST(6.00925E-005), FRAC_CONST(1.51992E-005), FRAC_CONST(3.81096E-006), FRAC_CONST(9.53441E-007) }, + { FRAC_CONST(0.00194884), FRAC_CONST(0.00194742), FRAC_CONST(0.00194175), FRAC_CONST(0.00191939), FRAC_CONST(0.00183486), FRAC_CONST(0.00156006), FRAC_CONST(0.00097561), FRAC_CONST(0.000390472), FRAC_CONST(0.000114877), FRAC_CONST(3.00472E-005), FRAC_CONST(7.59965E-006), FRAC_CONST(1.90548E-006), FRAC_CONST(4.76721E-007) }, + { FRAC_CONST(0.000975372), FRAC_CONST(0.000974659), FRAC_CONST(0.000971817), FRAC_CONST(0.000960615), FRAC_CONST(0.000918274), FRAC_CONST(0.00078064), FRAC_CONST(0.000488043), FRAC_CONST(0.000195274), FRAC_CONST(5.74416E-005), FRAC_CONST(1.50238E-005), FRAC_CONST(3.79984E-006), FRAC_CONST(9.52743E-007), FRAC_CONST(2.3836E-007) }, + { FRAC_CONST(0.000487924), FRAC_CONST(0.000487567), FRAC_CONST(0.000486145), FRAC_CONST(0.000480538), FRAC_CONST(0.000459348), FRAC_CONST(0.000390472), FRAC_CONST(0.000244081), FRAC_CONST(9.76467E-005), FRAC_CONST(2.87216E-005), FRAC_CONST(7.51196E-006), FRAC_CONST(1.89992E-006), FRAC_CONST(4.76372E-007), FRAC_CONST(1.1918E-007) }, + { FRAC_CONST(0.000244021), FRAC_CONST(0.000243843), FRAC_CONST(0.000243132), FRAC_CONST(0.000240327), FRAC_CONST(0.000229727), FRAC_CONST(0.000195274), FRAC_CONST(0.000122055), FRAC_CONST(4.88257E-005), FRAC_CONST(1.4361E-005), FRAC_CONST(3.756E-006), FRAC_CONST(9.49963E-007), FRAC_CONST(2.38186E-007), FRAC_CONST(5.95901E-008) }, + { FRAC_CONST(0.000122026), FRAC_CONST(0.000121936), FRAC_CONST(0.000121581), FRAC_CONST(0.000120178), FRAC_CONST(0.000114877), FRAC_CONST(9.76467E-005), FRAC_CONST(6.10314E-005), FRAC_CONST(2.44135E-005), FRAC_CONST(7.18056E-006), FRAC_CONST(1.878E-006), FRAC_CONST(4.74982E-007), FRAC_CONST(1.19093E-007), FRAC_CONST(2.9795E-008) }, + { FRAC_CONST(6.10165E-005), FRAC_CONST(6.09719E-005), FRAC_CONST(6.0794E-005), FRAC_CONST(6.00925E-005), FRAC_CONST(5.74416E-005), FRAC_CONST(4.88257E-005), FRAC_CONST(3.05166E-005), FRAC_CONST(1.22069E-005), FRAC_CONST(3.59029E-006), FRAC_CONST(9.39002E-007), FRAC_CONST(2.37491E-007), FRAC_CONST(5.95465E-008), FRAC_CONST(1.48975E-008) }, + { FRAC_CONST(3.05092E-005), FRAC_CONST(3.04869E-005), FRAC_CONST(3.03979E-005), FRAC_CONST(3.00472E-005), FRAC_CONST(2.87216E-005), FRAC_CONST(2.44135E-005), FRAC_CONST(1.52586E-005), FRAC_CONST(6.10348E-006), FRAC_CONST(1.79515E-006), FRAC_CONST(4.69501E-007), FRAC_CONST(1.18745E-007), FRAC_CONST(2.97732E-008), FRAC_CONST(7.44876E-009) }, + { FRAC_CONST(1.52548E-005), FRAC_CONST(1.52437E-005), FRAC_CONST(1.51992E-005), FRAC_CONST(1.50238E-005), FRAC_CONST(1.4361E-005), FRAC_CONST(1.22069E-005), FRAC_CONST(7.62934E-006), FRAC_CONST(3.05175E-006), FRAC_CONST(8.97575E-007), FRAC_CONST(2.34751E-007), FRAC_CONST(5.93727E-008), FRAC_CONST(1.48866E-008), FRAC_CONST(3.72438E-009) }, + { FRAC_CONST(7.62747E-006), FRAC_CONST(7.62189E-006), FRAC_CONST(7.59965E-006), FRAC_CONST(7.51196E-006), FRAC_CONST(7.18056E-006), FRAC_CONST(6.10348E-006), FRAC_CONST(3.81468E-006), FRAC_CONST(1.52588E-006), FRAC_CONST(4.48788E-007), FRAC_CONST(1.17375E-007), FRAC_CONST(2.96864E-008), FRAC_CONST(7.44331E-009), FRAC_CONST(1.86219E-009) }, + { FRAC_CONST(3.81375E-006), FRAC_CONST(3.81096E-006), FRAC_CONST(3.79984E-006), FRAC_CONST(3.756E-006), FRAC_CONST(3.59029E-006), FRAC_CONST(3.05175E-006), FRAC_CONST(1.90734E-006), FRAC_CONST(7.62939E-007), FRAC_CONST(2.24394E-007), FRAC_CONST(5.86876E-008), FRAC_CONST(1.48432E-008), FRAC_CONST(3.72166E-009), FRAC_CONST(9.31095E-010) }, + { FRAC_CONST(1.90688E-006), FRAC_CONST(1.90548E-006), FRAC_CONST(1.89992E-006), FRAC_CONST(1.878E-006), FRAC_CONST(1.79515E-006), FRAC_CONST(1.52588E-006), FRAC_CONST(9.53673E-007), FRAC_CONST(3.8147E-007), FRAC_CONST(1.12197E-007), FRAC_CONST(2.93438E-008), FRAC_CONST(7.42159E-009), FRAC_CONST(1.86083E-009), FRAC_CONST(4.65548E-010) }, + { FRAC_CONST(9.53441E-007), FRAC_CONST(9.52743E-007), FRAC_CONST(9.49963E-007), FRAC_CONST(9.39002E-007), FRAC_CONST(8.97575E-007), FRAC_CONST(7.62939E-007), FRAC_CONST(4.76837E-007), FRAC_CONST(1.90735E-007), FRAC_CONST(5.60985E-008), FRAC_CONST(1.46719E-008), FRAC_CONST(3.71079E-009), FRAC_CONST(9.30414E-010), FRAC_CONST(2.32774E-010) }, + { FRAC_CONST(4.76721E-007), FRAC_CONST(4.76372E-007), FRAC_CONST(4.74982E-007), FRAC_CONST(4.69501E-007), FRAC_CONST(4.48788E-007), FRAC_CONST(3.8147E-007), FRAC_CONST(2.38419E-007), FRAC_CONST(9.53674E-008), FRAC_CONST(2.80492E-008), FRAC_CONST(7.33596E-009), FRAC_CONST(1.8554E-009), FRAC_CONST(4.65207E-010), FRAC_CONST(1.16387E-010) }, + { FRAC_CONST(2.3836E-007), FRAC_CONST(2.38186E-007), FRAC_CONST(2.37491E-007), FRAC_CONST(2.34751E-007), FRAC_CONST(2.24394E-007), FRAC_CONST(1.90735E-007), FRAC_CONST(1.19209E-007), FRAC_CONST(4.76837E-008), FRAC_CONST(1.40246E-008), FRAC_CONST(3.66798E-009), FRAC_CONST(9.27699E-010), FRAC_CONST(2.32603E-010), FRAC_CONST(5.81935E-011) }, + { FRAC_CONST(1.1918E-007), FRAC_CONST(1.19093E-007), FRAC_CONST(1.18745E-007), FRAC_CONST(1.17375E-007), FRAC_CONST(1.12197E-007), FRAC_CONST(9.53674E-008), FRAC_CONST(5.96046E-008), FRAC_CONST(2.38419E-008), FRAC_CONST(7.01231E-009), FRAC_CONST(1.83399E-009), FRAC_CONST(4.63849E-010), FRAC_CONST(1.16302E-010), FRAC_CONST(2.90967E-011) } +}; + +/* calculates Q/(1+Q) */ +/* [0..1] */ +static real_t calc_Q_div2(sbr_info *sbr, uint8_t ch, uint8_t m, uint8_t l) +{ + if (sbr->bs_coupling) + { + if ((sbr->Q[0][m][l] < 0 || sbr->Q[0][m][l] > 30) || + (sbr->Q[1][m][l] < 0 || sbr->Q[1][m][l] > 24 /* 2*panOffset(1) */)) + { + return 0; + } else { + /* the pan parameter is always even */ + if (ch == 0) + { + return Q_div2_tab_left[sbr->Q[0][m][l]][sbr->Q[1][m][l] >> 1]; + } else { + return Q_div2_tab_right[sbr->Q[0][m][l]][sbr->Q[1][m][l] >> 1]; + } + } + } else { + /* no coupling */ + if (sbr->Q[ch][m][l] < 0 || sbr->Q[ch][m][l] > 30) + { + return 0; + } else { + return Q_div2_tab[sbr->Q[ch][m][l]]; + } + } +} + +static const real_t E_deq_tab[64] = { + 64.0f, 128.0f, 256.0f, 512.0f, 1024.0f, 2048.0f, 4096.0f, 8192.0f, + 16384.0f, 32768.0f, 65536.0f, 131072.0f, 262144.0f, 524288.0f, 1.04858E+006f, 2.09715E+006f, + 4.1943E+006f, 8.38861E+006f, 1.67772E+007f, 3.35544E+007f, 6.71089E+007f, 1.34218E+008f, 2.68435E+008f, 5.36871E+008f, + 1.07374E+009f, 2.14748E+009f, 4.29497E+009f, 8.58993E+009f, 1.71799E+010f, 3.43597E+010f, 6.87195E+010f, 1.37439E+011f, + 2.74878E+011f, 5.49756E+011f, 1.09951E+012f, 2.19902E+012f, 4.39805E+012f, 8.79609E+012f, 1.75922E+013f, 3.51844E+013f, + 7.03687E+013f, 1.40737E+014f, 2.81475E+014f, 5.6295E+014f, 1.1259E+015f, 2.2518E+015f, 4.5036E+015f, 9.0072E+015f, + 1.80144E+016f, 3.60288E+016f, 7.20576E+016f, 1.44115E+017f, 2.8823E+017f, 5.76461E+017f, 1.15292E+018f, 2.30584E+018f, + 4.61169E+018f, 9.22337E+018f, 1.84467E+019f, 3.68935E+019f, 7.3787E+019f, 1.47574E+020f, 2.95148E+020f, 5.90296E+020f +}; + +void envelope_noise_dequantisation(sbr_info *sbr, uint8_t ch) +{ + if (sbr->bs_coupling == 0) + { + int16_t exp; + uint8_t l, k; + uint8_t amp = (sbr->amp_res[ch]) ? 0 : 1; + + for (l = 0; l < sbr->L_E[ch]; l++) + { + for (k = 0; k < sbr->n[sbr->f[ch][l]]; k++) + { + /* +6 for the *64 and -10 for the /32 in the synthesis QMF (fixed) + * since this is a energy value: (x/32)^2 = (x^2)/1024 + */ + /* exp = (sbr->E[ch][k][l] >> amp) + 6; */ + exp = (sbr->E[ch][k][l] >> amp); + + if ((exp < 0) || (exp >= 64)) + { + sbr->E_orig[ch][k][l] = 0; + } else { + sbr->E_orig[ch][k][l] = E_deq_tab[exp]; + + /* save half the table size at the cost of 1 multiply */ + if (amp && (sbr->E[ch][k][l] & 1)) + { + sbr->E_orig[ch][k][l] = MUL_C(sbr->E_orig[ch][k][l], COEF_CONST(1.414213562)); + } + } + } + } + + for (l = 0; l < sbr->L_Q[ch]; l++) + { + for (k = 0; k < sbr->N_Q; k++) + { + sbr->Q_div[ch][k][l] = calc_Q_div(sbr, ch, k, l); + sbr->Q_div2[ch][k][l] = calc_Q_div2(sbr, ch, k, l); + } + } + } +} + +static const real_t E_pan_tab[25] = { + FRAC_CONST(0.000244081), FRAC_CONST(0.000488043), + FRAC_CONST(0.00097561), FRAC_CONST(0.00194932), + FRAC_CONST(0.00389105), FRAC_CONST(0.00775194), + FRAC_CONST(0.0153846), FRAC_CONST(0.030303), + FRAC_CONST(0.0588235), FRAC_CONST(0.111111), + FRAC_CONST(0.2), FRAC_CONST(0.333333), + FRAC_CONST(0.5), FRAC_CONST(0.666667), + FRAC_CONST(0.8), FRAC_CONST(0.888889), + FRAC_CONST(0.941176), FRAC_CONST(0.969697), + FRAC_CONST(0.984615), FRAC_CONST(0.992248), + FRAC_CONST(0.996109), FRAC_CONST(0.998051), + FRAC_CONST(0.999024), FRAC_CONST(0.999512), + FRAC_CONST(0.999756) +}; + +void unmap_envelope_noise(sbr_info *sbr) +{ + real_t tmp; + int16_t exp0, exp1; + uint8_t l, k; + uint8_t amp0 = (sbr->amp_res[0]) ? 0 : 1; + uint8_t amp1 = (sbr->amp_res[1]) ? 0 : 1; + + for (l = 0; l < sbr->L_E[0]; l++) + { + for (k = 0; k < sbr->n[sbr->f[0][l]]; k++) + { + /* +6: * 64 ; +1: * 2 ; */ + exp0 = (sbr->E[0][k][l] >> amp0) + 1; + + /* UN_MAP removed: (x / 4096) same as (x >> 12) */ + /* E[1] is always even so no need for compensating the divide by 2 with + * an extra multiplication + */ + /* exp1 = (sbr->E[1][k][l] >> amp1) - 12; */ + exp1 = (sbr->E[1][k][l] >> amp1); + + if ((exp0 < 0) || (exp0 >= 64) || + (exp1 < 0) || (exp1 > 24)) + { + sbr->E_orig[1][k][l] = 0; + sbr->E_orig[0][k][l] = 0; + } else { + tmp = E_deq_tab[exp0]; + if (amp0 && (sbr->E[0][k][l] & 1)) + { + tmp = MUL_C(tmp, COEF_CONST(1.414213562)); + } + + /* panning */ + sbr->E_orig[0][k][l] = MUL_F(tmp, E_pan_tab[exp1]); + sbr->E_orig[1][k][l] = MUL_F(tmp, E_pan_tab[24 - exp1]); + } + } + } + + for (l = 0; l < sbr->L_Q[0]; l++) + { + for (k = 0; k < sbr->N_Q; k++) + { + sbr->Q_div[0][k][l] = calc_Q_div(sbr, 0, k, l); + sbr->Q_div[1][k][l] = calc_Q_div(sbr, 1, k, l); + sbr->Q_div2[0][k][l] = calc_Q_div2(sbr, 0, k, l); + sbr->Q_div2[1][k][l] = calc_Q_div2(sbr, 1, k, l); + } + } +} + +#endif + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_e_nf.h b/third_party/faad_hdc/upstream/libfaad/sbr_e_nf.h new file mode 100644 index 000000000..7be681407 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_e_nf.h @@ -0,0 +1,50 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_e_nf.h,v 1.18 2007/11/01 12:33:35 menno Exp $ +**/ + +#ifndef __SBR_E_NF_H__ +#define __SBR_E_NF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +void extract_envelope_data(sbr_info *sbr, uint8_t ch); +void extract_noise_floor_data(sbr_info *sbr, uint8_t ch); +#ifndef FIXED_POINT +void envelope_noise_dequantisation(sbr_info *sbr, uint8_t ch); +void unmap_envelope_noise(sbr_info *sbr); +#endif + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_fbt.c b/third_party/faad_hdc/upstream/libfaad/sbr_fbt.c new file mode 100644 index 000000000..c7ea64465 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_fbt.c @@ -0,0 +1,767 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_fbt.c,v 1.21 2007/11/01 12:33:35 menno Exp $ +**/ + +/* Calculate frequency band tables */ + +#include "common.h" +#include "structs.h" + +#ifdef SBR_DEC + +#include + +#include "sbr_syntax.h" +#include "sbr_fbt.h" + +/* static function declarations */ +static int32_t find_bands(uint8_t warp, uint8_t bands, uint8_t a0, uint8_t a1); + + +/* calculate the start QMF channel for the master frequency band table */ +/* parameter is also called k0 */ +uint8_t qmf_start_channel(uint8_t bs_start_freq, uint8_t bs_samplerate_mode, + uint32_t sample_rate) +{ + static const uint8_t startMinTable[12] = { 7, 7, 10, 11, 12, 16, 16, + 17, 24, 32, 35, 48 }; + static const uint8_t offsetIndexTable[12] = { 5, 5, 4, 4, 4, 3, 2, 1, 0, + 6, 6, 6 }; + static const int8_t offset[7][16] = { + { -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7 }, + { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13 }, + { -5, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16 }, + { -6, -4, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16 }, + { -4, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16, 20 }, + { -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16, 20, 24 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16, 20, 24, 28, 33 } + }; + uint8_t startMin = startMinTable[get_sr_index(sample_rate)]; + uint8_t offsetIndex = offsetIndexTable[get_sr_index(sample_rate)]; + +#if 0 /* replaced with table (startMinTable) */ + if (sample_rate >= 64000) + { + startMin = (uint8_t)((5000.*128.)/(float)sample_rate + 0.5); + } else if (sample_rate < 32000) { + startMin = (uint8_t)((3000.*128.)/(float)sample_rate + 0.5); + } else { + startMin = (uint8_t)((4000.*128.)/(float)sample_rate + 0.5); + } +#endif + + if (bs_samplerate_mode) + { + return startMin + offset[offsetIndex][bs_start_freq]; + +#if 0 /* replaced by offsetIndexTable */ + switch (sample_rate) + { + case 16000: + return startMin + offset[0][bs_start_freq]; + case 22050: + return startMin + offset[1][bs_start_freq]; + case 24000: + return startMin + offset[2][bs_start_freq]; + case 32000: + return startMin + offset[3][bs_start_freq]; + default: + if (sample_rate > 64000) + { + return startMin + offset[5][bs_start_freq]; + } else { /* 44100 <= sample_rate <= 64000 */ + return startMin + offset[4][bs_start_freq]; + } + } +#endif + } else { + return startMin + offset[6][bs_start_freq]; + } +} + +static int int32cmp(const void *a, const void *b) +{ + return ((int)(*(int32_t*)a - *(int32_t*)b)); +} + +static int uint8cmp(const void *a, const void *b) +{ + return ((int)(*(uint8_t*)a - *(uint8_t*)b)); +} + +/* calculate the stop QMF channel for the master frequency band table */ +/* parameter is also called k2 */ +uint8_t qmf_stop_channel(uint8_t bs_stop_freq, uint32_t sample_rate, + uint8_t k0) +{ + if (bs_stop_freq == 15) + { + return min(64, k0 * 3); + } else if (bs_stop_freq == 14) { + return min(64, k0 * 2); + } else { + static const uint8_t stopMinTable[12] = { 13, 15, 20, 21, 23, + 32, 32, 35, 48, 64, 70, 96 }; + static const int8_t offset[12][14] = { + { 0, 2, 4, 6, 8, 11, 14, 18, 22, 26, 31, 37, 44, 51 }, + { 0, 2, 4, 6, 8, 11, 14, 18, 22, 26, 31, 36, 42, 49 }, + { 0, 2, 4, 6, 8, 11, 14, 17, 21, 25, 29, 34, 39, 44 }, + { 0, 2, 4, 6, 8, 11, 14, 17, 20, 24, 28, 33, 38, 43 }, + { 0, 2, 4, 6, 8, 11, 14, 17, 20, 24, 28, 32, 36, 41 }, + { 0, 2, 4, 6, 8, 10, 12, 14, 17, 20, 23, 26, 29, 32 }, + { 0, 2, 4, 6, 8, 10, 12, 14, 17, 20, 23, 26, 29, 32 }, + { 0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 20, 23, 26, 29 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, -1, -2, -3, -4, -5, -6, -6, -6, -6, -6, -6, -6, -6 }, + { 0, -3, -6, -9, -12, -15, -18, -20, -22, -24, -26, -28, -30, -32 } + }; +#if 0 + uint8_t i; + int32_t stopDk[13], stopDk_t[14], k2; +#endif + uint8_t stopMin = stopMinTable[get_sr_index(sample_rate)]; + +#if 0 /* replaced by table lookup */ + if (sample_rate >= 64000) + { + stopMin = (uint8_t)((10000.*128.)/(float)sample_rate + 0.5); + } else if (sample_rate < 32000) { + stopMin = (uint8_t)((6000.*128.)/(float)sample_rate + 0.5); + } else { + stopMin = (uint8_t)((8000.*128.)/(float)sample_rate + 0.5); + } +#endif + +#if 0 /* replaced by table lookup */ + /* diverging power series */ + for (i = 0; i <= 13; i++) + { + stopDk_t[i] = (int32_t)(stopMin*pow(64.0/stopMin, i/13.0) + 0.5); + } + for (i = 0; i < 13; i++) + { + stopDk[i] = stopDk_t[i+1] - stopDk_t[i]; + } + + /* needed? */ + qsort(stopDk, 13, sizeof(stopDk[0]), int32cmp); + + k2 = stopMin; + for (i = 0; i < bs_stop_freq; i++) + { + k2 += stopDk[i]; + } + return min(64, k2); +#endif + /* bs_stop_freq <= 13 */ + return min(64, stopMin + offset[get_sr_index(sample_rate)][min(bs_stop_freq, 13)]); + } + + // return 0; +} + +/* calculate the master frequency table from k0, k2, bs_freq_scale + and bs_alter_scale + + version for bs_freq_scale = 0 +*/ +uint8_t master_frequency_table_fs0(sbr_info *sbr, uint8_t k0, uint8_t k2, + uint8_t bs_alter_scale) +{ + int8_t incr; + uint8_t k; + uint8_t dk; + int32_t nrBands, k2Achieved; + int32_t k2Diff, vDk[64] = {0}; + + /* mft only defined for k2 > k0 */ + if (k2 <= k0) + { + sbr->N_master = 0; + return 1; + } + + dk = bs_alter_scale ? 2 : 1; + +#if 0 /* replaced by float-less design */ + nrBands = 2 * (int32_t)((float)(k2-k0)/(dk*2) + (-1+dk)/2.0f); +#else + if (bs_alter_scale) + { + nrBands = (((k2-k0+2)>>2)<<1); + } else { + nrBands = (((k2-k0)>>1)<<1); + } +#endif + nrBands = min(nrBands, 63); + if (nrBands <= 0) + return 1; + + k2Achieved = k0 + nrBands * dk; + k2Diff = k2 - k2Achieved; + for (k = 0; k < nrBands; k++) + vDk[k] = dk; + + if (k2Diff) + { + incr = (k2Diff > 0) ? -1 : 1; + k = (uint8_t) ((k2Diff > 0) ? (nrBands-1) : 0); + + while (k2Diff != 0) + { + vDk[k] -= incr; + k += incr; + k2Diff += incr; + } + } + + sbr->f_master[0] = k0; + for (k = 1; k <= nrBands; k++) + sbr->f_master[k] = (uint8_t)(sbr->f_master[k-1] + vDk[k-1]); + + sbr->N_master = (uint8_t)nrBands; + sbr->N_master = (min(sbr->N_master, 64)); + +#if 0 + printf("f_master[%d]: ", nrBands); + for (k = 0; k <= nrBands; k++) + { + printf("%d ", sbr->f_master[k]); + } + printf("\n"); +#endif + + return 0; +} + +/* + This function finds the number of bands using this formula: + bands * log(a1/a0)/log(2.0) + 0.5 +*/ +static int32_t find_bands(uint8_t warp, uint8_t bands, uint8_t a0, uint8_t a1) +{ +#ifdef FIXED_POINT + /* table with log2() values */ + static const real_t log2Table[65] = { + COEF_CONST(0.0), COEF_CONST(0.0), COEF_CONST(1.0000000000), COEF_CONST(1.5849625007), + COEF_CONST(2.0000000000), COEF_CONST(2.3219280949), COEF_CONST(2.5849625007), COEF_CONST(2.8073549221), + COEF_CONST(3.0000000000), COEF_CONST(3.1699250014), COEF_CONST(3.3219280949), COEF_CONST(3.4594316186), + COEF_CONST(3.5849625007), COEF_CONST(3.7004397181), COEF_CONST(3.8073549221), COEF_CONST(3.9068905956), + COEF_CONST(4.0000000000), COEF_CONST(4.0874628413), COEF_CONST(4.1699250014), COEF_CONST(4.2479275134), + COEF_CONST(4.3219280949), COEF_CONST(4.3923174228), COEF_CONST(4.4594316186), COEF_CONST(4.5235619561), + COEF_CONST(4.5849625007), COEF_CONST(4.6438561898), COEF_CONST(4.7004397181), COEF_CONST(4.7548875022), + COEF_CONST(4.8073549221), COEF_CONST(4.8579809951), COEF_CONST(4.9068905956), COEF_CONST(4.9541963104), + COEF_CONST(5.0000000000), COEF_CONST(5.0443941194), COEF_CONST(5.0874628413), COEF_CONST(5.1292830169), + COEF_CONST(5.1699250014), COEF_CONST(5.2094533656), COEF_CONST(5.2479275134), COEF_CONST(5.2854022189), + COEF_CONST(5.3219280949), COEF_CONST(5.3575520046), COEF_CONST(5.3923174228), COEF_CONST(5.4262647547), + COEF_CONST(5.4594316186), COEF_CONST(5.4918530963), COEF_CONST(5.5235619561), COEF_CONST(5.5545888517), + COEF_CONST(5.5849625007), COEF_CONST(5.6147098441), COEF_CONST(5.6438561898), COEF_CONST(5.6724253420), + COEF_CONST(5.7004397181), COEF_CONST(5.7279204546), COEF_CONST(5.7548875022), COEF_CONST(5.7813597135), + COEF_CONST(5.8073549221), COEF_CONST(5.8328900142), COEF_CONST(5.8579809951), COEF_CONST(5.8826430494), + COEF_CONST(5.9068905956), COEF_CONST(5.9307373376), COEF_CONST(5.9541963104), COEF_CONST(5.9772799235), + COEF_CONST(6.0) + }; + real_t r0 = log2Table[a0]; /* coef */ + real_t r1 = log2Table[a1]; /* coef */ + real_t r2 = (r1 - r0); /* coef */ + + if (warp) + r2 = MUL_C(r2, COEF_CONST(1.0/1.3)); + + /* convert r2 to real and then multiply and round */ + r2 = (r2 >> (COEF_BITS-REAL_BITS)) * bands + (1<<(REAL_BITS-1)); + + return (r2 >> REAL_BITS); +#else + real_t div = (real_t)log(2.0); + if (warp) div *= (real_t)1.3; + + return (int32_t)(bands * log((float)a1/(float)a0)/div + 0.5); +#endif +} + +static real_t find_initial_power(uint8_t bands, uint8_t a0, uint8_t a1) +{ +#ifdef FIXED_POINT + /* table with log() values */ + static const real_t logTable[65] = { + COEF_CONST(0.0), COEF_CONST(0.0), COEF_CONST(0.6931471806), COEF_CONST(1.0986122887), + COEF_CONST(1.3862943611), COEF_CONST(1.6094379124), COEF_CONST(1.7917594692), COEF_CONST(1.9459101491), + COEF_CONST(2.0794415417), COEF_CONST(2.1972245773), COEF_CONST(2.3025850930), COEF_CONST(2.3978952728), + COEF_CONST(2.4849066498), COEF_CONST(2.5649493575), COEF_CONST(2.6390573296), COEF_CONST(2.7080502011), + COEF_CONST(2.7725887222), COEF_CONST(2.8332133441), COEF_CONST(2.8903717579), COEF_CONST(2.9444389792), + COEF_CONST(2.9957322736), COEF_CONST(3.0445224377), COEF_CONST(3.0910424534), COEF_CONST(3.1354942159), + COEF_CONST(3.1780538303), COEF_CONST(3.2188758249), COEF_CONST(3.2580965380), COEF_CONST(3.2958368660), + COEF_CONST(3.3322045102), COEF_CONST(3.3672958300), COEF_CONST(3.4011973817), COEF_CONST(3.4339872045), + COEF_CONST(3.4657359028), COEF_CONST(3.4965075615), COEF_CONST(3.5263605246), COEF_CONST(3.5553480615), + COEF_CONST(3.5835189385), COEF_CONST(3.6109179126), COEF_CONST(3.6375861597), COEF_CONST(3.6635616461), + COEF_CONST(3.6888794541), COEF_CONST(3.7135720667), COEF_CONST(3.7376696183), COEF_CONST(3.7612001157), + COEF_CONST(3.7841896339), COEF_CONST(3.8066624898), COEF_CONST(3.8286413965), COEF_CONST(3.8501476017), + COEF_CONST(3.8712010109), COEF_CONST(3.8918202981), COEF_CONST(3.9120230054), COEF_CONST(3.9318256327), + COEF_CONST(3.9512437186), COEF_CONST(3.9702919136), COEF_CONST(3.9889840466), COEF_CONST(4.0073331852), + COEF_CONST(4.0253516907), COEF_CONST(4.0430512678), COEF_CONST(4.0604430105), COEF_CONST(4.0775374439), + COEF_CONST(4.0943445622), COEF_CONST(4.1108738642), COEF_CONST(4.1271343850), COEF_CONST(4.1431347264), + COEF_CONST(4.158883083) + }; + /* standard Taylor polynomial coefficients for exp(x) around 0 */ + /* a polynomial around x=1 is more precise, as most values are around 1.07, + but this is just fine already */ + static const real_t c1 = COEF_CONST(1.0); + static const real_t c2 = COEF_CONST(1.0/2.0); + static const real_t c3 = COEF_CONST(1.0/6.0); + static const real_t c4 = COEF_CONST(1.0/24.0); + + real_t r0 = logTable[a0]; /* coef */ + real_t r1 = logTable[a1]; /* coef */ + real_t r2 = (r1 - r0) / bands; /* coef */ + real_t rexp = c1 + MUL_C((c1 + MUL_C((c2 + MUL_C((c3 + MUL_C(c4,r2)), r2)), r2)), r2); + + return (rexp >> (COEF_BITS-REAL_BITS)); /* real */ +#else + return (real_t)pow((real_t)a1/(real_t)a0, 1.0/(real_t)bands); +#endif +} + +/* + version for bs_freq_scale > 0 +*/ +uint8_t master_frequency_table(sbr_info *sbr, uint8_t k0, uint8_t k2, + uint8_t bs_freq_scale, uint8_t bs_alter_scale) +{ + uint8_t k, bands, twoRegions; + uint8_t k1; + uint8_t nrBand0, nrBand1; + int32_t vDk0[64] = {0}, vDk1[64] = {0}; + int32_t vk0[64] = {0}, vk1[64] = {0}; + uint8_t temp1[] = { 6, 5, 4 }; + real_t q, qk; + int32_t A_1; +#ifdef FIXED_POINT + real_t rk2, rk0; +#endif + (void)bs_alter_scale; /* TODO: remove parameter? */ + + /* mft only defined for k2 > k0 */ + if (k2 <= k0) + { + sbr->N_master = 0; + return 1; + } + + bands = temp1[bs_freq_scale-1]; + +#ifdef FIXED_POINT + rk0 = (real_t)k0 << REAL_BITS; + rk2 = (real_t)k2 << REAL_BITS; + if (rk2 > MUL_C(rk0, COEF_CONST(2.2449))) +#else + if ((float)k2/(float)k0 > 2.2449) +#endif + { + twoRegions = 1; + k1 = k0 << 1; + } else { + twoRegions = 0; + k1 = k2; + } + + nrBand0 = (uint8_t)(2 * find_bands(0, bands, k0, k1)); + nrBand0 = min(nrBand0, 63); + if (nrBand0 <= 0) + return 1; + + q = find_initial_power(nrBand0, k0, k1); +#ifdef FIXED_POINT + qk = (real_t)k0 << REAL_BITS; + //A_1 = (int32_t)((qk + REAL_CONST(0.5)) >> REAL_BITS); + A_1 = k0; +#else + qk = REAL_CONST(k0); + A_1 = (int32_t)(qk + .5); +#endif + for (k = 0; k <= nrBand0; k++) + { + int32_t A_0 = A_1; +#ifdef FIXED_POINT + qk = MUL_R(qk,q); + A_1 = (int32_t)((qk + REAL_CONST(0.5)) >> REAL_BITS); +#else + qk *= q; + A_1 = (int32_t)(qk + 0.5); +#endif + vDk0[k] = A_1 - A_0; + } + + /* needed? */ + qsort(vDk0, nrBand0, sizeof(vDk0[0]), int32cmp); + + vk0[0] = k0; + for (k = 1; k <= nrBand0; k++) + { + vk0[k] = vk0[k-1] + vDk0[k-1]; + if (vDk0[k-1] == 0) + return 1; + } + + if (!twoRegions) + { + for (k = 0; k <= nrBand0; k++) + sbr->f_master[k] = (uint8_t) vk0[k]; + + sbr->N_master = nrBand0; + sbr->N_master = min(sbr->N_master, 64); + return 0; + } + + nrBand1 = (uint8_t)(2 * find_bands(1 /* warped */, bands, k1, k2)); + nrBand1 = min(nrBand1, 63); + + q = find_initial_power(nrBand1, k1, k2); +#ifdef FIXED_POINT + qk = (real_t)k1 << REAL_BITS; + //A_1 = (int32_t)((qk + REAL_CONST(0.5)) >> REAL_BITS); + A_1 = k1; +#else + qk = REAL_CONST(k1); + A_1 = (int32_t)(qk + .5); +#endif + for (k = 0; k <= nrBand1 - 1; k++) + { + int32_t A_0 = A_1; +#ifdef FIXED_POINT + qk = MUL_R(qk,q); + A_1 = (int32_t)((qk + REAL_CONST(0.5)) >> REAL_BITS); +#else + qk *= q; + A_1 = (int32_t)(qk + 0.5); +#endif + vDk1[k] = A_1 - A_0; + } + + if (vDk1[0] < vDk0[nrBand0 - 1]) + { + int32_t change; + + /* needed? */ + qsort(vDk1, nrBand1 + 1, sizeof(vDk1[0]), int32cmp); + change = vDk0[nrBand0 - 1] - vDk1[0]; + vDk1[0] = vDk0[nrBand0 - 1]; + vDk1[nrBand1 - 1] = vDk1[nrBand1 - 1] - change; + } + + /* needed? */ + qsort(vDk1, nrBand1, sizeof(vDk1[0]), int32cmp); + vk1[0] = k1; + for (k = 1; k <= nrBand1; k++) + { + vk1[k] = vk1[k-1] + vDk1[k-1]; + if (vDk1[k-1] == 0) + return 1; + } + + sbr->N_master = nrBand0 + nrBand1; + sbr->N_master = min(sbr->N_master, 64); + for (k = 0; k <= nrBand0; k++) + { + sbr->f_master[k] = (uint8_t) vk0[k]; + } + for (k = nrBand0 + 1; k <= sbr->N_master; k++) + { + sbr->f_master[k] = (uint8_t) vk1[k - nrBand0]; + } + +#if 0 + printf("f_master[%d]: ", sbr->N_master); + for (k = 0; k <= sbr->N_master; k++) + { + printf("%d ", sbr->f_master[k]); + } + printf("\n"); +#endif + + return 0; +} + +/* calculate the derived frequency border tables from f_master */ +uint8_t derived_frequency_table(sbr_info *sbr, uint8_t bs_xover_band, + uint8_t k2) +{ + uint8_t k, i; + uint32_t minus; + + /* The following relation shall be satisfied: bs_xover_band < N_Master */ + if (sbr->N_master <= bs_xover_band) + return 1; + + sbr->N_high = sbr->N_master - bs_xover_band; + sbr->N_low = (sbr->N_high>>1) + (sbr->N_high - ((sbr->N_high>>1)<<1)); + + sbr->n[0] = sbr->N_low; + sbr->n[1] = sbr->N_high; + + for (k = 0; k <= sbr->N_high; k++) + { + sbr->f_table_res[HI_RES][k] = sbr->f_master[k + bs_xover_band]; + } + + sbr->M = sbr->f_table_res[HI_RES][sbr->N_high] - sbr->f_table_res[HI_RES][0]; + if (sbr->M > MAX_M) + return 1; + sbr->kx = sbr->f_table_res[HI_RES][0]; + if (sbr->kx > 32) + return 1; + if (sbr->kx + sbr->M > 64) + return 1; + + minus = (sbr->N_high & 1) ? 1 : 0; + + i = 0; + for (k = 0; k <= sbr->N_low; k++) + { + if (k != 0) + i = (uint8_t)(2*k - minus); + sbr->f_table_res[LO_RES][k] = sbr->f_table_res[HI_RES][i]; + } + +#if 0 + printf("bs_freq_scale: %d\n", sbr->bs_freq_scale); + printf("bs_limiter_bands: %d\n", sbr->bs_limiter_bands); + printf("f_table_res[HI_RES][%d]: ", sbr->N_high); + for (k = 0; k <= sbr->N_high; k++) + { + printf("%d ", sbr->f_table_res[HI_RES][k]); + } + printf("\n"); +#endif +#if 0 + printf("f_table_res[LO_RES][%d]: ", sbr->N_low); + for (k = 0; k <= sbr->N_low; k++) + { + printf("%d ", sbr->f_table_res[LO_RES][k]); + } + printf("\n"); +#endif + + sbr->N_Q = 0; + if (sbr->bs_noise_bands == 0) + { + sbr->N_Q = 1; + } else { +#if 0 + sbr->N_Q = max(1, (int32_t)(sbr->bs_noise_bands*(log(k2/(float)sbr->kx)/log(2.0)) + 0.5)); +#else + sbr->N_Q = (uint8_t)(max(1, find_bands(0, sbr->bs_noise_bands, sbr->kx, k2))); +#endif + sbr->N_Q = min(5, sbr->N_Q); + } + + i = 0; + for (k = 0; k <= sbr->N_Q; k++) + { + if (k != 0) + i = i + (sbr->N_low - i)/(sbr->N_Q + 1 - k); + sbr->f_table_noise[k] = sbr->f_table_res[LO_RES][i]; + } + + /* build table for mapping k to g in hf patching */ + for (k = 0; k < 64; k++) + { + uint8_t g; + for (g = 0; g < sbr->N_Q; g++) + { + if ((sbr->f_table_noise[g] <= k) && + (k < sbr->f_table_noise[g+1])) + { + sbr->table_map_k_to_g[k] = g; + break; + } + } + } + +#if 0 + printf("f_table_noise[%d]: ", sbr->N_Q); + for (k = 0; k <= sbr->N_Q; k++) + { + printf("%d ", sbr->f_table_noise[k] - sbr->kx); + } + printf("\n"); +#endif + + return 0; +} + +/* TODO: blegh, ugly */ +/* Modified to calculate for all possible bs_limiter_bands always + * This reduces the number calls to this functions needed (now only on + * header reset) + */ +void limiter_frequency_table(sbr_info *sbr) +{ +#if 0 + static const real_t limiterBandsPerOctave[] = { REAL_CONST(1.2), + REAL_CONST(2), REAL_CONST(3) }; +#else + static const real_t limiterBandsCompare[] = { REAL_CONST(1.327152), + REAL_CONST(1.185093), REAL_CONST(1.119872) }; +#endif + uint8_t k, s; + int8_t nrLim; +#if 0 + real_t limBands; +#endif + + sbr->f_table_lim[0][0] = sbr->f_table_res[LO_RES][0] - sbr->kx; + sbr->f_table_lim[0][1] = sbr->f_table_res[LO_RES][sbr->N_low] - sbr->kx; + sbr->N_L[0] = 1; + +#if 0 + printf("f_table_lim[%d][%d]: ", 0, sbr->N_L[0]); + for (k = 0; k <= sbr->N_L[0]; k++) + { + printf("%d ", sbr->f_table_lim[0][k]); + } + printf("\n"); +#endif + + for (s = 1; s < 4; s++) + { + uint8_t limTable[100 /*TODO*/] = {0}; + uint8_t patchBorders[64/*??*/] = {0}; + +#if 0 + limBands = limiterBandsPerOctave[s - 1]; +#endif + + patchBorders[0] = sbr->kx; + for (k = 1; k <= sbr->noPatches; k++) + { + patchBorders[k] = patchBorders[k-1] + sbr->patchNoSubbands[k-1]; + } + + for (k = 0; k <= sbr->N_low; k++) + { + limTable[k] = sbr->f_table_res[LO_RES][k]; + } + for (k = 1; k < sbr->noPatches; k++) + { + limTable[k+sbr->N_low] = patchBorders[k]; + } + + /* needed */ + qsort(limTable, sbr->noPatches + sbr->N_low, sizeof(limTable[0]), uint8cmp); + k = 1; + nrLim = sbr->noPatches + sbr->N_low - 1; + + if (nrLim < 0) // TODO: BIG FAT PROBLEM + return; + +restart: + if (k <= nrLim) + { + real_t nOctaves; + + if (limTable[k-1] != 0) +#if 0 + nOctaves = REAL_CONST(log((float)limTable[k]/(float)limTable[k-1])/log(2.0)); +#else +#ifdef FIXED_POINT + nOctaves = DIV_R((limTable[k]<noPatches; i++) + { + if (limTable[k] == patchBorders[i]) + found = 1; + } + if (found) + { + found2 = 0; + for (i = 0; i <= sbr->noPatches; i++) + { + if (limTable[k-1] == patchBorders[i]) + found2 = 1; + } + if (found2) + { + k++; + goto restart; + } else { + /* remove (k-1)th element */ + limTable[k-1] = sbr->f_table_res[LO_RES][sbr->N_low]; + qsort(limTable, sbr->noPatches + sbr->N_low, sizeof(limTable[0]), uint8cmp); + nrLim--; + goto restart; + } + } + } + /* remove kth element */ + limTable[k] = sbr->f_table_res[LO_RES][sbr->N_low]; + qsort(limTable, nrLim, sizeof(limTable[0]), uint8cmp); + nrLim--; + goto restart; + } else { + k++; + goto restart; + } + } + + sbr->N_L[s] = nrLim; + for (k = 0; k <= nrLim; k++) + { + sbr->f_table_lim[s][k] = limTable[k] - sbr->kx; + } + +#if 0 + printf("f_table_lim[%d][%d]: ", s, sbr->N_L[s]); + for (k = 0; k <= sbr->N_L[s]; k++) + { + printf("%d ", sbr->f_table_lim[s][k]); + } + printf("\n"); +#endif + } +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_fbt.h b/third_party/faad_hdc/upstream/libfaad/sbr_fbt.h new file mode 100644 index 000000000..259422546 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_fbt.h @@ -0,0 +1,55 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_fbt.h,v 1.18 2007/11/01 12:33:35 menno Exp $ +**/ + +#ifndef __SBR_FBT_H__ +#define __SBR_FBT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t qmf_start_channel(uint8_t bs_start_freq, uint8_t bs_samplerate_mode, + uint32_t sample_rate); +uint8_t qmf_stop_channel(uint8_t bs_stop_freq, uint32_t sample_rate, + uint8_t k0); +uint8_t master_frequency_table_fs0(sbr_info *sbr, uint8_t k0, uint8_t k2, + uint8_t bs_alter_scale); +uint8_t master_frequency_table(sbr_info *sbr, uint8_t k0, uint8_t k2, + uint8_t bs_freq_scale, uint8_t bs_alter_scale); +uint8_t derived_frequency_table(sbr_info *sbr, uint8_t bs_xover_band, + uint8_t k2); +void limiter_frequency_table(sbr_info *sbr); + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_hfadj.c b/third_party/faad_hdc/upstream/libfaad/sbr_hfadj.c new file mode 100644 index 000000000..2fe69115a --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_hfadj.c @@ -0,0 +1,1742 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_hfadj.c,v 1.23 2008/09/19 22:50:20 menno Exp $ +**/ + +/* High Frequency adjustment */ +#include + +#include "common.h" +#include "structs.h" + +#ifdef SBR_DEC + +#include "sbr_syntax.h" +#include "sbr_hfadj.h" + +#include "sbr_noise.h" + + +/* static function declarations */ +static uint8_t estimate_current_envelope(sbr_info *sbr, sbr_hfadj_info *adj, + qmf_t Xsbr[MAX_NTSRHFG][64], uint8_t ch); +static void calculate_gain(sbr_info *sbr, sbr_hfadj_info *adj, uint8_t ch); +#ifdef SBR_LOW_POWER +static void calc_gain_groups(sbr_info *sbr, sbr_hfadj_info *adj, real_t *deg, uint8_t ch); +static void aliasing_reduction(sbr_info *sbr, sbr_hfadj_info *adj, real_t *deg, uint8_t ch); +#endif +static void hf_assembly(sbr_info *sbr, sbr_hfadj_info *adj, qmf_t Xsbr[MAX_NTSRHFG][64], uint8_t ch); + + +uint8_t hf_adjustment(sbr_info *sbr, qmf_t Xsbr[MAX_NTSRHFG][64] +#ifdef SBR_LOW_POWER + ,real_t *deg /* aliasing degree */ +#endif + ,uint8_t ch) +{ + ALIGN sbr_hfadj_info adj = {{{0}}}; + uint8_t ret = 0; + + if (sbr->bs_frame_class[ch] == FIXFIX) + { + sbr->l_A[ch] = -1; + } else if (sbr->bs_frame_class[ch] == VARFIX) { + if (sbr->bs_pointer[ch] > 1) + sbr->l_A[ch] = sbr->bs_pointer[ch] - 1; + else + sbr->l_A[ch] = -1; + } else { + if (sbr->bs_pointer[ch] == 0) + sbr->l_A[ch] = -1; + else + sbr->l_A[ch] = sbr->L_E[ch] + 1 - sbr->bs_pointer[ch]; + } + + ret = estimate_current_envelope(sbr, &adj, Xsbr, ch); + if (ret > 0) + return 1; + + calculate_gain(sbr, &adj, ch); + +#ifdef SBR_LOW_POWER + calc_gain_groups(sbr, &adj, deg, ch); + aliasing_reduction(sbr, &adj, deg, ch); +#endif + + hf_assembly(sbr, &adj, Xsbr, ch); + + return 0; +} + +static uint8_t get_S_mapped(sbr_info *sbr, uint8_t ch, uint8_t l, uint8_t current_band) +{ + if (sbr->f[ch][l] == HI_RES) + { + /* in case of using f_table_high we just have 1 to 1 mapping + * from bs_add_harmonic[l][k] + */ + if ((l >= sbr->l_A[ch]) || + (sbr->bs_add_harmonic_prev[ch][current_band] && sbr->bs_add_harmonic_flag_prev[ch])) + { + return sbr->bs_add_harmonic[ch][current_band]; + } + } else { + uint8_t b, lb, ub; + + /* in case of f_table_low we check if any of the HI_RES bands + * within this LO_RES band has bs_add_harmonic[l][k] turned on + * (note that borders in the LO_RES table are also present in + * the HI_RES table) + */ + + /* find first HI_RES band in current LO_RES band */ + lb = 2*current_band - ((sbr->N_high & 1) ? 1 : 0); + /* find first HI_RES band in next LO_RES band */ + ub = 2*(current_band+1) - ((sbr->N_high & 1) ? 1 : 0); + + /* check all HI_RES bands in current LO_RES band for sinusoid */ + for (b = lb; b < ub; b++) + { + if ((l >= sbr->l_A[ch]) || + (sbr->bs_add_harmonic_prev[ch][b] && sbr->bs_add_harmonic_flag_prev[ch])) + { + if (sbr->bs_add_harmonic[ch][b] == 1) + return 1; + } + } + } + + return 0; +} + +static uint8_t estimate_current_envelope(sbr_info *sbr, sbr_hfadj_info *adj, + qmf_t Xsbr[MAX_NTSRHFG][64], uint8_t ch) +{ + uint8_t m, l, j, k, k_l, k_h, p; + real_t nrg, div; + (void)adj; /* TODO: remove parameter? */ +#ifdef FIXED_POINT + const real_t half = REAL_CONST(0.5); + real_t limit; + real_t mul; +#else + const real_t half = 0; /* Compiler is smart enough to eliminate +0 op. */ + const real_t limit = FLT_MAX; +#endif + + if (sbr->bs_interpol_freq == 1) + { + for (l = 0; l < sbr->L_E[ch]; l++) + { + uint8_t i, l_i, u_i; + + l_i = sbr->t_E[ch][l]; + u_i = sbr->t_E[ch][l+1]; + + div = (real_t)(u_i - l_i); + + if (div <= 0) + div = 1; +#ifdef FIXED_POINT + limit = div << (30 - (COEF_BITS - REAL_BITS)); + mul = (1 << (COEF_BITS - REAL_BITS)) / div; +#endif + + for (m = 0; m < sbr->M; m++) + { + nrg = 0; + + for (i = l_i + sbr->tHFAdj; i < u_i + sbr->tHFAdj; i++) + { + real_t re = QMF_RE(Xsbr[i][m + sbr->kx]) + half; + real_t im = QMF_IM(Xsbr[i][m + sbr->kx]) + half; + (void)im; + /* Actually, that should be MUL_R. On floating-point build + that is the same. On fixed point-build we use it to + pre-scale result (to aviod overflow). That, of course + causes some precision loss. */ + nrg += MUL_C(re, re) +#ifndef SBR_LOW_POWER + + MUL_C(im, im) +#endif + ; + } + + if (nrg < -limit || nrg > limit) + return 1; +#ifdef FIXED_POINT + sbr->E_curr[ch][m][l] = nrg * mul; +#else + sbr->E_curr[ch][m][l] = nrg / div; +#endif +#ifdef SBR_LOW_POWER +#ifdef FIXED_POINT + sbr->E_curr[ch][m][l] <<= 1; +#else + sbr->E_curr[ch][m][l] *= 2; +#endif +#endif + } + } + } else { + for (l = 0; l < sbr->L_E[ch]; l++) + { + for (p = 0; p < sbr->n[sbr->f[ch][l]]; p++) + { + k_l = sbr->f_table_res[sbr->f[ch][l]][p]; + k_h = sbr->f_table_res[sbr->f[ch][l]][p+1]; + + for (k = k_l; k < k_h; k++) + { + uint8_t i, l_i, u_i; + nrg = 0; + + l_i = sbr->t_E[ch][l]; + u_i = sbr->t_E[ch][l+1]; + + div = (real_t)((u_i - l_i)*(k_h - k_l)); + + if (div <= 0) + div = 1; +#ifdef FIXED_POINT + limit = div << (30 - (COEF_BITS - REAL_BITS)); + mul = (1 << (COEF_BITS - REAL_BITS)) / div; +#endif + + for (i = l_i + sbr->tHFAdj; i < u_i + sbr->tHFAdj; i++) + { + for (j = k_l; j < k_h; j++) + { + real_t re = QMF_RE(Xsbr[i][j]) + half; + real_t im = QMF_IM(Xsbr[i][j]) + half; + (void)im; + /* Actually, that should be MUL_R. On floating-point build + that is the same. On fixed point-build we use it to + pre-scale result (to aviod overflow). That, of course + causes some precision loss. */ + nrg += MUL_C(re, re) +#ifndef SBR_LOW_POWER + + MUL_C(im, im) +#endif + ; + } + } + + if (nrg < -limit || nrg > limit) + return 1; +#ifdef FIXED_POINT + sbr->E_curr[ch][k - sbr->kx][l] = nrg * mul; +#else + sbr->E_curr[ch][k - sbr->kx][l] = nrg / div; +#endif +#ifdef SBR_LOW_POWER +#ifdef FIXED_POINT + sbr->E_curr[ch][k - sbr->kx][l] <<= 1; +#else + sbr->E_curr[ch][k - sbr->kx][l] *= 2; +#endif +#endif + } + } + } + } + + return 0; +} + +#ifdef FIXED_POINT +#define EPS (1) /* smallest number available in fixed point */ +#else +#define EPS (1e-12) +#endif + + + +#ifdef FIXED_POINT + +/* log2 values of [0..63] */ +static const real_t log2_int_tab[] = { + LOG2_MIN_INF, REAL_CONST(0.000000000000000), REAL_CONST(1.000000000000000), REAL_CONST(1.584962500721156), + REAL_CONST(2.000000000000000), REAL_CONST(2.321928094887362), REAL_CONST(2.584962500721156), REAL_CONST(2.807354922057604), + REAL_CONST(3.000000000000000), REAL_CONST(3.169925001442313), REAL_CONST(3.321928094887363), REAL_CONST(3.459431618637297), + REAL_CONST(3.584962500721156), REAL_CONST(3.700439718141092), REAL_CONST(3.807354922057604), REAL_CONST(3.906890595608519), + REAL_CONST(4.000000000000000), REAL_CONST(4.087462841250339), REAL_CONST(4.169925001442312), REAL_CONST(4.247927513443585), + REAL_CONST(4.321928094887362), REAL_CONST(4.392317422778761), REAL_CONST(4.459431618637297), REAL_CONST(4.523561956057013), + REAL_CONST(4.584962500721156), REAL_CONST(4.643856189774724), REAL_CONST(4.700439718141093), REAL_CONST(4.754887502163468), + REAL_CONST(4.807354922057604), REAL_CONST(4.857980995127572), REAL_CONST(4.906890595608519), REAL_CONST(4.954196310386875), + REAL_CONST(5.000000000000000), REAL_CONST(5.044394119358453), REAL_CONST(5.087462841250340), REAL_CONST(5.129283016944966), + REAL_CONST(5.169925001442312), REAL_CONST(5.209453365628949), REAL_CONST(5.247927513443585), REAL_CONST(5.285402218862248), + REAL_CONST(5.321928094887363), REAL_CONST(5.357552004618084), REAL_CONST(5.392317422778761), REAL_CONST(5.426264754702098), + REAL_CONST(5.459431618637297), REAL_CONST(5.491853096329675), REAL_CONST(5.523561956057013), REAL_CONST(5.554588851677637), + REAL_CONST(5.584962500721156), REAL_CONST(5.614709844115208), REAL_CONST(5.643856189774724), REAL_CONST(5.672425341971495), + REAL_CONST(5.700439718141093), REAL_CONST(5.727920454563200), REAL_CONST(5.754887502163469), REAL_CONST(5.781359713524660), + REAL_CONST(5.807354922057605), REAL_CONST(5.832890014164742), REAL_CONST(5.857980995127572), REAL_CONST(5.882643049361842), + REAL_CONST(5.906890595608518), REAL_CONST(5.930737337562887), REAL_CONST(5.954196310386876), REAL_CONST(5.977279923499916) +}; + +// pan_log2_tab[X] = log2(2**X + 1) - X +static const real_t pan_log2_tab[13] = { + REAL_CONST(1.000000000000000), REAL_CONST(0.584962500721156), REAL_CONST(0.321928094887362), REAL_CONST(0.169925001442312), REAL_CONST(0.087462841250339), + REAL_CONST(0.044394119358453), REAL_CONST(0.022367813028455), REAL_CONST(0.011227255423254), REAL_CONST(0.005624549193878), REAL_CONST(0.002815015607054), + REAL_CONST(0.001408194392808), REAL_CONST(0.000704269011247), REAL_CONST(0.000352177480301) +}; + +static real_t find_log2_E(sbr_info *sbr, uint8_t k, uint8_t l, uint8_t ch) +{ + /* check for coupled energy/noise data */ + if (sbr->bs_coupling == 1) + { + int16_t e = sbr->E[0][k][l]; + int16_t E = sbr->E[1][k][l]; + uint8_t amp0 = (sbr->amp_res[0]) ? 0 : 1; + uint8_t amp1 = (sbr->amp_res[1]) ? 0 : 1; + real_t tmp, pan; + + /* E[1] should always be even so shifting is OK */ + E >>= amp1; + if (e < 0 || e >= 64 || E < 0 || E > 24) + return LOG2_MIN_INF; + E -= 12; + + if (ch != 0) // L/R anti-symmetry + E = -E; + + if (E >= 0) + { + /* negative */ + pan = pan_log2_tab[E]; + } else { + /* positive */ + pan = pan_log2_tab[-E] + ((-E)<E[ch][k][l]; + uint8_t amp = (sbr->amp_res[ch]) ? 0 : 1; + if (e < 0 || (e >> amp) >= 64) + return LOG2_MIN_INF; + return 6 * REAL_PRECISION + e * (REAL_PRECISION >> amp); + } +} + +static real_t find_log2_Q(sbr_info *sbr, uint8_t k, uint8_t l, uint8_t ch) +{ + /* check for coupled energy/noise data */ + if (sbr->bs_coupling == 1) + { + int32_t q = sbr->Q[0][k][l]; + int32_t Q = sbr->Q[1][k][l]; + real_t tmp, pan; + + if (q < 0 || q > 30 || Q < 0 || Q > 24) + return LOG2_MIN_INF; + Q -= 12; + + if (ch != 0) // L/R anti-symmetry + Q = -Q; + + if (Q >= 0) + { + /* negative */ + pan = pan_log2_tab[Q]; + } else { + /* positive */ + pan = pan_log2_tab[-Q] + ((-Q)<Q[ch][k][l]; + if (q < 0 || q > 30) + return LOG2_MIN_INF; + return (6 - q) * REAL_PRECISION; + } +} + +static const real_t log_Qplus1_pan[31][13] = { + { REAL_CONST(0.044383447617292), REAL_CONST(0.169768601655960), REAL_CONST(0.583090126514435), REAL_CONST(1.570089221000671), REAL_CONST(3.092446088790894), REAL_CONST(4.733354568481445), REAL_CONST(6.022367954254150), REAL_CONST(6.692092418670654), REAL_CONST(6.924463272094727), REAL_CONST(6.989034175872803), REAL_CONST(7.005646705627441), REAL_CONST(7.009829998016357), REAL_CONST(7.010877609252930) }, + { REAL_CONST(0.022362394258380), REAL_CONST(0.087379962205887), REAL_CONST(0.320804953575134), REAL_CONST(0.988859415054321), REAL_CONST(2.252387046813965), REAL_CONST(3.786596298217773), REAL_CONST(5.044394016265869), REAL_CONST(5.705977916717529), REAL_CONST(5.936291694641113), REAL_CONST(6.000346660614014), REAL_CONST(6.016829967498779), REAL_CONST(6.020981311798096), REAL_CONST(6.022020816802979) }, + { REAL_CONST(0.011224525049329), REAL_CONST(0.044351425021887), REAL_CONST(0.169301137328148), REAL_CONST(0.577544987201691), REAL_CONST(1.527246952056885), REAL_CONST(2.887525320053101), REAL_CONST(4.087462902069092), REAL_CONST(4.733354568481445), REAL_CONST(4.959661006927490), REAL_CONST(5.022709369659424), REAL_CONST(5.038940429687500), REAL_CONST(5.043028831481934), REAL_CONST(5.044052600860596) }, + { REAL_CONST(0.005623178556561), REAL_CONST(0.022346137091517), REAL_CONST(0.087132595479488), REAL_CONST(0.317482173442841), REAL_CONST(0.956931233406067), REAL_CONST(2.070389270782471), REAL_CONST(3.169924974441528), REAL_CONST(3.786596298217773), REAL_CONST(4.005294322967529), REAL_CONST(4.066420555114746), REAL_CONST(4.082170009613037), REAL_CONST(4.086137294769287), REAL_CONST(4.087131500244141) }, + { REAL_CONST(0.002814328996465), REAL_CONST(0.011216334067285), REAL_CONST(0.044224001467228), REAL_CONST(0.167456731200218), REAL_CONST(0.556393325328827), REAL_CONST(1.378511548042297), REAL_CONST(2.321928024291992), REAL_CONST(2.887525320053101), REAL_CONST(3.092446088790894), REAL_CONST(3.150059700012207), REAL_CONST(3.164926528930664), REAL_CONST(3.168673276901245), REAL_CONST(3.169611930847168) }, + { REAL_CONST(0.001407850766554), REAL_CONST(0.005619067233056), REAL_CONST(0.022281449288130), REAL_CONST(0.086156636476517), REAL_CONST(0.304854571819305), REAL_CONST(0.847996890544891), REAL_CONST(1.584962487220764), REAL_CONST(2.070389270782471), REAL_CONST(2.252387046813965), REAL_CONST(2.304061651229858), REAL_CONST(2.317430257797241), REAL_CONST(2.320801734924316), REAL_CONST(2.321646213531494) }, + { REAL_CONST(0.000704097095877), REAL_CONST(0.002812269143760), REAL_CONST(0.011183738708496), REAL_CONST(0.043721374124289), REAL_CONST(0.160464659333229), REAL_CONST(0.485426813364029), REAL_CONST(1.000000000000000), REAL_CONST(1.378511548042297), REAL_CONST(1.527246952056885), REAL_CONST(1.570089221000671), REAL_CONST(1.581215262413025), REAL_CONST(1.584023833274841), REAL_CONST(1.584727644920349) }, + { REAL_CONST(0.000352177477907), REAL_CONST(0.001406819908880), REAL_CONST(0.005602621007711), REAL_CONST(0.022026389837265), REAL_CONST(0.082462236285210), REAL_CONST(0.263034462928772), REAL_CONST(0.584962487220764), REAL_CONST(0.847996890544891), REAL_CONST(0.956931233406067), REAL_CONST(0.988859415054321), REAL_CONST(0.997190535068512), REAL_CONST(0.999296069145203), REAL_CONST(0.999823868274689) }, + { REAL_CONST(0.000176099492819), REAL_CONST(0.000703581434209), REAL_CONST(0.002804030198604), REAL_CONST(0.011055230163038), REAL_CONST(0.041820213198662), REAL_CONST(0.137503549456596), REAL_CONST(0.321928083896637), REAL_CONST(0.485426813364029), REAL_CONST(0.556393325328827), REAL_CONST(0.577544987201691), REAL_CONST(0.583090126514435), REAL_CONST(0.584493279457092), REAL_CONST(0.584845066070557) }, + { REAL_CONST(0.000088052431238), REAL_CONST(0.000351833587047), REAL_CONST(0.001402696361765), REAL_CONST(0.005538204684854), REAL_CONST(0.021061634644866), REAL_CONST(0.070389263331890), REAL_CONST(0.169925004243851), REAL_CONST(0.263034462928772), REAL_CONST(0.304854571819305), REAL_CONST(0.317482173442841), REAL_CONST(0.320804953575134), REAL_CONST(0.321646571159363), REAL_CONST(0.321857661008835) }, + { REAL_CONST(0.000044026888645), REAL_CONST(0.000175927518285), REAL_CONST(0.000701518612914), REAL_CONST(0.002771759871393), REAL_CONST(0.010569252073765), REAL_CONST(0.035623874515295), REAL_CONST(0.087462842464447), REAL_CONST(0.137503549456596), REAL_CONST(0.160464659333229), REAL_CONST(0.167456731200218), REAL_CONST(0.169301137328148), REAL_CONST(0.169768601655960), REAL_CONST(0.169885858893394) }, + { REAL_CONST(0.000022013611670), REAL_CONST(0.000088052431238), REAL_CONST(0.000350801943569), REAL_CONST(0.001386545598507), REAL_CONST(0.005294219125062), REAL_CONST(0.017921976745129), REAL_CONST(0.044394120573997), REAL_CONST(0.070389263331890), REAL_CONST(0.082462236285210), REAL_CONST(0.086156636476517), REAL_CONST(0.087132595479488), REAL_CONST(0.087379962205887), REAL_CONST(0.087442122399807) }, + { REAL_CONST(0.000011006847672), REAL_CONST(0.000044026888645), REAL_CONST(0.000175411638338), REAL_CONST(0.000693439331371), REAL_CONST(0.002649537986144), REAL_CONST(0.008988817222416), REAL_CONST(0.022367812693119), REAL_CONST(0.035623874515295), REAL_CONST(0.041820213198662), REAL_CONST(0.043721374124289), REAL_CONST(0.044224001467228), REAL_CONST(0.044351425021887), REAL_CONST(0.044383447617292) }, + { REAL_CONST(0.000005503434295), REAL_CONST(0.000022013611670), REAL_CONST(0.000087708482170), REAL_CONST(0.000346675369656), REAL_CONST(0.001325377263129), REAL_CONST(0.004501323681325), REAL_CONST(0.011227255687118), REAL_CONST(0.017921976745129), REAL_CONST(0.021061634644866), REAL_CONST(0.022026389837265), REAL_CONST(0.022281449288130), REAL_CONST(0.022346137091517), REAL_CONST(0.022362394258380) }, + { REAL_CONST(0.000002751719876), REAL_CONST(0.000011006847672), REAL_CONST(0.000043854910473), REAL_CONST(0.000173348103999), REAL_CONST(0.000662840844598), REAL_CONST(0.002252417383716), REAL_CONST(0.005624548997730), REAL_CONST(0.008988817222416), REAL_CONST(0.010569252073765), REAL_CONST(0.011055230163038), REAL_CONST(0.011183738708496), REAL_CONST(0.011216334067285), REAL_CONST(0.011224525049329) }, + { REAL_CONST(0.000001375860506), REAL_CONST(0.000005503434295), REAL_CONST(0.000022013611670), REAL_CONST(0.000086676649516), REAL_CONST(0.000331544462824), REAL_CONST(0.001126734190620), REAL_CONST(0.002815015614033), REAL_CONST(0.004501323681325), REAL_CONST(0.005294219125062), REAL_CONST(0.005538204684854), REAL_CONST(0.005602621007711), REAL_CONST(0.005619067233056), REAL_CONST(0.005623178556561) }, + { REAL_CONST(0.000000687930424), REAL_CONST(0.000002751719876), REAL_CONST(0.000011006847672), REAL_CONST(0.000043338975956), REAL_CONST(0.000165781748365), REAL_CONST(0.000563477107789), REAL_CONST(0.001408194424585), REAL_CONST(0.002252417383716), REAL_CONST(0.002649537986144), REAL_CONST(0.002771759871393), REAL_CONST(0.002804030198604), REAL_CONST(0.002812269143760), REAL_CONST(0.002814328996465) }, + { REAL_CONST(0.000000343965269), REAL_CONST(0.000001375860506), REAL_CONST(0.000005503434295), REAL_CONST(0.000021669651687), REAL_CONST(0.000082893253420), REAL_CONST(0.000281680084299), REAL_CONST(0.000704268983100), REAL_CONST(0.001126734190620), REAL_CONST(0.001325377263129), REAL_CONST(0.001386545598507), REAL_CONST(0.001402696361765), REAL_CONST(0.001406819908880), REAL_CONST(0.001407850766554) }, + { REAL_CONST(0.000000171982634), REAL_CONST(0.000000687930424), REAL_CONST(0.000002751719876), REAL_CONST(0.000010834866771), REAL_CONST(0.000041447223339), REAL_CONST(0.000140846910654), REAL_CONST(0.000352177477907), REAL_CONST(0.000563477107789), REAL_CONST(0.000662840844598), REAL_CONST(0.000693439331371), REAL_CONST(0.000701518612914), REAL_CONST(0.000703581434209), REAL_CONST(0.000704097095877) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000001375860506), REAL_CONST(0.000005503434295), REAL_CONST(0.000020637769921), REAL_CONST(0.000070511166996), REAL_CONST(0.000176099492819), REAL_CONST(0.000281680084299), REAL_CONST(0.000331544462824), REAL_CONST(0.000346675369656), REAL_CONST(0.000350801943569), REAL_CONST(0.000351833587047), REAL_CONST(0.000352177477907) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000687930424), REAL_CONST(0.000002751719876), REAL_CONST(0.000010318922250), REAL_CONST(0.000035256012779), REAL_CONST(0.000088052431238), REAL_CONST(0.000140846910654), REAL_CONST(0.000165781748365), REAL_CONST(0.000173348103999), REAL_CONST(0.000175411638338), REAL_CONST(0.000175927518285), REAL_CONST(0.000176099492819) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000001375860506), REAL_CONST(0.000005159470220), REAL_CONST(0.000017542124624), REAL_CONST(0.000044026888645), REAL_CONST(0.000070511166996), REAL_CONST(0.000082893253420), REAL_CONST(0.000086676649516), REAL_CONST(0.000087708482170), REAL_CONST(0.000088052431238), REAL_CONST(0.000088052431238) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000687930424), REAL_CONST(0.000002579737384), REAL_CONST(0.000008771088687), REAL_CONST(0.000022013611670), REAL_CONST(0.000035256012779), REAL_CONST(0.000041447223339), REAL_CONST(0.000043338975956), REAL_CONST(0.000043854910473), REAL_CONST(0.000044026888645), REAL_CONST(0.000044026888645) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000001375860506), REAL_CONST(0.000004471542070), REAL_CONST(0.000011006847672), REAL_CONST(0.000017542124624), REAL_CONST(0.000020637769921), REAL_CONST(0.000021669651687), REAL_CONST(0.000022013611670), REAL_CONST(0.000022013611670), REAL_CONST(0.000022013611670) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000687930424), REAL_CONST(0.000002235772627), REAL_CONST(0.000005503434295), REAL_CONST(0.000008771088687), REAL_CONST(0.000010318922250), REAL_CONST(0.000010834866771), REAL_CONST(0.000011006847672), REAL_CONST(0.000011006847672), REAL_CONST(0.000011006847672) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000001031895522), REAL_CONST(0.000002751719876), REAL_CONST(0.000004471542070), REAL_CONST(0.000005159470220), REAL_CONST(0.000005503434295), REAL_CONST(0.000005503434295), REAL_CONST(0.000005503434295), REAL_CONST(0.000005503434295) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000515947875), REAL_CONST(0.000001375860506), REAL_CONST(0.000002235772627), REAL_CONST(0.000002579737384), REAL_CONST(0.000002751719876), REAL_CONST(0.000002751719876), REAL_CONST(0.000002751719876), REAL_CONST(0.000002751719876) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000000687930424), REAL_CONST(0.000001031895522), REAL_CONST(0.000001375860506), REAL_CONST(0.000001375860506), REAL_CONST(0.000001375860506), REAL_CONST(0.000001375860506), REAL_CONST(0.000001375860506) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000343965269), REAL_CONST(0.000000515947875), REAL_CONST(0.000000687930424), REAL_CONST(0.000000687930424), REAL_CONST(0.000000687930424), REAL_CONST(0.000000687930424), REAL_CONST(0.000000687930424) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634) } +}; + +static const real_t log_Qplus1[31] = { + REAL_CONST(6.022367813028454), REAL_CONST(5.044394119358453), REAL_CONST(4.087462841250339), + REAL_CONST(3.169925001442313), REAL_CONST(2.321928094887362), REAL_CONST(1.584962500721156), + REAL_CONST(1.000000000000000), REAL_CONST(0.584962500721156), REAL_CONST(0.321928094887362), + REAL_CONST(0.169925001442312), REAL_CONST(0.087462841250339), REAL_CONST(0.044394119358453), + REAL_CONST(0.022367813028455), REAL_CONST(0.011227255423254), REAL_CONST(0.005624549193878), + REAL_CONST(0.002815015607054), REAL_CONST(0.001408194392808), REAL_CONST(0.000704269011247), + REAL_CONST(0.000352177480301), REAL_CONST(0.000176099486443), REAL_CONST(0.000088052430122), + REAL_CONST(0.000044026886827), REAL_CONST(0.000022013611360), REAL_CONST(0.000011006847667), + REAL_CONST(0.000005503434331), REAL_CONST(0.000002751719790), REAL_CONST(0.000001375860551), + REAL_CONST(0.000000687930439), REAL_CONST(0.000000343965261), REAL_CONST(0.000000171982641), + REAL_CONST(0.000000000000000) +}; + +static real_t find_log2_Qplus1(sbr_info *sbr, uint8_t k, uint8_t l, uint8_t ch) +{ + /* check for coupled energy/noise data */ + if (sbr->bs_coupling == 1) + { + if ((sbr->Q[0][k][l] >= 0) && (sbr->Q[0][k][l] <= 30) && + (sbr->Q[1][k][l] >= 0) && (sbr->Q[1][k][l] <= 24)) + { + if (ch == 0) + { + return log_Qplus1_pan[sbr->Q[0][k][l]][sbr->Q[1][k][l] >> 1]; + } else { + return log_Qplus1_pan[sbr->Q[0][k][l]][12 - (sbr->Q[1][k][l] >> 1)]; + } + } else { + return 0; + } + } else { + if (sbr->Q[ch][k][l] >= 0 && sbr->Q[ch][k][l] <= 30) + { + return log_Qplus1[sbr->Q[ch][k][l]]; + } else { + return 0; + } + } +} + +static void calculate_gain(sbr_info *sbr, sbr_hfadj_info *adj, uint8_t ch) +{ + /* log2 values of limiter gains */ + /* Last one less than log2(1e10) due to FIXED POINT float limitations */ + static real_t limGain[] = { + REAL_CONST(-1.0), REAL_CONST(0.0), REAL_CONST(1.0), REAL_CONST(21.0) + }; + uint8_t m, l, k; + + uint8_t current_t_noise_band = 0; + uint8_t S_mapped; + + ALIGN real_t Q_M_lim[MAX_M]; + ALIGN real_t G_lim[MAX_M]; + ALIGN real_t G_boost; + ALIGN real_t S_M[MAX_M]; + + real_t exp = REAL_CONST(-10); + + for (l = 0; l < sbr->L_E[ch]; l++) + { + uint8_t current_f_noise_band = 0; + uint8_t current_res_band = 0; + uint8_t current_res_band2 = 0; + uint8_t current_hi_res_band = 0; + + real_t delta = (l == sbr->l_A[ch] || l == sbr->prevEnvIsShort[ch]) ? 0 : 1; + + S_mapped = get_S_mapped(sbr, ch, l, current_res_band2); + + if (sbr->t_E[ch][l+1] > sbr->t_Q[ch][current_t_noise_band+1]) + { + current_t_noise_band++; + } + + for (k = 0; k < sbr->N_L[sbr->bs_limiter_bands]; k++) + { + real_t Q_M = 0; + real_t G_max; + uint64_t den = 0, acc1 = 0, acc2 = 0; + uint8_t current_res_band_size = 0; + uint8_t Q_M_size = 0; + real_t log_e, log_den, log_acc1, log_acc2; + + uint8_t ml1, ml2; + + /* bounds of current limiter bands */ + ml1 = sbr->f_table_lim[sbr->bs_limiter_bands][k]; + ml2 = sbr->f_table_lim[sbr->bs_limiter_bands][k+1]; + + if (ml1 > MAX_M) + ml1 = MAX_M; + + if (ml2 > MAX_M) + ml2 = MAX_M; + + + /* calculate the accumulated E_orig and E_curr over the limiter band */ + for (m = ml1; m < ml2; m++) + { + if ((m + sbr->kx) < sbr->f_table_res[sbr->f[ch][l]][current_res_band+1]) + { + current_res_band_size++; + } else { + log_e = find_log2_E(sbr, current_res_band, l, ch); + acc1 += pow2_int(exp + log2_int_tab[current_res_band_size] + log_e); + + current_res_band++; + current_res_band_size = 1; + } + + acc2 += sbr->E_curr[ch][m][l]; + } + if (current_res_band_size) { + log_e = find_log2_E(sbr, current_res_band, l, ch); + acc1 += pow2_int(exp + log2_int_tab[current_res_band_size] + log_e); + } + + + if (acc1 == 0) + log_acc1 = LOG2_MIN_INF; + else + log_acc1 = log2_int(acc1); + + if (acc2 == 0) + log_acc2 = LOG2_MIN_INF; + else + log_acc2 = log2_int(acc2); + + /* calculate the maximum gain */ + /* ratio of the energy of the original signal and the energy + * of the HF generated signal + */ + G_max = log_acc1 - log_acc2 + limGain[sbr->bs_limiter_gains]; + G_max = min(G_max, limGain[3]); + + + for (m = ml1; m < ml2; m++) + { + real_t G; + real_t E_curr, E_orig; + real_t Q_orig, Q_orig_plus1; + uint8_t S_index_mapped; + + + /* check if m is on a noise band border */ + if ((m + sbr->kx) == sbr->f_table_noise[current_f_noise_band+1]) + { + /* step to next noise band */ + current_f_noise_band++; + } + + + /* check if m is on a resolution band border */ + if ((m + sbr->kx) == sbr->f_table_res[sbr->f[ch][l]][current_res_band2+1]) + { + /* accumulate a whole range of equal Q_Ms */ + if (Q_M_size > 0) + den += pow2_int(log2_int_tab[Q_M_size] + Q_M); + Q_M_size = 0; + + /* step to next resolution band */ + current_res_band2++; + + /* if we move to a new resolution band, we should check if we are + * going to add a sinusoid in this band + */ + S_mapped = get_S_mapped(sbr, ch, l, current_res_band2); + } + + + /* check if m is on a HI_RES band border */ + if ((m + sbr->kx) == sbr->f_table_res[HI_RES][current_hi_res_band+1]) + { + /* step to next HI_RES band */ + current_hi_res_band++; + } + + + /* find S_index_mapped + * S_index_mapped can only be 1 for the m in the middle of the + * current HI_RES band + */ + S_index_mapped = 0; + if ((l >= sbr->l_A[ch]) || + (sbr->bs_add_harmonic_prev[ch][current_hi_res_band] && sbr->bs_add_harmonic_flag_prev[ch])) + { + /* find the middle subband of the HI_RES frequency band */ + if ((m + sbr->kx) == (sbr->f_table_res[HI_RES][current_hi_res_band+1] + sbr->f_table_res[HI_RES][current_hi_res_band]) >> 1) + S_index_mapped = sbr->bs_add_harmonic[ch][current_hi_res_band]; + } + + + /* find bitstream parameters */ + if (sbr->E_curr[ch][m][l] == 0) + E_curr = LOG2_MIN_INF; + else + E_curr = log2_int(sbr->E_curr[ch][m][l]); + E_orig = exp + find_log2_E(sbr, current_res_band2, l, ch); + + + Q_orig = find_log2_Q(sbr, current_f_noise_band, current_t_noise_band, ch); + Q_orig_plus1 = find_log2_Qplus1(sbr, current_f_noise_band, current_t_noise_band, ch); + + + /* Q_M only depends on E_orig and Q_div2: + * since N_Q <= N_Low <= N_High we only need to recalculate Q_M on + * a change of current res band (HI or LO) + */ + Q_M = E_orig + Q_orig - Q_orig_plus1; + + + /* S_M only depends on E_orig, Q_div and S_index_mapped: + * S_index_mapped can only be non-zero once per HI_RES band + */ + if (S_index_mapped == 0) + { + S_M[m] = LOG2_MIN_INF; /* -inf */ + } else { + S_M[m] = E_orig - Q_orig_plus1; + S_M[m] = min(S_M[m], limGain[3]); + + /* accumulate sinusoid part of the total energy */ + den += pow2_int(S_M[m]); + } + + + /* calculate gain */ + /* ratio of the energy of the original signal and the energy + * of the HF generated signal + */ + /* E_curr here is officially E_curr+1 so the log2() of that can never be < 0 */ + /* scaled by exp */ + G = E_orig - max(exp, E_curr); + if ((S_mapped == 0) && (delta == 1)) + { + /* G = G * 1/(1+Q) */ + G -= Q_orig_plus1; + } else if (S_mapped == 1) { + /* G = G * Q/(1+Q) */ + G += Q_orig - Q_orig_plus1; + } + + + /* limit the additional noise energy level */ + /* and apply the limiter */ + if (G_max > G) + { + Q_M_lim[m] = Q_M; + G_lim[m] = G; + + if ((S_index_mapped == 0) && (l != sbr->l_A[ch])) + { + Q_M_size++; + } + } else { + /* G >= G_max */ + Q_M_lim[m] = Q_M + G_max - G; + G_lim[m] = G_max; + + /* accumulate limited Q_M */ + if ((S_index_mapped == 0) && (l != sbr->l_A[ch])) + { + den += pow2_int(Q_M_lim[m]); + } + } + + + /* accumulate the total energy */ + /* E_curr changes for every m so we do need to accumulate every m */ + den += pow2_int(E_curr + G_lim[m]); + } + + /* accumulate last range of equal Q_Ms */ + if (Q_M_size > 0) + { + den += pow2_int(log2_int_tab[Q_M_size] + Q_M); + } + + if (den == 0) + log_den = LOG2_MIN_INF; + else + log_den = log2_int(den /*+ EPS*/); + + /* calculate the final gain */ + /* G_boost: [0..2.51188643] */ + G_boost = log_acc1 - log_den; + G_boost = min(G_boost, REAL_CONST(1.328771237) /* log2(1.584893192 ^ 2) */); + + + for (m = ml1; m < ml2; m++) + { + /* apply compensation to gain, noise floor sf's and sinusoid levels */ +#ifndef SBR_LOW_POWER + adj->G_lim_boost[l][m] = pow2_fix((G_lim[m] + G_boost) >> 1); +#else + /* sqrt() will be done after the aliasing reduction to save a + * few multiplies + */ + adj->G_lim_boost[l][m] = pow2_fix(G_lim[m] + G_boost); +#endif + adj->Q_M_lim_boost[l][m] = pow2_fix((Q_M_lim[m] + G_boost) >> 1); + + adj->S_M_boost[l][m] = pow2_fix((S_M[m] + G_boost) >> 1); + } + } + } +} + +#else + +//#define LOG2_TEST + +#ifdef LOG2_TEST + +#define LOG2_MIN_INF -100000 + +__inline float pow2(float val) +{ + return pow(2.0, val); +} +__inline float log2(float val) +{ + return log(val)/log(2.0); +} + +#define RB 14 + +float QUANTISE2REAL(float val) +{ + __int32 ival = (__int32)(val * (1<bs_coupling == 1) + { + real_t amp0 = (sbr->amp_res[0]) ? 1.0 : 0.5; + real_t amp1 = (sbr->amp_res[1]) ? 1.0 : 0.5; + float tmp = QUANTISE2REAL(7.0 + (real_t)sbr->E[0][k][l] * amp0); + float pan; + + int E = (int)(sbr->E[1][k][l] * amp1); + + if (ch == 0) + { + if (E > 12) + { + /* negative */ + pan = QUANTISE2REAL(pan_log2_tab[-12 + E]); + } else { + /* positive */ + pan = QUANTISE2REAL(pan_log2_tab[12 - E] + (12 - E)); + } + } else { + if (E < 12) + { + /* negative */ + pan = QUANTISE2REAL(pan_log2_tab[-E + 12]); + } else { + /* positive */ + pan = QUANTISE2REAL(pan_log2_tab[E - 12] + (E - 12)); + } + } + + /* tmp / pan in log2 */ + return QUANTISE2REAL(tmp - pan); + } else { + real_t amp = (sbr->amp_res[ch]) ? 1.0 : 0.5; + + return QUANTISE2REAL(6.0 + (real_t)sbr->E[ch][k][l] * amp); + } +} + +static real_t find_log2_Q(sbr_info *sbr, uint8_t k, uint8_t l, uint8_t ch) +{ + /* check for coupled energy/noise data */ + if (sbr->bs_coupling == 1) + { + float tmp = QUANTISE2REAL(7.0 - (real_t)sbr->Q[0][k][l]); + float pan; + + int Q = (int)(sbr->Q[1][k][l]); + + if (ch == 0) + { + if (Q > 12) + { + /* negative */ + pan = QUANTISE2REAL(pan_log2_tab[-12 + Q]); + } else { + /* positive */ + pan = QUANTISE2REAL(pan_log2_tab[12 - Q] + (12 - Q)); + } + } else { + if (Q < 12) + { + /* negative */ + pan = QUANTISE2REAL(pan_log2_tab[-Q + 12]); + } else { + /* positive */ + pan = QUANTISE2REAL(pan_log2_tab[Q - 12] + (Q - 12)); + } + } + + /* tmp / pan in log2 */ + return QUANTISE2REAL(tmp - pan); + } else { + return QUANTISE2REAL(6.0 - (real_t)sbr->Q[ch][k][l]); + } +} + +static const real_t log_Qplus1_pan[31][13] = { + { REAL_CONST(0.044383447617292), REAL_CONST(0.169768601655960), REAL_CONST(0.583090126514435), REAL_CONST(1.570089221000671), REAL_CONST(3.092446088790894), REAL_CONST(4.733354568481445), REAL_CONST(6.022367954254150), REAL_CONST(6.692092418670654), REAL_CONST(6.924463272094727), REAL_CONST(6.989034175872803), REAL_CONST(7.005646705627441), REAL_CONST(7.009829998016357), REAL_CONST(7.010877609252930) }, + { REAL_CONST(0.022362394258380), REAL_CONST(0.087379962205887), REAL_CONST(0.320804953575134), REAL_CONST(0.988859415054321), REAL_CONST(2.252387046813965), REAL_CONST(3.786596298217773), REAL_CONST(5.044394016265869), REAL_CONST(5.705977916717529), REAL_CONST(5.936291694641113), REAL_CONST(6.000346660614014), REAL_CONST(6.016829967498779), REAL_CONST(6.020981311798096), REAL_CONST(6.022020816802979) }, + { REAL_CONST(0.011224525049329), REAL_CONST(0.044351425021887), REAL_CONST(0.169301137328148), REAL_CONST(0.577544987201691), REAL_CONST(1.527246952056885), REAL_CONST(2.887525320053101), REAL_CONST(4.087462902069092), REAL_CONST(4.733354568481445), REAL_CONST(4.959661006927490), REAL_CONST(5.022709369659424), REAL_CONST(5.038940429687500), REAL_CONST(5.043028831481934), REAL_CONST(5.044052600860596) }, + { REAL_CONST(0.005623178556561), REAL_CONST(0.022346137091517), REAL_CONST(0.087132595479488), REAL_CONST(0.317482173442841), REAL_CONST(0.956931233406067), REAL_CONST(2.070389270782471), REAL_CONST(3.169924974441528), REAL_CONST(3.786596298217773), REAL_CONST(4.005294322967529), REAL_CONST(4.066420555114746), REAL_CONST(4.082170009613037), REAL_CONST(4.086137294769287), REAL_CONST(4.087131500244141) }, + { REAL_CONST(0.002814328996465), REAL_CONST(0.011216334067285), REAL_CONST(0.044224001467228), REAL_CONST(0.167456731200218), REAL_CONST(0.556393325328827), REAL_CONST(1.378511548042297), REAL_CONST(2.321928024291992), REAL_CONST(2.887525320053101), REAL_CONST(3.092446088790894), REAL_CONST(3.150059700012207), REAL_CONST(3.164926528930664), REAL_CONST(3.168673276901245), REAL_CONST(3.169611930847168) }, + { REAL_CONST(0.001407850766554), REAL_CONST(0.005619067233056), REAL_CONST(0.022281449288130), REAL_CONST(0.086156636476517), REAL_CONST(0.304854571819305), REAL_CONST(0.847996890544891), REAL_CONST(1.584962487220764), REAL_CONST(2.070389270782471), REAL_CONST(2.252387046813965), REAL_CONST(2.304061651229858), REAL_CONST(2.317430257797241), REAL_CONST(2.320801734924316), REAL_CONST(2.321646213531494) }, + { REAL_CONST(0.000704097095877), REAL_CONST(0.002812269143760), REAL_CONST(0.011183738708496), REAL_CONST(0.043721374124289), REAL_CONST(0.160464659333229), REAL_CONST(0.485426813364029), REAL_CONST(1.000000000000000), REAL_CONST(1.378511548042297), REAL_CONST(1.527246952056885), REAL_CONST(1.570089221000671), REAL_CONST(1.581215262413025), REAL_CONST(1.584023833274841), REAL_CONST(1.584727644920349) }, + { REAL_CONST(0.000352177477907), REAL_CONST(0.001406819908880), REAL_CONST(0.005602621007711), REAL_CONST(0.022026389837265), REAL_CONST(0.082462236285210), REAL_CONST(0.263034462928772), REAL_CONST(0.584962487220764), REAL_CONST(0.847996890544891), REAL_CONST(0.956931233406067), REAL_CONST(0.988859415054321), REAL_CONST(0.997190535068512), REAL_CONST(0.999296069145203), REAL_CONST(0.999823868274689) }, + { REAL_CONST(0.000176099492819), REAL_CONST(0.000703581434209), REAL_CONST(0.002804030198604), REAL_CONST(0.011055230163038), REAL_CONST(0.041820213198662), REAL_CONST(0.137503549456596), REAL_CONST(0.321928083896637), REAL_CONST(0.485426813364029), REAL_CONST(0.556393325328827), REAL_CONST(0.577544987201691), REAL_CONST(0.583090126514435), REAL_CONST(0.584493279457092), REAL_CONST(0.584845066070557) }, + { REAL_CONST(0.000088052431238), REAL_CONST(0.000351833587047), REAL_CONST(0.001402696361765), REAL_CONST(0.005538204684854), REAL_CONST(0.021061634644866), REAL_CONST(0.070389263331890), REAL_CONST(0.169925004243851), REAL_CONST(0.263034462928772), REAL_CONST(0.304854571819305), REAL_CONST(0.317482173442841), REAL_CONST(0.320804953575134), REAL_CONST(0.321646571159363), REAL_CONST(0.321857661008835) }, + { REAL_CONST(0.000044026888645), REAL_CONST(0.000175927518285), REAL_CONST(0.000701518612914), REAL_CONST(0.002771759871393), REAL_CONST(0.010569252073765), REAL_CONST(0.035623874515295), REAL_CONST(0.087462842464447), REAL_CONST(0.137503549456596), REAL_CONST(0.160464659333229), REAL_CONST(0.167456731200218), REAL_CONST(0.169301137328148), REAL_CONST(0.169768601655960), REAL_CONST(0.169885858893394) }, + { REAL_CONST(0.000022013611670), REAL_CONST(0.000088052431238), REAL_CONST(0.000350801943569), REAL_CONST(0.001386545598507), REAL_CONST(0.005294219125062), REAL_CONST(0.017921976745129), REAL_CONST(0.044394120573997), REAL_CONST(0.070389263331890), REAL_CONST(0.082462236285210), REAL_CONST(0.086156636476517), REAL_CONST(0.087132595479488), REAL_CONST(0.087379962205887), REAL_CONST(0.087442122399807) }, + { REAL_CONST(0.000011006847672), REAL_CONST(0.000044026888645), REAL_CONST(0.000175411638338), REAL_CONST(0.000693439331371), REAL_CONST(0.002649537986144), REAL_CONST(0.008988817222416), REAL_CONST(0.022367812693119), REAL_CONST(0.035623874515295), REAL_CONST(0.041820213198662), REAL_CONST(0.043721374124289), REAL_CONST(0.044224001467228), REAL_CONST(0.044351425021887), REAL_CONST(0.044383447617292) }, + { REAL_CONST(0.000005503434295), REAL_CONST(0.000022013611670), REAL_CONST(0.000087708482170), REAL_CONST(0.000346675369656), REAL_CONST(0.001325377263129), REAL_CONST(0.004501323681325), REAL_CONST(0.011227255687118), REAL_CONST(0.017921976745129), REAL_CONST(0.021061634644866), REAL_CONST(0.022026389837265), REAL_CONST(0.022281449288130), REAL_CONST(0.022346137091517), REAL_CONST(0.022362394258380) }, + { REAL_CONST(0.000002751719876), REAL_CONST(0.000011006847672), REAL_CONST(0.000043854910473), REAL_CONST(0.000173348103999), REAL_CONST(0.000662840844598), REAL_CONST(0.002252417383716), REAL_CONST(0.005624548997730), REAL_CONST(0.008988817222416), REAL_CONST(0.010569252073765), REAL_CONST(0.011055230163038), REAL_CONST(0.011183738708496), REAL_CONST(0.011216334067285), REAL_CONST(0.011224525049329) }, + { REAL_CONST(0.000001375860506), REAL_CONST(0.000005503434295), REAL_CONST(0.000022013611670), REAL_CONST(0.000086676649516), REAL_CONST(0.000331544462824), REAL_CONST(0.001126734190620), REAL_CONST(0.002815015614033), REAL_CONST(0.004501323681325), REAL_CONST(0.005294219125062), REAL_CONST(0.005538204684854), REAL_CONST(0.005602621007711), REAL_CONST(0.005619067233056), REAL_CONST(0.005623178556561) }, + { REAL_CONST(0.000000687930424), REAL_CONST(0.000002751719876), REAL_CONST(0.000011006847672), REAL_CONST(0.000043338975956), REAL_CONST(0.000165781748365), REAL_CONST(0.000563477107789), REAL_CONST(0.001408194424585), REAL_CONST(0.002252417383716), REAL_CONST(0.002649537986144), REAL_CONST(0.002771759871393), REAL_CONST(0.002804030198604), REAL_CONST(0.002812269143760), REAL_CONST(0.002814328996465) }, + { REAL_CONST(0.000000343965269), REAL_CONST(0.000001375860506), REAL_CONST(0.000005503434295), REAL_CONST(0.000021669651687), REAL_CONST(0.000082893253420), REAL_CONST(0.000281680084299), REAL_CONST(0.000704268983100), REAL_CONST(0.001126734190620), REAL_CONST(0.001325377263129), REAL_CONST(0.001386545598507), REAL_CONST(0.001402696361765), REAL_CONST(0.001406819908880), REAL_CONST(0.001407850766554) }, + { REAL_CONST(0.000000171982634), REAL_CONST(0.000000687930424), REAL_CONST(0.000002751719876), REAL_CONST(0.000010834866771), REAL_CONST(0.000041447223339), REAL_CONST(0.000140846910654), REAL_CONST(0.000352177477907), REAL_CONST(0.000563477107789), REAL_CONST(0.000662840844598), REAL_CONST(0.000693439331371), REAL_CONST(0.000701518612914), REAL_CONST(0.000703581434209), REAL_CONST(0.000704097095877) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000001375860506), REAL_CONST(0.000005503434295), REAL_CONST(0.000020637769921), REAL_CONST(0.000070511166996), REAL_CONST(0.000176099492819), REAL_CONST(0.000281680084299), REAL_CONST(0.000331544462824), REAL_CONST(0.000346675369656), REAL_CONST(0.000350801943569), REAL_CONST(0.000351833587047), REAL_CONST(0.000352177477907) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000687930424), REAL_CONST(0.000002751719876), REAL_CONST(0.000010318922250), REAL_CONST(0.000035256012779), REAL_CONST(0.000088052431238), REAL_CONST(0.000140846910654), REAL_CONST(0.000165781748365), REAL_CONST(0.000173348103999), REAL_CONST(0.000175411638338), REAL_CONST(0.000175927518285), REAL_CONST(0.000176099492819) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000001375860506), REAL_CONST(0.000005159470220), REAL_CONST(0.000017542124624), REAL_CONST(0.000044026888645), REAL_CONST(0.000070511166996), REAL_CONST(0.000082893253420), REAL_CONST(0.000086676649516), REAL_CONST(0.000087708482170), REAL_CONST(0.000088052431238), REAL_CONST(0.000088052431238) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000687930424), REAL_CONST(0.000002579737384), REAL_CONST(0.000008771088687), REAL_CONST(0.000022013611670), REAL_CONST(0.000035256012779), REAL_CONST(0.000041447223339), REAL_CONST(0.000043338975956), REAL_CONST(0.000043854910473), REAL_CONST(0.000044026888645), REAL_CONST(0.000044026888645) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000001375860506), REAL_CONST(0.000004471542070), REAL_CONST(0.000011006847672), REAL_CONST(0.000017542124624), REAL_CONST(0.000020637769921), REAL_CONST(0.000021669651687), REAL_CONST(0.000022013611670), REAL_CONST(0.000022013611670), REAL_CONST(0.000022013611670) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000687930424), REAL_CONST(0.000002235772627), REAL_CONST(0.000005503434295), REAL_CONST(0.000008771088687), REAL_CONST(0.000010318922250), REAL_CONST(0.000010834866771), REAL_CONST(0.000011006847672), REAL_CONST(0.000011006847672), REAL_CONST(0.000011006847672) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000001031895522), REAL_CONST(0.000002751719876), REAL_CONST(0.000004471542070), REAL_CONST(0.000005159470220), REAL_CONST(0.000005503434295), REAL_CONST(0.000005503434295), REAL_CONST(0.000005503434295), REAL_CONST(0.000005503434295) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000515947875), REAL_CONST(0.000001375860506), REAL_CONST(0.000002235772627), REAL_CONST(0.000002579737384), REAL_CONST(0.000002751719876), REAL_CONST(0.000002751719876), REAL_CONST(0.000002751719876), REAL_CONST(0.000002751719876) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000343965269), REAL_CONST(0.000000687930424), REAL_CONST(0.000001031895522), REAL_CONST(0.000001375860506), REAL_CONST(0.000001375860506), REAL_CONST(0.000001375860506), REAL_CONST(0.000001375860506), REAL_CONST(0.000001375860506) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000343965269), REAL_CONST(0.000000515947875), REAL_CONST(0.000000687930424), REAL_CONST(0.000000687930424), REAL_CONST(0.000000687930424), REAL_CONST(0.000000687930424), REAL_CONST(0.000000687930424) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269), REAL_CONST(0.000000343965269) }, + { REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000000000000), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634), REAL_CONST(0.000000171982634) } +}; + +static const real_t log_Qplus1[31] = { + REAL_CONST(6.022367813028454), REAL_CONST(5.044394119358453), REAL_CONST(4.087462841250339), + REAL_CONST(3.169925001442313), REAL_CONST(2.321928094887362), REAL_CONST(1.584962500721156), + REAL_CONST(1.000000000000000), REAL_CONST(0.584962500721156), REAL_CONST(0.321928094887362), + REAL_CONST(0.169925001442312), REAL_CONST(0.087462841250339), REAL_CONST(0.044394119358453), + REAL_CONST(0.022367813028455), REAL_CONST(0.011227255423254), REAL_CONST(0.005624549193878), + REAL_CONST(0.002815015607054), REAL_CONST(0.001408194392808), REAL_CONST(0.000704269011247), + REAL_CONST(0.000352177480301), REAL_CONST(0.000176099486443), REAL_CONST(0.000088052430122), + REAL_CONST(0.000044026886827), REAL_CONST(0.000022013611360), REAL_CONST(0.000011006847667), + REAL_CONST(0.000005503434331), REAL_CONST(0.000002751719790), REAL_CONST(0.000001375860551), + REAL_CONST(0.000000687930439), REAL_CONST(0.000000343965261), REAL_CONST(0.000000171982641), + REAL_CONST(0.000000000000000) +}; + +static real_t find_log2_Qplus1(sbr_info *sbr, uint8_t k, uint8_t l, uint8_t ch) +{ + /* check for coupled energy/noise data */ + if (sbr->bs_coupling == 1) + { + if ((sbr->Q[0][k][l] >= 0) && (sbr->Q[0][k][l] <= 30) && + (sbr->Q[1][k][l] >= 0) && (sbr->Q[1][k][l] <= 24)) + { + if (ch == 0) + { + return QUANTISE2REAL(log_Qplus1_pan[sbr->Q[0][k][l]][sbr->Q[1][k][l] >> 1]); + } else { + return QUANTISE2REAL(log_Qplus1_pan[sbr->Q[0][k][l]][12 - (sbr->Q[1][k][l] >> 1)]); + } + } else { + return 0; + } + } else { + if (sbr->Q[ch][k][l] >= 0 && sbr->Q[ch][k][l] <= 30) + { + return QUANTISE2REAL(log_Qplus1[sbr->Q[ch][k][l]]); + } else { + return 0; + } + } +} + +static void calculate_gain(sbr_info *sbr, sbr_hfadj_info *adj, uint8_t ch) +{ + /* log2 values of limiter gains */ + static real_t limGain[] = { -1.0, 0.0, 1.0, 33.219 }; + uint8_t m, l, k; + + uint8_t current_t_noise_band = 0; + uint8_t S_mapped; + + ALIGN real_t Q_M_lim[MAX_M]; + ALIGN real_t G_lim[MAX_M]; + ALIGN real_t G_boost; + ALIGN real_t S_M[MAX_M]; + + + for (l = 0; l < sbr->L_E[ch]; l++) + { + uint8_t current_f_noise_band = 0; + uint8_t current_res_band = 0; + uint8_t current_res_band2 = 0; + uint8_t current_hi_res_band = 0; + + real_t delta = (l == sbr->l_A[ch] || l == sbr->prevEnvIsShort[ch]) ? 0 : 1; + + S_mapped = get_S_mapped(sbr, ch, l, current_res_band2); + + if (sbr->t_E[ch][l+1] > sbr->t_Q[ch][current_t_noise_band+1]) + { + current_t_noise_band++; + } + + for (k = 0; k < sbr->N_L[sbr->bs_limiter_bands]; k++) + { + real_t Q_M = 0; + real_t G_max; + real_t den = 0; + real_t acc1 = 0; + real_t acc2 = 0; + uint8_t current_res_band_size = 0; + uint8_t Q_M_size = 0; + + uint8_t ml1, ml2; + + /* bounds of current limiter bands */ + ml1 = sbr->f_table_lim[sbr->bs_limiter_bands][k]; + ml2 = sbr->f_table_lim[sbr->bs_limiter_bands][k+1]; + + if (ml1 > MAX_M) + ml1 = MAX_M; + + if (ml2 > MAX_M) + ml2 = MAX_M; + + + /* calculate the accumulated E_orig and E_curr over the limiter band */ + for (m = ml1; m < ml2; m++) + { + if ((m + sbr->kx) < sbr->f_table_res[sbr->f[ch][l]][current_res_band+1]) + { + current_res_band_size++; + } else { + acc1 += QUANTISE2INT(pow2(-10 + log2_int_tab[current_res_band_size] + find_log2_E(sbr, current_res_band, l, ch))); + + current_res_band++; + current_res_band_size = 1; + } + + acc2 += QUANTISE2INT(sbr->E_curr[ch][m][l]/1024.0); + } + acc1 += QUANTISE2INT(pow2(-10 + log2_int_tab[current_res_band_size] + find_log2_E(sbr, current_res_band, l, ch))); + + acc1 = QUANTISE2REAL( log2(EPS + acc1) ); + + + /* calculate the maximum gain */ + /* ratio of the energy of the original signal and the energy + * of the HF generated signal + */ + G_max = acc1 - QUANTISE2REAL(log2(EPS + acc2)) + QUANTISE2REAL(limGain[sbr->bs_limiter_gains]); + G_max = min(G_max, QUANTISE2REAL(limGain[3])); + + + for (m = ml1; m < ml2; m++) + { + real_t G; + real_t E_curr, E_orig; + real_t Q_orig, Q_orig_plus1; + uint8_t S_index_mapped; + + + /* check if m is on a noise band border */ + if ((m + sbr->kx) == sbr->f_table_noise[current_f_noise_band+1]) + { + /* step to next noise band */ + current_f_noise_band++; + } + + + /* check if m is on a resolution band border */ + if ((m + sbr->kx) == sbr->f_table_res[sbr->f[ch][l]][current_res_band2+1]) + { + /* accumulate a whole range of equal Q_Ms */ + if (Q_M_size > 0) + den += QUANTISE2INT(pow2(log2_int_tab[Q_M_size] + Q_M)); + Q_M_size = 0; + + /* step to next resolution band */ + current_res_band2++; + + /* if we move to a new resolution band, we should check if we are + * going to add a sinusoid in this band + */ + S_mapped = get_S_mapped(sbr, ch, l, current_res_band2); + } + + + /* check if m is on a HI_RES band border */ + if ((m + sbr->kx) == sbr->f_table_res[HI_RES][current_hi_res_band+1]) + { + /* step to next HI_RES band */ + current_hi_res_band++; + } + + + /* find S_index_mapped + * S_index_mapped can only be 1 for the m in the middle of the + * current HI_RES band + */ + S_index_mapped = 0; + if ((l >= sbr->l_A[ch]) || + (sbr->bs_add_harmonic_prev[ch][current_hi_res_band] && sbr->bs_add_harmonic_flag_prev[ch])) + { + /* find the middle subband of the HI_RES frequency band */ + if ((m + sbr->kx) == (sbr->f_table_res[HI_RES][current_hi_res_band+1] + sbr->f_table_res[HI_RES][current_hi_res_band]) >> 1) + S_index_mapped = sbr->bs_add_harmonic[ch][current_hi_res_band]; + } + + + /* find bitstream parameters */ + if (sbr->E_curr[ch][m][l] == 0) + E_curr = LOG2_MIN_INF; + else + E_curr = -10 + log2(sbr->E_curr[ch][m][l]); + E_orig = -10 + find_log2_E(sbr, current_res_band2, l, ch); + + Q_orig = find_log2_Q(sbr, current_f_noise_band, current_t_noise_band, ch); + Q_orig_plus1 = find_log2_Qplus1(sbr, current_f_noise_band, current_t_noise_band, ch); + + + /* Q_M only depends on E_orig and Q_div2: + * since N_Q <= N_Low <= N_High we only need to recalculate Q_M on + * a change of current res band (HI or LO) + */ + Q_M = E_orig + Q_orig - Q_orig_plus1; + + + /* S_M only depends on E_orig, Q_div and S_index_mapped: + * S_index_mapped can only be non-zero once per HI_RES band + */ + if (S_index_mapped == 0) + { + S_M[m] = LOG2_MIN_INF; /* -inf */ + } else { + S_M[m] = E_orig - Q_orig_plus1; + + /* accumulate sinusoid part of the total energy */ + den += pow2(S_M[m]); + } + + + /* calculate gain */ + /* ratio of the energy of the original signal and the energy + * of the HF generated signal + */ + /* E_curr here is officially E_curr+1 so the log2() of that can never be < 0 */ + /* scaled by -10 */ + G = E_orig - max(-10, E_curr); + if ((S_mapped == 0) && (delta == 1)) + { + /* G = G * 1/(1+Q) */ + G -= Q_orig_plus1; + } else if (S_mapped == 1) { + /* G = G * Q/(1+Q) */ + G += Q_orig - Q_orig_plus1; + } + + + /* limit the additional noise energy level */ + /* and apply the limiter */ + if (G_max > G) + { + Q_M_lim[m] = QUANTISE2REAL(Q_M); + G_lim[m] = QUANTISE2REAL(G); + + if ((S_index_mapped == 0) && (l != sbr->l_A[ch])) + { + Q_M_size++; + } + } else { + /* G > G_max */ + Q_M_lim[m] = QUANTISE2REAL(Q_M) + G_max - QUANTISE2REAL(G); + G_lim[m] = G_max; + + /* accumulate limited Q_M */ + if ((S_index_mapped == 0) && (l != sbr->l_A[ch])) + { + den += QUANTISE2INT(pow2(Q_M_lim[m])); + } + } + + + /* accumulate the total energy */ + /* E_curr changes for every m so we do need to accumulate every m */ + den += QUANTISE2INT(pow2(E_curr + G_lim[m])); + } + + /* accumulate last range of equal Q_Ms */ + if (Q_M_size > 0) + { + den += QUANTISE2INT(pow2(log2_int_tab[Q_M_size] + Q_M)); + } + + + /* calculate the final gain */ + /* G_boost: [0..2.51188643] */ + G_boost = acc1 - QUANTISE2REAL(log2(den + EPS)); + G_boost = min(G_boost, QUANTISE2REAL(1.328771237) /* log2(1.584893192 ^ 2) */); + + + for (m = ml1; m < ml2; m++) + { + /* apply compensation to gain, noise floor sf's and sinusoid levels */ +#ifndef SBR_LOW_POWER + adj->G_lim_boost[l][m] = QUANTISE2REAL(pow2((G_lim[m] + G_boost) / 2.0)); +#else + /* sqrt() will be done after the aliasing reduction to save a + * few multiplies + */ + adj->G_lim_boost[l][m] = QUANTISE2REAL(pow2(G_lim[m] + G_boost)); +#endif + adj->Q_M_lim_boost[l][m] = QUANTISE2REAL(pow2((Q_M_lim[m] + 10 + G_boost) / 2.0)); + + if (S_M[m] != LOG2_MIN_INF) + { + adj->S_M_boost[l][m] = QUANTISE2REAL(pow2((S_M[m] + 10 + G_boost) / 2.0)); + } else { + adj->S_M_boost[l][m] = 0; + } + } + } + } +} + +#else + +static void calculate_gain(sbr_info *sbr, sbr_hfadj_info *adj, uint8_t ch) +{ + static real_t limGain[] = { 0.5, 1.0, 2.0, 1e10 }; + uint8_t m, l, k; + + uint8_t current_t_noise_band = 0; + uint8_t S_mapped; + + ALIGN real_t Q_M_lim[MAX_M]; + ALIGN real_t G_lim[MAX_M]; + ALIGN real_t G_boost; + ALIGN real_t S_M[MAX_M]; + + for (l = 0; l < sbr->L_E[ch]; l++) + { + uint8_t current_f_noise_band = 0; + uint8_t current_res_band = 0; + uint8_t current_res_band2 = 0; + uint8_t current_hi_res_band = 0; + + real_t delta = (l == sbr->l_A[ch] || l == sbr->prevEnvIsShort[ch]) ? 0 : 1; + + S_mapped = get_S_mapped(sbr, ch, l, current_res_band2); + + if (sbr->t_E[ch][l+1] > sbr->t_Q[ch][current_t_noise_band+1]) + { + current_t_noise_band++; + } + + for (k = 0; k < sbr->N_L[sbr->bs_limiter_bands]; k++) + { + real_t G_max; + real_t den = 0; + real_t acc1 = 0; + real_t acc2 = 0; + + uint8_t ml1, ml2; + + ml1 = sbr->f_table_lim[sbr->bs_limiter_bands][k]; + ml2 = sbr->f_table_lim[sbr->bs_limiter_bands][k+1]; + + if (ml1 > MAX_M) + ml1 = MAX_M; + + if (ml2 > MAX_M) + ml2 = MAX_M; + + + /* calculate the accumulated E_orig and E_curr over the limiter band */ + for (m = ml1; m < ml2; m++) + { + if ((m + sbr->kx) == sbr->f_table_res[sbr->f[ch][l]][current_res_band+1]) + { + current_res_band++; + } + acc1 += sbr->E_orig[ch][current_res_band][l]; + acc2 += sbr->E_curr[ch][m][l]; + } + + + /* calculate the maximum gain */ + /* ratio of the energy of the original signal and the energy + * of the HF generated signal + */ + G_max = ((EPS + acc1) / (EPS + acc2)) * limGain[sbr->bs_limiter_gains]; + G_max = min(G_max, 1e10); + + + for (m = ml1; m < ml2; m++) + { + real_t Q_M, G; + real_t Q_div, Q_div2; + uint8_t S_index_mapped; + + + /* check if m is on a noise band border */ + if ((m + sbr->kx) == sbr->f_table_noise[current_f_noise_band+1]) + { + /* step to next noise band */ + current_f_noise_band++; + } + + + /* check if m is on a resolution band border */ + if ((m + sbr->kx) == sbr->f_table_res[sbr->f[ch][l]][current_res_band2+1]) + { + /* step to next resolution band */ + current_res_band2++; + + /* if we move to a new resolution band, we should check if we are + * going to add a sinusoid in this band + */ + S_mapped = get_S_mapped(sbr, ch, l, current_res_band2); + } + + + /* check if m is on a HI_RES band border */ + if ((m + sbr->kx) == sbr->f_table_res[HI_RES][current_hi_res_band+1]) + { + /* step to next HI_RES band */ + current_hi_res_band++; + } + + + /* find S_index_mapped + * S_index_mapped can only be 1 for the m in the middle of the + * current HI_RES band + */ + S_index_mapped = 0; + if ((l >= sbr->l_A[ch]) || + (sbr->bs_add_harmonic_prev[ch][current_hi_res_band] && sbr->bs_add_harmonic_flag_prev[ch])) + { + /* find the middle subband of the HI_RES frequency band */ + if ((m + sbr->kx) == (sbr->f_table_res[HI_RES][current_hi_res_band+1] + sbr->f_table_res[HI_RES][current_hi_res_band]) >> 1) + S_index_mapped = sbr->bs_add_harmonic[ch][current_hi_res_band]; + } + + + /* Q_div: [0..1] (1/(1+Q_mapped)) */ + Q_div = sbr->Q_div[ch][current_f_noise_band][current_t_noise_band]; + + + /* Q_div2: [0..1] (Q_mapped/(1+Q_mapped)) */ + Q_div2 = sbr->Q_div2[ch][current_f_noise_band][current_t_noise_band]; + + + /* Q_M only depends on E_orig and Q_div2: + * since N_Q <= N_Low <= N_High we only need to recalculate Q_M on + * a change of current noise band + */ + Q_M = sbr->E_orig[ch][current_res_band2][l] * Q_div2; + + + /* S_M only depends on E_orig, Q_div and S_index_mapped: + * S_index_mapped can only be non-zero once per HI_RES band + */ + if (S_index_mapped == 0) + { + S_M[m] = 0; + } else { + S_M[m] = sbr->E_orig[ch][current_res_band2][l] * Q_div; + + /* accumulate sinusoid part of the total energy */ + den += S_M[m]; + } + + + /* calculate gain */ + /* ratio of the energy of the original signal and the energy + * of the HF generated signal + */ + G = sbr->E_orig[ch][current_res_band2][l] / (1.0 + sbr->E_curr[ch][m][l]); + if ((S_mapped == 0) && (delta == 1)) + G *= Q_div; + else if (S_mapped == 1) + G *= Q_div2; + + + /* limit the additional noise energy level */ + /* and apply the limiter */ + if (G <= G_max) + { + Q_M_lim[m] = Q_M; + G_lim[m] = G; + } else { + Q_M_lim[m] = Q_M * G_max / G; + G_lim[m] = G_max; + } + + + /* accumulate the total energy */ + den += sbr->E_curr[ch][m][l] * G_lim[m]; + if ((S_index_mapped == 0) && (l != sbr->l_A[ch])) + den += Q_M_lim[m]; + } + + /* G_boost: [0..2.51188643] */ + G_boost = (acc1 + EPS) / (den + EPS); + G_boost = min(G_boost, 2.51188643 /* 1.584893192 ^ 2 */); + + for (m = ml1; m < ml2; m++) + { + /* apply compensation to gain, noise floor sf's and sinusoid levels */ +#ifndef SBR_LOW_POWER + adj->G_lim_boost[l][m] = sqrt(G_lim[m] * G_boost); +#else + /* sqrt() will be done after the aliasing reduction to save a + * few multiplies + */ + adj->G_lim_boost[l][m] = G_lim[m] * G_boost; +#endif + adj->Q_M_lim_boost[l][m] = sqrt(Q_M_lim[m] * G_boost); + + if (S_M[m] != 0) + { + adj->S_M_boost[l][m] = sqrt(S_M[m] * G_boost); + } else { + adj->S_M_boost[l][m] = 0; + } + } + } + } +} +#endif // log2_test + +#endif + +#ifdef SBR_LOW_POWER +static void calc_gain_groups(sbr_info *sbr, sbr_hfadj_info *adj, real_t *deg, uint8_t ch) +{ + uint8_t l, k, i; + uint8_t grouping; + uint8_t S_mapped; + + for (l = 0; l < sbr->L_E[ch]; l++) + { + uint8_t current_res_band = 0; + i = 0; + grouping = 0; + + S_mapped = get_S_mapped(sbr, ch, l, current_res_band); + + for (k = sbr->kx; k < sbr->kx + sbr->M - 1; k++) + { + if (k == sbr->f_table_res[sbr->f[ch][l]][current_res_band+1]) + { + /* step to next resolution band */ + current_res_band++; + + S_mapped = get_S_mapped(sbr, ch, l, current_res_band); + } + + if (deg[k + 1] && S_mapped == 0) + { + if (grouping == 0) + { + sbr->f_group[l][i] = k; + grouping = 1; + i++; + } + } else { + if (grouping) + { + if (S_mapped) + { + sbr->f_group[l][i] = k; + } else { + sbr->f_group[l][i] = k + 1; + } + grouping = 0; + i++; + } + } + } + + if (grouping) + { + sbr->f_group[l][i] = sbr->kx + sbr->M; + i++; + } + + sbr->N_G[l] = (uint8_t)(i >> 1); + } +} + +static void aliasing_reduction(sbr_info *sbr, sbr_hfadj_info *adj, real_t *deg, uint8_t ch) +{ + uint8_t l, k, m; + real_t E_total, E_total_est, G_target, acc; + + for (l = 0; l < sbr->L_E[ch]; l++) + { + for (k = 0; k < sbr->N_G[l]; k++) + { + E_total_est = E_total = 0; + + for (m = sbr->f_group[l][k<<1]; m < sbr->f_group[l][(k<<1) + 1]; m++) + { + /* E_curr: integer */ + /* G_lim_boost: fixed point */ + /* E_total_est: integer */ + /* E_total: integer */ + E_total_est += sbr->E_curr[ch][m-sbr->kx][l]; +#ifdef FIXED_POINT + E_total += MUL_Q2(sbr->E_curr[ch][m-sbr->kx][l], adj->G_lim_boost[l][m-sbr->kx]); +#else + E_total += sbr->E_curr[ch][m-sbr->kx][l] * adj->G_lim_boost[l][m-sbr->kx]; +#endif + } + + /* G_target: fixed point */ + if ((E_total_est + EPS) == 0) + { + G_target = 0; + } else { +#ifdef FIXED_POINT + G_target = (((int64_t)(E_total))<f_group[l][(k<<1)]; m < sbr->f_group[l][(k<<1) + 1]; m++) + { + real_t alpha; + + /* alpha: (COEF) fixed point */ + if (m < sbr->kx + sbr->M - 1) + { + alpha = max(deg[m], deg[m + 1]); + } else { + alpha = deg[m]; + } + + adj->G_lim_boost[l][m-sbr->kx] = MUL_C(alpha, G_target) + + MUL_C((COEF_CONST(1)-alpha), adj->G_lim_boost[l][m-sbr->kx]); + + /* acc: integer */ +#ifdef FIXED_POINT + acc += MUL_Q2(adj->G_lim_boost[l][m-sbr->kx], sbr->E_curr[ch][m-sbr->kx][l]); +#else + acc += adj->G_lim_boost[l][m-sbr->kx] * sbr->E_curr[ch][m-sbr->kx][l]; +#endif + } + + /* acc: fixed point */ + if (acc + EPS == 0) + { + acc = 0; + } else { +#ifdef FIXED_POINT + acc = (((int64_t)(E_total))<f_group[l][(k<<1)]; m < sbr->f_group[l][(k<<1) + 1]; m++) + { +#ifdef FIXED_POINT + adj->G_lim_boost[l][m-sbr->kx] = MUL_Q2(acc, adj->G_lim_boost[l][m-sbr->kx]); +#else + adj->G_lim_boost[l][m-sbr->kx] = acc * adj->G_lim_boost[l][m-sbr->kx]; +#endif + } + } + } + + for (l = 0; l < sbr->L_E[ch]; l++) + { + for (k = 0; k < sbr->N_L[sbr->bs_limiter_bands]; k++) + { + for (m = sbr->f_table_lim[sbr->bs_limiter_bands][k]; + m < sbr->f_table_lim[sbr->bs_limiter_bands][k+1]; m++) + { +#ifdef FIXED_POINT + adj->G_lim_boost[l][m] = SBR_SQRT_Q2(adj->G_lim_boost[l][m]); +#else + adj->G_lim_boost[l][m] = sqrt(adj->G_lim_boost[l][m]); +#endif + } + } + } +} +#endif + +static void hf_assembly(sbr_info *sbr, sbr_hfadj_info *adj, + qmf_t Xsbr[MAX_NTSRHFG][64], uint8_t ch) +{ + static real_t h_smooth[] = { + FRAC_CONST(0.03183050093751), FRAC_CONST(0.11516383427084), + FRAC_CONST(0.21816949906249), FRAC_CONST(0.30150283239582), + FRAC_CONST(0.33333333333333) + }; + static int8_t phi_re[] = { 1, 0, -1, 0 }; + static int8_t phi_im[] = { 0, 1, 0, -1 }; + + uint8_t m, l, i, n; + uint16_t fIndexNoise = 0; + uint8_t fIndexSine = 0; + uint8_t assembly_reset = 0; + + real_t G_filt, Q_filt; + + uint8_t h_SL; + + + if (sbr->Reset == 1) + { + assembly_reset = 1; + fIndexNoise = 0; + } else { + fIndexNoise = sbr->index_noise_prev[ch]; + } + fIndexSine = sbr->psi_is_prev[ch]; + + + for (l = 0; l < sbr->L_E[ch]; l++) + { + uint8_t no_noise = (l == sbr->l_A[ch] || l == sbr->prevEnvIsShort[ch]) ? 1 : 0; + +#ifdef SBR_LOW_POWER + h_SL = 0; +#else + h_SL = (sbr->bs_smoothing_mode == 1) ? 0 : 4; + h_SL = (no_noise ? 0 : h_SL); +#endif + + if (assembly_reset) + { + for (n = 0; n < 4; n++) + { + memcpy(sbr->G_temp_prev[ch][n], adj->G_lim_boost[l], sbr->M*sizeof(real_t)); + memcpy(sbr->Q_temp_prev[ch][n], adj->Q_M_lim_boost[l], sbr->M*sizeof(real_t)); + } + /* reset ringbuffer index */ + sbr->GQ_ringbuf_index[ch] = 4; + assembly_reset = 0; + } + + for (i = sbr->t_E[ch][l]; i < sbr->t_E[ch][l+1]; i++) + { +#ifdef SBR_LOW_POWER + uint8_t i_min1, i_plus1; + uint8_t sinusoids = 0; +#endif + + /* load new values into ringbuffer */ + memcpy(sbr->G_temp_prev[ch][sbr->GQ_ringbuf_index[ch]], adj->G_lim_boost[l], sbr->M*sizeof(real_t)); + memcpy(sbr->Q_temp_prev[ch][sbr->GQ_ringbuf_index[ch]], adj->Q_M_lim_boost[l], sbr->M*sizeof(real_t)); + + for (m = 0; m < sbr->M; m++) + { + qmf_t psi; + + G_filt = 0; + Q_filt = 0; + +#ifndef SBR_LOW_POWER + if (h_SL != 0) + { + uint8_t ri = sbr->GQ_ringbuf_index[ch]; + for (n = 0; n <= 4; n++) + { + real_t curr_h_smooth = h_smooth[n]; + ri++; + if (ri >= 5) + ri -= 5; + G_filt += MUL_F(sbr->G_temp_prev[ch][ri][m], curr_h_smooth); + Q_filt += MUL_F(sbr->Q_temp_prev[ch][ri][m], curr_h_smooth); + } + } else { +#endif + G_filt = sbr->G_temp_prev[ch][sbr->GQ_ringbuf_index[ch]][m]; + Q_filt = sbr->Q_temp_prev[ch][sbr->GQ_ringbuf_index[ch]][m]; +#ifndef SBR_LOW_POWER + } +#endif + if (adj->S_M_boost[l][m] != 0 || no_noise) + Q_filt = 0; + + /* add noise to the output */ + fIndexNoise = (fIndexNoise + 1) & 511; + + /* the smoothed gain values are applied to Xsbr */ + /* V is defined, not calculated */ + //QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) = MUL_Q2(G_filt, QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx])) + // + MUL_F(Q_filt, RE(V[fIndexNoise])); + QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) = MUL_R(G_filt, QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx])) + + MUL_F(Q_filt, RE(V[fIndexNoise])); + if (sbr->bs_extension_id == 3 && sbr->bs_extension_data == 42) + QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) = 16428320; +#ifndef SBR_LOW_POWER + //QMF_IM(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) = MUL_Q2(G_filt, QMF_IM(Xsbr[i + sbr->tHFAdj][m+sbr->kx])) + // + MUL_F(Q_filt, IM(V[fIndexNoise])); + QMF_IM(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) = MUL_R(G_filt, QMF_IM(Xsbr[i + sbr->tHFAdj][m+sbr->kx])) + + MUL_F(Q_filt, IM(V[fIndexNoise])); +#endif + + { + int8_t rev = (((m + sbr->kx) & 1) ? -1 : 1); + QMF_RE(psi) = adj->S_M_boost[l][m] * phi_re[fIndexSine]; + QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) += QMF_RE(psi); + +#ifndef SBR_LOW_POWER + QMF_IM(psi) = rev * adj->S_M_boost[l][m] * phi_im[fIndexSine]; + QMF_IM(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) += QMF_IM(psi); +#else + + i_min1 = (fIndexSine - 1) & 3; + i_plus1 = (fIndexSine + 1) & 3; + + if ((m == 0) && (phi_re[i_plus1] != 0)) + { + QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx - 1]) += + (rev*phi_re[i_plus1] * MUL_F(adj->S_M_boost[l][0], FRAC_CONST(0.00815))); + if (sbr->M != 0) + { + QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) -= + (rev*phi_re[i_plus1] * MUL_F(adj->S_M_boost[l][1], FRAC_CONST(0.00815))); + } + } + if ((m > 0) && (m < sbr->M - 1) && (sinusoids < 16) && (phi_re[i_min1] != 0)) + { + QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) -= + (rev*phi_re[i_min1] * MUL_F(adj->S_M_boost[l][m - 1], FRAC_CONST(0.00815))); + } + if ((m > 0) && (m < sbr->M - 1) && (sinusoids < 16) && (phi_re[i_plus1] != 0)) + { + QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) -= + (rev*phi_re[i_plus1] * MUL_F(adj->S_M_boost[l][m + 1], FRAC_CONST(0.00815))); + } + if ((m == sbr->M - 1) && (sinusoids < 16) && (phi_re[i_min1] != 0)) + { + if (m > 0) + { + QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx]) -= + (rev*phi_re[i_min1] * MUL_F(adj->S_M_boost[l][m - 1], FRAC_CONST(0.00815))); + } + if (m + sbr->kx < 64) + { + QMF_RE(Xsbr[i + sbr->tHFAdj][m+sbr->kx + 1]) += + (rev*phi_re[i_min1] * MUL_F(adj->S_M_boost[l][m], FRAC_CONST(0.00815))); + } + } + + if (adj->S_M_boost[l][m] != 0) + sinusoids++; +#endif + } + } + + fIndexSine = (fIndexSine + 1) & 3; + + /* update the ringbuffer index used for filtering G and Q with h_smooth */ + sbr->GQ_ringbuf_index[ch]++; + if (sbr->GQ_ringbuf_index[ch] >= 5) + sbr->GQ_ringbuf_index[ch] = 0; + } + } + + sbr->index_noise_prev[ch] = fIndexNoise; + sbr->psi_is_prev[ch] = fIndexSine; +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_hfadj.h b/third_party/faad_hdc/upstream/libfaad/sbr_hfadj.h new file mode 100644 index 000000000..624ef5c9d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_hfadj.h @@ -0,0 +1,57 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_hfadj.h,v 1.19 2007/11/01 12:33:35 menno Exp $ +**/ + +#ifndef __SBR_HFADJ_H__ +#define __SBR_HFADJ_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + real_t G_lim_boost[MAX_L_E][MAX_M]; + real_t Q_M_lim_boost[MAX_L_E][MAX_M]; + real_t S_M_boost[MAX_L_E][MAX_M]; +} sbr_hfadj_info; + + +uint8_t hf_adjustment(sbr_info *sbr, qmf_t Xsbr[MAX_NTSRHFG][64] +#ifdef SBR_LOW_POWER + ,real_t *deg +#endif + ,uint8_t ch); + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_hfgen.c b/third_party/faad_hdc/upstream/libfaad/sbr_hfgen.c new file mode 100644 index 000000000..05a9fea69 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_hfgen.c @@ -0,0 +1,684 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_hfgen.c,v 1.26 2007/11/01 12:33:35 menno Exp $ +**/ + +/* High Frequency generation */ + +#include "common.h" +#include "structs.h" + +#ifdef SBR_DEC + +#include "sbr_syntax.h" +#include "sbr_hfgen.h" +#include "sbr_fbt.h" + +/* static function declarations */ +#ifdef SBR_LOW_POWER +static void calc_prediction_coef_lp(sbr_info *sbr, qmf_t Xlow[MAX_NTSRHFG][64], + complex_t *alpha_0, complex_t *alpha_1, real_t *rxx); +static void calc_aliasing_degree(sbr_info *sbr, real_t *rxx, real_t *deg); +#else +static void calc_prediction_coef(sbr_info *sbr, qmf_t Xlow[MAX_NTSRHFG][64], + complex_t *alpha_0, complex_t *alpha_1, uint8_t k); +#endif +static void calc_chirp_factors(sbr_info *sbr, uint8_t ch); +static void patch_construction(sbr_info *sbr); + + +void hf_generation(sbr_info *sbr, qmf_t Xlow[MAX_NTSRHFG][64], + qmf_t Xhigh[MAX_NTSRHFG][64] +#ifdef SBR_LOW_POWER + ,real_t *deg +#endif + ,uint8_t ch) +{ + uint8_t l, i, x; + ALIGN complex_t alpha_0[64], alpha_1[64]; +#ifdef SBR_LOW_POWER + ALIGN real_t rxx[64]; +#endif + + uint8_t offset = sbr->tHFAdj; + uint8_t first = sbr->t_E[ch][0]; + uint8_t last = sbr->t_E[ch][sbr->L_E[ch]]; + + calc_chirp_factors(sbr, ch); + +#ifdef SBR_LOW_POWER + memset(deg, 0, 64*sizeof(real_t)); +#endif + + if ((ch == 0) && (sbr->Reset)) + patch_construction(sbr); + + /* calculate the prediction coefficients */ +#ifdef SBR_LOW_POWER + calc_prediction_coef_lp(sbr, Xlow, alpha_0, alpha_1, rxx); + calc_aliasing_degree(sbr, rxx, deg); +#endif + + /* actual HF generation */ + for (i = 0; i < sbr->noPatches; i++) + { + for (x = 0; x < sbr->patchNoSubbands[i]; x++) + { + real_t a0_r, a0_i, a1_r, a1_i; + real_t bw, bw2; + uint8_t q, p, k, g; + + /* find the low and high band for patching */ + k = sbr->kx + x; + for (q = 0; q < i; q++) + { + k += sbr->patchNoSubbands[q]; + } + p = sbr->patchStartSubband[i] + x; + +#ifdef SBR_LOW_POWER + if (x != 0 /*x < sbr->patchNoSubbands[i]-1*/) + deg[k] = deg[p]; + else + deg[k] = 0; +#endif + + g = sbr->table_map_k_to_g[k]; + + bw = sbr->bwArray[ch][g]; + bw2 = MUL_C(bw, bw); + + /* do the patching */ + /* with or without filtering */ + if (bw2 > 0) + { + real_t temp1_r, temp2_r, temp3_r; +#ifndef SBR_LOW_POWER + real_t temp1_i, temp2_i, temp3_i; + calc_prediction_coef(sbr, Xlow, alpha_0, alpha_1, p); +#endif + + a0_r = MUL_C(RE(alpha_0[p]), bw); + a1_r = MUL_C(RE(alpha_1[p]), bw2); +#ifndef SBR_LOW_POWER + a0_i = MUL_C(IM(alpha_0[p]), bw); + a1_i = MUL_C(IM(alpha_1[p]), bw2); +#endif + + temp2_r = QMF_RE(Xlow[first - 2 + offset][p]); + temp3_r = QMF_RE(Xlow[first - 1 + offset][p]); +#ifndef SBR_LOW_POWER + temp2_i = QMF_IM(Xlow[first - 2 + offset][p]); + temp3_i = QMF_IM(Xlow[first - 1 + offset][p]); +#endif + for (l = first; l < last; l++) + { + temp1_r = temp2_r; + temp2_r = temp3_r; + temp3_r = QMF_RE(Xlow[l + offset][p]); +#ifndef SBR_LOW_POWER + temp1_i = temp2_i; + temp2_i = temp3_i; + temp3_i = QMF_IM(Xlow[l + offset][p]); +#endif + +#ifdef SBR_LOW_POWER + QMF_RE(Xhigh[l + offset][k]) = + temp3_r + +(MUL_R(a0_r, temp2_r) + + MUL_R(a1_r, temp1_r)); +#else + QMF_RE(Xhigh[l + offset][k]) = + temp3_r + +(MUL_R(a0_r, temp2_r) - + MUL_R(a0_i, temp2_i) + + MUL_R(a1_r, temp1_r) - + MUL_R(a1_i, temp1_i)); + QMF_IM(Xhigh[l + offset][k]) = + temp3_i + +(MUL_R(a0_i, temp2_r) + + MUL_R(a0_r, temp2_i) + + MUL_R(a1_i, temp1_r) + + MUL_R(a1_r, temp1_i)); +#endif + } + } else { + for (l = first; l < last; l++) + { + QMF_RE(Xhigh[l + offset][k]) = QMF_RE(Xlow[l + offset][p]); +#ifndef SBR_LOW_POWER + QMF_IM(Xhigh[l + offset][k]) = QMF_IM(Xlow[l + offset][p]); +#endif + } + } + } + } + + if (sbr->Reset) + { + limiter_frequency_table(sbr); + } +} + +typedef struct +{ + complex_t r01; + complex_t r02; + complex_t r11; + complex_t r12; + complex_t r22; + real_t det; +} acorr_coef; + +#ifdef SBR_LOW_POWER +static void auto_correlation(sbr_info *sbr, acorr_coef *ac, + qmf_t buffer[MAX_NTSRHFG][64], + uint8_t bd, uint8_t len) +{ + real_t r01 = 0, r02 = 0, r11 = 0; + int8_t j; + uint8_t offset = sbr->tHFAdj; +#ifdef FIXED_POINT + const real_t rel = FRAC_CONST(0.999999); // 1 / (1 + 1e-6f); + uint32_t maxi = 0; + uint32_t pow2, exp; +#else + const real_t rel = 1 / (1 + 1e-6f); +#endif + + +#ifdef FIXED_POINT + mask = 0; + + for (j = (offset-2); j < (len + offset); j++) + { + real_t x; + x = QMF_RE(buffer[j][bd])>>REAL_BITS; + mask |= x ^ (x >> 31); + } + + exp = wl_min_lzc(mask); + + /* improves accuracy */ + if (exp > 0) + exp -= 1; + + for (j = offset; j < len + offset; j++) + { + real_t buf_j = ((QMF_RE(buffer[j][bd])+(1<<(exp-1)))>>exp); + real_t buf_j_1 = ((QMF_RE(buffer[j-1][bd])+(1<<(exp-1)))>>exp); + real_t buf_j_2 = ((QMF_RE(buffer[j-2][bd])+(1<<(exp-1)))>>exp); + + /* normalisation with rounding */ + r01 += MUL_R(buf_j, buf_j_1); + r02 += MUL_R(buf_j, buf_j_2); + r11 += MUL_R(buf_j_1, buf_j_1); + } + RE(ac->r12) = r01 - + MUL_R(((QMF_RE(buffer[len+offset-1][bd])+(1<<(exp-1)))>>exp), ((QMF_RE(buffer[len+offset-2][bd])+(1<<(exp-1)))>>exp)) + + MUL_R(((QMF_RE(buffer[offset-1][bd])+(1<<(exp-1)))>>exp), ((QMF_RE(buffer[offset-2][bd])+(1<<(exp-1)))>>exp)); + RE(ac->r22) = r11 - + MUL_R(((QMF_RE(buffer[len+offset-2][bd])+(1<<(exp-1)))>>exp), ((QMF_RE(buffer[len+offset-2][bd])+(1<<(exp-1)))>>exp)) + + MUL_R(((QMF_RE(buffer[offset-2][bd])+(1<<(exp-1)))>>exp), ((QMF_RE(buffer[offset-2][bd])+(1<<(exp-1)))>>exp)); +#else + for (j = offset; j < len + offset; j++) + { + r01 += QMF_RE(buffer[j][bd]) * QMF_RE(buffer[j-1][bd]); + r02 += QMF_RE(buffer[j][bd]) * QMF_RE(buffer[j-2][bd]); + r11 += QMF_RE(buffer[j-1][bd]) * QMF_RE(buffer[j-1][bd]); + } + RE(ac->r12) = r01 - + QMF_RE(buffer[len+offset-1][bd]) * QMF_RE(buffer[len+offset-2][bd]) + + QMF_RE(buffer[offset-1][bd]) * QMF_RE(buffer[offset-2][bd]); + RE(ac->r22) = r11 - + QMF_RE(buffer[len+offset-2][bd]) * QMF_RE(buffer[len+offset-2][bd]) + + QMF_RE(buffer[offset-2][bd]) * QMF_RE(buffer[offset-2][bd]); +#endif + RE(ac->r01) = r01; + RE(ac->r02) = r02; + RE(ac->r11) = r11; + + ac->det = MUL_R(RE(ac->r11), RE(ac->r22)) - MUL_F(MUL_R(RE(ac->r12), RE(ac->r12)), rel); +} +#else +static void auto_correlation(sbr_info *sbr, acorr_coef *ac, qmf_t buffer[MAX_NTSRHFG][64], + uint8_t bd, uint8_t len) +{ + real_t r01r = 0, r01i = 0, r02r = 0, r02i = 0, r11r = 0; + real_t temp1_r, temp1_i, temp2_r, temp2_i, temp3_r, temp3_i, temp4_r, temp4_i, temp5_r, temp5_i; +#ifdef FIXED_POINT + const real_t rel = FRAC_CONST(0.999999); // 1 / (1 + 1e-6f); + uint32_t mask, exp; + real_t half; +#else + const real_t rel = 1 / (1 + 1e-6f); +#endif + int8_t j; + uint8_t offset = sbr->tHFAdj; + +#ifdef FIXED_POINT + mask = 0; + + for (j = (offset-2); j < (len + offset); j++) + { + real_t x; + x = QMF_RE(buffer[j][bd])>>REAL_BITS; + mask |= x ^ (x >> 31); + x = QMF_IM(buffer[j][bd])>>REAL_BITS; + mask |= x ^ (x >> 31); + } + + exp = wl_min_lzc(mask); + + /* All-zero input. */ + if (exp == 0) { + RE(ac->r01) = 0; + IM(ac->r01) = 0; + RE(ac->r02) = 0; + IM(ac->r02) = 0; + RE(ac->r11) = 0; + // IM(ac->r11) = 0; // unused + RE(ac->r12) = 0; + IM(ac->r12) = 0; + RE(ac->r22) = 0; + // IM(ac->r22) = 0; // unused + ac->det = 0; + return; + } + /* Otherwise exp > 0. */ + /* improves accuracy */ + exp -= 1; + /* Now exp is 0..31 */ + half = (1 << exp) >> 1; + + temp2_r = (QMF_RE(buffer[offset-2][bd]) + half) >> exp; + temp2_i = (QMF_IM(buffer[offset-2][bd]) + half) >> exp; + temp3_r = (QMF_RE(buffer[offset-1][bd]) + half) >> exp; + temp3_i = (QMF_IM(buffer[offset-1][bd]) + half) >> exp; + // Save these because they are needed after loop + temp4_r = temp2_r; + temp4_i = temp2_i; + temp5_r = temp3_r; + temp5_i = temp3_i; + + for (j = offset; j < len + offset; j++) + { + temp1_r = temp2_r; // temp1_r = (QMF_RE(buffer[offset-2][bd] + (1<<(exp-1))) >> exp; + temp1_i = temp2_i; // temp1_i = (QMF_IM(buffer[offset-2][bd] + (1<<(exp-1))) >> exp; + temp2_r = temp3_r; // temp2_r = (QMF_RE(buffer[offset-1][bd] + (1<<(exp-1))) >> exp; + temp2_i = temp3_i; // temp2_i = (QMF_IM(buffer[offset-1][bd] + (1<<(exp-1))) >> exp; + temp3_r = (QMF_RE(buffer[j][bd]) + half) >> exp; + temp3_i = (QMF_IM(buffer[j][bd]) + half) >> exp; + r01r += MUL_R(temp3_r, temp2_r) + MUL_R(temp3_i, temp2_i); + r01i += MUL_R(temp3_i, temp2_r) - MUL_R(temp3_r, temp2_i); + r02r += MUL_R(temp3_r, temp1_r) + MUL_R(temp3_i, temp1_i); + r02i += MUL_R(temp3_i, temp1_r) - MUL_R(temp3_r, temp1_i); + r11r += MUL_R(temp2_r, temp2_r) + MUL_R(temp2_i, temp2_i); + } + + // These are actual values in temporary variable at this point + // temp1_r = (QMF_RE(buffer[len+offset-1-2][bd] + (1<<(exp-1))) >> exp; + // temp1_i = (QMF_IM(buffer[len+offset-1-2][bd] + (1<<(exp-1))) >> exp; + // temp2_r = (QMF_RE(buffer[len+offset-1-1][bd] + (1<<(exp-1))) >> exp; + // temp2_i = (QMF_IM(buffer[len+offset-1-1][bd] + (1<<(exp-1))) >> exp; + // temp3_r = (QMF_RE(buffer[len+offset-1][bd]) + (1<<(exp-1))) >> exp; + // temp3_i = (QMF_IM(buffer[len+offset-1][bd]) + (1<<(exp-1))) >> exp; + // temp4_r = (QMF_RE(buffer[offset-2][bd]) + (1<<(exp-1))) >> exp; + // temp4_i = (QMF_IM(buffer[offset-2][bd]) + (1<<(exp-1))) >> exp; + // temp5_r = (QMF_RE(buffer[offset-1][bd]) + (1<<(exp-1))) >> exp; + // temp5_i = (QMF_IM(buffer[offset-1][bd]) + (1<<(exp-1))) >> exp; + + RE(ac->r12) = r01r - + (MUL_R(temp3_r, temp2_r) + MUL_R(temp3_i, temp2_i)) + + (MUL_R(temp5_r, temp4_r) + MUL_R(temp5_i, temp4_i)); + IM(ac->r12) = r01i - + (MUL_R(temp3_i, temp2_r) - MUL_R(temp3_r, temp2_i)) + + (MUL_R(temp5_i, temp4_r) - MUL_R(temp5_r, temp4_i)); + RE(ac->r22) = r11r - + (MUL_R(temp2_r, temp2_r) + MUL_R(temp2_i, temp2_i)) + + (MUL_R(temp4_r, temp4_r) + MUL_R(temp4_i, temp4_i)); + +#else + + temp2_r = QMF_RE(buffer[offset-2][bd]); + temp2_i = QMF_IM(buffer[offset-2][bd]); + temp3_r = QMF_RE(buffer[offset-1][bd]); + temp3_i = QMF_IM(buffer[offset-1][bd]); + // Save these because they are needed after loop + temp4_r = temp2_r; + temp4_i = temp2_i; + temp5_r = temp3_r; + temp5_i = temp3_i; + + for (j = offset; j < len + offset; j++) + { + temp1_r = temp2_r; // temp1_r = QMF_RE(buffer[j-2][bd]; + temp1_i = temp2_i; // temp1_i = QMF_IM(buffer[j-2][bd]; + temp2_r = temp3_r; // temp2_r = QMF_RE(buffer[j-1][bd]; + temp2_i = temp3_i; // temp2_i = QMF_IM(buffer[j-1][bd]; + temp3_r = QMF_RE(buffer[j][bd]); + temp3_i = QMF_IM(buffer[j][bd]); + r01r += temp3_r * temp2_r + temp3_i * temp2_i; + r01i += temp3_i * temp2_r - temp3_r * temp2_i; + r02r += temp3_r * temp1_r + temp3_i * temp1_i; + r02i += temp3_i * temp1_r - temp3_r * temp1_i; + r11r += temp2_r * temp2_r + temp2_i * temp2_i; + } + + // These are actual values in temporary variable at this point + // temp1_r = QMF_RE(buffer[len+offset-1-2][bd]; + // temp1_i = QMF_IM(buffer[len+offset-1-2][bd]; + // temp2_r = QMF_RE(buffer[len+offset-1-1][bd]; + // temp2_i = QMF_IM(buffer[len+offset-1-1][bd]; + // temp3_r = QMF_RE(buffer[len+offset-1][bd]); + // temp3_i = QMF_IM(buffer[len+offset-1][bd]); + // temp4_r = QMF_RE(buffer[offset-2][bd]); + // temp4_i = QMF_IM(buffer[offset-2][bd]); + // temp5_r = QMF_RE(buffer[offset-1][bd]); + // temp5_i = QMF_IM(buffer[offset-1][bd]); + + RE(ac->r12) = r01r - + (temp3_r * temp2_r + temp3_i * temp2_i) + + (temp5_r * temp4_r + temp5_i * temp4_i); + IM(ac->r12) = r01i - + (temp3_i * temp2_r - temp3_r * temp2_i) + + (temp5_i * temp4_r - temp5_r * temp4_i); + RE(ac->r22) = r11r - + (temp2_r * temp2_r + temp2_i * temp2_i) + + (temp4_r * temp4_r + temp4_i * temp4_i); + +#endif + + RE(ac->r01) = r01r; + IM(ac->r01) = r01i; + RE(ac->r02) = r02r; + IM(ac->r02) = r02i; + RE(ac->r11) = r11r; + + ac->det = MUL_R(RE(ac->r11), RE(ac->r22)) - MUL_F(rel, (MUL_R(RE(ac->r12), RE(ac->r12)) + MUL_R(IM(ac->r12), IM(ac->r12)))); +} +#endif + +/* calculate linear prediction coefficients using the covariance method */ +#ifndef SBR_LOW_POWER +static void calc_prediction_coef(sbr_info *sbr, qmf_t Xlow[MAX_NTSRHFG][64], + complex_t *alpha_0, complex_t *alpha_1, uint8_t k) +{ + real_t tmp; + acorr_coef ac; + + auto_correlation(sbr, &ac, Xlow, k, sbr->numTimeSlotsRate + 6); + + if (ac.det == 0) + { + RE(alpha_1[k]) = 0; + IM(alpha_1[k]) = 0; + } else { +#ifdef FIXED_POINT + tmp = (MUL_R(RE(ac.r01), RE(ac.r12)) - MUL_R(IM(ac.r01), IM(ac.r12)) - MUL_R(RE(ac.r02), RE(ac.r11))); + RE(alpha_1[k]) = DIV_R(tmp, ac.det); + tmp = (MUL_R(IM(ac.r01), RE(ac.r12)) + MUL_R(RE(ac.r01), IM(ac.r12)) - MUL_R(IM(ac.r02), RE(ac.r11))); + IM(alpha_1[k]) = DIV_R(tmp, ac.det); +#else + tmp = REAL_CONST(1.0) / ac.det; + RE(alpha_1[k]) = (MUL_R(RE(ac.r01), RE(ac.r12)) - MUL_R(IM(ac.r01), IM(ac.r12)) - MUL_R(RE(ac.r02), RE(ac.r11))) * tmp; + IM(alpha_1[k]) = (MUL_R(IM(ac.r01), RE(ac.r12)) + MUL_R(RE(ac.r01), IM(ac.r12)) - MUL_R(IM(ac.r02), RE(ac.r11))) * tmp; +#endif + } + + if (RE(ac.r11) == 0) + { + RE(alpha_0[k]) = 0; + IM(alpha_0[k]) = 0; + } else { +#ifdef FIXED_POINT + tmp = -(RE(ac.r01) + MUL_R(RE(alpha_1[k]), RE(ac.r12)) + MUL_R(IM(alpha_1[k]), IM(ac.r12))); + RE(alpha_0[k]) = DIV_R(tmp, RE(ac.r11)); + tmp = -(IM(ac.r01) + MUL_R(IM(alpha_1[k]), RE(ac.r12)) - MUL_R(RE(alpha_1[k]), IM(ac.r12))); + IM(alpha_0[k]) = DIV_R(tmp, RE(ac.r11)); +#else + tmp = 1.0f / RE(ac.r11); + RE(alpha_0[k]) = -(RE(ac.r01) + MUL_R(RE(alpha_1[k]), RE(ac.r12)) + MUL_R(IM(alpha_1[k]), IM(ac.r12))) * tmp; + IM(alpha_0[k]) = -(IM(ac.r01) + MUL_R(IM(alpha_1[k]), RE(ac.r12)) - MUL_R(RE(alpha_1[k]), IM(ac.r12))) * tmp; +#endif + } + + /* Sanity check; important: use "yes" check to filter-out NaN values. */ + if ((MUL_R(RE(alpha_0[k]),RE(alpha_0[k])) + MUL_R(IM(alpha_0[k]),IM(alpha_0[k])) <= REAL_CONST(16)) && + (MUL_R(RE(alpha_1[k]),RE(alpha_1[k])) + MUL_R(IM(alpha_1[k]),IM(alpha_1[k])) <= REAL_CONST(16))) + return; + /* Fallback */ + RE(alpha_0[k]) = 0; + IM(alpha_0[k]) = 0; + RE(alpha_1[k]) = 0; + IM(alpha_1[k]) = 0; +} +#else +static void calc_prediction_coef_lp(sbr_info *sbr, qmf_t Xlow[MAX_NTSRHFG][64], + complex_t *alpha_0, complex_t *alpha_1, real_t *rxx) +{ + uint8_t k; + real_t tmp; + acorr_coef ac; + + for (k = 1; k < sbr->f_master[0]; k++) + { + auto_correlation(sbr, &ac, Xlow, k, sbr->numTimeSlotsRate + 6); + + if (ac.det == 0) + { + RE(alpha_0[k]) = 0; + RE(alpha_1[k]) = 0; + } else { + tmp = MUL_R(RE(ac.r01), RE(ac.r22)) - MUL_R(RE(ac.r12), RE(ac.r02)); + RE(alpha_0[k]) = DIV_R(tmp, (-ac.det)); + + tmp = MUL_R(RE(ac.r01), RE(ac.r12)) - MUL_R(RE(ac.r02), RE(ac.r11)); + RE(alpha_1[k]) = DIV_R(tmp, ac.det); + } + + if ((RE(alpha_0[k]) >= REAL_CONST(4)) || (RE(alpha_1[k]) >= REAL_CONST(4))) + { + RE(alpha_0[k]) = REAL_CONST(0); + RE(alpha_1[k]) = REAL_CONST(0); + } + + /* reflection coefficient */ + if (RE(ac.r11) == 0) + { + rxx[k] = COEF_CONST(0.0); + } else { + rxx[k] = DIV_C(RE(ac.r01), RE(ac.r11)); + rxx[k] = -rxx[k]; + if (rxx[k] > COEF_CONST(1.0)) rxx[k] = COEF_CONST(1.0); + if (rxx[k] < COEF_CONST(-1.0)) rxx[k] = COEF_CONST(-1.0); + } + } +} + +static void calc_aliasing_degree(sbr_info *sbr, real_t *rxx, real_t *deg) +{ + uint8_t k; + + rxx[0] = COEF_CONST(0.0); + deg[1] = COEF_CONST(0.0); + + for (k = 2; k < sbr->k0; k++) + { + deg[k] = 0.0; + + if ((k % 2 == 0) && (rxx[k] < COEF_CONST(0.0))) + { + if (rxx[k-1] < 0.0) + { + deg[k] = COEF_CONST(1.0); + + if (rxx[k-2] > COEF_CONST(0.0)) + { + deg[k-1] = COEF_CONST(1.0) - MUL_C(rxx[k-1], rxx[k-1]); + } + } else if (rxx[k-2] > COEF_CONST(0.0)) { + deg[k] = COEF_CONST(1.0) - MUL_C(rxx[k-1], rxx[k-1]); + } + } + + if ((k % 2 == 1) && (rxx[k] > COEF_CONST(0.0))) + { + if (rxx[k-1] > COEF_CONST(0.0)) + { + deg[k] = COEF_CONST(1.0); + + if (rxx[k-2] < COEF_CONST(0.0)) + { + deg[k-1] = COEF_CONST(1.0) - MUL_C(rxx[k-1], rxx[k-1]); + } + } else if (rxx[k-2] < COEF_CONST(0.0)) { + deg[k] = COEF_CONST(1.0) - MUL_C(rxx[k-1], rxx[k-1]); + } + } + } +} +#endif + +/* FIXED POINT: bwArray = COEF */ +static real_t mapNewBw(uint8_t invf_mode, uint8_t invf_mode_prev) +{ + switch (invf_mode) + { + case 1: /* LOW */ + if (invf_mode_prev == 0) /* NONE */ + return COEF_CONST(0.6); + else + return COEF_CONST(0.75); + + case 2: /* MID */ + return COEF_CONST(0.9); + + case 3: /* HIGH */ + return COEF_CONST(0.98); + + default: /* NONE */ + if (invf_mode_prev == 1) /* LOW */ + return COEF_CONST(0.6); + else + return COEF_CONST(0.0); + } +} + +/* FIXED POINT: bwArray = COEF */ +static void calc_chirp_factors(sbr_info *sbr, uint8_t ch) +{ + uint8_t i; + + for (i = 0; i < sbr->N_Q; i++) + { + sbr->bwArray[ch][i] = mapNewBw(sbr->bs_invf_mode[ch][i], sbr->bs_invf_mode_prev[ch][i]); + + if (sbr->bwArray[ch][i] < sbr->bwArray_prev[ch][i]) + sbr->bwArray[ch][i] = MUL_F(sbr->bwArray[ch][i], FRAC_CONST(0.75)) + MUL_F(sbr->bwArray_prev[ch][i], FRAC_CONST(0.25)); + else + sbr->bwArray[ch][i] = MUL_F(sbr->bwArray[ch][i], FRAC_CONST(0.90625)) + MUL_F(sbr->bwArray_prev[ch][i], FRAC_CONST(0.09375)); + + if (sbr->bwArray[ch][i] < COEF_CONST(0.015625)) + sbr->bwArray[ch][i] = COEF_CONST(0.0); + + if (sbr->bwArray[ch][i] >= COEF_CONST(0.99609375)) + sbr->bwArray[ch][i] = COEF_CONST(0.99609375); + + sbr->bwArray_prev[ch][i] = sbr->bwArray[ch][i]; + sbr->bs_invf_mode_prev[ch][i] = sbr->bs_invf_mode[ch][i]; + } +} + +static void patch_construction(sbr_info *sbr) +{ + uint8_t i, k; + uint8_t odd, sb; + uint8_t msb = sbr->k0; + uint8_t usb = sbr->kx; + uint8_t goalSbTab[] = { 21, 23, 32, 43, 46, 64, 85, 93, 128, 0, 0, 0 }; + /* (uint8_t)(2.048e6/sbr->sample_rate + 0.5); */ + uint8_t goalSb = goalSbTab[get_sr_index(sbr->sample_rate)]; + + sbr->noPatches = 0; + + if (goalSb < (sbr->kx + sbr->M)) + { + for (i = 0, k = 0; sbr->f_master[i] < goalSb; i++) + k = i+1; + } else { + k = sbr->N_master; + } + + if (sbr->N_master == 0) + { + sbr->noPatches = 0; + sbr->patchNoSubbands[0] = 0; + sbr->patchStartSubband[0] = 0; + + return; + } + + do + { + uint8_t j = k + 1; + + do + { + j--; + + sb = sbr->f_master[j]; + odd = (sb - 2 + sbr->k0) % 2; + } while (sb > (sbr->k0 - 1 + msb - odd)); + + sbr->patchNoSubbands[sbr->noPatches] = max(sb - usb, 0); + sbr->patchStartSubband[sbr->noPatches] = sbr->k0 - odd - + sbr->patchNoSubbands[sbr->noPatches]; + + if (sbr->patchNoSubbands[sbr->noPatches] > 0) + { + usb = sb; + msb = sb; + sbr->noPatches++; + } else { + msb = sbr->kx; + } + + if (sbr->f_master[k] - sb < 3) + k = sbr->N_master; + } while (sb != (sbr->kx + sbr->M)); + + if ((sbr->patchNoSubbands[sbr->noPatches-1] < 3) && (sbr->noPatches > 1)) + { + sbr->noPatches--; + } + + sbr->noPatches = min(sbr->noPatches, 5); +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_hfgen.h b/third_party/faad_hdc/upstream/libfaad/sbr_hfgen.h new file mode 100644 index 000000000..418cdfb57 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_hfgen.h @@ -0,0 +1,49 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_hfgen.h,v 1.20 2007/11/01 12:33:35 menno Exp $ +**/ + +#ifndef __SBR_HFGEN_H__ +#define __SBR_HFGEN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void hf_generation(sbr_info *sbr, qmf_t Xlow[MAX_NTSRHFG][64], + qmf_t Xhigh[MAX_NTSRHFG][64] +#ifdef SBR_LOW_POWER + ,real_t *deg +#endif + ,uint8_t ch); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_huff.c b/third_party/faad_hdc/upstream/libfaad/sbr_huff.c new file mode 100644 index 000000000..274290cae --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_huff.c @@ -0,0 +1,365 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_huff.c,v 1.21 2007/11/01 12:33:35 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#ifdef SBR_DEC + +#include "sbr_syntax.h" +#include "bits.h" +#include "sbr_huff.h" +#include "sbr_e_nf.h" + +/* + * Huffman tables describe structure and symbols of Huffman trees. Huffman tree + * is a kind of binary tree, i. e. each node is either a leaf or has 2 child + * nodes. Items of Huffman table describe non-leaf nodes, that is why those are + * represented by a pair of integers. Depending on a bit value left (0-th) or + * right (1-st) child chosen. If value is non-negative, then it is an index + * of next inner node. Otherwise it encodes a leaf (Huffman symbol). To get the + * symbol value 64 is added to leaf value. Since all integers are int8_t, the + * range of symbol values is -64..63. + * + * Note: table with N entries describes tree with N+1 leaves. + * Note: it is possible to extend range to -64..64, since index 0 is forbidden. + * Note: it is possible to extend the range even further by using deltas instead + * of absolute indices, switching to uint8_t, and employing all the values + * bigger than maximal delta. + */ +typedef const int8_t (*sbr_huff_tab)[2]; + +static const int8_t t_huffman_env_1_5dB[120][2] = { + { 1, 2 }, { -64, -65 }, { 3, 4 }, { -63, -66 }, + { 5, 6 }, { -62, -67 }, { 7, 8 }, { -61, -68 }, + { 9, 10 }, { -60, -69 }, { 11, 12 }, { -59, -70 }, + { 13, 14 }, { -58, -71 }, { 15, 16 }, { -57, -72 }, + { 17, 18 }, { -73, -56 }, { 19, 21 }, { -74, 20 }, + { -55, -75 }, { 22, 26 }, { 23, 24 }, { -54, -76 }, + { -77, 25 }, { -53, -78 }, { 27, 34 }, { 28, 29 }, + { -52, -79 }, { 30, 31 }, { -80, -51 }, { 32, 33 }, + { -83, -82 }, { -81, -50 }, { 35, 57 }, { 36, 40 }, + { 37, 38 }, { -88, -84 }, { -48, 39 }, { -90, -85 }, + { 41, 46 }, { 42, 43 }, { -49, -87 }, { 44, 45 }, + { -89, -86 }, {-124,-123 }, { 47, 50 }, { 48, 49 }, + {-122,-121 }, {-120,-119 }, { 51, 54 }, { 52, 53 }, + {-118,-117 }, {-116,-115 }, { 55, 56 }, {-114,-113 }, + {-112,-111 }, { 58, 89 }, { 59, 74 }, { 60, 67 }, + { 61, 64 }, { 62, 63 }, {-110,-109 }, {-108,-107 }, + { 65, 66 }, {-106,-105 }, {-104,-103 }, { 68, 71 }, + { 69, 70 }, {-102,-101 }, {-100, -99 }, { 72, 73 }, + { -98, -97 }, { -96, -95 }, { 75, 82 }, { 76, 79 }, + { 77, 78 }, { -94, -93 }, { -92, -91 }, { 80, 81 }, + { -47, -46 }, { -45, -44 }, { 83, 86 }, { 84, 85 }, + { -43, -42 }, { -41, -40 }, { 87, 88 }, { -39, -38 }, + { -37, -36 }, { 90, 105 }, { 91, 98 }, { 92, 95 }, + { 93, 94 }, { -35, -34 }, { -33, -32 }, { 96, 97 }, + { -31, -30 }, { -29, -28 }, { 99, 102 }, { 100, 101 }, + { -27, -26 }, { -25, -24 }, { 103, 104 }, { -23, -22 }, + { -21, -20 }, { 106, 113 }, { 107, 110 }, { 108, 109 }, + { -19, -18 }, { -17, -16 }, { 111, 112 }, { -15, -14 }, + { -13, -12 }, { 114, 117 }, { 115, 116 }, { -11, -10 }, + { -9, -8 }, { 118, 119 }, { -7, -6 }, { -5, -4 } +}; + +static const int8_t f_huffman_env_1_5dB[120][2] = { + { 1, 2 }, { -64, -65 }, { 3, 4 }, { -63, -66 }, + { 5, 6 }, { -67, -62 }, { 7, 8 }, { -68, -61 }, + { 9, 10 }, { -69, -60 }, { 11, 13 }, { -70, 12 }, + { -59, -71 }, { 14, 16 }, { -58, 15 }, { -72, -57 }, + { 17, 19 }, { -73, 18 }, { -56, -74 }, { 20, 23 }, + { 21, 22 }, { -55, -75 }, { -54, -53 }, { 24, 27 }, + { 25, 26 }, { -76, -52 }, { -77, -51 }, { 28, 31 }, + { 29, 30 }, { -50, -78 }, { -79, -49 }, { 32, 36 }, + { 33, 34 }, { -48, -47 }, { -80, 35 }, { -81, -82 }, + { 37, 47 }, { 38, 41 }, { 39, 40 }, { -83, -46 }, + { -45, -84 }, { 42, 44 }, { -85, 43 }, { -44, -43 }, + { 45, 46 }, { -88, -87 }, { -86, -90 }, { 48, 66 }, + { 49, 56 }, { 50, 53 }, { 51, 52 }, { -92, -42 }, + { -41, -39 }, { 54, 55 }, {-105, -89 }, { -38, -37 }, + { 57, 60 }, { 58, 59 }, { -94, -91 }, { -40, -36 }, + { 61, 63 }, { -20, 62 }, {-115,-110 }, { 64, 65 }, + {-108,-107 }, {-101, -97 }, { 67, 89 }, { 68, 75 }, + { 69, 72 }, { 70, 71 }, { -95, -93 }, { -34, -27 }, + { 73, 74 }, { -22, -17 }, { -16,-124 }, { 76, 82 }, + { 77, 79 }, {-123, 78 }, {-122,-121 }, { 80, 81 }, + {-120,-119 }, {-118,-117 }, { 83, 86 }, { 84, 85 }, + {-116,-114 }, {-113,-112 }, { 87, 88 }, {-111,-109 }, + {-106,-104 }, { 90, 105 }, { 91, 98 }, { 92, 95 }, + { 93, 94 }, {-103,-102 }, {-100, -99 }, { 96, 97 }, + { -98, -96 }, { -35, -33 }, { 99, 102 }, { 100, 101 }, + { -32, -31 }, { -30, -29 }, { 103, 104 }, { -28, -26 }, + { -25, -24 }, { 106, 113 }, { 107, 110 }, { 108, 109 }, + { -23, -21 }, { -19, -18 }, { 111, 112 }, { -15, -14 }, + { -13, -12 }, { 114, 117 }, { 115, 116 }, { -11, -10 }, + { -9, -8 }, { 118, 119 }, { -7, -6 }, { -5, -4 } +}; + +static const int8_t t_huffman_env_bal_1_5dB[48][2] = { + {-64, 1}, {-62, 2}, {-66, 3}, {-60, 4}, {-68, 5}, + {-58, 6}, {-70, 7}, {-56, 8}, {-72, 9}, {10, 11}, + {-74, -54}, {12, 13}, {-76, -52}, {14, 28}, {15, 21}, + {16, 18}, {-50, 17}, {-78, -48}, {19, 20}, {-112, -110}, + {-108, -106}, {22, 25}, {23, 24}, {-104, -102}, {-100, -98}, + {26, 27}, {-96, -94}, {-92, -90}, {29, 36}, {30, 33}, + {31, 32}, {-88, -86}, {-84, -82}, {34, 35}, {-80, -46}, + {-44, -42}, {37, 41}, {38, 39}, {-40, -38}, {-36, 40}, + {-34, -32}, {42, 45}, {43, 44}, {-30, -28}, {-26, -24}, + {46, 47}, {-22, -20}, {-18, -16} +}; + +static const int8_t f_huffman_env_bal_1_5dB[48][2] = { + {-64, 1}, {-66, 2}, {-62, 3}, {-68, 4}, {-60, 5}, + {-58, 6}, {-70, 7}, {-72, 8}, {-56, 9}, {10, 11}, + {-74, -54}, {-76, 12}, {-52, 13}, {14, 17}, {-78, 15}, + {-50, 16}, {-48, -82}, {18, 32}, {19, 25}, {20, 22}, + {-80, 21}, {-112, -110}, {23, 24}, {-108, -106}, {-104, -102}, + {26, 29}, {27, 28}, {-100, -98}, {-96, -94}, {30, 31}, + {-92, -90}, {-88, -86}, {33, 40}, {34, 37}, {35, 36}, + {-84, -46}, {-44, -42}, {38, 39}, {-40, -38}, {-36, -34}, + {41, 44}, {42, 43}, {-32, -30}, {-28, -26}, {45, 46}, + {-24, -22}, {-20, 47}, {-18, -16} +}; + +static const int8_t t_huffman_env_3_0dB[62][2] = { + { -64, 1 }, { -65, 2 }, { -63, 3 }, { -66, 4 }, + { -62, 5 }, { -67, 6 }, { -61, 7 }, { -68, 8 }, + { -60, 9 }, { 10, 11 }, { -69, -59 }, { 12, 14 }, + { -70, 13 }, { -71, -58 }, { 15, 18 }, { 16, 17 }, + { -72, -57 }, { -73, -74 }, { 19, 22 }, { -56, 20 }, + { -55, 21 }, { -54, -77 }, { 23, 31 }, { 24, 25 }, + { -75, -76 }, { 26, 27 }, { -78, -53 }, { 28, 29 }, + { -52, -95 }, { -94, 30 }, { -93, -92 }, { 32, 47 }, + { 33, 40 }, { 34, 37 }, { 35, 36 }, { -91, -90 }, + { -89, -88 }, { 38, 39 }, { -87, -86 }, { -85, -84 }, + { 41, 44 }, { 42, 43 }, { -83, -82 }, { -81, -80 }, + { 45, 46 }, { -79, -51 }, { -50, -49 }, { 48, 55 }, + { 49, 52 }, { 50, 51 }, { -48, -47 }, { -46, -45 }, + { 53, 54 }, { -44, -43 }, { -42, -41 }, { 56, 59 }, + { 57, 58 }, { -40, -39 }, { -38, -37 }, { 60, 61 }, + { -36, -35 }, { -34, -33 } +}; + +static const int8_t f_huffman_env_3_0dB[62][2] = { + { -64, 1 }, { -65, 2 }, { -63, 3 }, { -66, 4 }, + { -62, 5 }, { -67, 6 }, { 7, 8 }, { -61, -68 }, + { 9, 10 }, { -60, -69 }, { 11, 12 }, { -59, -70 }, + { 13, 14 }, { -58, -71 }, { 15, 16 }, { -57, -72 }, + { 17, 19 }, { -56, 18 }, { -55, -73 }, { 20, 24 }, + { 21, 22 }, { -74, -54 }, { -53, 23 }, { -75, -76 }, + { 25, 30 }, { 26, 27 }, { -52, -51 }, { 28, 29 }, + { -77, -79 }, { -50, -49 }, { 31, 39 }, { 32, 35 }, + { 33, 34 }, { -78, -46 }, { -82, -88 }, { 36, 37 }, + { -83, -48 }, { -47, 38 }, { -86, -85 }, { 40, 47 }, + { 41, 44 }, { 42, 43 }, { -80, -44 }, { -43, -42 }, + { 45, 46 }, { -39, -87 }, { -84, -40 }, { 48, 55 }, + { 49, 52 }, { 50, 51 }, { -95, -94 }, { -93, -92 }, + { 53, 54 }, { -91, -90 }, { -89, -81 }, { 56, 59 }, + { 57, 58 }, { -45, -41 }, { -38, -37 }, { 60, 61 }, + { -36, -35 }, { -34, -33 } +}; + +static const int8_t t_huffman_env_bal_3_0dB[24][2] = { + {-64, 1}, {-62, 2}, {-66, 3}, {-68, 4}, {-60, 5}, {-58, 6}, + {-70, 7}, {-72, 8}, {-56, 9}, {10, 16}, {11, 13}, {-74, 12}, + {-88, -86}, {14, 15}, {-84, -82}, {-80, -78}, {17, 20}, {18, 19}, + {-76, -54}, {-52, -50}, {21, 22}, {-48, -46}, {-44, 23}, {-42, -40} +}; + +static const int8_t f_huffman_env_bal_3_0dB[24][2] = { + {-64, 1}, {-66, 2}, {-62, 3}, {-68, 4}, {-60, 5}, {-58, 6}, + {-70, 7}, {-72, 8}, {-56, 9}, {10, 13}, {-74, 11}, {-54, 12}, + {-52, -88}, {14, 17}, {15, 16}, {-86, -84}, {-82, -80}, {18, 21}, + {19, 20}, {-78, -76}, {-50, -48}, {22, 23}, {-46, -44}, {-42, -40} +}; + +static const int8_t t_huffman_noise_3_0dB[62][2] = { + { -64, 1 }, { -63, 2 }, { -65, 3 }, { -66, 4 }, + { -62, 5 }, { -67, 6 }, { 7, 8 }, { -61, -68 }, + { 9, 30 }, { 10, 15 }, { -60, 11 }, { -69, 12 }, + { 13, 14 }, { -59, -53 }, { -95, -94 }, { 16, 23 }, + { 17, 20 }, { 18, 19 }, { -93, -92 }, { -91, -90 }, + { 21, 22 }, { -89, -88 }, { -87, -86 }, { 24, 27 }, + { 25, 26 }, { -85, -84 }, { -83, -82 }, { 28, 29 }, + { -81, -80 }, { -79, -78 }, { 31, 46 }, { 32, 39 }, + { 33, 36 }, { 34, 35 }, { -77, -76 }, { -75, -74 }, + { 37, 38 }, { -73, -72 }, { -71, -70 }, { 40, 43 }, + { 41, 42 }, { -58, -57 }, { -56, -55 }, { 44, 45 }, + { -54, -52 }, { -51, -50 }, { 47, 54 }, { 48, 51 }, + { 49, 50 }, { -49, -48 }, { -47, -46 }, { 52, 53 }, + { -45, -44 }, { -43, -42 }, { 55, 58 }, { 56, 57 }, + { -41, -40 }, { -39, -38 }, { 59, 60 }, { -37, -36 }, + { -35, 61 }, { -34, -33 } +}; + +static const int8_t t_huffman_noise_bal_3_0dB[24][2] = { + {-64, 1}, {-66, 2}, {-62, 3}, {4, 9}, {-68, 5}, {-60, 6}, + {7, 8}, {-88, -86}, {-84, -82}, {10, 17}, {11, 14}, {12, 13}, + {-80, -78}, {-76, -74}, {15, 16}, {-72, -70}, {-58, -56}, {18, 21}, + {19, 20}, {-54, -52}, {-50, -48}, {22, 23}, {-46, -44}, {-42, -40} +}; + + +static INLINE int16_t sbr_huff_dec(bitfile *ld, sbr_huff_tab t_huff) +{ + uint8_t bit; + int16_t index = 0; + + while (index >= 0) + { + bit = (uint8_t)faad_get1bit(ld); + index = t_huff[index][bit]; + } + + return index + 64; +} + +/* table 10 */ +void sbr_envelope(bitfile *ld, sbr_info *sbr, uint8_t ch) +{ + uint8_t env, band; + int8_t delta = 0; + sbr_huff_tab t_huff, f_huff; + + if ((sbr->L_E[ch] == 1) && (sbr->bs_frame_class[ch] == FIXFIX)) + sbr->amp_res[ch] = 0; + else + sbr->amp_res[ch] = sbr->bs_amp_res; + + if ((sbr->bs_coupling) && (ch == 1)) + { + delta = 1; + if (sbr->amp_res[ch]) + { + t_huff = t_huffman_env_bal_3_0dB; + f_huff = f_huffman_env_bal_3_0dB; + } else { + t_huff = t_huffman_env_bal_1_5dB; + f_huff = f_huffman_env_bal_1_5dB; + } + } else { + delta = 0; + if (sbr->amp_res[ch]) + { + t_huff = t_huffman_env_3_0dB; + f_huff = f_huffman_env_3_0dB; + } else { + t_huff = t_huffman_env_1_5dB; + f_huff = f_huffman_env_1_5dB; + } + } + + for (env = 0; env < sbr->L_E[ch]; env++) + { + if (sbr->bs_df_env[ch][env] == 0) + { + if ((sbr->bs_coupling == 1) && (ch == 1)) + { + if (sbr->amp_res[ch]) + { + sbr->E[ch][0][env] = (uint16_t)(faad_getbits(ld, 5 + DEBUGVAR(1,272,"sbr_envelope(): bs_data_env")) << delta); + } else { + sbr->E[ch][0][env] = (uint16_t)(faad_getbits(ld, 6 + DEBUGVAR(1,273,"sbr_envelope(): bs_data_env")) << delta); + } + } else { + if (sbr->amp_res[ch]) + { + sbr->E[ch][0][env] = (uint16_t)(faad_getbits(ld, 6 + DEBUGVAR(1,274,"sbr_envelope(): bs_data_env")) << delta); + } else { + sbr->E[ch][0][env] = (uint16_t)(faad_getbits(ld, 7 + DEBUGVAR(1,275,"sbr_envelope(): bs_data_env")) << delta); + } + } + + for (band = 1; band < sbr->n[sbr->f[ch][env]]; band++) + { + sbr->E[ch][band][env] = (sbr_huff_dec(ld, f_huff)); + } + + } else { + for (band = 0; band < sbr->n[sbr->f[ch][env]]; band++) + { + sbr->E[ch][band][env] = (sbr_huff_dec(ld, t_huff)); + } + } + } + + extract_envelope_data(sbr, ch); +} + +/* table 11 */ +void sbr_noise(bitfile *ld, sbr_info *sbr, uint8_t ch) +{ + uint8_t noise, band; + int8_t delta = 0; + sbr_huff_tab t_huff, f_huff; + + if ((sbr->bs_coupling == 1) && (ch == 1)) + { + delta = 1; + t_huff = t_huffman_noise_bal_3_0dB; + f_huff = f_huffman_env_bal_3_0dB; + } else { + delta = 0; + t_huff = t_huffman_noise_3_0dB; + f_huff = f_huffman_env_3_0dB; + } + + for (noise = 0; noise < sbr->L_Q[ch]; noise++) + { + if(sbr->bs_df_noise[ch][noise] == 0) + { + if ((sbr->bs_coupling == 1) && (ch == 1)) + { + sbr->Q[ch][0][noise] = (faad_getbits(ld, 5 + DEBUGVAR(1,276,"sbr_noise(): bs_data_noise")) << delta); + } else { + sbr->Q[ch][0][noise] = (faad_getbits(ld, 5 + DEBUGVAR(1,277,"sbr_noise(): bs_data_noise")) << delta); + } + for (band = 1; band < sbr->N_Q; band++) + { + sbr->Q[ch][band][noise] = (sbr_huff_dec(ld, f_huff)); + } + } else { + for (band = 0; band < sbr->N_Q; band++) + { + sbr->Q[ch][band][noise] = (sbr_huff_dec(ld, t_huff)); + } + } + } + + extract_noise_floor_data(sbr, ch); +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_huff.h b/third_party/faad_hdc/upstream/libfaad/sbr_huff.h new file mode 100644 index 000000000..67b937b1d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_huff.h @@ -0,0 +1,46 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_huff.h,v 1.21 2007/11/01 12:33:35 menno Exp $ +**/ + +#ifndef __SBR_HUFF_H__ +#define __SBR_HUFF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +void sbr_envelope(bitfile *ld, sbr_info *sbr, uint8_t ch); +void sbr_noise(bitfile *ld, sbr_info *sbr, uint8_t ch); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_noise.h b/third_party/faad_hdc/upstream/libfaad/sbr_noise.h new file mode 100644 index 000000000..42d993fd0 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_noise.h @@ -0,0 +1,564 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_noise.h,v 1.17 2007/11/01 12:33:35 menno Exp $ +**/ + +#ifndef __SBR_NOISE_H__ +#define __SBR_NOISE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4305) +#pragma warning(disable:4244) +#endif + + +/* Table 1.A.13 Noise table V */ +ALIGN static const complex_t V[] = { + { FRAC_CONST(-0.99948155879974), FRAC_CONST(-0.59483414888382) }, + { FRAC_CONST(0.97113454341888), FRAC_CONST(-0.67528516054153) }, + { FRAC_CONST(0.14130051434040), FRAC_CONST(-0.95090985298157) }, + { FRAC_CONST(-0.47005495429039), FRAC_CONST(-0.37340548634529) }, + { FRAC_CONST(0.80705064535141), FRAC_CONST(0.29653668403625) }, + { FRAC_CONST(-0.38981479406357), FRAC_CONST(0.89572608470917) }, + { FRAC_CONST(-0.01053049881011), FRAC_CONST(-0.66959059238434) }, + { FRAC_CONST(-0.91266369819641), FRAC_CONST(-0.11522938311100) }, + { FRAC_CONST(0.54840421676636), FRAC_CONST(0.75221365690231) }, + { FRAC_CONST(0.40009254217148), FRAC_CONST(-0.98929399251938) }, + { FRAC_CONST(-0.99867975711823), FRAC_CONST(-0.88147068023682) }, + { FRAC_CONST(-0.95531076192856), FRAC_CONST(0.90908759832382) }, + { FRAC_CONST(-0.45725932717323), FRAC_CONST(-0.56716322898865) }, + { FRAC_CONST(-0.72929674386978), FRAC_CONST(-0.98008275032043) }, + { FRAC_CONST(0.75622802972794), FRAC_CONST(0.20950329303741) }, + { FRAC_CONST(0.07069442421198), FRAC_CONST(-0.78247898817062) }, + { FRAC_CONST(0.74496251344681), FRAC_CONST(-0.91169005632401) }, + { FRAC_CONST(-0.96440184116364), FRAC_CONST(-0.94739919900894) }, + { FRAC_CONST(0.30424630641937), FRAC_CONST(-0.49438267946243) }, + { FRAC_CONST(0.66565030813217), FRAC_CONST(0.64652937650681) }, + { FRAC_CONST(0.91697007417679), FRAC_CONST(0.17514097690582) }, + { FRAC_CONST(-0.70774918794632), FRAC_CONST(0.52548652887344) }, + { FRAC_CONST(-0.70051413774490), FRAC_CONST(-0.45340028405190) }, + { FRAC_CONST(-0.99496513605118), FRAC_CONST(-0.90071910619736) }, + { FRAC_CONST(0.98164492845535), FRAC_CONST(-0.77463155984879) }, + { FRAC_CONST(-0.54671579599380), FRAC_CONST(-0.02570928446949) }, + { FRAC_CONST(-0.01689629070461), FRAC_CONST(0.00287506449968) }, + { FRAC_CONST(-0.86110347509384), FRAC_CONST(0.42548584938049) }, + { FRAC_CONST(-0.98892980813980), FRAC_CONST(-0.87881129980087) }, + { FRAC_CONST(0.51756626367569), FRAC_CONST(0.66926783323288) }, + { FRAC_CONST(-0.99635028839111), FRAC_CONST(-0.58107727766037) }, + { FRAC_CONST(-0.99969369173050), FRAC_CONST(0.98369991779327) }, + { FRAC_CONST(0.55266261100769), FRAC_CONST(0.59449058771133) }, + { FRAC_CONST(0.34581178426743), FRAC_CONST(0.94879418611526) }, + { FRAC_CONST(0.62664210796356), FRAC_CONST(-0.74402970075607) }, + { FRAC_CONST(-0.77149701118469), FRAC_CONST(-0.33883658051491) }, + { FRAC_CONST(-0.91592246294022), FRAC_CONST(0.03687901422381) }, + { FRAC_CONST(-0.76285493373871), FRAC_CONST(-0.91371870040894) }, + { FRAC_CONST(0.79788339138031), FRAC_CONST(-0.93180972337723) }, + { FRAC_CONST(0.54473078250885), FRAC_CONST(-0.11919206380844) }, + { FRAC_CONST(-0.85639280080795), FRAC_CONST(0.42429855465889) }, + { FRAC_CONST(-0.92882400751114), FRAC_CONST(0.27871808409691) }, + { FRAC_CONST(-0.11708371341228), FRAC_CONST(-0.99800843000412) }, + { FRAC_CONST(0.21356749534607), FRAC_CONST(-0.90716296434402) }, + { FRAC_CONST(-0.76191693544388), FRAC_CONST(0.99768120050430) }, + { FRAC_CONST(0.98111045360565), FRAC_CONST(-0.95854461193085) }, + { FRAC_CONST(-0.85913270711899), FRAC_CONST(0.95766568183899) }, + { FRAC_CONST(-0.93307244777679), FRAC_CONST(0.49431759119034) }, + { FRAC_CONST(0.30485755205154), FRAC_CONST(-0.70540034770966) }, + { FRAC_CONST(0.85289651155472), FRAC_CONST(0.46766132116318) }, + { FRAC_CONST(0.91328084468842), FRAC_CONST(-0.99839597940445) }, + { FRAC_CONST(-0.05890199914575), FRAC_CONST(0.70741826295853) }, + { FRAC_CONST(0.28398686647415), FRAC_CONST(0.34633556008339) }, + { FRAC_CONST(0.95258164405823), FRAC_CONST(-0.54893416166306) }, + { FRAC_CONST(-0.78566324710846), FRAC_CONST(-0.75568538904190) }, + { FRAC_CONST(-0.95789498090744), FRAC_CONST(-0.20423194766045) }, + { FRAC_CONST(0.82411158084869), FRAC_CONST(0.96654617786407) }, + { FRAC_CONST(-0.65185445547104), FRAC_CONST(-0.88734990358353) }, + { FRAC_CONST(-0.93643605709076), FRAC_CONST(0.99870789051056) }, + { FRAC_CONST(0.91427159309387), FRAC_CONST(-0.98290503025055) }, + { FRAC_CONST(-0.70395684242249), FRAC_CONST(0.58796799182892) }, + { FRAC_CONST(0.00563771976158), FRAC_CONST(0.61768198013306) }, + { FRAC_CONST(0.89065051078796), FRAC_CONST(0.52783352136612) }, + { FRAC_CONST(-0.68683707714081), FRAC_CONST(0.80806946754456) }, + { FRAC_CONST(0.72165340185165), FRAC_CONST(-0.69259858131409) }, + { FRAC_CONST(-0.62928247451782), FRAC_CONST(0.13627037405968) }, + { FRAC_CONST(0.29938435554504), FRAC_CONST(-0.46051329374313) }, + { FRAC_CONST(-0.91781955957413), FRAC_CONST(-0.74012714624405) }, + { FRAC_CONST(0.99298715591431), FRAC_CONST(0.40816611051559) }, + { FRAC_CONST(0.82368296384811), FRAC_CONST(-0.74036049842834) }, + { FRAC_CONST(-0.98512834310532), FRAC_CONST(-0.99972331523895) }, + { FRAC_CONST(-0.95915371179581), FRAC_CONST(-0.99237799644470) }, + { FRAC_CONST(-0.21411126852036), FRAC_CONST(-0.93424820899963) }, + { FRAC_CONST(-0.68821477890015), FRAC_CONST(-0.26892307400703) }, + { FRAC_CONST(0.91851997375488), FRAC_CONST(0.09358228743076) }, + { FRAC_CONST(-0.96062767505646), FRAC_CONST(0.36099094152451) }, + { FRAC_CONST(0.51646184921265), FRAC_CONST(-0.71373331546783) }, + { FRAC_CONST(0.61130720376968), FRAC_CONST(0.46950140595436) }, + { FRAC_CONST(0.47336128354073), FRAC_CONST(-0.27333179116249) }, + { FRAC_CONST(0.90998309850693), FRAC_CONST(0.96715664863586) }, + { FRAC_CONST(0.44844800233841), FRAC_CONST(0.99211573600769) }, + { FRAC_CONST(0.66614890098572), FRAC_CONST(0.96590173244476) }, + { FRAC_CONST(0.74922239780426), FRAC_CONST(-0.89879858493805) }, + { FRAC_CONST(-0.99571585655212), FRAC_CONST(0.52785521745682) }, + { FRAC_CONST(0.97401082515717), FRAC_CONST(-0.16855870187283) }, + { FRAC_CONST(0.72683745622635), FRAC_CONST(-0.48060774803162) }, + { FRAC_CONST(0.95432192087173), FRAC_CONST(0.68849605321884) }, + { FRAC_CONST(-0.72962206602097), FRAC_CONST(-0.76608443260193) }, + { FRAC_CONST(-0.85359477996826), FRAC_CONST(0.88738125562668) }, + { FRAC_CONST(-0.81412428617477), FRAC_CONST(-0.97480767965317) }, + { FRAC_CONST(-0.87930774688721), FRAC_CONST(0.74748307466507) }, + { FRAC_CONST(-0.71573328971863), FRAC_CONST(-0.98570609092712) }, + { FRAC_CONST(0.83524298667908), FRAC_CONST(0.83702534437180) }, + { FRAC_CONST(-0.48086065053940), FRAC_CONST(-0.98848503828049) }, + { FRAC_CONST(0.97139126062393), FRAC_CONST(0.80093622207642) }, + { FRAC_CONST(0.51992827653885), FRAC_CONST(0.80247628688812) }, + { FRAC_CONST(-0.00848591234535), FRAC_CONST(-0.76670128107071) }, + { FRAC_CONST(-0.70294374227524), FRAC_CONST(0.55359911918640) }, + { FRAC_CONST(-0.95894426107407), FRAC_CONST(-0.43265503644943) }, + { FRAC_CONST(0.97079253196716), FRAC_CONST(0.09325857460499) }, + { FRAC_CONST(-0.92404294013977), FRAC_CONST(0.85507702827454) }, + { FRAC_CONST(-0.69506472349167), FRAC_CONST(0.98633414506912) }, + { FRAC_CONST(0.26559203863144), FRAC_CONST(0.73314309120178) }, + { FRAC_CONST(0.28038442134857), FRAC_CONST(0.14537914097309) }, + { FRAC_CONST(-0.74138122797012), FRAC_CONST(0.99310338497162) }, + { FRAC_CONST(-0.01752796024084), FRAC_CONST(-0.82616633176804) }, + { FRAC_CONST(-0.55126774311066), FRAC_CONST(-0.98898541927338) }, + { FRAC_CONST(0.97960901260376), FRAC_CONST(-0.94021445512772) }, + { FRAC_CONST(-0.99196308851242), FRAC_CONST(0.67019015550613) }, + { FRAC_CONST(-0.67684930562973), FRAC_CONST(0.12631492316723) }, + { FRAC_CONST(0.09140039235353), FRAC_CONST(-0.20537731051445) }, + { FRAC_CONST(-0.71658962965012), FRAC_CONST(-0.97788202762604) }, + { FRAC_CONST(0.81014639139175), FRAC_CONST(0.53722649812698) }, + { FRAC_CONST(0.40616992115974), FRAC_CONST(-0.26469007134438) }, + { FRAC_CONST(-0.67680186033249), FRAC_CONST(0.94502049684525) }, + { FRAC_CONST(0.86849772930145), FRAC_CONST(-0.18333598971367) }, + { FRAC_CONST(-0.99500381946564), FRAC_CONST(-0.02634122036397) }, + { FRAC_CONST(0.84329187870026), FRAC_CONST(0.10406957566738) }, + { FRAC_CONST(-0.09215968847275), FRAC_CONST(0.69540011882782) }, + { FRAC_CONST(0.99956172704697), FRAC_CONST(-0.12358541786671) }, + { FRAC_CONST(-0.79732781648636), FRAC_CONST(-0.91582524776459) }, + { FRAC_CONST(0.96349972486496), FRAC_CONST(0.96640455722809) }, + { FRAC_CONST(-0.79942780733109), FRAC_CONST(0.64323902130127) }, + { FRAC_CONST(-0.11566039919853), FRAC_CONST(0.28587844967842) }, + { FRAC_CONST(-0.39922955632210), FRAC_CONST(0.94129604101181) }, + { FRAC_CONST(0.99089199304581), FRAC_CONST(-0.92062628269196) }, + { FRAC_CONST(0.28631284832954), FRAC_CONST(-0.91035044193268) }, + { FRAC_CONST(-0.83302724361420), FRAC_CONST(-0.67330408096313) }, + { FRAC_CONST(0.95404446125031), FRAC_CONST(0.49162766337395) }, + { FRAC_CONST(-0.06449863314629), FRAC_CONST(0.03250560909510) }, + { FRAC_CONST(-0.99575054645538), FRAC_CONST(0.42389783263206) }, + { FRAC_CONST(-0.65501141548157), FRAC_CONST(0.82546114921570) }, + { FRAC_CONST(-0.81254440546036), FRAC_CONST(-0.51627236604691) }, + { FRAC_CONST(-0.99646371603012), FRAC_CONST(0.84490531682968) }, + { FRAC_CONST(0.00287840608507), FRAC_CONST(0.64768260717392) }, + { FRAC_CONST(0.70176988840103), FRAC_CONST(-0.20453028380871) }, + { FRAC_CONST(0.96361881494522), FRAC_CONST(0.40706968307495) }, + { FRAC_CONST(-0.68883758783340), FRAC_CONST(0.91338956356049) }, + { FRAC_CONST(-0.34875586628914), FRAC_CONST(0.71472293138504) }, + { FRAC_CONST(0.91980081796646), FRAC_CONST(0.66507452726364) }, + { FRAC_CONST(-0.99009048938751), FRAC_CONST(0.85868018865585) }, + { FRAC_CONST(0.68865793943405), FRAC_CONST(0.55660319328308) }, + { FRAC_CONST(-0.99484401941299), FRAC_CONST(-0.20052559673786) }, + { FRAC_CONST(0.94214510917664), FRAC_CONST(-0.99696427583694) }, + { FRAC_CONST(-0.67414629459381), FRAC_CONST(0.49548220634460) }, + { FRAC_CONST(-0.47339352965355), FRAC_CONST(-0.85904330015182) }, + { FRAC_CONST(0.14323651790619), FRAC_CONST(-0.94145596027374) }, + { FRAC_CONST(-0.29268294572830), FRAC_CONST(0.05759225040674) }, + { FRAC_CONST(0.43793860077858), FRAC_CONST(-0.78904968500137) }, + { FRAC_CONST(-0.36345127224922), FRAC_CONST(0.64874434471130) }, + { FRAC_CONST(-0.08750604838133), FRAC_CONST(0.97686946392059) }, + { FRAC_CONST(-0.96495270729065), FRAC_CONST(-0.53960305452347) }, + { FRAC_CONST(0.55526942014694), FRAC_CONST(0.78891521692276) }, + { FRAC_CONST(0.73538213968277), FRAC_CONST(0.96452075242996) }, + { FRAC_CONST(-0.30889773368835), FRAC_CONST(-0.80664390325546) }, + { FRAC_CONST(0.03574995696545), FRAC_CONST(-0.97325617074966) }, + { FRAC_CONST(0.98720687627792), FRAC_CONST(0.48409134149551) }, + { FRAC_CONST(-0.81689298152924), FRAC_CONST(-0.90827703475952) }, + { FRAC_CONST(0.67866861820221), FRAC_CONST(0.81284505128860) }, + { FRAC_CONST(-0.15808570384979), FRAC_CONST(0.85279554128647) }, + { FRAC_CONST(0.80723392963409), FRAC_CONST(-0.24717418849468) }, + { FRAC_CONST(0.47788757085800), FRAC_CONST(-0.46333149075508) }, + { FRAC_CONST(0.96367555856705), FRAC_CONST(0.38486748933792) }, + { FRAC_CONST(-0.99143874645233), FRAC_CONST(-0.24945276975632) }, + { FRAC_CONST(0.83081877231598), FRAC_CONST(-0.94780850410461) }, + { FRAC_CONST(-0.58753192424774), FRAC_CONST(0.01290772389621) }, + { FRAC_CONST(0.95538109540939), FRAC_CONST(-0.85557049512863) }, + { FRAC_CONST(-0.96490919589996), FRAC_CONST(-0.64020973443985) }, + { FRAC_CONST(-0.97327101230621), FRAC_CONST(0.12378127872944) }, + { FRAC_CONST(0.91400367021561), FRAC_CONST(0.57972472906113) }, + { FRAC_CONST(-0.99925839900970), FRAC_CONST(0.71084845066071) }, + { FRAC_CONST(-0.86875903606415), FRAC_CONST(-0.20291699469090) }, + { FRAC_CONST(-0.26240035891533), FRAC_CONST(-0.68264555931091) }, + { FRAC_CONST(-0.24664412438869), FRAC_CONST(-0.87642270326614) }, + { FRAC_CONST(0.02416275814176), FRAC_CONST(0.27192914485931) }, + { FRAC_CONST(0.82068622112274), FRAC_CONST(-0.85087788105011) }, + { FRAC_CONST(0.88547372817993), FRAC_CONST(-0.89636802673340) }, + { FRAC_CONST(-0.18173077702522), FRAC_CONST(-0.26152145862579) }, + { FRAC_CONST(0.09355476498604), FRAC_CONST(0.54845124483109) }, + { FRAC_CONST(-0.54668414592743), FRAC_CONST(0.95980775356293) }, + { FRAC_CONST(0.37050989270210), FRAC_CONST(-0.59910142421722) }, + { FRAC_CONST(-0.70373594760895), FRAC_CONST(0.91227668523788) }, + { FRAC_CONST(-0.34600785374641), FRAC_CONST(-0.99441426992416) }, + { FRAC_CONST(-0.68774479627609), FRAC_CONST(-0.30238837003708) }, + { FRAC_CONST(-0.26843291521072), FRAC_CONST(0.83115667104721) }, + { FRAC_CONST(0.49072334170341), FRAC_CONST(-0.45359709858894) }, + { FRAC_CONST(0.38975992798805), FRAC_CONST(0.95515358448029) }, + { FRAC_CONST(-0.97757124900818), FRAC_CONST(0.05305894464254) }, + { FRAC_CONST(-0.17325553297997), FRAC_CONST(-0.92770671844482) }, + { FRAC_CONST(0.99948036670685), FRAC_CONST(0.58285546302795) }, + { FRAC_CONST(-0.64946246147156), FRAC_CONST(0.68645507097244) }, + { FRAC_CONST(-0.12016920745373), FRAC_CONST(-0.57147324085236) }, + { FRAC_CONST(-0.58947455883026), FRAC_CONST(-0.34847131371498) }, + { FRAC_CONST(-0.41815140843391), FRAC_CONST(0.16276422142982) }, + { FRAC_CONST(0.99885648488998), FRAC_CONST(0.11136095225811) }, + { FRAC_CONST(-0.56649613380432), FRAC_CONST(-0.90494865179062) }, + { FRAC_CONST(0.94138020277023), FRAC_CONST(0.35281917452812) }, + { FRAC_CONST(-0.75725078582764), FRAC_CONST(0.53650552034378) }, + { FRAC_CONST(0.20541973412037), FRAC_CONST(-0.94435143470764) }, + { FRAC_CONST(0.99980372190475), FRAC_CONST(0.79835915565491) }, + { FRAC_CONST(0.29078277945518), FRAC_CONST(0.35393777489662) }, + { FRAC_CONST(-0.62858772277832), FRAC_CONST(0.38765692710876) }, + { FRAC_CONST(0.43440905213356), FRAC_CONST(-0.98546332120895) }, + { FRAC_CONST(-0.98298585414886), FRAC_CONST(0.21021524071693) }, + { FRAC_CONST(0.19513028860092), FRAC_CONST(-0.94239830970764) }, + { FRAC_CONST(-0.95476663112640), FRAC_CONST(0.98364555835724) }, + { FRAC_CONST(0.93379634618759), FRAC_CONST(-0.70881992578506) }, + { FRAC_CONST(-0.85235410928726), FRAC_CONST(-0.08342348039150) }, + { FRAC_CONST(-0.86425095796585), FRAC_CONST(-0.45795026421547) }, + { FRAC_CONST(0.38879778981209), FRAC_CONST(0.97274428606033) }, + { FRAC_CONST(0.92045122385025), FRAC_CONST(-0.62433654069901) }, + { FRAC_CONST(0.89162534475327), FRAC_CONST(0.54950958490372) }, + { FRAC_CONST(-0.36834338307381), FRAC_CONST(0.96458297967911) }, + { FRAC_CONST(0.93891763687134), FRAC_CONST(-0.89968353509903) }, + { FRAC_CONST(0.99267655611038), FRAC_CONST(-0.03757034242153) }, + { FRAC_CONST(-0.94063472747803), FRAC_CONST(0.41332337260246) }, + { FRAC_CONST(0.99740225076675), FRAC_CONST(-0.16830494999886) }, + { FRAC_CONST(-0.35899412631989), FRAC_CONST(-0.46633225679398) }, + { FRAC_CONST(0.05237237364054), FRAC_CONST(-0.25640362501144) }, + { FRAC_CONST(0.36703583598137), FRAC_CONST(-0.38653266429901) }, + { FRAC_CONST(0.91653180122375), FRAC_CONST(-0.30587628483772) }, + { FRAC_CONST(0.69000804424286), FRAC_CONST(0.90952169895172) }, + { FRAC_CONST(-0.38658750057220), FRAC_CONST(0.99501574039459) }, + { FRAC_CONST(-0.29250815510750), FRAC_CONST(0.37444993853569) }, + { FRAC_CONST(-0.60182201862335), FRAC_CONST(0.86779648065567) }, + { FRAC_CONST(-0.97418588399887), FRAC_CONST(0.96468526124954) }, + { FRAC_CONST(0.88461571931839), FRAC_CONST(0.57508403062820) }, + { FRAC_CONST(0.05198933184147), FRAC_CONST(0.21269661188126) }, + { FRAC_CONST(-0.53499621152878), FRAC_CONST(0.97241556644440) }, + { FRAC_CONST(-0.49429559707642), FRAC_CONST(0.98183864355087) }, + { FRAC_CONST(-0.98935145139694), FRAC_CONST(-0.40249159932137) }, + { FRAC_CONST(-0.98081380128860), FRAC_CONST(-0.72856897115707) }, + { FRAC_CONST(-0.27338150143623), FRAC_CONST(0.99950921535492) }, + { FRAC_CONST(0.06310802698135), FRAC_CONST(-0.54539585113525) }, + { FRAC_CONST(-0.20461677014828), FRAC_CONST(-0.14209978282452) }, + { FRAC_CONST(0.66223841905594), FRAC_CONST(0.72528582811356) }, + { FRAC_CONST(-0.84764343500137), FRAC_CONST(0.02372316829860) }, + { FRAC_CONST(-0.89039862155914), FRAC_CONST(0.88866579532623) }, + { FRAC_CONST(0.95903307199478), FRAC_CONST(0.76744925975800) }, + { FRAC_CONST(0.73504126071930), FRAC_CONST(-0.03747203201056) }, + { FRAC_CONST(-0.31744435429573), FRAC_CONST(-0.36834111809731) }, + { FRAC_CONST(-0.34110826253891), FRAC_CONST(0.40211221575737) }, + { FRAC_CONST(0.47803884744644), FRAC_CONST(-0.39423218369484) }, + { FRAC_CONST(0.98299193382263), FRAC_CONST(0.01989791356027) }, + { FRAC_CONST(-0.30963072180748), FRAC_CONST(-0.18076720833778) }, + { FRAC_CONST(0.99992591142654), FRAC_CONST(-0.26281872391701) }, + { FRAC_CONST(-0.93149733543396), FRAC_CONST(-0.98313164710999) }, + { FRAC_CONST(0.99923473596573), FRAC_CONST(-0.80142992734909) }, + { FRAC_CONST(-0.26024168729782), FRAC_CONST(-0.75999760627747) }, + { FRAC_CONST(-0.35712513327599), FRAC_CONST(0.19298963248730) }, + { FRAC_CONST(-0.99899083375931), FRAC_CONST(0.74645155668259) }, + { FRAC_CONST(0.86557173728943), FRAC_CONST(0.55593866109848) }, + { FRAC_CONST(0.33408042788506), FRAC_CONST(0.86185956001282) }, + { FRAC_CONST(0.99010735750198), FRAC_CONST(0.04602397605777) }, + { FRAC_CONST(-0.66694271564484), FRAC_CONST(-0.91643613576889) }, + { FRAC_CONST(0.64016789197922), FRAC_CONST(0.15649530291557) }, + { FRAC_CONST(0.99570536613464), FRAC_CONST(0.45844584703445) }, + { FRAC_CONST(-0.63431465625763), FRAC_CONST(0.21079117059708) }, + { FRAC_CONST(-0.07706847041845), FRAC_CONST(-0.89581435918808) }, + { FRAC_CONST(0.98590087890625), FRAC_CONST(0.88241720199585) }, + { FRAC_CONST(0.80099332332611), FRAC_CONST(-0.36851897835732) }, + { FRAC_CONST(0.78368133306503), FRAC_CONST(0.45506998896599) }, + { FRAC_CONST(0.08707806468010), FRAC_CONST(0.80938994884491) }, + { FRAC_CONST(-0.86811882257462), FRAC_CONST(0.39347308874130) }, + { FRAC_CONST(-0.39466530084610), FRAC_CONST(-0.66809433698654) }, + { FRAC_CONST(0.97875326871872), FRAC_CONST(-0.72467839717865) }, + { FRAC_CONST(-0.95038563013077), FRAC_CONST(0.89563220739365) }, + { FRAC_CONST(0.17005239427090), FRAC_CONST(0.54683053493500) }, + { FRAC_CONST(-0.76910793781281), FRAC_CONST(-0.96226614713669) }, + { FRAC_CONST(0.99743282794952), FRAC_CONST(0.42697158455849) }, + { FRAC_CONST(0.95437383651733), FRAC_CONST(0.97002321481705) }, + { FRAC_CONST(0.99578905105591), FRAC_CONST(-0.54106825590134) }, + { FRAC_CONST(0.28058260679245), FRAC_CONST(-0.85361421108246) }, + { FRAC_CONST(0.85256522893906), FRAC_CONST(-0.64567607641220) }, + { FRAC_CONST(-0.50608539581299), FRAC_CONST(-0.65846014022827) }, + { FRAC_CONST(-0.97210735082626), FRAC_CONST(-0.23095212876797) }, + { FRAC_CONST(0.95424050092697), FRAC_CONST(-0.99240148067474) }, + { FRAC_CONST(-0.96926569938660), FRAC_CONST(0.73775655031204) }, + { FRAC_CONST(0.30872163176537), FRAC_CONST(0.41514959931374) }, + { FRAC_CONST(-0.24523839354515), FRAC_CONST(0.63206630945206) }, + { FRAC_CONST(-0.33813264966011), FRAC_CONST(-0.38661777973175) }, + { FRAC_CONST(-0.05826828256249), FRAC_CONST(-0.06940773874521) }, + { FRAC_CONST(-0.22898460924625), FRAC_CONST(0.97054851055145) }, + { FRAC_CONST(-0.18509915471077), FRAC_CONST(0.47565764188766) }, + { FRAC_CONST(-0.10488238185644), FRAC_CONST(-0.87769949436188) }, + { FRAC_CONST(-0.71886587142944), FRAC_CONST(0.78030979633331) }, + { FRAC_CONST(0.99793875217438), FRAC_CONST(0.90041309595108) }, + { FRAC_CONST(0.57563304901123), FRAC_CONST(-0.91034334897995) }, + { FRAC_CONST(0.28909647464752), FRAC_CONST(0.96307784318924) }, + { FRAC_CONST(0.42188999056816), FRAC_CONST(0.48148649930954) }, + { FRAC_CONST(0.93335050344467), FRAC_CONST(-0.43537023663521) }, + { FRAC_CONST(-0.97087377309799), FRAC_CONST(0.86636447906494) }, + { FRAC_CONST(0.36722871661186), FRAC_CONST(0.65291655063629) }, + { FRAC_CONST(-0.81093025207520), FRAC_CONST(0.08778370171785) }, + { FRAC_CONST(-0.26240602135658), FRAC_CONST(-0.92774093151093) }, + { FRAC_CONST(0.83996498584747), FRAC_CONST(0.55839848518372) }, + { FRAC_CONST(-0.99909615516663), FRAC_CONST(-0.96024608612061) }, + { FRAC_CONST(0.74649465084076), FRAC_CONST(0.12144893407822) }, + { FRAC_CONST(-0.74774593114853), FRAC_CONST(-0.26898062229156) }, + { FRAC_CONST(0.95781666040421), FRAC_CONST(-0.79047924280167) }, + { FRAC_CONST(0.95472306013107), FRAC_CONST(-0.08588775992393) }, + { FRAC_CONST(0.48708331584930), FRAC_CONST(0.99999040365219) }, + { FRAC_CONST(0.46332037448883), FRAC_CONST(0.10964126139879) }, + { FRAC_CONST(-0.76497006416321), FRAC_CONST(0.89210927486420) }, + { FRAC_CONST(0.57397389411926), FRAC_CONST(0.35289704799652) }, + { FRAC_CONST(0.75374317169189), FRAC_CONST(0.96705216169357) }, + { FRAC_CONST(-0.59174400568008), FRAC_CONST(-0.89405369758606) }, + { FRAC_CONST(0.75087904930115), FRAC_CONST(-0.29612672328949) }, + { FRAC_CONST(-0.98607856035233), FRAC_CONST(0.25034910440445) }, + { FRAC_CONST(-0.40761056542397), FRAC_CONST(-0.90045571327209) }, + { FRAC_CONST(0.66929268836975), FRAC_CONST(0.98629492521286) }, + { FRAC_CONST(-0.97463697195053), FRAC_CONST(-0.00190223299433) }, + { FRAC_CONST(0.90145510435104), FRAC_CONST(0.99781388044357) }, + { FRAC_CONST(-0.87259286642075), FRAC_CONST(0.99233585596085) }, + { FRAC_CONST(-0.91529458761215), FRAC_CONST(-0.15698707103729) }, + { FRAC_CONST(-0.03305738791823), FRAC_CONST(-0.37205263972282) }, + { FRAC_CONST(0.07223051041365), FRAC_CONST(-0.88805001974106) }, + { FRAC_CONST(0.99498009681702), FRAC_CONST(0.97094357013702) }, + { FRAC_CONST(-0.74904936552048), FRAC_CONST(0.99985486268997) }, + { FRAC_CONST(0.04585228487849), FRAC_CONST(0.99812334775925) }, + { FRAC_CONST(-0.89054954051971), FRAC_CONST(-0.31791913509369) }, + { FRAC_CONST(-0.83782142400742), FRAC_CONST(0.97637635469437) }, + { FRAC_CONST(0.33454805612564), FRAC_CONST(-0.86231517791748) }, + { FRAC_CONST(-0.99707579612732), FRAC_CONST(0.93237990140915) }, + { FRAC_CONST(-0.22827528417110), FRAC_CONST(0.18874759972095) }, + { FRAC_CONST(0.67248046398163), FRAC_CONST(-0.03646211326122) }, + { FRAC_CONST(-0.05146538093686), FRAC_CONST(-0.92599701881409) }, + { FRAC_CONST(0.99947297573090), FRAC_CONST(0.93625229597092) }, + { FRAC_CONST(0.66951125860214), FRAC_CONST(0.98905825614929) }, + { FRAC_CONST(-0.99602955579758), FRAC_CONST(-0.44654715061188) }, + { FRAC_CONST(0.82104903459549), FRAC_CONST(0.99540740251541) }, + { FRAC_CONST(0.99186509847641), FRAC_CONST(0.72022998332977) }, + { FRAC_CONST(-0.65284591913223), FRAC_CONST(0.52186721563339) }, + { FRAC_CONST(0.93885445594788), FRAC_CONST(-0.74895310401917) }, + { FRAC_CONST(0.96735250949860), FRAC_CONST(0.90891814231873) }, + { FRAC_CONST(-0.22225968539715), FRAC_CONST(0.57124030590057) }, + { FRAC_CONST(-0.44132784008980), FRAC_CONST(-0.92688840627670) }, + { FRAC_CONST(-0.85694974660873), FRAC_CONST(0.88844531774521) }, + { FRAC_CONST(0.91783040761948), FRAC_CONST(-0.46356892585754) }, + { FRAC_CONST(0.72556972503662), FRAC_CONST(-0.99899554252625) }, + { FRAC_CONST(-0.99711579084396), FRAC_CONST(0.58211559057236) }, + { FRAC_CONST(0.77638977766037), FRAC_CONST(0.94321835041046) }, + { FRAC_CONST(0.07717324048281), FRAC_CONST(0.58638399839401) }, + { FRAC_CONST(-0.56049829721451), FRAC_CONST(0.82522302865982) }, + { FRAC_CONST(0.98398894071579), FRAC_CONST(0.39467439055443) }, + { FRAC_CONST(0.47546947002411), FRAC_CONST(0.68613046407700) }, + { FRAC_CONST(0.65675091743469), FRAC_CONST(0.18331636488438) }, + { FRAC_CONST(0.03273375332355), FRAC_CONST(-0.74933111667633) }, + { FRAC_CONST(-0.38684144616127), FRAC_CONST(0.51337349414825) }, + { FRAC_CONST(-0.97346270084381), FRAC_CONST(-0.96549361944199) }, + { FRAC_CONST(-0.53282153606415), FRAC_CONST(-0.91423267126083) }, + { FRAC_CONST(0.99817311763763), FRAC_CONST(0.61133575439453) }, + { FRAC_CONST(-0.50254499912262), FRAC_CONST(-0.88829338550568) }, + { FRAC_CONST(0.01995873264968), FRAC_CONST(0.85223513841629) }, + { FRAC_CONST(0.99930381774902), FRAC_CONST(0.94578897953033) }, + { FRAC_CONST(0.82907766103745), FRAC_CONST(-0.06323442608118) }, + { FRAC_CONST(-0.58660709857941), FRAC_CONST(0.96840775012970) }, + { FRAC_CONST(-0.17573736608028), FRAC_CONST(-0.48166921734810) }, + { FRAC_CONST(0.83434289693832), FRAC_CONST(-0.13023450970650) }, + { FRAC_CONST(0.05946491286159), FRAC_CONST(0.20511047542095) }, + { FRAC_CONST(0.81505483388901), FRAC_CONST(-0.94685947895050) }, + { FRAC_CONST(-0.44976380467415), FRAC_CONST(0.40894573926926) }, + { FRAC_CONST(-0.89746475219727), FRAC_CONST(0.99846577644348) }, + { FRAC_CONST(0.39677256345749), FRAC_CONST(-0.74854665994644) }, + { FRAC_CONST(-0.07588948309422), FRAC_CONST(0.74096214771271) }, + { FRAC_CONST(0.76343196630478), FRAC_CONST(0.41746628284454) }, + { FRAC_CONST(-0.74490106105804), FRAC_CONST(0.94725912809372) }, + { FRAC_CONST(0.64880120754242), FRAC_CONST(0.41336661577225) }, + { FRAC_CONST(0.62319535017014), FRAC_CONST(-0.93098312616348) }, + { FRAC_CONST(0.42215818166733), FRAC_CONST(-0.07712787389755) }, + { FRAC_CONST(0.02704554051161), FRAC_CONST(-0.05417517945170) }, + { FRAC_CONST(0.80001771450043), FRAC_CONST(0.91542196273804) }, + { FRAC_CONST(-0.79351830482483), FRAC_CONST(-0.36208897829056) }, + { FRAC_CONST(0.63872361183167), FRAC_CONST(0.08128252625465) }, + { FRAC_CONST(0.52890521287918), FRAC_CONST(0.60048872232437) }, + { FRAC_CONST(0.74238550662994), FRAC_CONST(0.04491915181279) }, + { FRAC_CONST(0.99096131324768), FRAC_CONST(-0.19451183080673) }, + { FRAC_CONST(-0.80412328243256), FRAC_CONST(-0.88513815402985) }, + { FRAC_CONST(-0.64612615108490), FRAC_CONST(0.72198677062988) }, + { FRAC_CONST(0.11657770723104), FRAC_CONST(-0.83662831783295) }, + { FRAC_CONST(-0.95053184032440), FRAC_CONST(-0.96939903497696) }, + { FRAC_CONST(-0.62228870391846), FRAC_CONST(0.82767260074615) }, + { FRAC_CONST(0.03004475869238), FRAC_CONST(-0.99738895893097) }, + { FRAC_CONST(-0.97987216711044), FRAC_CONST(0.36526128649712) }, + { FRAC_CONST(-0.99986982345581), FRAC_CONST(-0.36021611094475) }, + { FRAC_CONST(0.89110648632050), FRAC_CONST(-0.97894251346588) }, + { FRAC_CONST(0.10407960414886), FRAC_CONST(0.77357792854309) }, + { FRAC_CONST(0.95964735746384), FRAC_CONST(-0.35435819625854) }, + { FRAC_CONST(0.50843232870102), FRAC_CONST(0.96107691526413) }, + { FRAC_CONST(0.17006334662437), FRAC_CONST(-0.76854026317596) }, + { FRAC_CONST(0.25872674584389), FRAC_CONST(0.99893301725388) }, + { FRAC_CONST(-0.01115998718888), FRAC_CONST(0.98496019840240) }, + { FRAC_CONST(-0.79598701000214), FRAC_CONST(0.97138410806656) }, + { FRAC_CONST(-0.99264711141586), FRAC_CONST(-0.99542820453644) }, + { FRAC_CONST(-0.99829661846161), FRAC_CONST(0.01877138763666) }, + { FRAC_CONST(-0.70801013708115), FRAC_CONST(0.33680686354637) }, + { FRAC_CONST(-0.70467054843903), FRAC_CONST(0.93272775411606) }, + { FRAC_CONST(0.99846023321152), FRAC_CONST(-0.98725748062134) }, + { FRAC_CONST(-0.63364970684052), FRAC_CONST(-0.16473594307899) }, + { FRAC_CONST(-0.16258217394352), FRAC_CONST(-0.95939123630524) }, + { FRAC_CONST(-0.43645593523979), FRAC_CONST(-0.94805032014847) }, + { FRAC_CONST(-0.99848473072052), FRAC_CONST(0.96245169639587) }, + { FRAC_CONST(-0.16796459257603), FRAC_CONST(-0.98987513780594) }, + { FRAC_CONST(-0.87979227304459), FRAC_CONST(-0.71725726127625) }, + { FRAC_CONST(0.44183099269867), FRAC_CONST(-0.93568974733353) }, + { FRAC_CONST(0.93310177326202), FRAC_CONST(-0.99913311004639) }, + { FRAC_CONST(-0.93941932916641), FRAC_CONST(-0.56409376859665) }, + { FRAC_CONST(-0.88590002059937), FRAC_CONST(0.47624599933624) }, + { FRAC_CONST(0.99971461296082), FRAC_CONST(-0.83889955282211) }, + { FRAC_CONST(-0.75376385450363), FRAC_CONST(0.00814643409103) }, + { FRAC_CONST(0.93887686729431), FRAC_CONST(-0.11284527927637) }, + { FRAC_CONST(0.85126435756683), FRAC_CONST(0.52349251508713) }, + { FRAC_CONST(0.39701420068741), FRAC_CONST(0.81779634952545) }, + { FRAC_CONST(-0.37024465203285), FRAC_CONST(-0.87071657180786) }, + { FRAC_CONST(-0.36024826765060), FRAC_CONST(0.34655734896660) }, + { FRAC_CONST(-0.93388813734055), FRAC_CONST(-0.84476542472839) }, + { FRAC_CONST(-0.65298801660538), FRAC_CONST(-0.18439576029778) }, + { FRAC_CONST(0.11960318684578), FRAC_CONST(0.99899345636368) }, + { FRAC_CONST(0.94292563199997), FRAC_CONST(0.83163905143738) }, + { FRAC_CONST(0.75081145763397), FRAC_CONST(-0.35533222556114) }, + { FRAC_CONST(0.56721979379654), FRAC_CONST(-0.24076835811138) }, + { FRAC_CONST(0.46857765316963), FRAC_CONST(-0.30140233039856) }, + { FRAC_CONST(0.97312313318253), FRAC_CONST(-0.99548190832138) }, + { FRAC_CONST(-0.38299977779388), FRAC_CONST(0.98516911268234) }, + { FRAC_CONST(0.41025799512863), FRAC_CONST(0.02116736955941) }, + { FRAC_CONST(0.09638062119484), FRAC_CONST(0.04411984235048) }, + { FRAC_CONST(-0.85283249616623), FRAC_CONST(0.91475564241409) }, + { FRAC_CONST(0.88866806030273), FRAC_CONST(-0.99735265970230) }, + { FRAC_CONST(-0.48202428221703), FRAC_CONST(-0.96805608272552) }, + { FRAC_CONST(0.27572581171989), FRAC_CONST(0.58634752035141) }, + { FRAC_CONST(-0.65889132022858), FRAC_CONST(0.58835631608963) }, + { FRAC_CONST(0.98838084936142), FRAC_CONST(0.99994349479675) }, + { FRAC_CONST(-0.20651349425316), FRAC_CONST(0.54593044519424) }, + { FRAC_CONST(-0.62126415967941), FRAC_CONST(-0.59893679618835) }, + { FRAC_CONST(0.20320105552673), FRAC_CONST(-0.86879181861877) }, + { FRAC_CONST(-0.97790551185608), FRAC_CONST(0.96290808916092) }, + { FRAC_CONST(0.11112534999847), FRAC_CONST(0.21484763920307) }, + { FRAC_CONST(-0.41368338465691), FRAC_CONST(0.28216838836670) }, + { FRAC_CONST(0.24133038520813), FRAC_CONST(0.51294362545013) }, + { FRAC_CONST(-0.66393411159515), FRAC_CONST(-0.08249679952860) }, + { FRAC_CONST(-0.53697830438614), FRAC_CONST(-0.97649902105331) }, + { FRAC_CONST(-0.97224736213684), FRAC_CONST(0.22081333398819) }, + { FRAC_CONST(0.87392479181290), FRAC_CONST(-0.12796173989773) }, + { FRAC_CONST(0.19050361216068), FRAC_CONST(0.01602615416050) }, + { FRAC_CONST(-0.46353441476822), FRAC_CONST(-0.95249038934708) }, + { FRAC_CONST(-0.07064096629620), FRAC_CONST(-0.94479805231094) }, + { FRAC_CONST(-0.92444086074829), FRAC_CONST(-0.10457590222359) }, + { FRAC_CONST(-0.83822596073151), FRAC_CONST(-0.01695043221116) }, + { FRAC_CONST(0.75214684009552), FRAC_CONST(-0.99955683946609) }, + { FRAC_CONST(-0.42102998495102), FRAC_CONST(0.99720942974091) }, + { FRAC_CONST(-0.72094786167145), FRAC_CONST(-0.35008960962296) }, + { FRAC_CONST(0.78843313455582), FRAC_CONST(0.52851396799088) }, + { FRAC_CONST(0.97394025325775), FRAC_CONST(-0.26695942878723) }, + { FRAC_CONST(0.99206465482712), FRAC_CONST(-0.57010120153427) }, + { FRAC_CONST(0.76789611577988), FRAC_CONST(-0.76519358158112) }, + { FRAC_CONST(-0.82002419233322), FRAC_CONST(-0.73530179262161) }, + { FRAC_CONST(0.81924992799759), FRAC_CONST(0.99698424339294) }, + { FRAC_CONST(-0.26719850301743), FRAC_CONST(0.68903368711472) }, + { FRAC_CONST(-0.43311259150505), FRAC_CONST(0.85321813821793) }, + { FRAC_CONST(0.99194979667664), FRAC_CONST(0.91876250505447) }, + { FRAC_CONST(-0.80691999197006), FRAC_CONST(-0.32627540826797) }, + { FRAC_CONST(0.43080005049706), FRAC_CONST(-0.21919095516205) }, + { FRAC_CONST(0.67709493637085), FRAC_CONST(-0.95478075742722) }, + { FRAC_CONST(0.56151771545410), FRAC_CONST(-0.70693808794022) }, + { FRAC_CONST(0.10831862688065), FRAC_CONST(-0.08628837019205) }, + { FRAC_CONST(0.91229414939880), FRAC_CONST(-0.65987348556519) }, + { FRAC_CONST(-0.48972892761230), FRAC_CONST(0.56289243698120) }, + { FRAC_CONST(-0.89033657312393), FRAC_CONST(-0.71656566858292) }, + { FRAC_CONST(0.65269446372986), FRAC_CONST(0.65916007757187) }, + { FRAC_CONST(0.67439478635788), FRAC_CONST(-0.81684380769730) }, + { FRAC_CONST(-0.47770830988884), FRAC_CONST(-0.16789555549622) }, + { FRAC_CONST(-0.99715977907181), FRAC_CONST(-0.93565785884857) }, + { FRAC_CONST(-0.90889590978622), FRAC_CONST(0.62034398317337) }, + { FRAC_CONST(-0.06618622690439), FRAC_CONST(-0.23812216520309) }, + { FRAC_CONST(0.99430269002914), FRAC_CONST(0.18812555074692) }, + { FRAC_CONST(0.97686403989792), FRAC_CONST(-0.28664535284042) }, + { FRAC_CONST(0.94813650846481), FRAC_CONST(-0.97506642341614) }, + { FRAC_CONST(-0.95434498786926), FRAC_CONST(-0.79607981443405) }, + { FRAC_CONST(-0.49104782938957), FRAC_CONST(0.32895213365555) }, + { FRAC_CONST(0.99881172180176), FRAC_CONST(0.88993984460831) }, + { FRAC_CONST(0.50449168682098), FRAC_CONST(-0.85995072126389) }, + { FRAC_CONST(0.47162890434265), FRAC_CONST(-0.18680204451084) }, + { FRAC_CONST(-0.62081581354141), FRAC_CONST(0.75000673532486) }, + { FRAC_CONST(-0.43867015838623), FRAC_CONST(0.99998068809509) }, + { FRAC_CONST(0.98630565404892), FRAC_CONST(-0.53578901290894) }, + { FRAC_CONST(-0.61510360240936), FRAC_CONST(-0.89515018463135) }, + { FRAC_CONST(-0.03841517493129), FRAC_CONST(-0.69888818264008) }, + { FRAC_CONST(-0.30102157592773), FRAC_CONST(-0.07667808979750) }, + { FRAC_CONST(0.41881284117699), FRAC_CONST(0.02188098989427) }, + { FRAC_CONST(-0.86135452985764), FRAC_CONST(0.98947483301163) }, + { FRAC_CONST(0.67226862907410), FRAC_CONST(-0.13494388759136) }, + { FRAC_CONST(-0.70737397670746), FRAC_CONST(-0.76547348499298) }, + { FRAC_CONST(0.94044947624207), FRAC_CONST(0.09026201069355) }, + { FRAC_CONST(-0.82386350631714), FRAC_CONST(0.08924768865108) }, + { FRAC_CONST(-0.32070666551590), FRAC_CONST(0.50143420696259) }, + { FRAC_CONST(0.57593160867691), FRAC_CONST(-0.98966425657272) }, + { FRAC_CONST(-0.36326017975807), FRAC_CONST(0.07440242916346) }, + { FRAC_CONST(0.99979043006897), FRAC_CONST(-0.14130286872387) }, + { FRAC_CONST(-0.92366021871567), FRAC_CONST(-0.97979295253754) }, + { FRAC_CONST(-0.44607177376747), FRAC_CONST(-0.54233253002167) }, + { FRAC_CONST(0.44226801395416), FRAC_CONST(0.71326756477356) }, + { FRAC_CONST(0.03671907261014), FRAC_CONST(0.63606387376785) }, + { FRAC_CONST(0.52175426483154), FRAC_CONST(-0.85396826267242) }, + { FRAC_CONST(-0.94701141119003), FRAC_CONST(-0.01826348155737) }, + { FRAC_CONST(-0.98759609460831), FRAC_CONST(0.82288712263107) }, + { FRAC_CONST(0.87434792518616), FRAC_CONST(0.89399492740631) }, + { FRAC_CONST(-0.93412041664124), FRAC_CONST(0.41374051570892) }, + { FRAC_CONST(0.96063941717148), FRAC_CONST(0.93116706609726) }, + { FRAC_CONST(0.97534251213074), FRAC_CONST(0.86150932312012) }, + { FRAC_CONST(0.99642467498779), FRAC_CONST(0.70190042257309) }, + { FRAC_CONST(-0.94705086946487), FRAC_CONST(-0.29580041766167) }, + { FRAC_CONST(0.91599804162979), FRAC_CONST(-0.98147833347321) } +}; + +#ifdef __cplusplus + +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_qmf.c b/third_party/faad_hdc/upstream/libfaad/sbr_qmf.c new file mode 100644 index 000000000..84b74cef5 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_qmf.c @@ -0,0 +1,635 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_qmf.c,v 1.32 2007/11/01 12:33:36 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#ifdef SBR_DEC + + +#include +#include "sbr_dct.h" +#include "sbr_qmf.h" +#include "sbr_qmf_c.h" +#include "sbr_syntax.h" + +qmfa_info *qmfa_init(uint8_t channels) +{ + qmfa_info *qmfa = (qmfa_info*)faad_malloc(sizeof(qmfa_info)); + + /* x is implemented as double ringbuffer */ + qmfa->x = (real_t*)faad_malloc(2 * channels * 10 * sizeof(real_t)); + memset(qmfa->x, 0, 2 * channels * 10 * sizeof(real_t)); + + /* ringbuffer index */ + qmfa->x_index = 0; + + qmfa->channels = channels; + + return qmfa; +} + +void qmfa_end(qmfa_info *qmfa) +{ + if (qmfa) + { + if (qmfa->x) faad_free(qmfa->x); + faad_free(qmfa); + } +} + +void sbr_qmf_analysis_32(sbr_info *sbr, qmfa_info *qmfa, const real_t *input, + qmf_t X[MAX_NTSRHFG][64], uint8_t offset, uint8_t kx) +{ + ALIGN real_t u[64]; +#ifndef SBR_LOW_POWER + ALIGN real_t in_real[32], in_imag[32], out_real[32], out_imag[32]; +#else + ALIGN real_t y[32]; +#endif + uint32_t in = 0; + uint8_t l; + + /* qmf subsample l */ + for (l = 0; l < sbr->numTimeSlotsRate; l++) + { + int16_t n; + + /* shift input buffer x */ + /* input buffer is not shifted anymore, x is implemented as double ringbuffer */ + //memmove(qmfa->x + 32, qmfa->x, (320-32)*sizeof(real_t)); + + /* add new samples to input buffer x */ + for (n = 32 - 1; n >= 0; n--) + { +#ifdef FIXED_POINT + qmfa->x[qmfa->x_index + n] = qmfa->x[qmfa->x_index + n + 320] = (input[in++]) >> 4; +#else + qmfa->x[qmfa->x_index + n] = qmfa->x[qmfa->x_index + n + 320] = input[in++]; +#endif + } + + /* window and summation to create array u */ + for (n = 0; n < 64; n++) + { + u[n] = MUL_F(qmfa->x[qmfa->x_index + n], qmf_c[2*n]) + + MUL_F(qmfa->x[qmfa->x_index + n + 64], qmf_c[2*(n + 64)]) + + MUL_F(qmfa->x[qmfa->x_index + n + 128], qmf_c[2*(n + 128)]) + + MUL_F(qmfa->x[qmfa->x_index + n + 192], qmf_c[2*(n + 192)]) + + MUL_F(qmfa->x[qmfa->x_index + n + 256], qmf_c[2*(n + 256)]); + } + + /* update ringbuffer index */ + qmfa->x_index -= 32; + if (qmfa->x_index < 0) + qmfa->x_index = (320-32); + + /* calculate 32 subband samples by introducing X */ +#ifdef SBR_LOW_POWER + y[0] = u[48]; + for (n = 1; n < 16; n++) + y[n] = u[n+48] + u[48-n]; + for (n = 16; n < 32; n++) + y[n] = -u[n-16] + u[48-n]; + + DCT3_32_unscaled(u, y); + + for (n = 0; n < 32; n++) + { + if (n < kx) + { +#ifdef FIXED_POINT + QMF_RE(X[l + offset][n]) = u[n] /*<< 1*/; +#else + QMF_RE(X[l + offset][n]) = 2. * u[n]; +#endif + } else { + QMF_RE(X[l + offset][n]) = 0; + } + } +#else + + // Reordering of data moved from DCT_IV to here + in_imag[31] = u[1]; + in_real[0] = u[0]; + for (n = 1; n < 31; n++) + { + in_imag[31 - n] = u[n+1]; + in_real[n] = -u[64-n]; + } + in_imag[0] = u[32]; + in_real[31] = -u[33]; + + // dct4_kernel is DCT_IV without reordering which is done before and after FFT + dct4_kernel(in_real, in_imag, out_real, out_imag); + + // Reordering of data moved from DCT_IV to here + for (n = 0; n < 16; n++) { + if (2*n+1 < kx) { +#ifdef FIXED_POINT + QMF_RE(X[l + offset][2*n]) = out_real[n]; + QMF_IM(X[l + offset][2*n]) = out_imag[n]; + QMF_RE(X[l + offset][2*n+1]) = -out_imag[31-n]; + QMF_IM(X[l + offset][2*n+1]) = -out_real[31-n]; +#else + QMF_RE(X[l + offset][2*n]) = 2. * out_real[n]; + QMF_IM(X[l + offset][2*n]) = 2. * out_imag[n]; + QMF_RE(X[l + offset][2*n+1]) = -2. * out_imag[31-n]; + QMF_IM(X[l + offset][2*n+1]) = -2. * out_real[31-n]; +#endif + } else { + if (2*n < kx) { +#ifdef FIXED_POINT + QMF_RE(X[l + offset][2*n]) = out_real[n]; + QMF_IM(X[l + offset][2*n]) = out_imag[n]; +#else + QMF_RE(X[l + offset][2*n]) = 2. * out_real[n]; + QMF_IM(X[l + offset][2*n]) = 2. * out_imag[n]; +#endif + } + else { + QMF_RE(X[l + offset][2*n]) = 0; + QMF_IM(X[l + offset][2*n]) = 0; + } + QMF_RE(X[l + offset][2*n+1]) = 0; + QMF_IM(X[l + offset][2*n+1]) = 0; + } + } +#endif + } +} + +static const complex_t qmf32_pre_twiddle[] = +{ + { FRAC_CONST(0.999924701839145), FRAC_CONST(-0.012271538285720) }, + { FRAC_CONST(0.999322384588350), FRAC_CONST(-0.036807222941359) }, + { FRAC_CONST(0.998118112900149), FRAC_CONST(-0.061320736302209) }, + { FRAC_CONST(0.996312612182778), FRAC_CONST(-0.085797312344440) }, + { FRAC_CONST(0.993906970002356), FRAC_CONST(-0.110222207293883) }, + { FRAC_CONST(0.990902635427780), FRAC_CONST(-0.134580708507126) }, + { FRAC_CONST(0.987301418157858), FRAC_CONST(-0.158858143333861) }, + { FRAC_CONST(0.983105487431216), FRAC_CONST(-0.183039887955141) }, + { FRAC_CONST(0.978317370719628), FRAC_CONST(-0.207111376192219) }, + { FRAC_CONST(0.972939952205560), FRAC_CONST(-0.231058108280671) }, + { FRAC_CONST(0.966976471044852), FRAC_CONST(-0.254865659604515) }, + { FRAC_CONST(0.960430519415566), FRAC_CONST(-0.278519689385053) }, + { FRAC_CONST(0.953306040354194), FRAC_CONST(-0.302005949319228) }, + { FRAC_CONST(0.945607325380521), FRAC_CONST(-0.325310292162263) }, + { FRAC_CONST(0.937339011912575), FRAC_CONST(-0.348418680249435) }, + { FRAC_CONST(0.928506080473216), FRAC_CONST(-0.371317193951838) }, + { FRAC_CONST(0.919113851690058), FRAC_CONST(-0.393992040061048) }, + { FRAC_CONST(0.909167983090522), FRAC_CONST(-0.416429560097637) }, + { FRAC_CONST(0.898674465693954), FRAC_CONST(-0.438616238538528) }, + { FRAC_CONST(0.887639620402854), FRAC_CONST(-0.460538710958240) }, + { FRAC_CONST(0.876070094195407), FRAC_CONST(-0.482183772079123) }, + { FRAC_CONST(0.863972856121587), FRAC_CONST(-0.503538383725718) }, + { FRAC_CONST(0.851355193105265), FRAC_CONST(-0.524589682678469) }, + { FRAC_CONST(0.838224705554838), FRAC_CONST(-0.545324988422046) }, + { FRAC_CONST(0.824589302785025), FRAC_CONST(-0.565731810783613) }, + { FRAC_CONST(0.810457198252595), FRAC_CONST(-0.585797857456439) }, + { FRAC_CONST(0.795836904608884), FRAC_CONST(-0.605511041404326) }, + { FRAC_CONST(0.780737228572094), FRAC_CONST(-0.624859488142386) }, + { FRAC_CONST(0.765167265622459), FRAC_CONST(-0.643831542889791) }, + { FRAC_CONST(0.749136394523459), FRAC_CONST(-0.662415777590172) }, + { FRAC_CONST(0.732654271672413), FRAC_CONST(-0.680600997795453) }, + { FRAC_CONST(0.715730825283819), FRAC_CONST(-0.698376249408973) } +}; + +qmfs_info *qmfs_init(uint8_t channels) +{ + qmfs_info *qmfs = (qmfs_info*)faad_malloc(sizeof(qmfs_info)); + + /* v is a double ringbuffer */ + qmfs->v = (real_t*)faad_malloc(2 * channels * 20 * sizeof(real_t)); + memset(qmfs->v, 0, 2 * channels * 20 * sizeof(real_t)); + + qmfs->v_index = 0; + + qmfs->channels = channels; + + return qmfs; +} + +void qmfs_end(qmfs_info *qmfs) +{ + if (qmfs) + { + if (qmfs->v) faad_free(qmfs->v); + faad_free(qmfs); + } +} + +#ifdef SBR_LOW_POWER + +void sbr_qmf_synthesis_32(sbr_info *sbr, qmfs_info *qmfs, qmf_t X[MAX_NTSRHFG][64], + real_t *output) +{ + ALIGN real_t x[16]; + ALIGN real_t y[16]; + int32_t n, k, out = 0; + uint8_t l; + + /* qmf subsample l */ + for (l = 0; l < sbr->numTimeSlotsRate; l++) + { + /* shift buffers */ + /* we are not shifting v, it is a double ringbuffer */ + //memmove(qmfs->v + 64, qmfs->v, (640-64)*sizeof(real_t)); + + /* calculate 64 samples */ + for (k = 0; k < 16; k++) + { +#ifdef FIXED_POINT + y[k] = (QMF_RE(X[l][k]) - QMF_RE(X[l][31 - k])); + x[k] = (QMF_RE(X[l][k]) + QMF_RE(X[l][31 - k])); +#else + y[k] = (QMF_RE(X[l][k]) - QMF_RE(X[l][31 - k])) / 32.0; + x[k] = (QMF_RE(X[l][k]) + QMF_RE(X[l][31 - k])) / 32.0; +#endif + } + + /* even n samples */ + DCT2_16_unscaled(x, x); + /* odd n samples */ + DCT4_16(y, y); + + for (n = 8; n < 24; n++) + { + qmfs->v[qmfs->v_index + n*2] = qmfs->v[qmfs->v_index + 640 + n*2] = x[n-8]; + qmfs->v[qmfs->v_index + n*2+1] = qmfs->v[qmfs->v_index + 640 + n*2+1] = y[n-8]; + } + for (n = 0; n < 16; n++) + { + qmfs->v[qmfs->v_index + n] = qmfs->v[qmfs->v_index + 640 + n] = qmfs->v[qmfs->v_index + 32-n]; + } + qmfs->v[qmfs->v_index + 48] = qmfs->v[qmfs->v_index + 640 + 48] = 0; + for (n = 1; n < 16; n++) + { + qmfs->v[qmfs->v_index + 48+n] = qmfs->v[qmfs->v_index + 640 + 48+n] = -qmfs->v[qmfs->v_index + 48-n]; + } + + /* calculate 32 output samples and window */ + for (k = 0; k < 32; k++) + { + output[out++] = MUL_F(qmfs->v[qmfs->v_index + k], qmf_c[2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 96 + k], qmf_c[64 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 128 + k], qmf_c[128 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 224 + k], qmf_c[192 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 256 + k], qmf_c[256 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 352 + k], qmf_c[320 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 384 + k], qmf_c[384 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 480 + k], qmf_c[448 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 512 + k], qmf_c[512 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 608 + k], qmf_c[576 + 2*k]); + } + + /* update the ringbuffer index */ + qmfs->v_index -= 64; + if (qmfs->v_index < 0) + qmfs->v_index = (640-64); + } +} + +void sbr_qmf_synthesis_64(sbr_info *sbr, qmfs_info *qmfs, qmf_t X[MAX_NTSRHFG][64], + real_t *output) +{ + ALIGN real_t x[64]; + ALIGN real_t y[64]; + int32_t n, k, out = 0; + uint8_t l; + + + /* qmf subsample l */ + for (l = 0; l < sbr->numTimeSlotsRate; l++) + { + /* shift buffers */ + /* we are not shifting v, it is a double ringbuffer */ + //memmove(qmfs->v + 128, qmfs->v, (1280-128)*sizeof(real_t)); + + /* calculate 128 samples */ + for (k = 0; k < 32; k++) + { +#ifdef FIXED_POINT + y[k] = (QMF_RE(X[l][k]) - QMF_RE(X[l][63 - k])); + x[k] = (QMF_RE(X[l][k]) + QMF_RE(X[l][63 - k])); +#else + y[k] = (QMF_RE(X[l][k]) - QMF_RE(X[l][63 - k])) / 32.0; + x[k] = (QMF_RE(X[l][k]) + QMF_RE(X[l][63 - k])) / 32.0; +#endif + } + + /* even n samples */ + DCT2_32_unscaled(x, x); + /* odd n samples */ + DCT4_32(y, y); + + for (n = 16; n < 48; n++) + { + qmfs->v[qmfs->v_index + n*2] = qmfs->v[qmfs->v_index + 1280 + n*2] = x[n-16]; + qmfs->v[qmfs->v_index + n*2+1] = qmfs->v[qmfs->v_index + 1280 + n*2+1] = y[n-16]; + } + for (n = 0; n < 32; n++) + { + qmfs->v[qmfs->v_index + n] = qmfs->v[qmfs->v_index + 1280 + n] = qmfs->v[qmfs->v_index + 64-n]; + } + qmfs->v[qmfs->v_index + 96] = qmfs->v[qmfs->v_index + 1280 + 96] = 0; + for (n = 1; n < 32; n++) + { + qmfs->v[qmfs->v_index + 96+n] = qmfs->v[qmfs->v_index + 1280 + 96+n] = -qmfs->v[qmfs->v_index + 96-n]; + } + + /* calculate 64 output samples and window */ + for (k = 0; k < 64; k++) + { + output[out++] = MUL_F(qmfs->v[qmfs->v_index + k], qmf_c[k]) + + MUL_F(qmfs->v[qmfs->v_index + 192 + k], qmf_c[64 + k]) + + MUL_F(qmfs->v[qmfs->v_index + 256 + k], qmf_c[128 + k]) + + MUL_F(qmfs->v[qmfs->v_index + 256 + 192 + k], qmf_c[128 + 64 + k]) + + MUL_F(qmfs->v[qmfs->v_index + 512 + k], qmf_c[256 + k]) + + MUL_F(qmfs->v[qmfs->v_index + 512 + 192 + k], qmf_c[256 + 64 + k]) + + MUL_F(qmfs->v[qmfs->v_index + 768 + k], qmf_c[384 + k]) + + MUL_F(qmfs->v[qmfs->v_index + 768 + 192 + k], qmf_c[384 + 64 + k]) + + MUL_F(qmfs->v[qmfs->v_index + 1024 + k], qmf_c[512 + k]) + + MUL_F(qmfs->v[qmfs->v_index + 1024 + 192 + k], qmf_c[512 + 64 + k]); + } + + /* update the ringbuffer index */ + qmfs->v_index -= 128; + if (qmfs->v_index < 0) + qmfs->v_index = (1280-128); + } +} +#else +void sbr_qmf_synthesis_32(sbr_info *sbr, qmfs_info *qmfs, qmf_t X[MAX_NTSRHFG][64], + real_t *output) +{ + ALIGN real_t x1[32], x2[32]; +#ifndef FIXED_POINT + real_t scale = 1.f/64.f; +#endif + int32_t n, k, out = 0; + uint8_t l; + + + /* qmf subsample l */ + for (l = 0; l < sbr->numTimeSlotsRate; l++) + { + /* shift buffer v */ + /* buffer is not shifted, we are using a ringbuffer */ + //memmove(qmfs->v + 64, qmfs->v, (640-64)*sizeof(real_t)); + + /* calculate 64 samples */ + /* complex pre-twiddle */ + for (k = 0; k < 32; k++) + { + x1[k] = MUL_F(QMF_RE(X[l][k]), RE(qmf32_pre_twiddle[k])) - MUL_F(QMF_IM(X[l][k]), IM(qmf32_pre_twiddle[k])); + x2[k] = MUL_F(QMF_IM(X[l][k]), RE(qmf32_pre_twiddle[k])) + MUL_F(QMF_RE(X[l][k]), IM(qmf32_pre_twiddle[k])); + +#ifndef FIXED_POINT + x1[k] *= scale; + x2[k] *= scale; +#else + x1[k] >>= 1; + x2[k] >>= 1; +#endif + } + + /* transform */ + DCT4_32(x1, x1); + DST4_32(x2, x2); + + for (n = 0; n < 32; n++) + { + qmfs->v[qmfs->v_index + n] = qmfs->v[qmfs->v_index + 640 + n] = -x1[n] + x2[n]; + qmfs->v[qmfs->v_index + 63 - n] = qmfs->v[qmfs->v_index + 640 + 63 - n] = x1[n] + x2[n]; + } + + /* calculate 32 output samples and window */ + for (k = 0; k < 32; k++) + { + output[out++] = MUL_F(qmfs->v[qmfs->v_index + k], qmf_c[2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 96 + k], qmf_c[64 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 128 + k], qmf_c[128 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 224 + k], qmf_c[192 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 256 + k], qmf_c[256 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 352 + k], qmf_c[320 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 384 + k], qmf_c[384 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 480 + k], qmf_c[448 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 512 + k], qmf_c[512 + 2*k]) + + MUL_F(qmfs->v[qmfs->v_index + 608 + k], qmf_c[576 + 2*k]); + } + + /* update ringbuffer index */ + qmfs->v_index -= 64; + if (qmfs->v_index < 0) + qmfs->v_index = (640 - 64); + } +} + +void sbr_qmf_synthesis_64(sbr_info *sbr, qmfs_info *qmfs, qmf_t X[MAX_NTSRHFG][64], + real_t *output) +{ +// ALIGN real_t x1[64], x2[64]; +#ifndef SBR_LOW_POWER + ALIGN real_t in_real1[32], in_imag1[32], out_real1[32], out_imag1[32]; + ALIGN real_t in_real2[32], in_imag2[32], out_real2[32], out_imag2[32]; +#endif + qmf_t * pX; + real_t * pring_buffer_1, * pring_buffer_3; +// real_t * ptemp_1, * ptemp_2; +#ifdef PREFER_POINTERS + // These pointers are used if target platform has autoinc address generators + real_t * pring_buffer_2, * pring_buffer_4; + real_t * pring_buffer_5, * pring_buffer_6; + real_t * pring_buffer_7, * pring_buffer_8; + real_t * pring_buffer_9, * pring_buffer_10; + const real_t * pqmf_c_1, * pqmf_c_2, * pqmf_c_3, * pqmf_c_4; + const real_t * pqmf_c_5, * pqmf_c_6, * pqmf_c_7, * pqmf_c_8; + const real_t * pqmf_c_9, * pqmf_c_10; +#endif // #ifdef PREFER_POINTERS +#ifndef FIXED_POINT + real_t scale = 1.f/64.f; +#endif + int32_t n, k, out = 0; + uint8_t l; + + + /* qmf subsample l */ + for (l = 0; l < sbr->numTimeSlotsRate; l++) + { + /* shift buffer v */ + /* buffer is not shifted, we use double ringbuffer */ + //memmove(qmfs->v + 128, qmfs->v, (1280-128)*sizeof(real_t)); + + /* calculate 128 samples */ +#ifndef FIXED_POINT + + pX = X[l]; + + in_imag1[31] = scale*QMF_RE(pX[1]); + in_real1[0] = scale*QMF_RE(pX[0]); + in_imag2[31] = scale*QMF_IM(pX[63-1]); + in_real2[0] = scale*QMF_IM(pX[63-0]); + for (k = 1; k < 31; k++) + { + in_imag1[31 - k] = scale*QMF_RE(pX[2*k + 1]); + in_real1[ k] = scale*QMF_RE(pX[2*k ]); + in_imag2[31 - k] = scale*QMF_IM(pX[63 - (2*k + 1)]); + in_real2[ k] = scale*QMF_IM(pX[63 - (2*k )]); + } + in_imag1[0] = scale*QMF_RE(pX[63]); + in_real1[31] = scale*QMF_RE(pX[62]); + in_imag2[0] = scale*QMF_IM(pX[63-63]); + in_real2[31] = scale*QMF_IM(pX[63-62]); + +#else + + pX = X[l]; + + in_imag1[31] = QMF_RE(pX[1]) >> 1; + in_real1[0] = QMF_RE(pX[0]) >> 1; + in_imag2[31] = QMF_IM(pX[62]) >> 1; + in_real2[0] = QMF_IM(pX[63]) >> 1; + for (k = 1; k < 31; k++) + { + in_imag1[31 - k] = QMF_RE(pX[2*k + 1]) >> 1; + in_real1[ k] = QMF_RE(pX[2*k ]) >> 1; + in_imag2[31 - k] = QMF_IM(pX[63 - (2*k + 1)]) >> 1; + in_real2[ k] = QMF_IM(pX[63 - (2*k )]) >> 1; + } + in_imag1[0] = QMF_RE(pX[63]) >> 1; + in_real1[31] = QMF_RE(pX[62]) >> 1; + in_imag2[0] = QMF_IM(pX[0]) >> 1; + in_real2[31] = QMF_IM(pX[1]) >> 1; + +#endif + + + // dct4_kernel is DCT_IV without reordering which is done before and after FFT + dct4_kernel(in_real1, in_imag1, out_real1, out_imag1); + dct4_kernel(in_real2, in_imag2, out_real2, out_imag2); + + + pring_buffer_1 = qmfs->v + qmfs->v_index; + pring_buffer_3 = pring_buffer_1 + 1280; +#ifdef PREFER_POINTERS + pring_buffer_2 = pring_buffer_1 + 127; + pring_buffer_4 = pring_buffer_1 + (1280 + 127); +#endif // #ifdef PREFER_POINTERS +// ptemp_1 = x1; +// ptemp_2 = x2; +#ifdef PREFER_POINTERS + for (n = 0; n < 32; n ++) + { + //real_t x1 = *ptemp_1++; + //real_t x2 = *ptemp_2++; + // pring_buffer_3 and pring_buffer_4 are needed only for double ring buffer + *pring_buffer_1++ = *pring_buffer_3++ = out_real2[n] - out_real1[n]; + *pring_buffer_2-- = *pring_buffer_4-- = out_real2[n] + out_real1[n]; + //x1 = *ptemp_1++; + //x2 = *ptemp_2++; + *pring_buffer_1++ = *pring_buffer_3++ = out_imag2[31-n] + out_imag1[31-n]; + *pring_buffer_2-- = *pring_buffer_4-- = out_imag2[31-n] - out_imag1[31-n]; + } +#else // #ifdef PREFER_POINTERS + + for (n = 0; n < 32; n++) + { + // pring_buffer_3 and pring_buffer_4 are needed only for double ring buffer + pring_buffer_1[2*n] = pring_buffer_3[2*n] = out_real2[n] - out_real1[n]; + pring_buffer_1[127-2*n] = pring_buffer_3[127-2*n] = out_real2[n] + out_real1[n]; + pring_buffer_1[2*n+1] = pring_buffer_3[2*n+1] = out_imag2[31-n] + out_imag1[31-n]; + pring_buffer_1[127-(2*n+1)] = pring_buffer_3[127-(2*n+1)] = out_imag2[31-n] - out_imag1[31-n]; + } + +#endif // #ifdef PREFER_POINTERS + + pring_buffer_1 = qmfs->v + qmfs->v_index; +#ifdef PREFER_POINTERS + pring_buffer_2 = pring_buffer_1 + 192; + pring_buffer_3 = pring_buffer_1 + 256; + pring_buffer_4 = pring_buffer_1 + (256 + 192); + pring_buffer_5 = pring_buffer_1 + 512; + pring_buffer_6 = pring_buffer_1 + (512 + 192); + pring_buffer_7 = pring_buffer_1 + 768; + pring_buffer_8 = pring_buffer_1 + (768 + 192); + pring_buffer_9 = pring_buffer_1 + 1024; + pring_buffer_10 = pring_buffer_1 + (1024 + 192); + pqmf_c_1 = qmf_c; + pqmf_c_2 = qmf_c + 64; + pqmf_c_3 = qmf_c + 128; + pqmf_c_4 = qmf_c + 192; + pqmf_c_5 = qmf_c + 256; + pqmf_c_6 = qmf_c + 320; + pqmf_c_7 = qmf_c + 384; + pqmf_c_8 = qmf_c + 448; + pqmf_c_9 = qmf_c + 512; + pqmf_c_10 = qmf_c + 576; +#endif // #ifdef PREFER_POINTERS + + /* calculate 64 output samples and window */ + for (k = 0; k < 64; k++) + { +#ifdef PREFER_POINTERS + output[out++] = + MUL_F(*pring_buffer_1++, *pqmf_c_1++) + + MUL_F(*pring_buffer_2++, *pqmf_c_2++) + + MUL_F(*pring_buffer_3++, *pqmf_c_3++) + + MUL_F(*pring_buffer_4++, *pqmf_c_4++) + + MUL_F(*pring_buffer_5++, *pqmf_c_5++) + + MUL_F(*pring_buffer_6++, *pqmf_c_6++) + + MUL_F(*pring_buffer_7++, *pqmf_c_7++) + + MUL_F(*pring_buffer_8++, *pqmf_c_8++) + + MUL_F(*pring_buffer_9++, *pqmf_c_9++) + + MUL_F(*pring_buffer_10++, *pqmf_c_10++); +#else // #ifdef PREFER_POINTERS + output[out++] = + MUL_F(pring_buffer_1[k+0], qmf_c[k+0]) + + MUL_F(pring_buffer_1[k+192], qmf_c[k+64]) + + MUL_F(pring_buffer_1[k+256], qmf_c[k+128]) + + MUL_F(pring_buffer_1[k+(256+192)], qmf_c[k+192]) + + MUL_F(pring_buffer_1[k+512], qmf_c[k+256]) + + MUL_F(pring_buffer_1[k+(512+192)], qmf_c[k+320]) + + MUL_F(pring_buffer_1[k+768], qmf_c[k+384]) + + MUL_F(pring_buffer_1[k+(768+192)], qmf_c[k+448]) + + MUL_F(pring_buffer_1[k+1024], qmf_c[k+512]) + + MUL_F(pring_buffer_1[k+(1024+192)], qmf_c[k+576]); +#endif // #ifdef PREFER_POINTERS + } + + /* update ringbuffer index */ + qmfs->v_index -= 128; + if (qmfs->v_index < 0) + qmfs->v_index = (1280 - 128); + } +} +#endif + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_qmf.h b/third_party/faad_hdc/upstream/libfaad/sbr_qmf.h new file mode 100644 index 000000000..ac749e0d8 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_qmf.h @@ -0,0 +1,55 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_qmf.h,v 1.25 2007/11/01 12:33:36 menno Exp $ +**/ + +#ifndef __SBR_QMF_H__ +#define __SBR_QMF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +qmfa_info *qmfa_init(uint8_t channels); +void qmfa_end(qmfa_info *qmfa); +qmfs_info *qmfs_init(uint8_t channels); +void qmfs_end(qmfs_info *qmfs); + +void sbr_qmf_analysis_32(sbr_info *sbr, qmfa_info *qmfa, const real_t *input, + qmf_t X[MAX_NTSRHFG][64], uint8_t offset, uint8_t kx); +void sbr_qmf_synthesis_32(sbr_info *sbr, qmfs_info *qmfs, qmf_t X[MAX_NTSRHFG][64], + real_t *output); +void sbr_qmf_synthesis_64(sbr_info *sbr, qmfs_info *qmfs, qmf_t X[MAX_NTSRHFG][64], + real_t *output); + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_qmf_c.h b/third_party/faad_hdc/upstream/libfaad/sbr_qmf_c.h new file mode 100644 index 000000000..2297af031 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_qmf_c.h @@ -0,0 +1,368 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_qmf_c.h,v 1.17 2007/11/01 12:33:36 menno Exp $ +**/ + +#ifndef __SBR_QMF_C_H__ +#define __SBR_QMF_C_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef _MSC_VER +#pragma warning(disable:4305) +#pragma warning(disable:4244) +#endif + +ALIGN static const real_t qmf_c[640] = { + FRAC_CONST(0), FRAC_CONST(-0.00055252865047), + FRAC_CONST(-0.00056176925738), FRAC_CONST(-0.00049475180896), + FRAC_CONST(-0.00048752279712), FRAC_CONST(-0.00048937912498), + FRAC_CONST(-0.00050407143497), FRAC_CONST(-0.00052265642972), + FRAC_CONST(-0.00054665656337), FRAC_CONST(-0.00056778025613), + FRAC_CONST(-0.00058709304852), FRAC_CONST(-0.00061327473938), + FRAC_CONST(-0.00063124935319), FRAC_CONST(-0.00065403333621), + FRAC_CONST(-0.00067776907764), FRAC_CONST(-0.00069416146273), + FRAC_CONST(-0.00071577364744), FRAC_CONST(-0.00072550431222), + FRAC_CONST(-0.00074409418541), FRAC_CONST(-0.00074905980532), + FRAC_CONST(-0.0007681371927), FRAC_CONST(-0.00077248485949), + FRAC_CONST(-0.00078343322877), FRAC_CONST(-0.00077798694927), + FRAC_CONST(-0.000780366471), FRAC_CONST(-0.00078014496257), + FRAC_CONST(-0.0007757977331), FRAC_CONST(-0.00076307935757), + FRAC_CONST(-0.00075300014201), FRAC_CONST(-0.00073193571525), + FRAC_CONST(-0.00072153919876), FRAC_CONST(-0.00069179375372), + FRAC_CONST(-0.00066504150893), FRAC_CONST(-0.00063415949025), + FRAC_CONST(-0.0005946118933), FRAC_CONST(-0.00055645763906), + FRAC_CONST(-0.00051455722108), FRAC_CONST(-0.00046063254803), + FRAC_CONST(-0.00040951214522), FRAC_CONST(-0.00035011758756), + FRAC_CONST(-0.00028969811748), FRAC_CONST(-0.0002098337344), + FRAC_CONST(-0.00014463809349), FRAC_CONST(-6.173344072E-005), + FRAC_CONST(1.349497418E-005), FRAC_CONST(0.00010943831274), + FRAC_CONST(0.00020430170688), FRAC_CONST(0.00029495311041), + FRAC_CONST(0.0004026540216), FRAC_CONST(0.00051073884952), + FRAC_CONST(0.00062393761391), FRAC_CONST(0.00074580258865), + FRAC_CONST(0.00086084433262), FRAC_CONST(0.00098859883015), + FRAC_CONST(0.00112501551307), FRAC_CONST(0.00125778846475), + FRAC_CONST(0.00139024948272), FRAC_CONST(0.00154432198471), + FRAC_CONST(0.00168680832531), FRAC_CONST(0.00183482654224), + FRAC_CONST(0.00198411407369), FRAC_CONST(0.00214615835557), + FRAC_CONST(0.00230172547746), FRAC_CONST(0.00246256169126), + FRAC_CONST(0.00262017586902), FRAC_CONST(0.00278704643465), + FRAC_CONST(0.00294694477165), FRAC_CONST(0.00311254206525), + FRAC_CONST(0.00327396134847), FRAC_CONST(0.00344188741828), + FRAC_CONST(0.00360082681231), FRAC_CONST(0.00376039229104), + FRAC_CONST(0.00392074323703), FRAC_CONST(0.00408197531935), + FRAC_CONST(0.0042264269227), FRAC_CONST(0.00437307196781), + FRAC_CONST(0.00452098527825), FRAC_CONST(0.00466064606118), + FRAC_CONST(0.00479325608498), FRAC_CONST(0.00491376035745), + FRAC_CONST(0.00503930226013), FRAC_CONST(0.00514073539032), + FRAC_CONST(0.00524611661324), FRAC_CONST(0.00534716811982), + FRAC_CONST(0.00541967759307), FRAC_CONST(0.00548760401507), + FRAC_CONST(0.00554757145088), FRAC_CONST(0.00559380230045), + FRAC_CONST(0.00562206432097), FRAC_CONST(0.00564551969164), + FRAC_CONST(0.00563891995151), FRAC_CONST(0.00562661141932), + FRAC_CONST(0.0055917128663), FRAC_CONST(0.005540436394), + FRAC_CONST(0.0054753783077), FRAC_CONST(0.0053838975897), + FRAC_CONST(0.00527157587272), FRAC_CONST(0.00513822754514), + FRAC_CONST(0.00498396877629), FRAC_CONST(0.004810946906), + FRAC_CONST(0.00460395301471), FRAC_CONST(0.00438018617447), + FRAC_CONST(0.0041251642327), FRAC_CONST(0.00384564081246), + FRAC_CONST(0.00354012465507), FRAC_CONST(0.00320918858098), + FRAC_CONST(0.00284467578623), FRAC_CONST(0.00245085400321), + FRAC_CONST(0.0020274176185), FRAC_CONST(0.00157846825768), + FRAC_CONST(0.00109023290512), FRAC_CONST(0.0005832264248), + FRAC_CONST(2.760451905E-005), FRAC_CONST(-0.00054642808664), + FRAC_CONST(-0.00115681355227), FRAC_CONST(-0.00180394725893), + FRAC_CONST(-0.00248267236449), FRAC_CONST(-0.003193377839), + FRAC_CONST(-0.00394011240522), FRAC_CONST(-0.004722259624), + FRAC_CONST(-0.00553372111088), FRAC_CONST(-0.00637922932685), + FRAC_CONST(-0.00726158168517), FRAC_CONST(-0.00817982333726), + FRAC_CONST(-0.00913253296085), FRAC_CONST(-0.01011502154986), + FRAC_CONST(-0.01113155480321), FRAC_CONST(-0.01218499959508), + FRAC_CONST(0.01327182200351), FRAC_CONST(0.01439046660792), + FRAC_CONST(0.01554055533423), FRAC_CONST(0.01673247129989), + FRAC_CONST(0.01794333813443), FRAC_CONST(0.01918724313698), + FRAC_CONST(0.02045317933555), FRAC_CONST(0.02174675502535), + FRAC_CONST(0.02306801692862), FRAC_CONST(0.02441609920285), + FRAC_CONST(0.02578758475467), FRAC_CONST(0.02718594296329), + FRAC_CONST(0.02860721736385), FRAC_CONST(0.03005026574279), + FRAC_CONST(0.03150176087389), FRAC_CONST(0.03297540810337), + FRAC_CONST(0.03446209487686), FRAC_CONST(0.03596975605542), + FRAC_CONST(0.03748128504252), FRAC_CONST(0.03900536794745), + FRAC_CONST(0.04053491705584), FRAC_CONST(0.04206490946367), + FRAC_CONST(0.04360975421304), FRAC_CONST(0.04514884056413), + FRAC_CONST(0.04668430272642), FRAC_CONST(0.04821657200672), + FRAC_CONST(0.04973857556014), FRAC_CONST(0.05125561555216), + FRAC_CONST(0.05276307465207), FRAC_CONST(0.05424527683589), + FRAC_CONST(0.05571736482138), FRAC_CONST(0.05716164501299), + FRAC_CONST(0.0585915683626), FRAC_CONST(0.05998374801761), + FRAC_CONST(0.06134551717207), FRAC_CONST(0.06268578081172), + FRAC_CONST(0.06397158980681), FRAC_CONST(0.0652247106438), + FRAC_CONST(0.06643675122104), FRAC_CONST(0.06760759851228), + FRAC_CONST(0.06870438283512), FRAC_CONST(0.06976302447127), + FRAC_CONST(0.07076287107266), FRAC_CONST(0.07170026731102), + FRAC_CONST(0.07256825833083), FRAC_CONST(0.07336202550803), + FRAC_CONST(0.07410036424342), FRAC_CONST(0.07474525581194), + FRAC_CONST(0.07531373362019), FRAC_CONST(0.07580083586584), + FRAC_CONST(0.07619924793396), FRAC_CONST(0.07649921704119), + FRAC_CONST(0.07670934904245), FRAC_CONST(0.07681739756964), + FRAC_CONST(0.07682300113923), FRAC_CONST(0.07672049241746), + FRAC_CONST(0.07650507183194), FRAC_CONST(0.07617483218536), + FRAC_CONST(0.07573057565061), FRAC_CONST(0.0751576255287), + FRAC_CONST(0.07446643947564), FRAC_CONST(0.0736406005762), + FRAC_CONST(0.07267746427299), FRAC_CONST(0.07158263647903), + FRAC_CONST(0.07035330735093), FRAC_CONST(0.06896640131951), + FRAC_CONST(0.06745250215166), FRAC_CONST(0.06576906686508), + FRAC_CONST(0.06394448059633), FRAC_CONST(0.06196027790387), + FRAC_CONST(0.0598166570809), FRAC_CONST(0.05751526919867), + FRAC_CONST(0.05504600343009), FRAC_CONST(0.05240938217366), + FRAC_CONST(0.04959786763445), FRAC_CONST(0.04663033051701), + FRAC_CONST(0.04347687821958), FRAC_CONST(0.04014582784127), + FRAC_CONST(0.03664181168133), FRAC_CONST(0.03295839306691), + FRAC_CONST(0.02908240060125), FRAC_CONST(0.02503075618909), + FRAC_CONST(0.02079970728622), FRAC_CONST(0.01637012582228), + FRAC_CONST(0.01176238327857), FRAC_CONST(0.00696368621617), + FRAC_CONST(0.00197656014503), FRAC_CONST(-0.00320868968304), + FRAC_CONST(-0.00857117491366), FRAC_CONST(-0.01412888273558), + FRAC_CONST(-0.01988341292573), FRAC_CONST(-0.02582272888064), + FRAC_CONST(-0.03195312745332), FRAC_CONST(-0.03827765720822), + FRAC_CONST(-0.04478068215856), FRAC_CONST(-0.05148041767934), + FRAC_CONST(-0.05837053268336), FRAC_CONST(-0.06544098531359), + FRAC_CONST(-0.07269433008129), FRAC_CONST(-0.08013729344279), + FRAC_CONST(-0.08775475365593), FRAC_CONST(-0.09555333528914), + FRAC_CONST(-0.10353295311463), FRAC_CONST(-0.1116826931773), + FRAC_CONST(-0.120007798468), FRAC_CONST(-0.12850028503878), + FRAC_CONST(-0.13715517611934), FRAC_CONST(-0.1459766491187), + FRAC_CONST(-0.15496070710605), FRAC_CONST(-0.16409588556669), + FRAC_CONST(-0.17338081721706), FRAC_CONST(-0.18281725485142), + FRAC_CONST(-0.19239667457267), FRAC_CONST(-0.20212501768103), + FRAC_CONST(-0.21197358538056), FRAC_CONST(-0.22196526964149), + FRAC_CONST(-0.23206908706791), FRAC_CONST(-0.24230168845974), + FRAC_CONST(-0.25264803095722), FRAC_CONST(-0.26310532994603), + FRAC_CONST(-0.27366340405625), FRAC_CONST(-0.28432141891085), + FRAC_CONST(-0.29507167170646), FRAC_CONST(-0.30590985751916), + FRAC_CONST(-0.31682789136456), FRAC_CONST(-0.32781137272105), + FRAC_CONST(-0.33887226938665), FRAC_CONST(-0.3499914122931), + FRAC_CONST(0.36115899031355), FRAC_CONST(0.37237955463061), + FRAC_CONST(0.38363500139043), FRAC_CONST(0.39492117615675), + FRAC_CONST(0.40623176767625), FRAC_CONST(0.41756968968409), + FRAC_CONST(0.42891199207373), FRAC_CONST(0.44025537543665), + FRAC_CONST(0.45159965356824), FRAC_CONST(0.46293080852757), + FRAC_CONST(0.47424532146115), FRAC_CONST(0.48552530911099), + FRAC_CONST(0.49677082545707), FRAC_CONST(0.50798175000434), + FRAC_CONST(0.51912349702391), FRAC_CONST(0.53022408956855), + FRAC_CONST(0.54125534487322), FRAC_CONST(0.55220512585061), + FRAC_CONST(0.5630789140137), FRAC_CONST(0.57385241316923), + FRAC_CONST(0.58454032354679), FRAC_CONST(0.59511230862496), + FRAC_CONST(0.6055783538918), FRAC_CONST(0.61591099320291), + FRAC_CONST(0.62612426956055), FRAC_CONST(0.63619801077286), + FRAC_CONST(0.64612696959461), FRAC_CONST(0.65590163024671), + FRAC_CONST(0.66551398801627), FRAC_CONST(0.67496631901712), + FRAC_CONST(0.68423532934598), FRAC_CONST(0.69332823767032), + FRAC_CONST(0.70223887193539), FRAC_CONST(0.71094104263095), + FRAC_CONST(0.71944626349561), FRAC_CONST(0.72774489002994), + FRAC_CONST(0.73582117582769), FRAC_CONST(0.74368278636488), + FRAC_CONST(0.75131374561237), FRAC_CONST(0.75870807608242), + FRAC_CONST(0.76586748650939), FRAC_CONST(0.77277808813327), + FRAC_CONST(0.77942875190216), FRAC_CONST(0.7858353120392), + FRAC_CONST(0.79197358416424), FRAC_CONST(0.797846641377), + FRAC_CONST(0.80344857518505), FRAC_CONST(0.80876950044491), + FRAC_CONST(0.81381912706217), FRAC_CONST(0.81857760046468), + FRAC_CONST(0.82304198905409), FRAC_CONST(0.8272275347336), + FRAC_CONST(0.8311038457152), FRAC_CONST(0.83469373618402), + FRAC_CONST(0.83797173378865), FRAC_CONST(0.84095413924722), + FRAC_CONST(0.84362382812005), FRAC_CONST(0.84598184698206), + FRAC_CONST(0.84803157770763), FRAC_CONST(0.84978051984268), + FRAC_CONST(0.85119715249343), FRAC_CONST(0.85230470352147), + FRAC_CONST(0.85310209497017), FRAC_CONST(0.85357205739107), + FRAC_CONST(0.85373856005937 /*max*/), FRAC_CONST(0.85357205739107), + FRAC_CONST(0.85310209497017), FRAC_CONST(0.85230470352147), + FRAC_CONST(0.85119715249343), FRAC_CONST(0.84978051984268), + FRAC_CONST(0.84803157770763), FRAC_CONST(0.84598184698206), + FRAC_CONST(0.84362382812005), FRAC_CONST(0.84095413924722), + FRAC_CONST(0.83797173378865), FRAC_CONST(0.83469373618402), + FRAC_CONST(0.8311038457152), FRAC_CONST(0.8272275347336), + FRAC_CONST(0.82304198905409), FRAC_CONST(0.81857760046468), + FRAC_CONST(0.81381912706217), FRAC_CONST(0.80876950044491), + FRAC_CONST(0.80344857518505), FRAC_CONST(0.797846641377), + FRAC_CONST(0.79197358416424), FRAC_CONST(0.7858353120392), + FRAC_CONST(0.77942875190216), FRAC_CONST(0.77277808813327), + FRAC_CONST(0.76586748650939), FRAC_CONST(0.75870807608242), + FRAC_CONST(0.75131374561237), FRAC_CONST(0.74368278636488), + FRAC_CONST(0.73582117582769), FRAC_CONST(0.72774489002994), + FRAC_CONST(0.71944626349561), FRAC_CONST(0.71094104263095), + FRAC_CONST(0.70223887193539), FRAC_CONST(0.69332823767032), + FRAC_CONST(0.68423532934598), FRAC_CONST(0.67496631901712), + FRAC_CONST(0.66551398801627), FRAC_CONST(0.65590163024671), + FRAC_CONST(0.64612696959461), FRAC_CONST(0.63619801077286), + FRAC_CONST(0.62612426956055), FRAC_CONST(0.61591099320291), + FRAC_CONST(0.6055783538918), FRAC_CONST(0.59511230862496), + FRAC_CONST(0.58454032354679), FRAC_CONST(0.57385241316923), + FRAC_CONST(0.5630789140137), FRAC_CONST(0.55220512585061), + FRAC_CONST(0.54125534487322), FRAC_CONST(0.53022408956855), + FRAC_CONST(0.51912349702391), FRAC_CONST(0.50798175000434), + FRAC_CONST(0.49677082545707), FRAC_CONST(0.48552530911099), + FRAC_CONST(0.47424532146115), FRAC_CONST(0.46293080852757), + FRAC_CONST(0.45159965356824), FRAC_CONST(0.44025537543665), + FRAC_CONST(0.42891199207373), FRAC_CONST(0.41756968968409), + FRAC_CONST(0.40623176767625), FRAC_CONST(0.39492117615675), + FRAC_CONST(0.38363500139043), FRAC_CONST(0.37237955463061), + FRAC_CONST(-0.36115899031355), FRAC_CONST(-0.3499914122931), + FRAC_CONST(-0.33887226938665), FRAC_CONST(-0.32781137272105), + FRAC_CONST(-0.31682789136456), FRAC_CONST(-0.30590985751916), + FRAC_CONST(-0.29507167170646), FRAC_CONST(-0.28432141891085), + FRAC_CONST(-0.27366340405625), FRAC_CONST(-0.26310532994603), + FRAC_CONST(-0.25264803095722), FRAC_CONST(-0.24230168845974), + FRAC_CONST(-0.23206908706791), FRAC_CONST(-0.22196526964149), + FRAC_CONST(-0.21197358538056), FRAC_CONST(-0.20212501768103), + FRAC_CONST(-0.19239667457267), FRAC_CONST(-0.18281725485142), + FRAC_CONST(-0.17338081721706), FRAC_CONST(-0.16409588556669), + FRAC_CONST(-0.15496070710605), FRAC_CONST(-0.1459766491187), + FRAC_CONST(-0.13715517611934), FRAC_CONST(-0.12850028503878), + FRAC_CONST(-0.120007798468), FRAC_CONST(-0.1116826931773), + FRAC_CONST(-0.10353295311463), FRAC_CONST(-0.09555333528914), + FRAC_CONST(-0.08775475365593), FRAC_CONST(-0.08013729344279), + FRAC_CONST(-0.07269433008129), FRAC_CONST(-0.06544098531359), + FRAC_CONST(-0.05837053268336), FRAC_CONST(-0.05148041767934), + FRAC_CONST(-0.04478068215856), FRAC_CONST(-0.03827765720822), + FRAC_CONST(-0.03195312745332), FRAC_CONST(-0.02582272888064), + FRAC_CONST(-0.01988341292573), FRAC_CONST(-0.01412888273558), + FRAC_CONST(-0.00857117491366), FRAC_CONST(-0.00320868968304), + FRAC_CONST(0.00197656014503), FRAC_CONST(0.00696368621617), + FRAC_CONST(0.01176238327857), FRAC_CONST(0.01637012582228), + FRAC_CONST(0.02079970728622), FRAC_CONST(0.02503075618909), + FRAC_CONST(0.02908240060125), FRAC_CONST(0.03295839306691), + FRAC_CONST(0.03664181168133), FRAC_CONST(0.04014582784127), + FRAC_CONST(0.04347687821958), FRAC_CONST(0.04663033051701), + FRAC_CONST(0.04959786763445), FRAC_CONST(0.05240938217366), + FRAC_CONST(0.05504600343009), FRAC_CONST(0.05751526919867), + FRAC_CONST(0.0598166570809), FRAC_CONST(0.06196027790387), + FRAC_CONST(0.06394448059633), FRAC_CONST(0.06576906686508), + FRAC_CONST(0.06745250215166), FRAC_CONST(0.06896640131951), + FRAC_CONST(0.07035330735093), FRAC_CONST(0.07158263647903), + FRAC_CONST(0.07267746427299), FRAC_CONST(0.0736406005762), + FRAC_CONST(0.07446643947564), FRAC_CONST(0.0751576255287), + FRAC_CONST(0.07573057565061), FRAC_CONST(0.07617483218536), + FRAC_CONST(0.07650507183194), FRAC_CONST(0.07672049241746), + FRAC_CONST(0.07682300113923), FRAC_CONST(0.07681739756964), + FRAC_CONST(0.07670934904245), FRAC_CONST(0.07649921704119), + FRAC_CONST(0.07619924793396), FRAC_CONST(0.07580083586584), + FRAC_CONST(0.07531373362019), FRAC_CONST(0.07474525581194), + FRAC_CONST(0.07410036424342), FRAC_CONST(0.07336202550803), + FRAC_CONST(0.07256825833083), FRAC_CONST(0.07170026731102), + FRAC_CONST(0.07076287107266), FRAC_CONST(0.06976302447127), + FRAC_CONST(0.06870438283512), FRAC_CONST(0.06760759851228), + FRAC_CONST(0.06643675122104), FRAC_CONST(0.0652247106438), + FRAC_CONST(0.06397158980681), FRAC_CONST(0.06268578081172), + FRAC_CONST(0.06134551717207), FRAC_CONST(0.05998374801761), + FRAC_CONST(0.0585915683626), FRAC_CONST(0.05716164501299), + FRAC_CONST(0.05571736482138), FRAC_CONST(0.05424527683589), + FRAC_CONST(0.05276307465207), FRAC_CONST(0.05125561555216), + FRAC_CONST(0.04973857556014), FRAC_CONST(0.04821657200672), + FRAC_CONST(0.04668430272642), FRAC_CONST(0.04514884056413), + FRAC_CONST(0.04360975421304), FRAC_CONST(0.04206490946367), + FRAC_CONST(0.04053491705584), FRAC_CONST(0.03900536794745), + FRAC_CONST(0.03748128504252), FRAC_CONST(0.03596975605542), + FRAC_CONST(0.03446209487686), FRAC_CONST(0.03297540810337), + FRAC_CONST(0.03150176087389), FRAC_CONST(0.03005026574279), + FRAC_CONST(0.02860721736385), FRAC_CONST(0.02718594296329), + FRAC_CONST(0.02578758475467), FRAC_CONST(0.02441609920285), + FRAC_CONST(0.02306801692862), FRAC_CONST(0.02174675502535), + FRAC_CONST(0.02045317933555), FRAC_CONST(0.01918724313698), + FRAC_CONST(0.01794333813443), FRAC_CONST(0.01673247129989), + FRAC_CONST(0.01554055533423), FRAC_CONST(0.01439046660792), + FRAC_CONST(-0.01327182200351), FRAC_CONST(-0.01218499959508), + FRAC_CONST(-0.01113155480321), FRAC_CONST(-0.01011502154986), + FRAC_CONST(-0.00913253296085), FRAC_CONST(-0.00817982333726), + FRAC_CONST(-0.00726158168517), FRAC_CONST(-0.00637922932685), + FRAC_CONST(-0.00553372111088), FRAC_CONST(-0.004722259624), + FRAC_CONST(-0.00394011240522), FRAC_CONST(-0.003193377839), + FRAC_CONST(-0.00248267236449), FRAC_CONST(-0.00180394725893), + FRAC_CONST(-0.00115681355227), FRAC_CONST(-0.00054642808664), + FRAC_CONST(2.760451905E-005), FRAC_CONST(0.0005832264248), + FRAC_CONST(0.00109023290512), FRAC_CONST(0.00157846825768), + FRAC_CONST(0.0020274176185), FRAC_CONST(0.00245085400321), + FRAC_CONST(0.00284467578623), FRAC_CONST(0.00320918858098), + FRAC_CONST(0.00354012465507), FRAC_CONST(0.00384564081246), + FRAC_CONST(0.0041251642327), FRAC_CONST(0.00438018617447), + FRAC_CONST(0.00460395301471), FRAC_CONST(0.004810946906), + FRAC_CONST(0.00498396877629), FRAC_CONST(0.00513822754514), + FRAC_CONST(0.00527157587272), FRAC_CONST(0.0053838975897), + FRAC_CONST(0.0054753783077), FRAC_CONST(0.005540436394), + FRAC_CONST(0.0055917128663), FRAC_CONST(0.00562661141932), + FRAC_CONST(0.00563891995151), FRAC_CONST(0.00564551969164), + FRAC_CONST(0.00562206432097), FRAC_CONST(0.00559380230045), + FRAC_CONST(0.00554757145088), FRAC_CONST(0.00548760401507), + FRAC_CONST(0.00541967759307), FRAC_CONST(0.00534716811982), + FRAC_CONST(0.00524611661324), FRAC_CONST(0.00514073539032), + FRAC_CONST(0.00503930226013), FRAC_CONST(0.00491376035745), + FRAC_CONST(0.00479325608498), FRAC_CONST(0.00466064606118), + FRAC_CONST(0.00452098527825), FRAC_CONST(0.00437307196781), + FRAC_CONST(0.0042264269227), FRAC_CONST(0.00408197531935), + FRAC_CONST(0.00392074323703), FRAC_CONST(0.00376039229104), + FRAC_CONST(0.00360082681231), FRAC_CONST(0.00344188741828), + FRAC_CONST(0.00327396134847), FRAC_CONST(0.00311254206525), + FRAC_CONST(0.00294694477165), FRAC_CONST(0.00278704643465), + FRAC_CONST(0.00262017586902), FRAC_CONST(0.00246256169126), + FRAC_CONST(0.00230172547746), FRAC_CONST(0.00214615835557), + FRAC_CONST(0.00198411407369), FRAC_CONST(0.00183482654224), + FRAC_CONST(0.00168680832531), FRAC_CONST(0.00154432198471), + FRAC_CONST(0.00139024948272), FRAC_CONST(0.00125778846475), + FRAC_CONST(0.00112501551307), FRAC_CONST(0.00098859883015), + FRAC_CONST(0.00086084433262), FRAC_CONST(0.00074580258865), + FRAC_CONST(0.00062393761391), FRAC_CONST(0.00051073884952), + FRAC_CONST(0.0004026540216), FRAC_CONST(0.00029495311041), + FRAC_CONST(0.00020430170688), FRAC_CONST(0.00010943831274), + FRAC_CONST(1.349497418E-005), FRAC_CONST(-6.173344072E-005), + FRAC_CONST(-0.00014463809349), FRAC_CONST(-0.0002098337344), + FRAC_CONST(-0.00028969811748), FRAC_CONST(-0.00035011758756), + FRAC_CONST(-0.00040951214522), FRAC_CONST(-0.00046063254803), + FRAC_CONST(-0.00051455722108), FRAC_CONST(-0.00055645763906), + FRAC_CONST(-0.0005946118933), FRAC_CONST(-0.00063415949025), + FRAC_CONST(-0.00066504150893), FRAC_CONST(-0.00069179375372), + FRAC_CONST(-0.00072153919876), FRAC_CONST(-0.00073193571525), + FRAC_CONST(-0.00075300014201), FRAC_CONST(-0.00076307935757), + FRAC_CONST(-0.0007757977331), FRAC_CONST(-0.00078014496257), + FRAC_CONST(-0.000780366471), FRAC_CONST(-0.00077798694927), + FRAC_CONST(-0.00078343322877), FRAC_CONST(-0.00077248485949), + FRAC_CONST(-0.0007681371927), FRAC_CONST(-0.00074905980532), + FRAC_CONST(-0.00074409418541), FRAC_CONST(-0.00072550431222), + FRAC_CONST(-0.00071577364744), FRAC_CONST(-0.00069416146273), + FRAC_CONST(-0.00067776907764), FRAC_CONST(-0.00065403333621), + FRAC_CONST(-0.00063124935319), FRAC_CONST(-0.00061327473938), + FRAC_CONST(-0.00058709304852), FRAC_CONST(-0.00056778025613), + FRAC_CONST(-0.00054665656337), FRAC_CONST(-0.00052265642972), + FRAC_CONST(-0.00050407143497), FRAC_CONST(-0.00048937912498), + FRAC_CONST(-0.00048752279712), FRAC_CONST(-0.00049475180896), + FRAC_CONST(-0.00056176925738), FRAC_CONST(-0.00055252865047) +}; + +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_syntax.c b/third_party/faad_hdc/upstream/libfaad/sbr_syntax.c new file mode 100644 index 000000000..4c2b91c64 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_syntax.c @@ -0,0 +1,939 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_syntax.c,v 1.39 2009/01/26 22:32:31 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#ifdef SBR_DEC + +#include "sbr_syntax.h" +#include "syntax.h" +#include "sbr_huff.h" +#include "sbr_fbt.h" +#include "sbr_tf_grid.h" +#include "sbr_e_nf.h" +#include "bits.h" +#ifdef PS_DEC +#include "ps_dec.h" +#endif +#ifdef DRM_PS +#include "drm_dec.h" +#endif +#include "analysis.h" + +/* static function declarations */ +/* static function declarations */ +static void sbr_header(bitfile *ld, sbr_info *sbr); +static uint8_t calc_sbr_tables(sbr_info *sbr, uint8_t start_freq, uint8_t stop_freq, + uint8_t samplerate_mode, uint8_t freq_scale, + uint8_t alter_scale, uint8_t xover_band); +static uint8_t sbr_data(bitfile *ld, sbr_info *sbr); +static uint16_t sbr_extension(bitfile *ld, sbr_info *sbr, + uint8_t bs_extension_id, uint16_t num_bits_left); +static uint8_t sbr_single_channel_element(bitfile *ld, sbr_info *sbr); +static uint8_t sbr_channel_pair_element(bitfile *ld, sbr_info *sbr); +static uint8_t sbr_grid(bitfile *ld, sbr_info *sbr, uint8_t ch); +static void sbr_dtdf(bitfile *ld, sbr_info *sbr, uint8_t ch); +static void invf_mode(bitfile *ld, sbr_info *sbr, uint8_t ch); +static void sinusoidal_coding(bitfile *ld, sbr_info *sbr, uint8_t ch); + + +static void sbr_reset(sbr_info *sbr) +{ +#if 0 + printf("%d\n", sbr->bs_start_freq_prev); + printf("%d\n", sbr->bs_stop_freq_prev); + printf("%d\n", sbr->bs_freq_scale_prev); + printf("%d\n", sbr->bs_alter_scale_prev); + printf("%d\n", sbr->bs_xover_band_prev); + printf("%d\n\n", sbr->bs_noise_bands_prev); +#endif + + /* if these are different from the previous frame: Reset = 1 */ + if ((sbr->bs_start_freq != sbr->bs_start_freq_prev) || + (sbr->bs_stop_freq != sbr->bs_stop_freq_prev) || + (sbr->bs_freq_scale != sbr->bs_freq_scale_prev) || + (sbr->bs_alter_scale != sbr->bs_alter_scale_prev) || + (sbr->bs_xover_band != sbr->bs_xover_band_prev) || + (sbr->bs_noise_bands != sbr->bs_noise_bands_prev)) + { + sbr->Reset = 1; + } else { + sbr->Reset = 0; + } + + sbr->bs_start_freq_prev = sbr->bs_start_freq; + sbr->bs_stop_freq_prev = sbr->bs_stop_freq; + sbr->bs_freq_scale_prev = sbr->bs_freq_scale; + sbr->bs_alter_scale_prev = sbr->bs_alter_scale; + sbr->bs_xover_band_prev = sbr->bs_xover_band; + sbr->bs_noise_bands_prev = sbr->bs_noise_bands; +} + +static uint8_t calc_sbr_tables(sbr_info *sbr, uint8_t start_freq, uint8_t stop_freq, + uint8_t samplerate_mode, uint8_t freq_scale, + uint8_t alter_scale, uint8_t xover_band) +{ + uint8_t result = 0; + uint8_t k2; + + /* calculate the Master Frequency Table */ + sbr->k0 = qmf_start_channel(start_freq, samplerate_mode, sbr->sample_rate); + k2 = qmf_stop_channel(stop_freq, sbr->sample_rate, sbr->k0); + + /* check k0 and k2 */ + if (sbr->sample_rate >= 48000) + { + if ((k2 - sbr->k0) > 32) + result += 1; + } else if (sbr->sample_rate <= 32000) { + if ((k2 - sbr->k0) > 48) + result += 1; + } else { /* (sbr->sample_rate == 44100) */ + if ((k2 - sbr->k0) > 45) + result += 1; + } + + if (freq_scale == 0) + { + result += master_frequency_table_fs0(sbr, sbr->k0, k2, alter_scale); + } else { + result += master_frequency_table(sbr, sbr->k0, k2, freq_scale, alter_scale); + } + result += derived_frequency_table(sbr, xover_band, k2); + + result = (result > 0) ? 1 : 0; + + return result; +} + +/* table 2 */ +uint8_t sbr_extension_data(bitfile *ld, sbr_info *sbr, uint16_t cnt, + uint8_t psResetFlag) +{ + uint8_t result = 0; + uint16_t num_align_bits = 0; + uint16_t num_sbr_bits1 = (uint16_t)faad_get_processed_bits(ld); + uint16_t num_sbr_bits2; + + uint8_t saved_start_freq, saved_samplerate_mode; + uint8_t saved_stop_freq, saved_freq_scale; + uint8_t saved_alter_scale, saved_xover_band; + +#if (defined(PS_DEC) || defined(DRM_PS)) + if (psResetFlag) + sbr->psResetFlag = psResetFlag; +#endif + +#ifdef DRM +#ifdef HDC + if (!sbr->Is_DRM_SBR && !sbr->Is_HDC_SBR) +#else + if (!sbr->Is_DRM_SBR) +#endif +#endif + { + uint8_t bs_extension_type = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,198,"sbr_bitstream(): bs_extension_type")); + + if (bs_extension_type == EXT_SBR_DATA_CRC) + { + sbr->bs_sbr_crc_bits = (uint16_t)faad_getbits(ld, 10 + DEBUGVAR(1,199,"sbr_bitstream(): bs_sbr_crc_bits")); + } + } + + /* save old header values, in case the new ones are corrupted */ + saved_start_freq = sbr->bs_start_freq; + saved_samplerate_mode = sbr->bs_samplerate_mode; + saved_stop_freq = sbr->bs_stop_freq; + saved_freq_scale = sbr->bs_freq_scale; + saved_alter_scale = sbr->bs_alter_scale; + saved_xover_band = sbr->bs_xover_band; + + sbr->bs_header_flag = faad_get1bit(ld + DEBUGVAR(1,200,"sbr_bitstream(): bs_header_flag")); + + if (sbr->bs_header_flag) + sbr_header(ld, sbr); + + /* Reset? */ + sbr_reset(sbr); + + /* first frame should have a header */ + //if (!(sbr->frame == 0 && sbr->bs_header_flag == 0)) + if (sbr->header_count != 0) + { + if (sbr->Reset || (sbr->bs_header_flag && sbr->just_seeked)) + { + uint8_t rt = calc_sbr_tables(sbr, sbr->bs_start_freq, sbr->bs_stop_freq, + sbr->bs_samplerate_mode, sbr->bs_freq_scale, + sbr->bs_alter_scale, sbr->bs_xover_band); + + /* if an error occured with the new header values revert to the old ones */ + if (rt > 0) + { + result += calc_sbr_tables(sbr, saved_start_freq, saved_stop_freq, + saved_samplerate_mode, saved_freq_scale, + saved_alter_scale, saved_xover_band); + } + } + + if (result == 0) + { + result = sbr_data(ld, sbr); + + /* sbr_data() returning an error means that there was an error in + envelope_time_border_vector(). + In this case the old time border vector is saved and all the previous + data normally read after sbr_grid() is saved. + */ + /* to be on the safe side, calculate old sbr tables in case of error */ + if ((result > 0) && + (sbr->Reset || (sbr->bs_header_flag && sbr->just_seeked))) + { + result += calc_sbr_tables(sbr, saved_start_freq, saved_stop_freq, + saved_samplerate_mode, saved_freq_scale, + saved_alter_scale, saved_xover_band); + } + + /* we should be able to safely set result to 0 now, */ + /* but practise indicates this doesn't work well */ + } + } else { + result = 1; + } + + num_sbr_bits2 = (uint16_t)faad_get_processed_bits(ld) - num_sbr_bits1; + + /* check if we read more bits then were available for sbr */ + if (8*cnt < num_sbr_bits2) + { + faad_resetbits(ld, num_sbr_bits1 + 8u * cnt); + num_sbr_bits2 = 8*cnt; + +#ifdef PS_DEC + /* turn off PS for the unfortunate case that we randomly read some + * PS data that looks correct */ + sbr->ps_used = 0; +#endif + + /* Make sure it doesn't decode SBR in this frame, or we'll get glitches */ + return 1; + } + +#ifdef DRM +#ifdef HDC + if (!sbr->Is_DRM_SBR && !sbr->Is_HDC_SBR) +#else + if (!sbr->Is_DRM_SBR) +#endif +#endif + { + /* -4 does not apply, bs_extension_type is re-read in this function */ + num_align_bits = 8*cnt /*- 4*/ - num_sbr_bits2; + + while (num_align_bits > 7) + { + faad_getbits(ld, 8 + DEBUGVAR(1,999,"sbr_bitstream(): num_align_bits")); + num_align_bits -= 8; + } + faad_getbits(ld, num_align_bits + DEBUGVAR(1,999,"sbr_bitstream(): num_align_bits")); + } + + return result; +} + +/* table 3 */ +static void sbr_header(bitfile *ld, sbr_info *sbr) +{ + uint8_t bs_header_extra_1, bs_header_extra_2; + + sbr->header_count++; + + sbr->bs_amp_res = faad_get1bit(ld + DEBUGVAR(1,203,"sbr_header(): bs_amp_res")); + + /* bs_start_freq and bs_stop_freq must define a fequency band that does + not exceed 48 channels */ + sbr->bs_start_freq = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,204,"sbr_header(): bs_start_freq")); + sbr->bs_stop_freq = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,205,"sbr_header(): bs_stop_freq")); + sbr->bs_xover_band = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,206,"sbr_header(): bs_xover_band")); + faad_getbits(ld, 2 + DEBUGVAR(1,207,"sbr_header(): bs_reserved_bits_hdr")); + bs_header_extra_1 = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,208,"sbr_header(): bs_header_extra_1")); + bs_header_extra_2 = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,209,"sbr_header(): bs_header_extra_2")); + + if (bs_header_extra_1) + { + sbr->bs_freq_scale = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,211,"sbr_header(): bs_freq_scale")); + sbr->bs_alter_scale = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,212,"sbr_header(): bs_alter_scale")); + sbr->bs_noise_bands = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,213,"sbr_header(): bs_noise_bands")); + } else { + /* Default values */ + sbr->bs_freq_scale = 2; + sbr->bs_alter_scale = 1; + sbr->bs_noise_bands = 2; + } + + if (bs_header_extra_2) + { + sbr->bs_limiter_bands = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,214,"sbr_header(): bs_limiter_bands")); + sbr->bs_limiter_gains = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,215,"sbr_header(): bs_limiter_gains")); + sbr->bs_interpol_freq = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,216,"sbr_header(): bs_interpol_freq")); + sbr->bs_smoothing_mode = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,217,"sbr_header(): bs_smoothing_mode")); + } else { + /* Default values */ + sbr->bs_limiter_bands = 2; + sbr->bs_limiter_gains = 2; + sbr->bs_interpol_freq = 1; + sbr->bs_smoothing_mode = 1; + } + +#if 0 + /* print the header to screen */ + printf("bs_amp_res: %d\n", sbr->bs_amp_res); + printf("bs_start_freq: %d\n", sbr->bs_start_freq); + printf("bs_stop_freq: %d\n", sbr->bs_stop_freq); + printf("bs_xover_band: %d\n", sbr->bs_xover_band); + if (bs_header_extra_1) + { + printf("bs_freq_scale: %d\n", sbr->bs_freq_scale); + printf("bs_alter_scale: %d\n", sbr->bs_alter_scale); + printf("bs_noise_bands: %d\n", sbr->bs_noise_bands); + } + if (bs_header_extra_2) + { + printf("bs_limiter_bands: %d\n", sbr->bs_limiter_bands); + printf("bs_limiter_gains: %d\n", sbr->bs_limiter_gains); + printf("bs_interpol_freq: %d\n", sbr->bs_interpol_freq); + printf("bs_smoothing_mode: %d\n", sbr->bs_smoothing_mode); + } + printf("\n"); +#endif +} + +/* table 4 */ +static uint8_t sbr_data(bitfile *ld, sbr_info *sbr) +{ + uint8_t result; +#if 0 + sbr->bs_samplerate_mode = faad_get1bit(ld + DEBUGVAR(1,219,"sbr_data(): bs_samplerate_mode")); +#endif + + sbr->rate = (sbr->bs_samplerate_mode) ? 2 : 1; + + switch (sbr->id_aac) + { + case ID_SCE: + if ((result = sbr_single_channel_element(ld, sbr)) > 0) + return result; + break; + case ID_CPE: + if ((result = sbr_channel_pair_element(ld, sbr)) > 0) + return result; + break; + } + + return 0; +} + +/* table 5 */ +static uint8_t sbr_single_channel_element(bitfile *ld, sbr_info *sbr) +{ + uint8_t result; + + if (faad_get1bit(ld + DEBUGVAR(1,220,"sbr_single_channel_element(): bs_data_extra"))) + { + faad_getbits(ld, 4 + DEBUGVAR(1,221,"sbr_single_channel_element(): bs_reserved_bits_data")); + } + +#ifdef DRM + /* bs_coupling, from sbr_channel_pair_base_element(bs_amp_res) */ + if (sbr->Is_DRM_SBR) + { + faad_get1bit(ld); + } +#endif +#ifdef HDC + if (sbr->Is_HDC_SBR) + { + faad_get1bit(ld); + } +#endif + + if ((result = sbr_grid(ld, sbr, 0)) > 0) + return result; + + sbr_dtdf(ld, sbr, 0); + invf_mode(ld, sbr, 0); + sbr_envelope(ld, sbr, 0); + sbr_noise(ld, sbr, 0); + +#ifndef FIXED_POINT + envelope_noise_dequantisation(sbr, 0); +#endif + + memset(sbr->bs_add_harmonic[0], 0, 64*sizeof(uint8_t)); + + sbr->bs_add_harmonic_flag[0] = faad_get1bit(ld + DEBUGVAR(1,223,"sbr_single_channel_element(): bs_add_harmonic_flag[0]")); + if (sbr->bs_add_harmonic_flag[0]) + sinusoidal_coding(ld, sbr, 0); + + sbr->bs_extended_data = faad_get1bit(ld + DEBUGVAR(1,224,"sbr_single_channel_element(): bs_extended_data[0]")); + + if (sbr->bs_extended_data) + { + uint16_t nr_bits_left; +#if (defined(PS_DEC) || defined(DRM_PS)) + uint8_t ps_ext_read = 0; +#endif + uint16_t cnt = (uint16_t)faad_getbits(ld, 4 + DEBUGVAR(1,225,"sbr_single_channel_element(): bs_extension_size")); + if (cnt == 15) + { + cnt += (uint16_t)faad_getbits(ld, 8 + DEBUGVAR(1,226,"sbr_single_channel_element(): bs_esc_count")); + } + + nr_bits_left = 8 * cnt; + while (nr_bits_left > 7) + { + uint16_t tmp_nr_bits = 0; + + sbr->bs_extension_id = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,227,"sbr_single_channel_element(): bs_extension_id")); + tmp_nr_bits += 2; + + /* allow only 1 PS extension element per extension data */ +#if (defined(PS_DEC) || defined(DRM_PS)) +#if (defined(PS_DEC) && defined(DRM_PS)) + if (sbr->bs_extension_id == EXTENSION_ID_PS || sbr->bs_extension_id == DRM_PARAMETRIC_STEREO) +#else +#ifdef PS_DEC + if (sbr->bs_extension_id == EXTENSION_ID_PS) +#else +#ifdef DRM_PS + if (sbr->bs_extension_id == DRM_PARAMETRIC_STEREO) +#endif +#endif +#endif + { + if (ps_ext_read == 0) + { + ps_ext_read = 1; + } else { + /* to be safe make it 3, will switch to "default" + * in sbr_extension() */ +#ifdef DRM + return 1; +#else + sbr->bs_extension_id = 3; +#endif + } + } +#endif + + tmp_nr_bits += sbr_extension(ld, sbr, sbr->bs_extension_id, nr_bits_left); + + /* check if the data read is bigger than the number of available bits */ + if (tmp_nr_bits > nr_bits_left) + return 1; + + nr_bits_left -= tmp_nr_bits; + } + + /* Corrigendum */ + if (nr_bits_left > 0) + { + faad_getbits(ld, nr_bits_left + DEBUGVAR(1,280,"sbr_single_channel_element(): nr_bits_left")); + } + } + + return 0; +} + +/* table 6 */ +static uint8_t sbr_channel_pair_element(bitfile *ld, sbr_info *sbr) +{ + uint8_t n, result; + + if (faad_get1bit(ld + DEBUGVAR(1,228,"sbr_single_channel_element(): bs_data_extra"))) + { + faad_getbits(ld, 4 + DEBUGVAR(1,228,"sbr_channel_pair_element(): bs_reserved_bits_data")); + faad_getbits(ld, 4 + DEBUGVAR(1,228,"sbr_channel_pair_element(): bs_reserved_bits_data")); + } + + sbr->bs_coupling = faad_get1bit(ld + DEBUGVAR(1,228,"sbr_channel_pair_element(): bs_coupling")); + + if (sbr->bs_coupling) + { + if ((result = sbr_grid(ld, sbr, 0)) > 0) + return result; + + /* need to copy some data from left to right */ + sbr->bs_frame_class[1] = sbr->bs_frame_class[0]; + sbr->L_E[1] = sbr->L_E[0]; + sbr->L_Q[1] = sbr->L_Q[0]; + sbr->bs_pointer[1] = sbr->bs_pointer[0]; + + for (n = 0; n <= sbr->L_E[0]; n++) + { + sbr->t_E[1][n] = sbr->t_E[0][n]; + sbr->f[1][n] = sbr->f[0][n]; + } + for (n = 0; n <= sbr->L_Q[0]; n++) + sbr->t_Q[1][n] = sbr->t_Q[0][n]; + + sbr_dtdf(ld, sbr, 0); + sbr_dtdf(ld, sbr, 1); + invf_mode(ld, sbr, 0); + + /* more copying */ + for (n = 0; n < sbr->N_Q; n++) + sbr->bs_invf_mode[1][n] = sbr->bs_invf_mode[0][n]; + + sbr_envelope(ld, sbr, 0); + sbr_noise(ld, sbr, 0); + sbr_envelope(ld, sbr, 1); + sbr_noise(ld, sbr, 1); + + memset(sbr->bs_add_harmonic[0], 0, 64*sizeof(uint8_t)); + memset(sbr->bs_add_harmonic[1], 0, 64*sizeof(uint8_t)); + + sbr->bs_add_harmonic_flag[0] = faad_get1bit(ld + DEBUGVAR(1,231,"sbr_channel_pair_element(): bs_add_harmonic_flag[0]")); + if (sbr->bs_add_harmonic_flag[0]) + sinusoidal_coding(ld, sbr, 0); + + sbr->bs_add_harmonic_flag[1] = faad_get1bit(ld + DEBUGVAR(1,232,"sbr_channel_pair_element(): bs_add_harmonic_flag[1]")); + if (sbr->bs_add_harmonic_flag[1]) + sinusoidal_coding(ld, sbr, 1); + } else { + uint8_t saved_t_E[6] = {0}, saved_t_Q[3] = {0}; + uint8_t saved_L_E = sbr->L_E[0]; + uint8_t saved_L_Q = sbr->L_Q[0]; + uint8_t saved_frame_class = sbr->bs_frame_class[0]; + + for (n = 0; n < saved_L_E; n++) + saved_t_E[n] = sbr->t_E[0][n]; + for (n = 0; n < saved_L_Q; n++) + saved_t_Q[n] = sbr->t_Q[0][n]; + + if ((result = sbr_grid(ld, sbr, 0)) > 0) + return result; + if ((result = sbr_grid(ld, sbr, 1)) > 0) + { + /* restore first channel data as well */ + sbr->bs_frame_class[0] = saved_frame_class; + sbr->L_E[0] = saved_L_E; + sbr->L_Q[0] = saved_L_Q; + for (n = 0; n < 6; n++) + sbr->t_E[0][n] = saved_t_E[n]; + for (n = 0; n < 3; n++) + sbr->t_Q[0][n] = saved_t_Q[n]; + + return result; + } + sbr_dtdf(ld, sbr, 0); + sbr_dtdf(ld, sbr, 1); + invf_mode(ld, sbr, 0); + invf_mode(ld, sbr, 1); + sbr_envelope(ld, sbr, 0); + sbr_envelope(ld, sbr, 1); + sbr_noise(ld, sbr, 0); + sbr_noise(ld, sbr, 1); + + memset(sbr->bs_add_harmonic[0], 0, 64*sizeof(uint8_t)); + memset(sbr->bs_add_harmonic[1], 0, 64*sizeof(uint8_t)); + + sbr->bs_add_harmonic_flag[0] = faad_get1bit(ld + DEBUGVAR(1,239,"sbr_channel_pair_element(): bs_add_harmonic_flag[0]")); + if (sbr->bs_add_harmonic_flag[0]) + sinusoidal_coding(ld, sbr, 0); + + sbr->bs_add_harmonic_flag[1] = faad_get1bit(ld + DEBUGVAR(1,240,"sbr_channel_pair_element(): bs_add_harmonic_flag[1]")); + if (sbr->bs_add_harmonic_flag[1]) + sinusoidal_coding(ld, sbr, 1); + } +#ifndef FIXED_POINT + envelope_noise_dequantisation(sbr, 0); + envelope_noise_dequantisation(sbr, 1); + + if (sbr->bs_coupling) + unmap_envelope_noise(sbr); +#endif + + sbr->bs_extended_data = faad_get1bit(ld + DEBUGVAR(1,233,"sbr_channel_pair_element(): bs_extended_data[0]")); + if (sbr->bs_extended_data) + { + uint16_t nr_bits_left; + uint16_t cnt = (uint16_t)faad_getbits(ld, 4 + DEBUGVAR(1,234,"sbr_channel_pair_element(): bs_extension_size")); + if (cnt == 15) + { + cnt += (uint16_t)faad_getbits(ld, 8 + DEBUGVAR(1,235,"sbr_channel_pair_element(): bs_esc_count")); + } + + nr_bits_left = 8 * cnt; + while (nr_bits_left > 7) + { + uint16_t tmp_nr_bits = 0; + + sbr->bs_extension_id = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,236,"sbr_channel_pair_element(): bs_extension_id")); + tmp_nr_bits += 2; + tmp_nr_bits += sbr_extension(ld, sbr, sbr->bs_extension_id, nr_bits_left); + + /* check if the data read is bigger than the number of available bits */ + if (tmp_nr_bits > nr_bits_left) + return 1; + + nr_bits_left -= tmp_nr_bits; + } + + /* Corrigendum */ + if (nr_bits_left > 0) + { + faad_getbits(ld, nr_bits_left + DEBUGVAR(1,280,"sbr_channel_pair_element(): nr_bits_left")); + } + } + + return 0; +} + +/* integer log[2](x): input range [0,10) */ +static uint8_t sbr_log2(const uint8_t val) +{ + uint8_t log2tab[] = { 0, 0, 1, 2, 2, 3, 3, 3, 3, 4 }; + if (val < 10 && val >= 0) + return log2tab[val]; + else + return 0; +} + + +/* table 7 */ +static uint8_t sbr_grid(bitfile *ld, sbr_info *sbr, uint8_t ch) +{ + uint8_t i, j, env, rel, result; + uint8_t bs_abs_bord, bs_abs_bord_1; + uint8_t bs_num_env = 0; + uint8_t saved_L_E = sbr->L_E[ch]; + uint8_t saved_L_Q = sbr->L_Q[ch]; + uint8_t saved_frame_class = sbr->bs_frame_class[ch]; + + sbr->bs_frame_class[ch] = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,248,"sbr_grid(): bs_frame_class")); + + switch (sbr->bs_frame_class[ch]) + { + case FIXFIX: + i = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,249,"sbr_grid(): bs_num_env_raw")); + + bs_num_env = min(1u << i, 5u); + + i = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,250,"sbr_grid(): bs_freq_res_flag")); + for (env = 0; env < bs_num_env; env++) + sbr->f[ch][env] = i; + + sbr->abs_bord_lead[ch] = 0; + sbr->abs_bord_trail[ch] = sbr->numTimeSlots; + sbr->n_rel_lead[ch] = bs_num_env - 1; + sbr->n_rel_trail[ch] = 0; + break; + + case FIXVAR: + bs_abs_bord = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,251,"sbr_grid(): bs_abs_bord")) + sbr->numTimeSlots; + bs_num_env = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,252,"sbr_grid(): bs_num_env")) + 1; + + for (rel = 0; rel < bs_num_env-1; rel++) + { + sbr->bs_rel_bord[ch][rel] = 2 * (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,253,"sbr_grid(): bs_rel_bord")) + 2; + } + j = bs_num_env; + i = sbr_log2(j + 1); + sbr->bs_pointer[ch] = (uint8_t)faad_getbits(ld, i + DEBUGVAR(1,254,"sbr_grid(): bs_pointer")); + sbr->bs_pointer[ch] = min(sbr->bs_pointer[ch], j); + + for (env = 0; env < bs_num_env; env++) + { + sbr->f[ch][bs_num_env - env - 1] = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,255,"sbr_grid(): bs_freq_res")); + } + + sbr->abs_bord_lead[ch] = 0; + sbr->abs_bord_trail[ch] = bs_abs_bord; + sbr->n_rel_lead[ch] = 0; + sbr->n_rel_trail[ch] = bs_num_env - 1; + break; + + case VARFIX: + bs_abs_bord = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,256,"sbr_grid(): bs_abs_bord")); + bs_num_env = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,257,"sbr_grid(): bs_num_env")) + 1; + + for (rel = 0; rel < bs_num_env-1; rel++) + { + sbr->bs_rel_bord[ch][rel] = 2 * (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,258,"sbr_grid(): bs_rel_bord")) + 2; + } + j = bs_num_env; + i = sbr_log2(j + 1); + sbr->bs_pointer[ch] = (uint8_t)faad_getbits(ld, i + DEBUGVAR(1,259,"sbr_grid(): bs_pointer")); + sbr->bs_pointer[ch] = min(sbr->bs_pointer[ch], j); + + for (env = 0; env < bs_num_env; env++) + { + sbr->f[ch][env] = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,260,"sbr_grid(): bs_freq_res")); + } + + sbr->abs_bord_lead[ch] = bs_abs_bord; + sbr->abs_bord_trail[ch] = sbr->numTimeSlots; + sbr->n_rel_lead[ch] = bs_num_env - 1; + sbr->n_rel_trail[ch] = 0; + break; + + case VARVAR: + bs_abs_bord = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,261,"sbr_grid(): bs_abs_bord_0")); + bs_abs_bord_1 = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,262,"sbr_grid(): bs_abs_bord_1")) + sbr->numTimeSlots; + sbr->bs_num_rel_0[ch] = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,263,"sbr_grid(): bs_num_rel_0")); + sbr->bs_num_rel_1[ch] = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,264,"sbr_grid(): bs_num_rel_1")); + + bs_num_env = min(5, sbr->bs_num_rel_0[ch] + sbr->bs_num_rel_1[ch] + 1); + + for (rel = 0; rel < sbr->bs_num_rel_0[ch]; rel++) + { + sbr->bs_rel_bord_0[ch][rel] = 2 * (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,265,"sbr_grid(): bs_rel_bord")) + 2; + } + for(rel = 0; rel < sbr->bs_num_rel_1[ch]; rel++) + { + sbr->bs_rel_bord_1[ch][rel] = 2 * (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,266,"sbr_grid(): bs_rel_bord")) + 2; + } + j = sbr->bs_num_rel_0[ch] + sbr->bs_num_rel_1[ch] + 1; + i = sbr_log2(j + 1); + sbr->bs_pointer[ch] = (uint8_t)faad_getbits(ld, i + DEBUGVAR(1,267,"sbr_grid(): bs_pointer")); + sbr->bs_pointer[ch] = min(sbr->bs_pointer[ch], j); + + for (env = 0; env < bs_num_env; env++) + { + sbr->f[ch][env] = (uint8_t)faad_get1bit(ld + DEBUGVAR(1,268,"sbr_grid(): bs_freq_res")); + } + + sbr->abs_bord_lead[ch] = bs_abs_bord; + sbr->abs_bord_trail[ch] = bs_abs_bord_1; + sbr->n_rel_lead[ch] = sbr->bs_num_rel_0[ch]; + sbr->n_rel_trail[ch] = sbr->bs_num_rel_1[ch]; + break; + } + + if (sbr->bs_frame_class[ch] == VARVAR) + sbr->L_E[ch] = min(bs_num_env, 5); + else + sbr->L_E[ch] = min(bs_num_env, 4); + + if (sbr->L_E[ch] <= 0) + return 1; + + if (sbr->L_E[ch] > 1) + sbr->L_Q[ch] = 2; + else + sbr->L_Q[ch] = 1; + + /* TODO: this code can probably be integrated into the code above! */ + if ((result = envelope_time_border_vector(sbr, ch)) > 0) + { + sbr->bs_frame_class[ch] = saved_frame_class; + sbr->L_E[ch] = saved_L_E; + sbr->L_Q[ch] = saved_L_Q; + return result; + } + noise_floor_time_border_vector(sbr, ch); + +#if 0 + for (env = 0; env < bs_num_env; env++) + { + printf("freq_res[ch:%d][env:%d]: %d\n", ch, env, sbr->f[ch][env]); + } +#endif + + return 0; +} + +/* table 8 */ +static void sbr_dtdf(bitfile *ld, sbr_info *sbr, uint8_t ch) +{ + uint8_t i; + + for (i = 0; i < sbr->L_E[ch]; i++) + { + sbr->bs_df_env[ch][i] = faad_get1bit(ld + DEBUGVAR(1,269,"sbr_dtdf(): bs_df_env")); + } + + for (i = 0; i < sbr->L_Q[ch]; i++) + { + sbr->bs_df_noise[ch][i] = faad_get1bit(ld + DEBUGVAR(1,270,"sbr_dtdf(): bs_df_noise")); + } +} + +/* table 9 */ +static void invf_mode(bitfile *ld, sbr_info *sbr, uint8_t ch) +{ + uint8_t n; + + for (n = 0; n < sbr->N_Q; n++) + { + sbr->bs_invf_mode[ch][n] = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,271,"invf_mode(): bs_invf_mode")); + } +} + +static uint16_t sbr_extension(bitfile *ld, sbr_info *sbr, + uint8_t bs_extension_id, uint16_t num_bits_left) +{ +#ifdef PS_DEC + uint8_t header; + uint16_t ret; +#endif + (void)num_bits_left; /* TODO: remove or actually use parameter. */ + + switch (bs_extension_id) + { +#ifdef PS_DEC + case EXTENSION_ID_PS: + if (!sbr->ps) + { + sbr->ps = ps_init(get_sr_index(sbr->sample_rate), sbr->numTimeSlotsRate); + } + if (sbr->psResetFlag) + { + sbr->ps->header_read = 0; + } + ret = ps_data(sbr->ps, ld, &header); + + /* enable PS if and only if: a header has been decoded */ + if (sbr->ps_used == 0 && header == 1) + { + sbr->ps_used = 1; + } + + if (header == 1) + { + sbr->psResetFlag = 0; + } + + return ret; +#endif +#ifdef DRM_PS + case DRM_PARAMETRIC_STEREO: + /* If not expected then only decode but do not expose. */ +#ifdef HDC + if (sbr->Is_DRM_SBR || sbr->Is_HDC_SBR) +#else + if (sbr->Is_DRM_SBR) +#endif + { + sbr->ps_used = 1; + } + if (!sbr->drm_ps) + { + sbr->drm_ps = drm_ps_init(); + } + return drm_ps_data(sbr->drm_ps, ld); +#endif + default: + sbr->bs_extension_data = (uint8_t)faad_getbits(ld, num_bits_left + DEBUGVAR(1,279,"sbr_single_channel_element(): bs_extension_data")); + return num_bits_left; + } +} + +/* table 12 */ +static void sinusoidal_coding(bitfile *ld, sbr_info *sbr, uint8_t ch) +{ + uint8_t n; + + for (n = 0; n < sbr->N_high; n++) + { + sbr->bs_add_harmonic[ch][n] = faad_get1bit(ld + DEBUGVAR(1,278,"sinusoidal_coding(): bs_add_harmonic")); + } +} + + +#endif /* SBR_DEC */ diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_syntax.h b/third_party/faad_hdc/upstream/libfaad/sbr_syntax.h new file mode 100644 index 000000000..60c5ad35f --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_syntax.h @@ -0,0 +1,68 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_syntax.h,v 1.23 2007/11/01 12:33:36 menno Exp $ +**/ + +#ifndef __SBR_SYNTAX_H__ +#define __SBR_SYNTAX_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" + +#define T_HFGEN 8 +#define T_HFADJ 2 + +#define EXT_SBR_DATA 13 +#define EXT_SBR_DATA_CRC 14 + +#define FIXFIX 0 +#define FIXVAR 1 +#define VARFIX 2 +#define VARVAR 3 + +#define LO_RES 0 +#define HI_RES 1 + +#define NO_TIME_SLOTS_960 15 +#define NO_TIME_SLOTS 16 +#define RATE 2 + +#define NOISE_FLOOR_OFFSET 6 + + +uint8_t sbr_extension_data(bitfile *ld, sbr_info *sbr, uint16_t cnt, + uint8_t resetFlag); + +#ifdef __cplusplus +} +#endif +#endif /* __SBR_SYNTAX_H__ */ + diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_tf_grid.c b/third_party/faad_hdc/upstream/libfaad/sbr_tf_grid.c new file mode 100644 index 000000000..63ff42ca9 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_tf_grid.c @@ -0,0 +1,262 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_tf_grid.c,v 1.20 2008/09/19 22:50:20 menno Exp $ +**/ + +/* Time/Frequency grid */ + +#include "common.h" +#include "structs.h" + +#ifdef SBR_DEC + +#include + +#include "sbr_syntax.h" +#include "sbr_tf_grid.h" + + +/* static function declarations */ +#if 0 +static int16_t rel_bord_lead(sbr_info *sbr, uint8_t ch, uint8_t l); +static int16_t rel_bord_trail(sbr_info *sbr, uint8_t ch, uint8_t l); +#endif +static uint8_t middleBorder(sbr_info *sbr, uint8_t ch); + + +/* function constructs new time border vector */ +/* first build into temp vector to be able to use previous vector on error */ +uint8_t envelope_time_border_vector(sbr_info *sbr, uint8_t ch) +{ + uint8_t l, border, temp, trail; + uint8_t t_E_temp[6] = {0}; + + trail = sbr->abs_bord_trail[ch]; + t_E_temp[0] = sbr->rate * sbr->abs_bord_lead[ch]; + t_E_temp[sbr->L_E[ch]] = sbr->rate * trail; + + switch (sbr->bs_frame_class[ch]) + { + case FIXFIX: + switch (sbr->L_E[ch]) + { + case 4: + temp = (sbr->numTimeSlots / 4); + t_E_temp[3] = sbr->rate * 3 * temp; + t_E_temp[2] = sbr->rate * 2 * temp; + t_E_temp[1] = sbr->rate * temp; + break; + case 2: + t_E_temp[1] = sbr->rate * (sbr->numTimeSlots / 2); + break; + default: + break; + } + break; + + case FIXVAR: + if (sbr->L_E[ch] > 1) + { + int8_t i = sbr->L_E[ch]; + border = sbr->abs_bord_trail[ch]; + + for (l = 0; l < (sbr->L_E[ch] - 1); l++) + { + if (border < sbr->bs_rel_bord[ch][l]) + return 1; + + border -= sbr->bs_rel_bord[ch][l]; + t_E_temp[--i] = sbr->rate * border; + } + } + break; + + case VARFIX: + if (sbr->L_E[ch] > 1) + { + int8_t i = 1; + border = sbr->abs_bord_lead[ch]; + + for (l = 0; l < (sbr->L_E[ch] - 1); l++) + { + border += sbr->bs_rel_bord[ch][l]; + + if (border > trail) + return 1; + + t_E_temp[i++] = sbr->rate * border; + } + } + break; + + case VARVAR: + if (sbr->bs_num_rel_0[ch]) + { + int8_t i = 1; + border = sbr->abs_bord_lead[ch]; + + for (l = 0; l < sbr->bs_num_rel_0[ch]; l++) + { + border += sbr->bs_rel_bord_0[ch][l]; + + if (border > trail) + return 1; + + t_E_temp[i++] = sbr->rate * border; + } + } + + if (sbr->bs_num_rel_1[ch]) + { + int8_t i = sbr->L_E[ch]; + border = sbr->abs_bord_trail[ch]; + + for (l = 0; l < sbr->bs_num_rel_1[ch]; l++) + { + if (border < sbr->bs_rel_bord_1[ch][l]) + return 1; + + border -= sbr->bs_rel_bord_1[ch][l]; + t_E_temp[--i] = sbr->rate * border; + } + } + break; + } + + /* no error occured, we can safely use this t_E vector */ + for (l = 0; l < 6; l++) + { + sbr->t_E[ch][l] = t_E_temp[l]; + } + + return 0; +} + +void noise_floor_time_border_vector(sbr_info *sbr, uint8_t ch) +{ + sbr->t_Q[ch][0] = sbr->t_E[ch][0]; + + if (sbr->L_E[ch] == 1) + { + sbr->t_Q[ch][1] = sbr->t_E[ch][1]; + sbr->t_Q[ch][2] = 0; + } else { + uint8_t index = middleBorder(sbr, ch); + sbr->t_Q[ch][1] = sbr->t_E[ch][index]; + sbr->t_Q[ch][2] = sbr->t_E[ch][sbr->L_E[ch]]; + } +} + +#if 0 +static int16_t rel_bord_lead(sbr_info *sbr, uint8_t ch, uint8_t l) +{ + uint8_t i; + int16_t acc = 0; + + switch (sbr->bs_frame_class[ch]) + { + case FIXFIX: + return sbr->numTimeSlots/sbr->L_E[ch]; + case FIXVAR: + return 0; + case VARFIX: + for (i = 0; i < l; i++) + { + acc += sbr->bs_rel_bord[ch][i]; + } + return acc; + case VARVAR: + for (i = 0; i < l; i++) + { + acc += sbr->bs_rel_bord_0[ch][i]; + } + return acc; + } + + return 0; +} + +static int16_t rel_bord_trail(sbr_info *sbr, uint8_t ch, uint8_t l) +{ + uint8_t i; + int16_t acc = 0; + + switch (sbr->bs_frame_class[ch]) + { + case FIXFIX: + case VARFIX: + return 0; + case FIXVAR: + for (i = 0; i < l; i++) + { + acc += sbr->bs_rel_bord[ch][i]; + } + return acc; + case VARVAR: + for (i = 0; i < l; i++) + { + acc += sbr->bs_rel_bord_1[ch][i]; + } + return acc; + } + + return 0; +} +#endif + +static uint8_t middleBorder(sbr_info *sbr, uint8_t ch) +{ + int8_t retval = 0; + + switch (sbr->bs_frame_class[ch]) + { + case FIXFIX: + retval = sbr->L_E[ch]/2; + break; + case VARFIX: + if (sbr->bs_pointer[ch] == 0) + retval = 1; + else if (sbr->bs_pointer[ch] == 1) + retval = sbr->L_E[ch] - 1; + else + retval = sbr->bs_pointer[ch] - 1; + break; + case FIXVAR: + case VARVAR: + if (sbr->bs_pointer[ch] > 1) + retval = sbr->L_E[ch] + 1 - sbr->bs_pointer[ch]; + else + retval = sbr->L_E[ch] - 1; + break; + } + + return (retval > 0) ? retval : 0; +} + + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/sbr_tf_grid.h b/third_party/faad_hdc/upstream/libfaad/sbr_tf_grid.h new file mode 100644 index 000000000..fb110c1dc --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sbr_tf_grid.h @@ -0,0 +1,47 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sbr_tf_grid.h,v 1.17 2007/11/01 12:33:36 menno Exp $ +**/ + +#ifndef __SBR_TF_GRID_H__ +#define __SBR_TF_GRID_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +uint8_t envelope_time_border_vector(sbr_info *sbr, uint8_t ch); +void noise_floor_time_border_vector(sbr_info *sbr, uint8_t ch); + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/third_party/faad_hdc/upstream/libfaad/sine_win.h b/third_party/faad_hdc/upstream/libfaad/sine_win.h new file mode 100644 index 000000000..879971001 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/sine_win.h @@ -0,0 +1,4304 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: sine_win.h,v 1.19 2007/11/01 12:33:36 menno Exp $ +**/ + +#ifndef __SINE_WIN_H__ +#define __SINE_WIN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4305) +#pragma warning(disable:4244) +#endif + +ALIGN static const real_t sine_long_1024[] = +{ + FRAC_CONST(0.00076699031874270449), + FRAC_CONST(0.002300969151425805), + FRAC_CONST(0.0038349425697062275), + FRAC_CONST(0.0053689069639963425), + FRAC_CONST(0.0069028587247297558), + FRAC_CONST(0.0084367942423697988), + FRAC_CONST(0.0099707099074180308), + FRAC_CONST(0.011504602110422714), + FRAC_CONST(0.013038467241987334), + FRAC_CONST(0.014572301692779064), + FRAC_CONST(0.016106101853537287), + FRAC_CONST(0.017639864115082053), + FRAC_CONST(0.019173584868322623), + FRAC_CONST(0.020707260504265895), + FRAC_CONST(0.022240887414024961), + FRAC_CONST(0.023774461988827555), + FRAC_CONST(0.025307980620024571), + FRAC_CONST(0.026841439699098531), + FRAC_CONST(0.028374835617672099), + FRAC_CONST(0.029908164767516555), + FRAC_CONST(0.031441423540560301), + FRAC_CONST(0.032974608328897335), + FRAC_CONST(0.03450771552479575), + FRAC_CONST(0.036040741520706229), + FRAC_CONST(0.037573682709270494), + FRAC_CONST(0.039106535483329888), + FRAC_CONST(0.040639296235933736), + FRAC_CONST(0.042171961360347947), + FRAC_CONST(0.043704527250063421), + FRAC_CONST(0.04523699029880459), + FRAC_CONST(0.046769346900537863), + FRAC_CONST(0.048301593449480144), + FRAC_CONST(0.049833726340107277), + FRAC_CONST(0.051365741967162593), + FRAC_CONST(0.052897636725665324), + FRAC_CONST(0.054429407010919133), + FRAC_CONST(0.055961049218520569), + FRAC_CONST(0.057492559744367566), + FRAC_CONST(0.059023934984667931), + FRAC_CONST(0.060555171335947788), + FRAC_CONST(0.062086265195060088), + FRAC_CONST(0.063617212959193106), + FRAC_CONST(0.065148011025878833), + FRAC_CONST(0.066678655793001557), + FRAC_CONST(0.068209143658806329), + FRAC_CONST(0.069739471021907307), + FRAC_CONST(0.071269634281296401), + FRAC_CONST(0.072799629836351673), + FRAC_CONST(0.074329454086845756), + FRAC_CONST(0.075859103432954447), + FRAC_CONST(0.077388574275265049), + FRAC_CONST(0.078917863014784942), + FRAC_CONST(0.080446966052950014), + FRAC_CONST(0.081975879791633066), + FRAC_CONST(0.083504600633152432), + FRAC_CONST(0.085033124980280275), + FRAC_CONST(0.08656144923625117), + FRAC_CONST(0.088089569804770507), + FRAC_CONST(0.089617483090022959), + FRAC_CONST(0.091145185496681005), + FRAC_CONST(0.09267267342991331), + FRAC_CONST(0.094199943295393204), + FRAC_CONST(0.095726991499307162), + FRAC_CONST(0.097253814448363271), + FRAC_CONST(0.098780408549799623), + FRAC_CONST(0.10030677021139286), + FRAC_CONST(0.10183289584146653), + FRAC_CONST(0.10335878184889961), + FRAC_CONST(0.10488442464313497), + FRAC_CONST(0.10640982063418768), + FRAC_CONST(0.10793496623265365), + FRAC_CONST(0.10945985784971798), + FRAC_CONST(0.11098449189716339), + FRAC_CONST(0.11250886478737869), + FRAC_CONST(0.1140329729333672), + FRAC_CONST(0.11555681274875526), + FRAC_CONST(0.11708038064780059), + FRAC_CONST(0.11860367304540072), + FRAC_CONST(0.1201266863571015), + FRAC_CONST(0.12164941699910553), + FRAC_CONST(0.12317186138828048), + FRAC_CONST(0.12469401594216764), + FRAC_CONST(0.12621587707899035), + FRAC_CONST(0.12773744121766231), + FRAC_CONST(0.12925870477779614), + FRAC_CONST(0.13077966417971171), + FRAC_CONST(0.13230031584444465), + FRAC_CONST(0.13382065619375472), + FRAC_CONST(0.13534068165013421), + FRAC_CONST(0.13686038863681638), + FRAC_CONST(0.13837977357778389), + FRAC_CONST(0.13989883289777721), + FRAC_CONST(0.14141756302230302), + FRAC_CONST(0.14293596037764267), + FRAC_CONST(0.14445402139086047), + FRAC_CONST(0.14597174248981221), + FRAC_CONST(0.14748912010315357), + FRAC_CONST(0.14900615066034845), + FRAC_CONST(0.1505228305916774), + FRAC_CONST(0.15203915632824605), + FRAC_CONST(0.15355512430199345), + FRAC_CONST(0.15507073094570051), + FRAC_CONST(0.15658597269299843), + FRAC_CONST(0.15810084597837698), + FRAC_CONST(0.15961534723719306), + FRAC_CONST(0.16112947290567881), + FRAC_CONST(0.16264321942095031), + FRAC_CONST(0.16415658322101581), + FRAC_CONST(0.16566956074478412), + FRAC_CONST(0.16718214843207294), + FRAC_CONST(0.16869434272361733), + FRAC_CONST(0.17020614006107807), + FRAC_CONST(0.17171753688704997), + FRAC_CONST(0.17322852964507032), + FRAC_CONST(0.1747391147796272), + FRAC_CONST(0.17624928873616788), + FRAC_CONST(0.17775904796110717), + FRAC_CONST(0.17926838890183575), + FRAC_CONST(0.18077730800672859), + FRAC_CONST(0.1822858017251533), + FRAC_CONST(0.18379386650747845), + FRAC_CONST(0.1853014988050819), + FRAC_CONST(0.18680869507035927), + FRAC_CONST(0.18831545175673212), + FRAC_CONST(0.18982176531865641), + FRAC_CONST(0.1913276322116309), + FRAC_CONST(0.19283304889220523), + FRAC_CONST(0.1943380118179886), + FRAC_CONST(0.19584251744765785), + FRAC_CONST(0.19734656224096592), + FRAC_CONST(0.19885014265875009), + FRAC_CONST(0.20035325516294045), + FRAC_CONST(0.20185589621656805), + FRAC_CONST(0.20335806228377332), + FRAC_CONST(0.20485974982981442), + FRAC_CONST(0.20636095532107551), + FRAC_CONST(0.20786167522507507), + FRAC_CONST(0.20936190601047416), + FRAC_CONST(0.21086164414708486), + FRAC_CONST(0.21236088610587842), + FRAC_CONST(0.21385962835899375), + FRAC_CONST(0.21535786737974555), + FRAC_CONST(0.21685559964263262), + FRAC_CONST(0.21835282162334632), + FRAC_CONST(0.2198495297987787), + FRAC_CONST(0.22134572064703081), + FRAC_CONST(0.22284139064742112), + FRAC_CONST(0.2243365362804936), + FRAC_CONST(0.22583115402802617), + FRAC_CONST(0.22732524037303886), + FRAC_CONST(0.22881879179980222), + FRAC_CONST(0.23031180479384544), + FRAC_CONST(0.23180427584196478), + FRAC_CONST(0.23329620143223159), + FRAC_CONST(0.23478757805400097), + FRAC_CONST(0.23627840219791957), + FRAC_CONST(0.23776867035593419), + FRAC_CONST(0.23925837902129998), + FRAC_CONST(0.24074752468858843), + FRAC_CONST(0.24223610385369601), + FRAC_CONST(0.24372411301385216), + FRAC_CONST(0.24521154866762754), + FRAC_CONST(0.24669840731494241), + FRAC_CONST(0.24818468545707478), + FRAC_CONST(0.24967037959666857), + FRAC_CONST(0.25115548623774192), + FRAC_CONST(0.25264000188569552), + FRAC_CONST(0.25412392304732062), + FRAC_CONST(0.25560724623080738), + FRAC_CONST(0.25708996794575312), + FRAC_CONST(0.25857208470317034), + FRAC_CONST(0.26005359301549519), + FRAC_CONST(0.26153448939659552), + FRAC_CONST(0.263014770361779), + FRAC_CONST(0.26449443242780163), + FRAC_CONST(0.26597347211287559), + FRAC_CONST(0.26745188593667762), + FRAC_CONST(0.26892967042035726), + FRAC_CONST(0.27040682208654482), + FRAC_CONST(0.27188333745935972), + FRAC_CONST(0.27335921306441868), + FRAC_CONST(0.27483444542884394), + FRAC_CONST(0.27630903108127108), + FRAC_CONST(0.27778296655185769), + FRAC_CONST(0.27925624837229118), + FRAC_CONST(0.28072887307579719), + FRAC_CONST(0.28220083719714756), + FRAC_CONST(0.28367213727266843), + FRAC_CONST(0.28514276984024867), + FRAC_CONST(0.28661273143934779), + FRAC_CONST(0.28808201861100413), + FRAC_CONST(0.28955062789784303), + FRAC_CONST(0.29101855584408509), + FRAC_CONST(0.29248579899555388), + FRAC_CONST(0.29395235389968466), + FRAC_CONST(0.29541821710553201), + FRAC_CONST(0.29688338516377827), + FRAC_CONST(0.2983478546267414), + FRAC_CONST(0.29981162204838335), + FRAC_CONST(0.30127468398431795), + FRAC_CONST(0.30273703699181914), + FRAC_CONST(0.30419867762982911), + FRAC_CONST(0.30565960245896612), + FRAC_CONST(0.3071198080415331), + FRAC_CONST(0.30857929094152509), + FRAC_CONST(0.31003804772463789), + FRAC_CONST(0.31149607495827591), + FRAC_CONST(0.3129533692115602), + FRAC_CONST(0.31440992705533666), + FRAC_CONST(0.31586574506218396), + FRAC_CONST(0.31732081980642174), + FRAC_CONST(0.31877514786411848), + FRAC_CONST(0.32022872581309986), + FRAC_CONST(0.32168155023295658), + FRAC_CONST(0.32313361770505233), + FRAC_CONST(0.32458492481253215), + FRAC_CONST(0.32603546814033024), + FRAC_CONST(0.327485244275178), + FRAC_CONST(0.3289342498056122), + FRAC_CONST(0.33038248132198278), + FRAC_CONST(0.33182993541646111), + FRAC_CONST(0.33327660868304793), + FRAC_CONST(0.33472249771758122), + FRAC_CONST(0.33616759911774452), + FRAC_CONST(0.33761190948307462), + FRAC_CONST(0.33905542541496964), + FRAC_CONST(0.34049814351669716), + FRAC_CONST(0.34194006039340219), + FRAC_CONST(0.34338117265211504), + FRAC_CONST(0.34482147690175929), + FRAC_CONST(0.34626096975316001), + FRAC_CONST(0.34769964781905138), + FRAC_CONST(0.34913750771408497), + FRAC_CONST(0.35057454605483751), + FRAC_CONST(0.35201075945981908), + FRAC_CONST(0.35344614454948081), + FRAC_CONST(0.35488069794622279), + FRAC_CONST(0.35631441627440241), + FRAC_CONST(0.3577472961603419), + FRAC_CONST(0.3591793342323365), + FRAC_CONST(0.36061052712066227), + FRAC_CONST(0.36204087145758418), + FRAC_CONST(0.36347036387736376), + FRAC_CONST(0.36489900101626732), + FRAC_CONST(0.36632677951257359), + FRAC_CONST(0.36775369600658198), + FRAC_CONST(0.36917974714062002), + FRAC_CONST(0.37060492955905167), + FRAC_CONST(0.37202923990828501), + FRAC_CONST(0.3734526748367803), + FRAC_CONST(0.37487523099505754), + FRAC_CONST(0.37629690503570479), + FRAC_CONST(0.37771769361338564), + FRAC_CONST(0.37913759338484732), + FRAC_CONST(0.38055660100892852), + FRAC_CONST(0.38197471314656722), + FRAC_CONST(0.38339192646080866), + FRAC_CONST(0.38480823761681288), + FRAC_CONST(0.38622364328186298), + FRAC_CONST(0.38763814012537273), + FRAC_CONST(0.38905172481889438), + FRAC_CONST(0.39046439403612659), + FRAC_CONST(0.39187614445292235), + FRAC_CONST(0.3932869727472964), + FRAC_CONST(0.39469687559943356), + FRAC_CONST(0.39610584969169627), + FRAC_CONST(0.39751389170863233), + FRAC_CONST(0.39892099833698291), + FRAC_CONST(0.40032716626569009), + FRAC_CONST(0.40173239218590501), + FRAC_CONST(0.4031366727909953), + FRAC_CONST(0.404540004776553), + FRAC_CONST(0.40594238484040251), + FRAC_CONST(0.40734380968260797), + FRAC_CONST(0.40874427600548136), + FRAC_CONST(0.41014378051359024), + FRAC_CONST(0.41154231991376522), + FRAC_CONST(0.41293989091510808), + FRAC_CONST(0.4143364902289991), + FRAC_CONST(0.41573211456910536), + FRAC_CONST(0.41712676065138787), + FRAC_CONST(0.4185204251941097), + FRAC_CONST(0.41991310491784362), + FRAC_CONST(0.42130479654547964), + FRAC_CONST(0.42269549680223295), + FRAC_CONST(0.42408520241565156), + FRAC_CONST(0.4254739101156238), + FRAC_CONST(0.42686161663438643), + FRAC_CONST(0.42824831870653196), + FRAC_CONST(0.42963401306901638), + FRAC_CONST(0.43101869646116703), + FRAC_CONST(0.43240236562469014), + FRAC_CONST(0.43378501730367852), + FRAC_CONST(0.43516664824461926), + FRAC_CONST(0.4365472551964012), + FRAC_CONST(0.43792683491032286), + FRAC_CONST(0.43930538414009995), + FRAC_CONST(0.4406828996418729), + FRAC_CONST(0.4420593781742147), + FRAC_CONST(0.44343481649813848), + FRAC_CONST(0.44480921137710488), + FRAC_CONST(0.44618255957703007), + FRAC_CONST(0.44755485786629301), + FRAC_CONST(0.44892610301574326), + FRAC_CONST(0.45029629179870861), + FRAC_CONST(0.45166542099100249), + FRAC_CONST(0.45303348737093158), + FRAC_CONST(0.45440048771930358), + FRAC_CONST(0.45576641881943464), + FRAC_CONST(0.45713127745715698), + FRAC_CONST(0.45849506042082627), + FRAC_CONST(0.45985776450132954), + FRAC_CONST(0.46121938649209238), + FRAC_CONST(0.46257992318908681), + FRAC_CONST(0.46393937139083852), + FRAC_CONST(0.4652977278984346), + FRAC_CONST(0.46665498951553092), + FRAC_CONST(0.46801115304835983), + FRAC_CONST(0.46936621530573752), + FRAC_CONST(0.4707201730990716), + FRAC_CONST(0.47207302324236866), + FRAC_CONST(0.47342476255224153), + FRAC_CONST(0.47477538784791712), + FRAC_CONST(0.47612489595124358), + FRAC_CONST(0.47747328368669806), + FRAC_CONST(0.47882054788139389), + FRAC_CONST(0.48016668536508839), + FRAC_CONST(0.48151169297018986), + FRAC_CONST(0.48285556753176567), + FRAC_CONST(0.48419830588754903), + FRAC_CONST(0.48553990487794696), + FRAC_CONST(0.48688036134604734), + FRAC_CONST(0.48821967213762679), + FRAC_CONST(0.48955783410115744), + FRAC_CONST(0.49089484408781509), + FRAC_CONST(0.49223069895148602), + FRAC_CONST(0.49356539554877477), + FRAC_CONST(0.49489893073901126), + FRAC_CONST(0.49623130138425825), + FRAC_CONST(0.49756250434931915), + FRAC_CONST(0.49889253650174459), + FRAC_CONST(0.50022139471184068), + FRAC_CONST(0.50154907585267539), + FRAC_CONST(0.50287557680008699), + FRAC_CONST(0.50420089443269034), + FRAC_CONST(0.50552502563188539), + FRAC_CONST(0.50684796728186321), + FRAC_CONST(0.5081697162696146), + FRAC_CONST(0.50949026948493636), + FRAC_CONST(0.51080962382043904), + FRAC_CONST(0.51212777617155469), + FRAC_CONST(0.51344472343654346), + FRAC_CONST(0.5147604625165012), + FRAC_CONST(0.51607499031536663), + FRAC_CONST(0.51738830373992906), + FRAC_CONST(0.51870039969983495), + FRAC_CONST(0.52001127510759604), + FRAC_CONST(0.52132092687859566), + FRAC_CONST(0.52262935193109661), + FRAC_CONST(0.5239365471862486), + FRAC_CONST(0.52524250956809471), + FRAC_CONST(0.52654723600357944), + FRAC_CONST(0.52785072342255523), + FRAC_CONST(0.52915296875779061), + FRAC_CONST(0.53045396894497632), + FRAC_CONST(0.53175372092273332), + FRAC_CONST(0.53305222163261945), + FRAC_CONST(0.53434946801913752), + FRAC_CONST(0.53564545702974109), + FRAC_CONST(0.53694018561484291), + FRAC_CONST(0.5382336507278217), + FRAC_CONST(0.53952584932502889), + FRAC_CONST(0.54081677836579667), + FRAC_CONST(0.54210643481244392), + FRAC_CONST(0.5433948156302848), + FRAC_CONST(0.54468191778763453), + FRAC_CONST(0.54596773825581757), + FRAC_CONST(0.54725227400917409), + FRAC_CONST(0.54853552202506739), + FRAC_CONST(0.54981747928389091), + FRAC_CONST(0.55109814276907543), + FRAC_CONST(0.55237750946709607), + FRAC_CONST(0.55365557636747931), + FRAC_CONST(0.55493234046281037), + FRAC_CONST(0.55620779874873993), + FRAC_CONST(0.55748194822399155), + FRAC_CONST(0.55875478589036831), + FRAC_CONST(0.56002630875276038), + FRAC_CONST(0.56129651381915147), + FRAC_CONST(0.56256539810062656), + FRAC_CONST(0.56383295861137817), + FRAC_CONST(0.56509919236871398), + FRAC_CONST(0.56636409639306384), + FRAC_CONST(0.56762766770798623), + FRAC_CONST(0.56888990334017586), + FRAC_CONST(0.5701508003194703), + FRAC_CONST(0.57141035567885723), + FRAC_CONST(0.57266856645448116), + FRAC_CONST(0.57392542968565075), + FRAC_CONST(0.57518094241484508), + FRAC_CONST(0.57643510168772183), + FRAC_CONST(0.5776879045531228), + FRAC_CONST(0.57893934806308178), + FRAC_CONST(0.58018942927283168), + FRAC_CONST(0.58143814524081017), + FRAC_CONST(0.58268549302866846), + FRAC_CONST(0.58393146970127618), + FRAC_CONST(0.58517607232673041), + FRAC_CONST(0.5864192979763605), + FRAC_CONST(0.58766114372473666), + FRAC_CONST(0.58890160664967572), + FRAC_CONST(0.59014068383224882), + FRAC_CONST(0.59137837235678758), + FRAC_CONST(0.59261466931089113), + FRAC_CONST(0.59384957178543363), + FRAC_CONST(0.59508307687456996), + FRAC_CONST(0.59631518167574371), + FRAC_CONST(0.59754588328969316), + FRAC_CONST(0.59877517882045872), + FRAC_CONST(0.60000306537538894), + FRAC_CONST(0.6012295400651485), + FRAC_CONST(0.60245460000372375), + FRAC_CONST(0.60367824230843037), + FRAC_CONST(0.60490046409991982), + FRAC_CONST(0.60612126250218612), + FRAC_CONST(0.60734063464257293), + FRAC_CONST(0.60855857765177945), + FRAC_CONST(0.60977508866386843), + FRAC_CONST(0.61099016481627166), + FRAC_CONST(0.61220380324979795), + FRAC_CONST(0.61341600110863859), + FRAC_CONST(0.61462675554037505), + FRAC_CONST(0.61583606369598509), + FRAC_CONST(0.61704392272984976), + FRAC_CONST(0.61825032979976025), + FRAC_CONST(0.61945528206692402), + FRAC_CONST(0.62065877669597214), + FRAC_CONST(0.62186081085496536), + FRAC_CONST(0.62306138171540126), + FRAC_CONST(0.62426048645222065), + FRAC_CONST(0.62545812224381436), + FRAC_CONST(0.62665428627202935), + FRAC_CONST(0.62784897572217646), + FRAC_CONST(0.629042187783036), + FRAC_CONST(0.63023391964686437), + FRAC_CONST(0.63142416850940186), + FRAC_CONST(0.63261293156987741), + FRAC_CONST(0.63380020603101728), + FRAC_CONST(0.63498598909904946), + FRAC_CONST(0.63617027798371217), + FRAC_CONST(0.63735306989825913), + FRAC_CONST(0.63853436205946679), + FRAC_CONST(0.63971415168764045), + FRAC_CONST(0.64089243600662138), + FRAC_CONST(0.64206921224379254), + FRAC_CONST(0.64324447763008585), + FRAC_CONST(0.64441822939998838), + FRAC_CONST(0.64559046479154869), + FRAC_CONST(0.64676118104638392), + FRAC_CONST(0.64793037540968534), + FRAC_CONST(0.64909804513022595), + FRAC_CONST(0.65026418746036585), + FRAC_CONST(0.65142879965605982), + FRAC_CONST(0.65259187897686244), + FRAC_CONST(0.65375342268593606), + FRAC_CONST(0.65491342805005603), + FRAC_CONST(0.6560718923396176), + FRAC_CONST(0.65722881282864254), + FRAC_CONST(0.65838418679478505), + FRAC_CONST(0.65953801151933866), + FRAC_CONST(0.6606902842872423), + FRAC_CONST(0.66184100238708687), + FRAC_CONST(0.66299016311112147), + FRAC_CONST(0.66413776375526001), + FRAC_CONST(0.66528380161908718), + FRAC_CONST(0.66642827400586524), + FRAC_CONST(0.66757117822254031), + FRAC_CONST(0.66871251157974798), + FRAC_CONST(0.66985227139182102), + FRAC_CONST(0.67099045497679422), + FRAC_CONST(0.67212705965641173), + FRAC_CONST(0.67326208275613297), + FRAC_CONST(0.67439552160513905), + FRAC_CONST(0.67552737353633852), + FRAC_CONST(0.67665763588637495), + FRAC_CONST(0.6777863059956315), + FRAC_CONST(0.67891338120823841), + FRAC_CONST(0.68003885887207893), + FRAC_CONST(0.68116273633879543), + FRAC_CONST(0.68228501096379557), + FRAC_CONST(0.68340568010625868), + FRAC_CONST(0.6845247411291423), + FRAC_CONST(0.68564219139918747), + FRAC_CONST(0.68675802828692589), + FRAC_CONST(0.68787224916668555), + FRAC_CONST(0.68898485141659704), + FRAC_CONST(0.69009583241859995), + FRAC_CONST(0.69120518955844845), + FRAC_CONST(0.69231292022571822), + FRAC_CONST(0.69341902181381176), + FRAC_CONST(0.69452349171996552), + FRAC_CONST(0.69562632734525487), + FRAC_CONST(0.6967275260946012), + FRAC_CONST(0.69782708537677729), + FRAC_CONST(0.69892500260441415), + FRAC_CONST(0.70002127519400625), + FRAC_CONST(0.70111590056591866), + FRAC_CONST(0.70220887614439187), + FRAC_CONST(0.70330019935754873), + FRAC_CONST(0.70438986763740041), + FRAC_CONST(0.7054778784198521), + FRAC_CONST(0.70656422914470951), + FRAC_CONST(0.70764891725568435), + FRAC_CONST(0.70873194020040065), + FRAC_CONST(0.70981329543040084), + FRAC_CONST(0.71089298040115168), + FRAC_CONST(0.71197099257204999), + FRAC_CONST(0.71304732940642923), + FRAC_CONST(0.71412198837156471), + FRAC_CONST(0.71519496693868001), + FRAC_CONST(0.71626626258295312), + FRAC_CONST(0.71733587278352173), + FRAC_CONST(0.71840379502348972), + FRAC_CONST(0.71947002678993299), + FRAC_CONST(0.72053456557390527), + FRAC_CONST(0.72159740887044366), + FRAC_CONST(0.72265855417857561), + FRAC_CONST(0.72371799900132339), + FRAC_CONST(0.72477574084571128), + FRAC_CONST(0.72583177722277037), + FRAC_CONST(0.72688610564754497), + FRAC_CONST(0.72793872363909862), + FRAC_CONST(0.72898962872051931), + FRAC_CONST(0.73003881841892615), + FRAC_CONST(0.73108629026547423), + FRAC_CONST(0.73213204179536129), + FRAC_CONST(0.73317607054783274), + FRAC_CONST(0.73421837406618817), + FRAC_CONST(0.73525894989778673), + FRAC_CONST(0.73629779559405306), + FRAC_CONST(0.73733490871048279), + FRAC_CONST(0.73837028680664851), + FRAC_CONST(0.73940392744620576), + FRAC_CONST(0.74043582819689802), + FRAC_CONST(0.74146598663056329), + FRAC_CONST(0.74249440032313918), + FRAC_CONST(0.74352106685466912), + FRAC_CONST(0.74454598380930725), + FRAC_CONST(0.74556914877532543), + FRAC_CONST(0.74659055934511731), + FRAC_CONST(0.74761021311520515), + FRAC_CONST(0.74862810768624533), + FRAC_CONST(0.74964424066303348), + FRAC_CONST(0.75065860965451059), + FRAC_CONST(0.75167121227376843), + FRAC_CONST(0.75268204613805523), + FRAC_CONST(0.75369110886878121), + FRAC_CONST(0.75469839809152439), + FRAC_CONST(0.75570391143603588), + FRAC_CONST(0.75670764653624567), + FRAC_CONST(0.75770960103026808), + FRAC_CONST(0.75870977256040739), + FRAC_CONST(0.75970815877316344), + FRAC_CONST(0.76070475731923692), + FRAC_CONST(0.76169956585353527), + FRAC_CONST(0.76269258203517787), + FRAC_CONST(0.76368380352750187), + FRAC_CONST(0.76467322799806714), + FRAC_CONST(0.76566085311866239), + FRAC_CONST(0.76664667656531038), + FRAC_CONST(0.76763069601827327), + FRAC_CONST(0.76861290916205827), + FRAC_CONST(0.76959331368542294), + FRAC_CONST(0.7705719072813807), + FRAC_CONST(0.7715486876472063), + FRAC_CONST(0.77252365248444133), + FRAC_CONST(0.77349679949889905), + FRAC_CONST(0.77446812640067086), + FRAC_CONST(0.77543763090413043), + FRAC_CONST(0.77640531072794039), + FRAC_CONST(0.7773711635950562), + FRAC_CONST(0.77833518723273309), + FRAC_CONST(0.7792973793725303), + FRAC_CONST(0.78025773775031659), + FRAC_CONST(0.78121626010627609), + FRAC_CONST(0.7821729441849129), + FRAC_CONST(0.78312778773505731), + FRAC_CONST(0.78408078850986995), + FRAC_CONST(0.78503194426684808), + FRAC_CONST(0.78598125276783015), + FRAC_CONST(0.7869287117790017), + FRAC_CONST(0.78787431907090011), + FRAC_CONST(0.78881807241842017), + FRAC_CONST(0.78975996960081907), + FRAC_CONST(0.79070000840172161), + FRAC_CONST(0.79163818660912577), + FRAC_CONST(0.79257450201540758), + FRAC_CONST(0.79350895241732666), + FRAC_CONST(0.79444153561603059), + FRAC_CONST(0.79537224941706119), + FRAC_CONST(0.79630109163035911), + FRAC_CONST(0.7972280600702687), + FRAC_CONST(0.79815315255554375), + FRAC_CONST(0.79907636690935235), + FRAC_CONST(0.79999770095928191), + FRAC_CONST(0.8009171525373443), + FRAC_CONST(0.80183471947998131), + FRAC_CONST(0.80275039962806916), + FRAC_CONST(0.80366419082692409), + FRAC_CONST(0.804576090926307), + FRAC_CONST(0.80548609778042912), + FRAC_CONST(0.80639420924795624), + FRAC_CONST(0.80730042319201445), + FRAC_CONST(0.80820473748019472), + FRAC_CONST(0.80910714998455813), + FRAC_CONST(0.81000765858164114), + FRAC_CONST(0.81090626115245967), + FRAC_CONST(0.81180295558251536), + FRAC_CONST(0.81269773976179949), + FRAC_CONST(0.81359061158479851), + FRAC_CONST(0.81448156895049861), + FRAC_CONST(0.81537060976239129), + FRAC_CONST(0.81625773192847739), + FRAC_CONST(0.81714293336127297), + FRAC_CONST(0.81802621197781344), + FRAC_CONST(0.81890756569965895), + FRAC_CONST(0.81978699245289899), + FRAC_CONST(0.82066449016815746), + FRAC_CONST(0.82154005678059761), + FRAC_CONST(0.82241369022992639), + FRAC_CONST(0.82328538846040011), + FRAC_CONST(0.82415514942082857), + FRAC_CONST(0.82502297106458022), + FRAC_CONST(0.82588885134958678), + FRAC_CONST(0.82675278823834852), + FRAC_CONST(0.8276147796979384), + FRAC_CONST(0.82847482370000713), + FRAC_CONST(0.82933291822078825), + FRAC_CONST(0.83018906124110237), + FRAC_CONST(0.83104325074636232), + FRAC_CONST(0.83189548472657759), + FRAC_CONST(0.83274576117635946), + FRAC_CONST(0.83359407809492514), + FRAC_CONST(0.83444043348610319), + FRAC_CONST(0.83528482535833737), + FRAC_CONST(0.83612725172469216), + FRAC_CONST(0.83696771060285702), + FRAC_CONST(0.83780620001515094), + FRAC_CONST(0.8386427179885273), + FRAC_CONST(0.83947726255457855), + FRAC_CONST(0.84030983174954077), + FRAC_CONST(0.84114042361429808), + FRAC_CONST(0.84196903619438768), + FRAC_CONST(0.84279566754000412), + FRAC_CONST(0.84362031570600404), + FRAC_CONST(0.84444297875191066), + FRAC_CONST(0.84526365474191822), + FRAC_CONST(0.84608234174489694), + FRAC_CONST(0.84689903783439735), + FRAC_CONST(0.84771374108865427), + FRAC_CONST(0.84852644959059265), + FRAC_CONST(0.84933716142783067), + FRAC_CONST(0.85014587469268521), + FRAC_CONST(0.85095258748217573), + FRAC_CONST(0.85175729789802912), + FRAC_CONST(0.85256000404668397), + FRAC_CONST(0.85336070403929543), + FRAC_CONST(0.85415939599173873), + FRAC_CONST(0.85495607802461482), + FRAC_CONST(0.85575074826325392), + FRAC_CONST(0.85654340483771996), + FRAC_CONST(0.85733404588281559), + FRAC_CONST(0.85812266953808602), + FRAC_CONST(0.8589092739478239), + FRAC_CONST(0.85969385726107261), + FRAC_CONST(0.86047641763163207), + FRAC_CONST(0.86125695321806206), + FRAC_CONST(0.86203546218368721), + FRAC_CONST(0.86281194269660033), + FRAC_CONST(0.86358639292966799), + FRAC_CONST(0.86435881106053403), + FRAC_CONST(0.86512919527162369), + FRAC_CONST(0.86589754375014882), + FRAC_CONST(0.86666385468811102), + FRAC_CONST(0.86742812628230692), + FRAC_CONST(0.86819035673433131), + FRAC_CONST(0.86895054425058238), + FRAC_CONST(0.86970868704226556), + FRAC_CONST(0.87046478332539767), + FRAC_CONST(0.8712188313208109), + FRAC_CONST(0.8719708292541577), + FRAC_CONST(0.8727207753559143), + FRAC_CONST(0.87346866786138488), + FRAC_CONST(0.8742145050107063), + FRAC_CONST(0.87495828504885154), + FRAC_CONST(0.8757000062256346), + FRAC_CONST(0.87643966679571361), + FRAC_CONST(0.87717726501859594), + FRAC_CONST(0.87791279915864173), + FRAC_CONST(0.87864626748506813), + FRAC_CONST(0.87937766827195318), + FRAC_CONST(0.88010699979824036), + FRAC_CONST(0.88083426034774204), + FRAC_CONST(0.88155944820914378), + FRAC_CONST(0.8822825616760086), + FRAC_CONST(0.88300359904678072), + FRAC_CONST(0.88372255862478966), + FRAC_CONST(0.8844394387182537), + FRAC_CONST(0.88515423764028511), + FRAC_CONST(0.88586695370889279), + FRAC_CONST(0.88657758524698704), + FRAC_CONST(0.88728613058238315), + FRAC_CONST(0.88799258804780556), + FRAC_CONST(0.88869695598089171), + FRAC_CONST(0.88939923272419552), + FRAC_CONST(0.89009941662519221), + FRAC_CONST(0.89079750603628149), + FRAC_CONST(0.89149349931479138), + FRAC_CONST(0.89218739482298248), + FRAC_CONST(0.89287919092805168), + FRAC_CONST(0.89356888600213602), + FRAC_CONST(0.89425647842231604), + FRAC_CONST(0.89494196657062075), + FRAC_CONST(0.89562534883403), + FRAC_CONST(0.89630662360447966), + FRAC_CONST(0.89698578927886397), + FRAC_CONST(0.89766284425904075), + FRAC_CONST(0.89833778695183419), + FRAC_CONST(0.89901061576903907), + FRAC_CONST(0.89968132912742393), + FRAC_CONST(0.9003499254487356), + FRAC_CONST(0.90101640315970233), + FRAC_CONST(0.90168076069203773), + FRAC_CONST(0.9023429964824442), + FRAC_CONST(0.90300310897261704), + FRAC_CONST(0.90366109660924798), + FRAC_CONST(0.90431695784402832), + FRAC_CONST(0.90497069113365325), + FRAC_CONST(0.90562229493982516), + FRAC_CONST(0.90627176772925766), + FRAC_CONST(0.90691910797367803), + FRAC_CONST(0.90756431414983252), + FRAC_CONST(0.9082073847394887), + FRAC_CONST(0.90884831822943912), + FRAC_CONST(0.90948711311150543), + FRAC_CONST(0.91012376788254157), + FRAC_CONST(0.91075828104443757), + FRAC_CONST(0.91139065110412232), + FRAC_CONST(0.91202087657356823), + FRAC_CONST(0.9126489559697939), + FRAC_CONST(0.91327488781486776), + FRAC_CONST(0.91389867063591168), + FRAC_CONST(0.91452030296510445), + FRAC_CONST(0.91513978333968526), + FRAC_CONST(0.91575711030195672), + FRAC_CONST(0.91637228239928914), + FRAC_CONST(0.91698529818412289), + FRAC_CONST(0.91759615621397295), + FRAC_CONST(0.9182048550514309), + FRAC_CONST(0.91881139326416994), + FRAC_CONST(0.91941576942494696), + FRAC_CONST(0.92001798211160657), + FRAC_CONST(0.92061802990708386), + FRAC_CONST(0.92121591139940873), + FRAC_CONST(0.92181162518170812), + FRAC_CONST(0.92240516985220988), + FRAC_CONST(0.92299654401424625), + FRAC_CONST(0.92358574627625656), + FRAC_CONST(0.9241727752517912), + FRAC_CONST(0.92475762955951391), + FRAC_CONST(0.9253403078232062), + FRAC_CONST(0.92592080867176996), + FRAC_CONST(0.92649913073923051), + FRAC_CONST(0.9270752726647401), + FRAC_CONST(0.92764923309258118), + FRAC_CONST(0.92822101067216944), + FRAC_CONST(0.92879060405805702), + FRAC_CONST(0.9293580119099355), + FRAC_CONST(0.92992323289263956), + FRAC_CONST(0.93048626567614978), + FRAC_CONST(0.93104710893559517), + FRAC_CONST(0.93160576135125783), + FRAC_CONST(0.93216222160857432), + FRAC_CONST(0.93271648839814025), + FRAC_CONST(0.93326856041571205), + FRAC_CONST(0.93381843636221096), + FRAC_CONST(0.9343661149437259), + FRAC_CONST(0.93491159487151609), + FRAC_CONST(0.93545487486201462), + FRAC_CONST(0.9359959536368313), + FRAC_CONST(0.9365348299227555), + FRAC_CONST(0.93707150245175919), + FRAC_CONST(0.93760596996099999), + FRAC_CONST(0.93813823119282436), + FRAC_CONST(0.93866828489477017), + FRAC_CONST(0.9391961298195699), + FRAC_CONST(0.93972176472515334), + FRAC_CONST(0.94024518837465088), + FRAC_CONST(0.94076639953639607), + FRAC_CONST(0.94128539698392866), + FRAC_CONST(0.94180217949599765), + FRAC_CONST(0.94231674585656378), + FRAC_CONST(0.94282909485480271), + FRAC_CONST(0.94333922528510772), + FRAC_CONST(0.94384713594709269), + FRAC_CONST(0.94435282564559475), + FRAC_CONST(0.94485629319067721), + FRAC_CONST(0.94535753739763229), + FRAC_CONST(0.94585655708698391), + FRAC_CONST(0.94635335108449059), + FRAC_CONST(0.946847918221148), + FRAC_CONST(0.94734025733319194), + FRAC_CONST(0.94783036726210101), + FRAC_CONST(0.94831824685459909), + FRAC_CONST(0.94880389496265838), + FRAC_CONST(0.94928731044350201), + FRAC_CONST(0.94976849215960668), + FRAC_CONST(0.95024743897870523), + FRAC_CONST(0.95072414977378961), + FRAC_CONST(0.95119862342311323), + FRAC_CONST(0.95167085881019386), + FRAC_CONST(0.95214085482381583), + FRAC_CONST(0.95260861035803324), + FRAC_CONST(0.9530741243121722), + FRAC_CONST(0.95353739559083328), + FRAC_CONST(0.95399842310389449), + FRAC_CONST(0.95445720576651349), + FRAC_CONST(0.95491374249913052), + FRAC_CONST(0.95536803222747024), + FRAC_CONST(0.95582007388254542), + FRAC_CONST(0.95626986640065814), + FRAC_CONST(0.95671740872340305), + FRAC_CONST(0.9571626997976701), + FRAC_CONST(0.95760573857564624), + FRAC_CONST(0.9580465240148186), + FRAC_CONST(0.9584850550779761), + FRAC_CONST(0.95892133073321306), + FRAC_CONST(0.95935534995393079), + FRAC_CONST(0.9597871117188399), + FRAC_CONST(0.96021661501196343), + FRAC_CONST(0.96064385882263847), + FRAC_CONST(0.96106884214551935), + FRAC_CONST(0.961491563980579), + FRAC_CONST(0.9619120233331121), + FRAC_CONST(0.9623302192137374), + FRAC_CONST(0.96274615063839941), + FRAC_CONST(0.96315981662837136), + FRAC_CONST(0.96357121621025721), + FRAC_CONST(0.96398034841599411), + FRAC_CONST(0.96438721228285429), + FRAC_CONST(0.9647918068534479), + FRAC_CONST(0.96519413117572472), + FRAC_CONST(0.96559418430297683), + FRAC_CONST(0.96599196529384057), + FRAC_CONST(0.96638747321229879), + FRAC_CONST(0.96678070712768327), + FRAC_CONST(0.96717166611467664), + FRAC_CONST(0.96756034925331436), + FRAC_CONST(0.9679467556289878), + FRAC_CONST(0.9683308843324453), + FRAC_CONST(0.96871273445979478), + FRAC_CONST(0.9690923051125061), + FRAC_CONST(0.96946959539741295), + FRAC_CONST(0.96984460442671483), + FRAC_CONST(0.97021733131797916), + FRAC_CONST(0.97058777519414363), + FRAC_CONST(0.97095593518351797), + FRAC_CONST(0.97132181041978616), + FRAC_CONST(0.97168540004200854), + FRAC_CONST(0.9720467031946235), + FRAC_CONST(0.97240571902744977), + FRAC_CONST(0.97276244669568857), + FRAC_CONST(0.97311688535992513), + FRAC_CONST(0.97346903418613095), + FRAC_CONST(0.9738188923456661), + FRAC_CONST(0.97416645901528032), + FRAC_CONST(0.97451173337711572), + FRAC_CONST(0.97485471461870843), + FRAC_CONST(0.97519540193299037), + FRAC_CONST(0.97553379451829136), + FRAC_CONST(0.97586989157834103), + FRAC_CONST(0.97620369232227056), + FRAC_CONST(0.97653519596461447), + FRAC_CONST(0.97686440172531264), + FRAC_CONST(0.97719130882971228), + FRAC_CONST(0.97751591650856928), + FRAC_CONST(0.97783822399805043), + FRAC_CONST(0.97815823053973505), + FRAC_CONST(0.97847593538061683), + FRAC_CONST(0.97879133777310567), + FRAC_CONST(0.97910443697502925), + FRAC_CONST(0.97941523224963478), + FRAC_CONST(0.97972372286559117), + FRAC_CONST(0.98002990809698998), + FRAC_CONST(0.98033378722334796), + FRAC_CONST(0.98063535952960812), + FRAC_CONST(0.98093462430614164), + FRAC_CONST(0.98123158084874973), + FRAC_CONST(0.98152622845866466), + FRAC_CONST(0.9818185664425525), + FRAC_CONST(0.98210859411251361), + FRAC_CONST(0.98239631078608469), + FRAC_CONST(0.98268171578624086), + FRAC_CONST(0.98296480844139644), + FRAC_CONST(0.98324558808540707), + FRAC_CONST(0.98352405405757126), + FRAC_CONST(0.98380020570263149), + FRAC_CONST(0.98407404237077645), + FRAC_CONST(0.9843455634176419), + FRAC_CONST(0.9846147682043126), + FRAC_CONST(0.9848816560973237), + FRAC_CONST(0.98514622646866223), + FRAC_CONST(0.98540847869576842), + FRAC_CONST(0.98566841216153755), + FRAC_CONST(0.98592602625432113), + FRAC_CONST(0.98618132036792827), + FRAC_CONST(0.98643429390162707), + FRAC_CONST(0.98668494626014669), + FRAC_CONST(0.98693327685367771), + FRAC_CONST(0.98717928509787434), + FRAC_CONST(0.98742297041385541), + FRAC_CONST(0.98766433222820571), + FRAC_CONST(0.98790336997297779), + FRAC_CONST(0.98814008308569257), + FRAC_CONST(0.98837447100934128), + FRAC_CONST(0.98860653319238645), + FRAC_CONST(0.98883626908876354), + FRAC_CONST(0.98906367815788154), + FRAC_CONST(0.98928875986462517), + FRAC_CONST(0.98951151367935519), + FRAC_CONST(0.98973193907791057), + FRAC_CONST(0.98995003554160899), + FRAC_CONST(0.9901658025572484), + FRAC_CONST(0.99037923961710816), + FRAC_CONST(0.99059034621895015), + FRAC_CONST(0.99079912186602037), + FRAC_CONST(0.99100556606704937), + FRAC_CONST(0.99120967833625406), + FRAC_CONST(0.99141145819333854), + FRAC_CONST(0.99161090516349537), + FRAC_CONST(0.99180801877740643), + FRAC_CONST(0.99200279857124452), + FRAC_CONST(0.99219524408667392), + FRAC_CONST(0.99238535487085167), + FRAC_CONST(0.99257313047642881), + FRAC_CONST(0.99275857046155114), + FRAC_CONST(0.99294167438986047), + FRAC_CONST(0.99312244183049558), + FRAC_CONST(0.99330087235809328), + FRAC_CONST(0.99347696555278919), + FRAC_CONST(0.99365072100021912), + FRAC_CONST(0.99382213829151966), + FRAC_CONST(0.99399121702332938), + FRAC_CONST(0.99415795679778973), + FRAC_CONST(0.99432235722254581), + FRAC_CONST(0.9944844179107476), + FRAC_CONST(0.99464413848105071), + FRAC_CONST(0.99480151855761711), + FRAC_CONST(0.99495655777011638), + FRAC_CONST(0.99510925575372611), + FRAC_CONST(0.99525961214913339), + FRAC_CONST(0.9954076266025349), + FRAC_CONST(0.99555329876563847), + FRAC_CONST(0.99569662829566352), + FRAC_CONST(0.99583761485534161), + FRAC_CONST(0.99597625811291779), + FRAC_CONST(0.99611255774215113), + FRAC_CONST(0.99624651342231552), + FRAC_CONST(0.99637812483820021), + FRAC_CONST(0.99650739168011082), + FRAC_CONST(0.9966343136438699), + FRAC_CONST(0.996758890430818), + FRAC_CONST(0.99688112174781385), + FRAC_CONST(0.99700100730723529), + FRAC_CONST(0.99711854682697998), + FRAC_CONST(0.99723374003046616), + FRAC_CONST(0.99734658664663323), + FRAC_CONST(0.99745708640994191), + FRAC_CONST(0.99756523906037575), + FRAC_CONST(0.997671044343441), + FRAC_CONST(0.99777450201016782), + FRAC_CONST(0.99787561181711015), + FRAC_CONST(0.99797437352634699), + FRAC_CONST(0.99807078690548234), + FRAC_CONST(0.99816485172764624), + FRAC_CONST(0.99825656777149518), + FRAC_CONST(0.99834593482121237), + FRAC_CONST(0.99843295266650844), + FRAC_CONST(0.99851762110262221), + FRAC_CONST(0.99859993993032037), + FRAC_CONST(0.99867990895589909), + FRAC_CONST(0.99875752799118334), + FRAC_CONST(0.99883279685352799), + FRAC_CONST(0.99890571536581829), + FRAC_CONST(0.99897628335646982), + FRAC_CONST(0.99904450065942929), + FRAC_CONST(0.99911036711417489), + FRAC_CONST(0.99917388256571638), + FRAC_CONST(0.99923504686459585), + FRAC_CONST(0.99929385986688779), + FRAC_CONST(0.99935032143419944), + FRAC_CONST(0.9994044314336713), + FRAC_CONST(0.99945618973797734), + FRAC_CONST(0.99950559622532531), + FRAC_CONST(0.99955265077945699), + FRAC_CONST(0.99959735328964838), + FRAC_CONST(0.9996397036507102), + FRAC_CONST(0.99967970176298793), + FRAC_CONST(0.99971734753236219), + FRAC_CONST(0.99975264087024884), + FRAC_CONST(0.99978558169359921), + FRAC_CONST(0.99981616992490041), + FRAC_CONST(0.99984440549217524), + FRAC_CONST(0.99987028832898295), + FRAC_CONST(0.99989381837441849), + FRAC_CONST(0.99991499557311347), + FRAC_CONST(0.999933819875236), + FRAC_CONST(0.99995029123649048), + FRAC_CONST(0.99996440961811828), + FRAC_CONST(0.99997617498689761), + FRAC_CONST(0.9999855873151432), + FRAC_CONST(0.99999264658070719), + FRAC_CONST(0.99999735276697821), + FRAC_CONST(0.99999970586288223) +}; + +#ifdef ALLOW_SMALL_FRAMELENGTH +ALIGN static const real_t sine_long_960[] = +{ + FRAC_CONST(0.00081812299560725323), + FRAC_CONST(0.0024543667964602917), + FRAC_CONST(0.0040906040262347889), + FRAC_CONST(0.0057268303042312674), + FRAC_CONST(0.0073630412497795667), + FRAC_CONST(0.0089992324822505774), + FRAC_CONST(0.010635399621067975), + FRAC_CONST(0.012271538285719924), + FRAC_CONST(0.013907644095770845), + FRAC_CONST(0.015543712670873098), + FRAC_CONST(0.017179739630778748), + FRAC_CONST(0.018815720595351273), + FRAC_CONST(0.020451651184577292), + FRAC_CONST(0.022087527018578291), + FRAC_CONST(0.023723343717622358), + FRAC_CONST(0.025359096902135895), + FRAC_CONST(0.02699478219271537), + FRAC_CONST(0.028630395210139003), + FRAC_CONST(0.030265931575378519), + FRAC_CONST(0.031901386909610863), + FRAC_CONST(0.033536756834229922), + FRAC_CONST(0.035172036970858266), + FRAC_CONST(0.036807222941358832), + FRAC_CONST(0.038442310367846677), + FRAC_CONST(0.040077294872700696), + FRAC_CONST(0.041712172078575326), + FRAC_CONST(0.043346937608412288), + FRAC_CONST(0.044981587085452281), + FRAC_CONST(0.046616116133246711), + FRAC_CONST(0.048250520375669431), + FRAC_CONST(0.049884795436928406), + FRAC_CONST(0.051518936941577477), + FRAC_CONST(0.053152940514528055), + FRAC_CONST(0.05478680178106083), + FRAC_CONST(0.056420516366837495), + FRAC_CONST(0.05805407989791244), + FRAC_CONST(0.059687488000744485), + FRAC_CONST(0.061320736302208578), + FRAC_CONST(0.062953820429607482), + FRAC_CONST(0.064586736010683557), + FRAC_CONST(0.066219478673630344), + FRAC_CONST(0.06785204404710439), + FRAC_CONST(0.069484427760236861), + FRAC_CONST(0.071116625442645326), + FRAC_CONST(0.072748632724445372), + FRAC_CONST(0.07438044523626236), + FRAC_CONST(0.076012058609243122), + FRAC_CONST(0.077643468475067631), + FRAC_CONST(0.079274670465960706), + FRAC_CONST(0.080905660214703745), + FRAC_CONST(0.082536433354646319), + FRAC_CONST(0.084166985519717977), + FRAC_CONST(0.085797312344439894), + FRAC_CONST(0.08742740946393647), + FRAC_CONST(0.089057272513947183), + FRAC_CONST(0.090686897130838162), + FRAC_CONST(0.092316278951613845), + FRAC_CONST(0.093945413613928788), + FRAC_CONST(0.095574296756099186), + FRAC_CONST(0.097202924017114667), + FRAC_CONST(0.098831291036649963), + FRAC_CONST(0.10045939345507648), + FRAC_CONST(0.10208722691347409), + FRAC_CONST(0.10371478705364276), + FRAC_CONST(0.10534206951811415), + FRAC_CONST(0.10696906995016341), + FRAC_CONST(0.10859578399382072), + FRAC_CONST(0.11022220729388306), + FRAC_CONST(0.11184833549592579), + FRAC_CONST(0.11347416424631435), + FRAC_CONST(0.11509968919221586), + FRAC_CONST(0.11672490598161089), + FRAC_CONST(0.11834981026330495), + FRAC_CONST(0.11997439768694031), + FRAC_CONST(0.12159866390300751), + FRAC_CONST(0.12322260456285709), + FRAC_CONST(0.12484621531871121), + FRAC_CONST(0.12646949182367517), + FRAC_CONST(0.12809242973174936), + FRAC_CONST(0.12971502469784052), + FRAC_CONST(0.13133727237777362), + FRAC_CONST(0.13295916842830346), + FRAC_CONST(0.13458070850712617), + FRAC_CONST(0.13620188827289101), + FRAC_CONST(0.1378227033852118), + FRAC_CONST(0.13944314950467873), + FRAC_CONST(0.14106322229286994), + FRAC_CONST(0.14268291741236291), + FRAC_CONST(0.14430223052674654), + FRAC_CONST(0.1459211573006321), + FRAC_CONST(0.14753969339966552), + FRAC_CONST(0.14915783449053857), + FRAC_CONST(0.15077557624100058), + FRAC_CONST(0.15239291431987001), + FRAC_CONST(0.1540098443970461), + FRAC_CONST(0.15562636214352044), + FRAC_CONST(0.15724246323138855), + FRAC_CONST(0.15885814333386142), + FRAC_CONST(0.16047339812527725), + FRAC_CONST(0.16208822328111283), + FRAC_CONST(0.16370261447799525), + FRAC_CONST(0.16531656739371339), + FRAC_CONST(0.16693007770722967), + FRAC_CONST(0.16854314109869134), + FRAC_CONST(0.17015575324944232), + FRAC_CONST(0.17176790984203447), + FRAC_CONST(0.17337960656023954), + FRAC_CONST(0.1749908390890603), + FRAC_CONST(0.17660160311474243), + FRAC_CONST(0.17821189432478593), + FRAC_CONST(0.17982170840795647), + FRAC_CONST(0.18143104105429744), + FRAC_CONST(0.18303988795514095), + FRAC_CONST(0.1846482448031197), + FRAC_CONST(0.18625610729217834), + FRAC_CONST(0.1878634711175852), + FRAC_CONST(0.18947033197594348), + FRAC_CONST(0.19107668556520319), + FRAC_CONST(0.19268252758467228), + FRAC_CONST(0.19428785373502844), + FRAC_CONST(0.19589265971833042), + FRAC_CONST(0.19749694123802966), + FRAC_CONST(0.19910069399898173), + FRAC_CONST(0.20070391370745785), + FRAC_CONST(0.20230659607115639), + FRAC_CONST(0.20390873679921437), + FRAC_CONST(0.20551033160221882), + FRAC_CONST(0.20711137619221856), + FRAC_CONST(0.2087118662827353), + FRAC_CONST(0.21031179758877552), + FRAC_CONST(0.21191116582684155), + FRAC_CONST(0.21350996671494335), + FRAC_CONST(0.21510819597260972), + FRAC_CONST(0.21670584932089998), + FRAC_CONST(0.2183029224824154), + FRAC_CONST(0.21989941118131037), + FRAC_CONST(0.22149531114330431), + FRAC_CONST(0.22309061809569264), + FRAC_CONST(0.22468532776735861), + FRAC_CONST(0.22627943588878449), + FRAC_CONST(0.22787293819206314), + FRAC_CONST(0.22946583041090929), + FRAC_CONST(0.23105810828067114), + FRAC_CONST(0.23264976753834157), + FRAC_CONST(0.23424080392256985), + FRAC_CONST(0.2358312131736727), + FRAC_CONST(0.23742099103364595), + FRAC_CONST(0.23901013324617584), + FRAC_CONST(0.24059863555665045), + FRAC_CONST(0.24218649371217096), + FRAC_CONST(0.24377370346156332), + FRAC_CONST(0.24536026055538934), + FRAC_CONST(0.24694616074595824), + FRAC_CONST(0.24853139978733788), + FRAC_CONST(0.25011597343536629), + FRAC_CONST(0.25169987744766298), + FRAC_CONST(0.25328310758364025), + FRAC_CONST(0.25486565960451457), + FRAC_CONST(0.25644752927331788), + FRAC_CONST(0.25802871235490898), + FRAC_CONST(0.25960920461598508), + FRAC_CONST(0.26118900182509258), + FRAC_CONST(0.26276809975263904), + FRAC_CONST(0.264346494170904), + FRAC_CONST(0.26592418085405067), + FRAC_CONST(0.26750115557813692), + FRAC_CONST(0.2690774141211269), + FRAC_CONST(0.27065295226290209), + FRAC_CONST(0.2722277657852728), + FRAC_CONST(0.27380185047198918), + FRAC_CONST(0.27537520210875299), + FRAC_CONST(0.2769478164832283), + FRAC_CONST(0.27851968938505312), + FRAC_CONST(0.28009081660585067), + FRAC_CONST(0.28166119393924061), + FRAC_CONST(0.28323081718085019), + FRAC_CONST(0.28479968212832563), + FRAC_CONST(0.28636778458134327), + FRAC_CONST(0.28793512034162105), + FRAC_CONST(0.2895016852129294), + FRAC_CONST(0.29106747500110264), + FRAC_CONST(0.29263248551405047), + FRAC_CONST(0.2941967125617686), + FRAC_CONST(0.29576015195635058), + FRAC_CONST(0.29732279951199847), + FRAC_CONST(0.29888465104503475), + FRAC_CONST(0.30044570237391266), + FRAC_CONST(0.30200594931922808), + FRAC_CONST(0.30356538770373032), + FRAC_CONST(0.30512401335233358), + FRAC_CONST(0.30668182209212791), + FRAC_CONST(0.3082388097523906), + FRAC_CONST(0.30979497216459695), + FRAC_CONST(0.31135030516243201), + FRAC_CONST(0.3129048045818012), + FRAC_CONST(0.31445846626084178), + FRAC_CONST(0.31601128603993378), + FRAC_CONST(0.31756325976171151), + FRAC_CONST(0.31911438327107416), + FRAC_CONST(0.32066465241519732), + FRAC_CONST(0.32221406304354389), + FRAC_CONST(0.3237626110078754), + FRAC_CONST(0.32531029216226293), + FRAC_CONST(0.32685710236309828), + FRAC_CONST(0.32840303746910487), + FRAC_CONST(0.32994809334134939), + FRAC_CONST(0.3314922658432522), + FRAC_CONST(0.33303555084059877), + FRAC_CONST(0.33457794420155085), + FRAC_CONST(0.33611944179665709), + FRAC_CONST(0.33766003949886464), + FRAC_CONST(0.33919973318352969), + FRAC_CONST(0.34073851872842903), + FRAC_CONST(0.34227639201377064), + FRAC_CONST(0.34381334892220483), + FRAC_CONST(0.34534938533883547), + FRAC_CONST(0.34688449715123082), + FRAC_CONST(0.34841868024943456), + FRAC_CONST(0.34995193052597684), + FRAC_CONST(0.35148424387588523), + FRAC_CONST(0.3530156161966958), + FRAC_CONST(0.35454604338846402), + FRAC_CONST(0.35607552135377557), + FRAC_CONST(0.35760404599775775), + FRAC_CONST(0.35913161322809023), + FRAC_CONST(0.36065821895501554), + FRAC_CONST(0.36218385909135092), + FRAC_CONST(0.36370852955249849), + FRAC_CONST(0.36523222625645668), + FRAC_CONST(0.36675494512383078), + FRAC_CONST(0.36827668207784414), + FRAC_CONST(0.36979743304434909), + FRAC_CONST(0.37131719395183754), + FRAC_CONST(0.37283596073145214), + FRAC_CONST(0.37435372931699717), + FRAC_CONST(0.37587049564494951), + FRAC_CONST(0.37738625565446909), + FRAC_CONST(0.37890100528741022), + FRAC_CONST(0.38041474048833229), + FRAC_CONST(0.38192745720451066), + FRAC_CONST(0.38343915138594736), + FRAC_CONST(0.38494981898538222), + FRAC_CONST(0.38645945595830333), + FRAC_CONST(0.38796805826295838), + FRAC_CONST(0.38947562186036483), + FRAC_CONST(0.39098214271432141), + FRAC_CONST(0.39248761679141814), + FRAC_CONST(0.3939920400610481), + FRAC_CONST(0.39549540849541737), + FRAC_CONST(0.39699771806955625), + FRAC_CONST(0.39849896476132979), + FRAC_CONST(0.39999914455144892), + FRAC_CONST(0.40149825342348083), + FRAC_CONST(0.4029962873638599), + FRAC_CONST(0.40449324236189854), + FRAC_CONST(0.40598911440979762), + FRAC_CONST(0.40748389950265762), + FRAC_CONST(0.40897759363848879), + FRAC_CONST(0.41047019281822261), + FRAC_CONST(0.41196169304572178), + FRAC_CONST(0.4134520903277914), + FRAC_CONST(0.41494138067418929), + FRAC_CONST(0.41642956009763715), + FRAC_CONST(0.41791662461383078), + FRAC_CONST(0.41940257024145089), + FRAC_CONST(0.42088739300217382), + FRAC_CONST(0.42237108892068231), + FRAC_CONST(0.42385365402467584), + FRAC_CONST(0.42533508434488143), + FRAC_CONST(0.42681537591506419), + FRAC_CONST(0.42829452477203828), + FRAC_CONST(0.42977252695567697), + FRAC_CONST(0.43124937850892364), + FRAC_CONST(0.4327250754778022), + FRAC_CONST(0.43419961391142781), + FRAC_CONST(0.43567298986201736), + FRAC_CONST(0.43714519938489987), + FRAC_CONST(0.43861623853852766), + FRAC_CONST(0.44008610338448595), + FRAC_CONST(0.44155478998750436), + FRAC_CONST(0.44302229441546676), + FRAC_CONST(0.4444886127394222), + FRAC_CONST(0.44595374103359531), + FRAC_CONST(0.44741767537539667), + FRAC_CONST(0.44888041184543348), + FRAC_CONST(0.45034194652752002), + FRAC_CONST(0.45180227550868812), + FRAC_CONST(0.45326139487919759), + FRAC_CONST(0.45471930073254679), + FRAC_CONST(0.45617598916548296), + FRAC_CONST(0.45763145627801283), + FRAC_CONST(0.45908569817341294), + FRAC_CONST(0.46053871095824001), + FRAC_CONST(0.46199049074234161), + FRAC_CONST(0.46344103363886635), + FRAC_CONST(0.46489033576427435), + FRAC_CONST(0.46633839323834758), + FRAC_CONST(0.46778520218420055), + FRAC_CONST(0.46923075872829029), + FRAC_CONST(0.47067505900042683), + FRAC_CONST(0.47211809913378361), + FRAC_CONST(0.47355987526490806), + FRAC_CONST(0.47500038353373153), + FRAC_CONST(0.47643962008357982), + FRAC_CONST(0.47787758106118372), + FRAC_CONST(0.47931426261668875), + FRAC_CONST(0.48074966090366611), + FRAC_CONST(0.48218377207912272), + FRAC_CONST(0.48361659230351117), + FRAC_CONST(0.48504811774074069), + FRAC_CONST(0.48647834455818684), + FRAC_CONST(0.48790726892670194), + FRAC_CONST(0.48933488702062544), + FRAC_CONST(0.49076119501779414), + FRAC_CONST(0.49218618909955225), + FRAC_CONST(0.4936098654507618), + FRAC_CONST(0.49503222025981269), + FRAC_CONST(0.49645324971863303), + FRAC_CONST(0.49787295002269943), + FRAC_CONST(0.49929131737104687), + FRAC_CONST(0.50070834796627917), + FRAC_CONST(0.50212403801457872), + FRAC_CONST(0.50353838372571758), + FRAC_CONST(0.50495138131306638), + FRAC_CONST(0.50636302699360547), + FRAC_CONST(0.50777331698793449), + FRAC_CONST(0.50918224752028263), + FRAC_CONST(0.51058981481851906), + FRAC_CONST(0.51199601511416237), + FRAC_CONST(0.51340084464239111), + FRAC_CONST(0.51480429964205421), + FRAC_CONST(0.51620637635567967), + FRAC_CONST(0.51760707102948678), + FRAC_CONST(0.51900637991339404), + FRAC_CONST(0.5204042992610306), + FRAC_CONST(0.52180082532974559), + FRAC_CONST(0.5231959543806185), + FRAC_CONST(0.52458968267846895), + FRAC_CONST(0.52598200649186677), + FRAC_CONST(0.52737292209314235), + FRAC_CONST(0.52876242575839572), + FRAC_CONST(0.53015051376750777), + FRAC_CONST(0.53153718240414882), + FRAC_CONST(0.53292242795578992), + FRAC_CONST(0.53430624671371152), + FRAC_CONST(0.53568863497301467), + FRAC_CONST(0.5370695890326298), + FRAC_CONST(0.5384491051953274), + FRAC_CONST(0.53982717976772743), + FRAC_CONST(0.54120380906030963), + FRAC_CONST(0.54257898938742311), + FRAC_CONST(0.54395271706729609), + FRAC_CONST(0.54532498842204646), + FRAC_CONST(0.54669579977769045), + FRAC_CONST(0.54806514746415402), + FRAC_CONST(0.54943302781528081), + FRAC_CONST(0.55079943716884383), + FRAC_CONST(0.55216437186655387), + FRAC_CONST(0.55352782825406999), + FRAC_CONST(0.55488980268100907), + FRAC_CONST(0.55625029150095584), + FRAC_CONST(0.55760929107147217), + FRAC_CONST(0.55896679775410718), + FRAC_CONST(0.56032280791440714), + FRAC_CONST(0.56167731792192455), + FRAC_CONST(0.56303032415022869), + FRAC_CONST(0.56438182297691453), + FRAC_CONST(0.56573181078361312), + FRAC_CONST(0.56708028395600085), + FRAC_CONST(0.56842723888380908), + FRAC_CONST(0.56977267196083425), + FRAC_CONST(0.57111657958494688), + FRAC_CONST(0.5724589581581021), + FRAC_CONST(0.57379980408634845), + FRAC_CONST(0.57513911377983773), + FRAC_CONST(0.57647688365283478), + FRAC_CONST(0.57781311012372738), + FRAC_CONST(0.57914778961503466), + FRAC_CONST(0.58048091855341843), + FRAC_CONST(0.5818124933696911), + FRAC_CONST(0.58314251049882604), + FRAC_CONST(0.58447096637996743), + FRAC_CONST(0.58579785745643886), + FRAC_CONST(0.5871231801757536), + FRAC_CONST(0.58844693098962408), + FRAC_CONST(0.58976910635397084), + FRAC_CONST(0.59108970272893235), + FRAC_CONST(0.59240871657887517), + FRAC_CONST(0.59372614437240179), + FRAC_CONST(0.59504198258236196), + FRAC_CONST(0.5963562276858605), + FRAC_CONST(0.59766887616426767), + FRAC_CONST(0.5989799245032289), + FRAC_CONST(0.60028936919267273), + FRAC_CONST(0.60159720672682204), + FRAC_CONST(0.60290343360420195), + FRAC_CONST(0.60420804632765002), + FRAC_CONST(0.60551104140432543), + FRAC_CONST(0.60681241534571839), + FRAC_CONST(0.60811216466765883), + FRAC_CONST(0.60941028589032709), + FRAC_CONST(0.61070677553826169), + FRAC_CONST(0.61200163014036979), + FRAC_CONST(0.61329484622993602), + FRAC_CONST(0.6145864203446314), + FRAC_CONST(0.61587634902652377), + FRAC_CONST(0.61716462882208556), + FRAC_CONST(0.61845125628220421), + FRAC_CONST(0.61973622796219074), + FRAC_CONST(0.6210195404217892), + FRAC_CONST(0.62230119022518593), + FRAC_CONST(0.62358117394101897), + FRAC_CONST(0.62485948814238634), + FRAC_CONST(0.62613612940685637), + FRAC_CONST(0.62741109431647646), + FRAC_CONST(0.62868437945778133), + FRAC_CONST(0.62995598142180387), + FRAC_CONST(0.6312258968040827), + FRAC_CONST(0.63249412220467238), + FRAC_CONST(0.63376065422815175), + FRAC_CONST(0.63502548948363347), + FRAC_CONST(0.63628862458477287), + FRAC_CONST(0.63755005614977711), + FRAC_CONST(0.63880978080141437), + FRAC_CONST(0.6400677951670225), + FRAC_CONST(0.6413240958785188), + FRAC_CONST(0.64257867957240766), + FRAC_CONST(0.6438315428897915), + FRAC_CONST(0.64508268247637779), + FRAC_CONST(0.64633209498248945), + FRAC_CONST(0.64757977706307335), + FRAC_CONST(0.64882572537770888), + FRAC_CONST(0.65006993659061751), + FRAC_CONST(0.65131240737067142), + FRAC_CONST(0.65255313439140239), + FRAC_CONST(0.65379211433101081), + FRAC_CONST(0.65502934387237444), + FRAC_CONST(0.6562648197030575), + FRAC_CONST(0.65749853851531959), + FRAC_CONST(0.65873049700612374), + FRAC_CONST(0.65996069187714679), + FRAC_CONST(0.66118911983478657), + FRAC_CONST(0.66241577759017178), + FRAC_CONST(0.66364066185917048), + FRAC_CONST(0.66486376936239888), + FRAC_CONST(0.66608509682523009), + FRAC_CONST(0.66730464097780284), + FRAC_CONST(0.66852239855503071), + FRAC_CONST(0.66973836629660977), + FRAC_CONST(0.67095254094702894), + FRAC_CONST(0.67216491925557675), + FRAC_CONST(0.67337549797635199), + FRAC_CONST(0.67458427386827102), + FRAC_CONST(0.67579124369507693), + FRAC_CONST(0.67699640422534846), + FRAC_CONST(0.67819975223250772), + FRAC_CONST(0.6794012844948305), + FRAC_CONST(0.68060099779545302), + FRAC_CONST(0.68179888892238183), + FRAC_CONST(0.6829949546685018), + FRAC_CONST(0.68418919183158522), + FRAC_CONST(0.68538159721429948), + FRAC_CONST(0.6865721676242168), + FRAC_CONST(0.68776089987382172), + FRAC_CONST(0.68894779078052026), + FRAC_CONST(0.69013283716664853), + FRAC_CONST(0.69131603585948032), + FRAC_CONST(0.69249738369123692), + FRAC_CONST(0.69367687749909468), + FRAC_CONST(0.69485451412519361), + FRAC_CONST(0.69603029041664599), + FRAC_CONST(0.6972042032255451), + FRAC_CONST(0.6983762494089728), + FRAC_CONST(0.69954642582900894), + FRAC_CONST(0.70071472935273893), + FRAC_CONST(0.70188115685226271), + FRAC_CONST(0.703045705204703), + FRAC_CONST(0.70420837129221303), + FRAC_CONST(0.70536915200198613), + FRAC_CONST(0.70652804422626281), + FRAC_CONST(0.70768504486233985), + FRAC_CONST(0.70884015081257845), + FRAC_CONST(0.70999335898441229), + FRAC_CONST(0.711144666290356), + FRAC_CONST(0.71229406964801356), + FRAC_CONST(0.71344156598008623), + FRAC_CONST(0.71458715221438096), + FRAC_CONST(0.71573082528381871), + FRAC_CONST(0.71687258212644234), + FRAC_CONST(0.7180124196854254), + FRAC_CONST(0.71915033490907943), + FRAC_CONST(0.72028632475086318), + FRAC_CONST(0.72142038616938997), + FRAC_CONST(0.72255251612843596), + FRAC_CONST(0.72368271159694852), + FRAC_CONST(0.72481096954905444), + FRAC_CONST(0.72593728696406756), + FRAC_CONST(0.72706166082649704), + FRAC_CONST(0.72818408812605595), + FRAC_CONST(0.72930456585766834), + FRAC_CONST(0.73042309102147851), + FRAC_CONST(0.73153966062285747), + FRAC_CONST(0.73265427167241282), + FRAC_CONST(0.73376692118599507), + FRAC_CONST(0.73487760618470677), + FRAC_CONST(0.73598632369490979), + FRAC_CONST(0.73709307074823405), + FRAC_CONST(0.73819784438158409), + FRAC_CONST(0.73930064163714881), + FRAC_CONST(0.74040145956240788), + FRAC_CONST(0.74150029521014049), + FRAC_CONST(0.74259714563843304), + FRAC_CONST(0.74369200791068657), + FRAC_CONST(0.74478487909562552), + FRAC_CONST(0.74587575626730485), + FRAC_CONST(0.74696463650511791), + FRAC_CONST(0.74805151689380456), + FRAC_CONST(0.74913639452345926), + FRAC_CONST(0.75021926648953785), + FRAC_CONST(0.75130012989286621), + FRAC_CONST(0.7523789818396478), + FRAC_CONST(0.75345581944147111), + FRAC_CONST(0.75453063981531809), + FRAC_CONST(0.75560344008357094), + FRAC_CONST(0.75667421737402052), + FRAC_CONST(0.7577429688198738), + FRAC_CONST(0.75880969155976163), + FRAC_CONST(0.75987438273774599), + FRAC_CONST(0.76093703950332836), + FRAC_CONST(0.76199765901145666), + FRAC_CONST(0.76305623842253345), + FRAC_CONST(0.76411277490242291), + FRAC_CONST(0.76516726562245885), + FRAC_CONST(0.76621970775945258), + FRAC_CONST(0.76727009849569949), + FRAC_CONST(0.76831843501898767), + FRAC_CONST(0.76936471452260458), + FRAC_CONST(0.77040893420534517), + FRAC_CONST(0.77145109127151923), + FRAC_CONST(0.77249118293095853), + FRAC_CONST(0.77352920639902467), + FRAC_CONST(0.77456515889661659), + FRAC_CONST(0.77559903765017746), + FRAC_CONST(0.7766308398917029), + FRAC_CONST(0.77766056285874774), + FRAC_CONST(0.77868820379443371), + FRAC_CONST(0.77971375994745684), + FRAC_CONST(0.78073722857209438), + FRAC_CONST(0.7817586069282132), + FRAC_CONST(0.78277789228127592), + FRAC_CONST(0.78379508190234881), + FRAC_CONST(0.78481017306810918), + FRAC_CONST(0.78582316306085265), + FRAC_CONST(0.78683404916849986), + FRAC_CONST(0.78784282868460476), + FRAC_CONST(0.78884949890836087), + FRAC_CONST(0.78985405714460888), + FRAC_CONST(0.7908565007038445), + FRAC_CONST(0.79185682690222425), + FRAC_CONST(0.79285503306157412), + FRAC_CONST(0.79385111650939566), + FRAC_CONST(0.79484507457887377), + FRAC_CONST(0.79583690460888357), + FRAC_CONST(0.79682660394399751), + FRAC_CONST(0.79781416993449272), + FRAC_CONST(0.79879959993635785), + FRAC_CONST(0.7997828913113002), + FRAC_CONST(0.80076404142675273), + FRAC_CONST(0.80174304765588156), + FRAC_CONST(0.80271990737759213), + FRAC_CONST(0.80369461797653707), + FRAC_CONST(0.80466717684312306), + FRAC_CONST(0.80563758137351682), + FRAC_CONST(0.80660582896965372), + FRAC_CONST(0.80757191703924336), + FRAC_CONST(0.80853584299577752), + FRAC_CONST(0.80949760425853612), + FRAC_CONST(0.81045719825259477), + FRAC_CONST(0.81141462240883167), + FRAC_CONST(0.81236987416393436), + FRAC_CONST(0.81332295096040608), + FRAC_CONST(0.81427385024657373), + FRAC_CONST(0.81522256947659355), + FRAC_CONST(0.81616910611045879), + FRAC_CONST(0.817113457614006), + FRAC_CONST(0.81805562145892186), + FRAC_CONST(0.81899559512275044), + FRAC_CONST(0.81993337608889916), + FRAC_CONST(0.82086896184664637), + FRAC_CONST(0.8218023498911472), + FRAC_CONST(0.82273353772344116), + FRAC_CONST(0.82366252285045805), + FRAC_CONST(0.82458930278502529), + FRAC_CONST(0.82551387504587381), + FRAC_CONST(0.82643623715764558), + FRAC_CONST(0.82735638665089983), + FRAC_CONST(0.82827432106211907), + FRAC_CONST(0.82919003793371693), + FRAC_CONST(0.83010353481404364), + FRAC_CONST(0.83101480925739324), + FRAC_CONST(0.83192385882400965), + FRAC_CONST(0.83283068108009373), + FRAC_CONST(0.8337352735978093), + FRAC_CONST(0.83463763395529011), + FRAC_CONST(0.83553775973664579), + FRAC_CONST(0.83643564853196872), + FRAC_CONST(0.83733129793734051), + FRAC_CONST(0.83822470555483797), + FRAC_CONST(0.83911586899254031), + FRAC_CONST(0.84000478586453453), + FRAC_CONST(0.84089145379092289), + FRAC_CONST(0.84177587039782842), + FRAC_CONST(0.84265803331740163), + FRAC_CONST(0.84353794018782702), + FRAC_CONST(0.844415588653329), + FRAC_CONST(0.8452909763641786), + FRAC_CONST(0.84616410097669936), + FRAC_CONST(0.84703496015327406), + FRAC_CONST(0.84790355156235053), + FRAC_CONST(0.84876987287844818), + FRAC_CONST(0.8496339217821639), + FRAC_CONST(0.85049569596017938), + FRAC_CONST(0.85135519310526508), + FRAC_CONST(0.85221241091628896), + FRAC_CONST(0.85306734709822085), + FRAC_CONST(0.85391999936213903), + FRAC_CONST(0.85477036542523732), + FRAC_CONST(0.85561844301082923), + FRAC_CONST(0.85646422984835635), + FRAC_CONST(0.85730772367339259), + FRAC_CONST(0.85814892222765116), + FRAC_CONST(0.85898782325899026), + FRAC_CONST(0.85982442452141961), + FRAC_CONST(0.86065872377510555), + FRAC_CONST(0.86149071878637817), + FRAC_CONST(0.8623204073277364), + FRAC_CONST(0.86314778717785412), + FRAC_CONST(0.8639728561215867), + FRAC_CONST(0.86479561194997623), + FRAC_CONST(0.86561605246025763), + FRAC_CONST(0.86643417545586487), + FRAC_CONST(0.8672499787464365), + FRAC_CONST(0.86806346014782154), + FRAC_CONST(0.8688746174820855), + FRAC_CONST(0.86968344857751589), + FRAC_CONST(0.87048995126862883), + FRAC_CONST(0.87129412339617363), + FRAC_CONST(0.87209596280713941), + FRAC_CONST(0.8728954673547612), + FRAC_CONST(0.87369263489852422), + FRAC_CONST(0.87448746330417149), + FRAC_CONST(0.87527995044370765), + FRAC_CONST(0.8760700941954066), + FRAC_CONST(0.87685789244381551), + FRAC_CONST(0.87764334307976144), + FRAC_CONST(0.87842644400035663), + FRAC_CONST(0.8792071931090043), + FRAC_CONST(0.87998558831540408), + FRAC_CONST(0.88076162753555787), + FRAC_CONST(0.88153530869177488), + FRAC_CONST(0.88230662971267804), + FRAC_CONST(0.88307558853320878), + FRAC_CONST(0.88384218309463292), + FRAC_CONST(0.8846064113445461), + FRAC_CONST(0.88536827123687933), + FRAC_CONST(0.88612776073190425), + FRAC_CONST(0.88688487779623937), + FRAC_CONST(0.88763962040285393), + FRAC_CONST(0.8883919865310751), + FRAC_CONST(0.88914197416659235), + FRAC_CONST(0.88988958130146301), + FRAC_CONST(0.8906348059341177), + FRAC_CONST(0.89137764606936609), + FRAC_CONST(0.89211809971840139), + FRAC_CONST(0.89285616489880615), + FRAC_CONST(0.89359183963455813), + FRAC_CONST(0.89432512195603453), + FRAC_CONST(0.89505600990001799), + FRAC_CONST(0.89578450150970124), + FRAC_CONST(0.8965105948346932), + FRAC_CONST(0.89723428793102367), + FRAC_CONST(0.89795557886114807), + FRAC_CONST(0.89867446569395382), + FRAC_CONST(0.89939094650476448), + FRAC_CONST(0.90010501937534515), + FRAC_CONST(0.900816682393908), + FRAC_CONST(0.90152593365511691), + FRAC_CONST(0.90223277126009283), + FRAC_CONST(0.90293719331641886), + FRAC_CONST(0.90363919793814496), + FRAC_CONST(0.90433878324579353), + FRAC_CONST(0.90503594736636439), + FRAC_CONST(0.90573068843333915), + FRAC_CONST(0.90642300458668679), + FRAC_CONST(0.90711289397286898), + FRAC_CONST(0.90780035474484411), + FRAC_CONST(0.90848538506207266), + FRAC_CONST(0.90916798309052227), + FRAC_CONST(0.90984814700267291), + FRAC_CONST(0.9105258749775208), + FRAC_CONST(0.91120116520058425), + FRAC_CONST(0.91187401586390815), + FRAC_CONST(0.91254442516606893), + FRAC_CONST(0.9132123913121788), + FRAC_CONST(0.91387791251389161), + FRAC_CONST(0.91454098698940678), + FRAC_CONST(0.91520161296347435), + FRAC_CONST(0.91585978866739981), + FRAC_CONST(0.91651551233904871), + FRAC_CONST(0.91716878222285148), + FRAC_CONST(0.91781959656980805), + FRAC_CONST(0.91846795363749245), + FRAC_CONST(0.91911385169005766), + FRAC_CONST(0.9197572889982405), + FRAC_CONST(0.9203982638393654), + FRAC_CONST(0.92103677449734989), + FRAC_CONST(0.92167281926270861), + FRAC_CONST(0.92230639643255874), + FRAC_CONST(0.92293750431062316), + FRAC_CONST(0.92356614120723612), + FRAC_CONST(0.92419230543934783), + FRAC_CONST(0.92481599533052783), + FRAC_CONST(0.92543720921097061), + FRAC_CONST(0.92605594541749991), + FRAC_CONST(0.92667220229357261), + FRAC_CONST(0.92728597818928349), + FRAC_CONST(0.9278972714613698), + FRAC_CONST(0.92850608047321548), + FRAC_CONST(0.9291124035948557), + FRAC_CONST(0.92971623920298097), + FRAC_CONST(0.93031758568094147), + FRAC_CONST(0.93091644141875196), + FRAC_CONST(0.93151280481309506), + FRAC_CONST(0.93210667426732674), + FRAC_CONST(0.93269804819147983), + FRAC_CONST(0.93328692500226818), + FRAC_CONST(0.93387330312309147), + FRAC_CONST(0.93445718098403896), + FRAC_CONST(0.93503855702189376), + FRAC_CONST(0.9356174296801375), + FRAC_CONST(0.93619379740895381), + FRAC_CONST(0.93676765866523259), + FRAC_CONST(0.93733901191257496), + FRAC_CONST(0.93790785562129597), + FRAC_CONST(0.93847418826842988), + FRAC_CONST(0.93903800833773399), + FRAC_CONST(0.93959931431969212), + FRAC_CONST(0.94015810471151917), + FRAC_CONST(0.94071437801716529), + FRAC_CONST(0.94126813274731924), + FRAC_CONST(0.94181936741941319), + FRAC_CONST(0.94236808055762578), + FRAC_CONST(0.94291427069288691), + FRAC_CONST(0.94345793636288133), + FRAC_CONST(0.94399907611205225), + FRAC_CONST(0.9445376884916058), + FRAC_CONST(0.94507377205951448), + FRAC_CONST(0.94560732538052128), + FRAC_CONST(0.94613834702614352), + FRAC_CONST(0.94666683557467624), + FRAC_CONST(0.94719278961119657), + FRAC_CONST(0.94771620772756759), + FRAC_CONST(0.94823708852244104), + FRAC_CONST(0.94875543060126255), + FRAC_CONST(0.94927123257627433), + FRAC_CONST(0.94978449306651924), + FRAC_CONST(0.95029521069784428), + FRAC_CONST(0.9508033841029051), + FRAC_CONST(0.95130901192116835), + FRAC_CONST(0.9518120927989161), + FRAC_CONST(0.95231262538924943), + FRAC_CONST(0.95281060835209208), + FRAC_CONST(0.95330604035419386), + FRAC_CONST(0.95379892006913403), + FRAC_CONST(0.95428924617732525), + FRAC_CONST(0.95477701736601728), + FRAC_CONST(0.95526223232929941), + FRAC_CONST(0.95574488976810545), + FRAC_CONST(0.95622498839021619), + FRAC_CONST(0.95670252691026292), + FRAC_CONST(0.95717750404973156), + FRAC_CONST(0.95764991853696524), + FRAC_CONST(0.95811976910716812), + FRAC_CONST(0.95858705450240911), + FRAC_CONST(0.95905177347162429), + FRAC_CONST(0.95951392477062125), + FRAC_CONST(0.95997350716208196), + FRAC_CONST(0.96043051941556579), + FRAC_CONST(0.96088496030751369), + FRAC_CONST(0.96133682862125036), + FRAC_CONST(0.96178612314698864), + FRAC_CONST(0.96223284268183173), + FRAC_CONST(0.9626769860297768), + FRAC_CONST(0.96311855200171881), + FRAC_CONST(0.96355753941545252), + FRAC_CONST(0.96399394709567654), + FRAC_CONST(0.96442777387399625), + FRAC_CONST(0.96485901858892686), + FRAC_CONST(0.96528768008589627), + FRAC_CONST(0.96571375721724895), + FRAC_CONST(0.96613724884224783), + FRAC_CONST(0.96655815382707866), + FRAC_CONST(0.96697647104485207), + FRAC_CONST(0.96739219937560694), + FRAC_CONST(0.96780533770631338), + FRAC_CONST(0.96821588493087585), + FRAC_CONST(0.9686238399501359), + FRAC_CONST(0.96902920167187501), + FRAC_CONST(0.96943196901081796), + FRAC_CONST(0.96983214088863534), + FRAC_CONST(0.9702297162339466), + FRAC_CONST(0.97062469398232287), + FRAC_CONST(0.97101707307629004), + FRAC_CONST(0.97140685246533098), + FRAC_CONST(0.97179403110588902), + FRAC_CONST(0.97217860796137046), + FRAC_CONST(0.97256058200214734), + FRAC_CONST(0.97293995220556007), + FRAC_CONST(0.97331671755592064), + FRAC_CONST(0.97369087704451474), + FRAC_CONST(0.97406242966960455), + FRAC_CONST(0.97443137443643235), + FRAC_CONST(0.97479771035722163), + FRAC_CONST(0.97516143645118103), + FRAC_CONST(0.97552255174450631), + FRAC_CONST(0.97588105527038305), + FRAC_CONST(0.97623694606898959), + FRAC_CONST(0.97659022318749911), + FRAC_CONST(0.97694088568008242), + FRAC_CONST(0.97728893260791039), + FRAC_CONST(0.97763436303915685), + FRAC_CONST(0.97797717604900047), + FRAC_CONST(0.97831737071962765), + FRAC_CONST(0.97865494614023485), + FRAC_CONST(0.97898990140703124), + FRAC_CONST(0.97932223562324061), + FRAC_CONST(0.97965194789910426), + FRAC_CONST(0.9799790373518833), + FRAC_CONST(0.98030350310586067), + FRAC_CONST(0.98062534429234405), + FRAC_CONST(0.98094456004966768), + FRAC_CONST(0.98126114952319499), + FRAC_CONST(0.98157511186532054), + FRAC_CONST(0.98188644623547261), + FRAC_CONST(0.98219515180011563), + FRAC_CONST(0.98250122773275184), + FRAC_CONST(0.98280467321392362), + FRAC_CONST(0.98310548743121629), + FRAC_CONST(0.98340366957925973), + FRAC_CONST(0.98369921885973044), + FRAC_CONST(0.98399213448135414), + FRAC_CONST(0.98428241565990748), + FRAC_CONST(0.98457006161822058), + FRAC_CONST(0.98485507158617835), + FRAC_CONST(0.98513744480072363), + FRAC_CONST(0.98541718050585803), + FRAC_CONST(0.98569427795264519), + FRAC_CONST(0.98596873639921168), + FRAC_CONST(0.98624055511074971), + FRAC_CONST(0.98650973335951875), + FRAC_CONST(0.98677627042484772), + FRAC_CONST(0.98704016559313645), + FRAC_CONST(0.98730141815785832), + FRAC_CONST(0.98756002741956173), + FRAC_CONST(0.9878159926858715), + FRAC_CONST(0.98806931327149194), + FRAC_CONST(0.98831998849820735), + FRAC_CONST(0.98856801769488489), + FRAC_CONST(0.98881340019747566), + FRAC_CONST(0.98905613534901682), + FRAC_CONST(0.98929622249963345), + FRAC_CONST(0.98953366100653983), + FRAC_CONST(0.98976845023404181), + FRAC_CONST(0.99000058955353776), + FRAC_CONST(0.99023007834352106), + FRAC_CONST(0.99045691598958097), + FRAC_CONST(0.99068110188440506), + FRAC_CONST(0.99090263542778001), + FRAC_CONST(0.99112151602659404), + FRAC_CONST(0.99133774309483769), + FRAC_CONST(0.99155131605360625), + FRAC_CONST(0.99176223433110056), + FRAC_CONST(0.99197049736262888), + FRAC_CONST(0.99217610459060845), + FRAC_CONST(0.99237905546456673), + FRAC_CONST(0.99257934944114334), + FRAC_CONST(0.99277698598409092), + FRAC_CONST(0.99297196456427694), + FRAC_CONST(0.99316428465968509), + FRAC_CONST(0.99335394575541669), + FRAC_CONST(0.99354094734369169), + FRAC_CONST(0.99372528892385081), + FRAC_CONST(0.99390697000235606), + FRAC_CONST(0.99408599009279242), + FRAC_CONST(0.99426234871586938), + FRAC_CONST(0.99443604539942176), + FRAC_CONST(0.99460707967841133), + FRAC_CONST(0.99477545109492771), + FRAC_CONST(0.99494115919819004), + FRAC_CONST(0.99510420354454787), + FRAC_CONST(0.99526458369748239), + FRAC_CONST(0.99542229922760772), + FRAC_CONST(0.99557734971267187), + FRAC_CONST(0.9957297347375581), + FRAC_CONST(0.99587945389428578), + FRAC_CONST(0.99602650678201154), + FRAC_CONST(0.99617089300703077), + FRAC_CONST(0.996312612182778), + FRAC_CONST(0.99645166392982831), + FRAC_CONST(0.99658804787589839), + FRAC_CONST(0.99672176365584741), + FRAC_CONST(0.99685281091167788), + FRAC_CONST(0.99698118929253687), + FRAC_CONST(0.99710689845471678), + FRAC_CONST(0.99722993806165661), + FRAC_CONST(0.99735030778394196), + FRAC_CONST(0.99746800729930707), + FRAC_CONST(0.99758303629263489), + FRAC_CONST(0.99769539445595812), + FRAC_CONST(0.99780508148846014), + FRAC_CONST(0.99791209709647588), + FRAC_CONST(0.99801644099349218), + FRAC_CONST(0.99811811290014918), + FRAC_CONST(0.9982171125442405), + FRAC_CONST(0.9983134396607144), + FRAC_CONST(0.99840709399167404), + FRAC_CONST(0.99849807528637868), + FRAC_CONST(0.99858638330124405), + FRAC_CONST(0.99867201779984294), + FRAC_CONST(0.99875497855290607), + FRAC_CONST(0.99883526533832245), + FRAC_CONST(0.99891287794114036), + FRAC_CONST(0.99898781615356746), + FRAC_CONST(0.99906007977497147), + FRAC_CONST(0.99912966861188113), + FRAC_CONST(0.99919658247798593), + FRAC_CONST(0.99926082119413751), + FRAC_CONST(0.99932238458834954), + FRAC_CONST(0.999381272495798), + FRAC_CONST(0.99943748475882255), + FRAC_CONST(0.9994910212269259), + FRAC_CONST(0.99954188175677483), + FRAC_CONST(0.99959006621220048), + FRAC_CONST(0.99963557446419837), + FRAC_CONST(0.99967840639092931), + FRAC_CONST(0.99971856187771946), + FRAC_CONST(0.99975604081706027), + FRAC_CONST(0.99979084310860955), + FRAC_CONST(0.99982296865919107), + FRAC_CONST(0.99985241738279484), + FRAC_CONST(0.99987918920057806), + FRAC_CONST(0.99990328404086426), + FRAC_CONST(0.9999247018391445), + FRAC_CONST(0.99994344253807688), + FRAC_CONST(0.99995950608748674), + FRAC_CONST(0.99997289244436727), + FRAC_CONST(0.99998360157287902), + FRAC_CONST(0.9999916334443506), + FRAC_CONST(0.99999698803727821), + FRAC_CONST(0.99999966533732598) +}; +#endif + +ALIGN static const real_t sine_short_128[] = +{ + FRAC_CONST(0.0061358846491544753), + FRAC_CONST(0.01840672990580482), + FRAC_CONST(0.030674803176636626), + FRAC_CONST(0.04293825693494082), + FRAC_CONST(0.055195244349689934), + FRAC_CONST(0.067443919563664051), + FRAC_CONST(0.079682437971430126), + FRAC_CONST(0.091908956497132724), + FRAC_CONST(0.10412163387205459), + FRAC_CONST(0.11631863091190475), + FRAC_CONST(0.12849811079379317), + FRAC_CONST(0.14065823933284921), + FRAC_CONST(0.15279718525844344), + FRAC_CONST(0.16491312048996989), + FRAC_CONST(0.17700422041214875), + FRAC_CONST(0.18906866414980619), + FRAC_CONST(0.2011046348420919), + FRAC_CONST(0.21311031991609136), + FRAC_CONST(0.22508391135979283), + FRAC_CONST(0.2370236059943672), + FRAC_CONST(0.24892760574572015), + FRAC_CONST(0.26079411791527551), + FRAC_CONST(0.27262135544994898), + FRAC_CONST(0.28440753721127188), + FRAC_CONST(0.29615088824362379), + FRAC_CONST(0.30784964004153487), + FRAC_CONST(0.31950203081601569), + FRAC_CONST(0.33110630575987643), + FRAC_CONST(0.34266071731199438), + FRAC_CONST(0.35416352542049034), + FRAC_CONST(0.36561299780477385), + FRAC_CONST(0.37700741021641826), + FRAC_CONST(0.38834504669882625), + FRAC_CONST(0.39962419984564679), + FRAC_CONST(0.41084317105790391), + FRAC_CONST(0.42200027079979968), + FRAC_CONST(0.43309381885315196), + FRAC_CONST(0.4441221445704292), + FRAC_CONST(0.45508358712634384), + FRAC_CONST(0.46597649576796618), + FRAC_CONST(0.47679923006332209), + FRAC_CONST(0.487550160148436), + FRAC_CONST(0.49822766697278187), + FRAC_CONST(0.50883014254310699), + FRAC_CONST(0.51935599016558964), + FRAC_CONST(0.52980362468629461), + FRAC_CONST(0.54017147272989285), + FRAC_CONST(0.55045797293660481), + FRAC_CONST(0.56066157619733603), + FRAC_CONST(0.57078074588696726), + FRAC_CONST(0.58081395809576453), + FRAC_CONST(0.59075970185887416), + FRAC_CONST(0.60061647938386897), + FRAC_CONST(0.61038280627630948), + FRAC_CONST(0.6200572117632891), + FRAC_CONST(0.62963823891492698), + FRAC_CONST(0.63912444486377573), + FRAC_CONST(0.64851440102211244), + FRAC_CONST(0.65780669329707864), + FRAC_CONST(0.66699992230363747), + FRAC_CONST(0.67609270357531592), + FRAC_CONST(0.68508366777270036), + FRAC_CONST(0.693971460889654), + FRAC_CONST(0.7027547444572253), + FRAC_CONST(0.71143219574521643), + FRAC_CONST(0.72000250796138165), + FRAC_CONST(0.7284643904482252), + FRAC_CONST(0.73681656887736979), + FRAC_CONST(0.74505778544146595), + FRAC_CONST(0.75318679904361241), + FRAC_CONST(0.76120238548426178), + FRAC_CONST(0.76910333764557959), + FRAC_CONST(0.77688846567323244), + FRAC_CONST(0.78455659715557524), + FRAC_CONST(0.79210657730021239), + FRAC_CONST(0.79953726910790501), + FRAC_CONST(0.80684755354379922), + FRAC_CONST(0.8140363297059483), + FRAC_CONST(0.82110251499110465), + FRAC_CONST(0.8280450452577558), + FRAC_CONST(0.83486287498638001), + FRAC_CONST(0.84155497743689833), + FRAC_CONST(0.84812034480329712), + FRAC_CONST(0.85455798836540053), + FRAC_CONST(0.86086693863776731), + FRAC_CONST(0.86704624551569265), + FRAC_CONST(0.87309497841829009), + FRAC_CONST(0.87901222642863341), + FRAC_CONST(0.88479709843093779), + FRAC_CONST(0.89044872324475788), + FRAC_CONST(0.89596624975618511), + FRAC_CONST(0.90134884704602203), + FRAC_CONST(0.90659570451491533), + FRAC_CONST(0.91170603200542988), + FRAC_CONST(0.9166790599210427), + FRAC_CONST(0.9215140393420419), + FRAC_CONST(0.92621024213831127), + FRAC_CONST(0.93076696107898371), + FRAC_CONST(0.9351835099389475), + FRAC_CONST(0.93945922360218992), + FRAC_CONST(0.94359345816196039), + FRAC_CONST(0.94758559101774109), + FRAC_CONST(0.95143502096900834), + FRAC_CONST(0.95514116830577067), + FRAC_CONST(0.9587034748958716), + FRAC_CONST(0.96212140426904158), + FRAC_CONST(0.9653944416976894), + FRAC_CONST(0.96852209427441727), + FRAC_CONST(0.97150389098625178), + FRAC_CONST(0.97433938278557586), + FRAC_CONST(0.97702814265775439), + FRAC_CONST(0.97956976568544052), + FRAC_CONST(0.98196386910955524), + FRAC_CONST(0.98421009238692903), + FRAC_CONST(0.98630809724459867), + FRAC_CONST(0.98825756773074946), + FRAC_CONST(0.99005821026229712), + FRAC_CONST(0.99170975366909953), + FRAC_CONST(0.9932119492347945), + FRAC_CONST(0.99456457073425542), + FRAC_CONST(0.99576741446765982), + FRAC_CONST(0.99682029929116567), + FRAC_CONST(0.99772306664419164), + FRAC_CONST(0.99847558057329477), + FRAC_CONST(0.99907772775264536), + FRAC_CONST(0.99952941750109314), + FRAC_CONST(0.9998305817958234), + FRAC_CONST(0.99998117528260111) +}; + +#ifdef ALLOW_SMALL_FRAMELENGTH +ALIGN static const real_t sine_short_120[] = +{ + FRAC_CONST(0.0065449379673518581), + FRAC_CONST(0.019633692460628301), + FRAC_CONST(0.032719082821776137), + FRAC_CONST(0.045798866936520771), + FRAC_CONST(0.058870803651189033), + FRAC_CONST(0.071932653156719387), + FRAC_CONST(0.084982177372441667), + FRAC_CONST(0.09801714032956059), + FRAC_CONST(0.11103530855427769), + FRAC_CONST(0.12403445145048532), + FRAC_CONST(0.13701234168196802), + FRAC_CONST(0.14996675555404498), + FRAC_CONST(0.16289547339458874), + FRAC_CONST(0.17579627993435451), + FRAC_CONST(0.18866696468655525), + FRAC_CONST(0.2015053223256171), + FRAC_CONST(0.21430915306505074), + FRAC_CONST(0.2270762630343732), + FRAC_CONST(0.23980446465501654), + FRAC_CONST(0.25249157701515795), + FRAC_CONST(0.26513542624340797), + FRAC_CONST(0.27773384588129219), + FRAC_CONST(0.29028467725446233), + FRAC_CONST(0.3027857698425746), + FRAC_CONST(0.31523498164776964), + FRAC_CONST(0.32763017956169349), + FRAC_CONST(0.33996923973099424), + FRAC_CONST(0.35225004792123354), + FRAC_CONST(0.36447049987914965), + FRAC_CONST(0.37662850169321077), + FRAC_CONST(0.38872197015239557), + FRAC_CONST(0.40074883310314097), + FRAC_CONST(0.41270702980439467), + FRAC_CONST(0.42459451128071307), + FRAC_CONST(0.43640924067334208), + FRAC_CONST(0.44814919358922256), + FRAC_CONST(0.45981235844785984), + FRAC_CONST(0.47139673682599764), + FRAC_CONST(0.48290034380003727), + FRAC_CONST(0.49432120828614462), + FRAC_CONST(0.50565737337798455), + FRAC_CONST(0.51690689668202761), + FRAC_CONST(0.52806785065036799), + FRAC_CONST(0.53913832291100017), + FRAC_CONST(0.55011641659549337), + FRAC_CONST(0.56100025066400983), + FRAC_CONST(0.57178796022761225), + FRAC_CONST(0.58247769686780215), + FRAC_CONST(0.59306762895323706), + FRAC_CONST(0.60355594195357143), + FRAC_CONST(0.61394083875036642), + FRAC_CONST(0.62422053994501758), + FRAC_CONST(0.63439328416364549), + FRAC_CONST(0.64445732835889735), + FRAC_CONST(0.65441094810861034), + FRAC_CONST(0.66425243791128175), + FRAC_CONST(0.67398011147829784), + FRAC_CONST(0.68359230202287125), + FRAC_CONST(0.69308736254563585), + FRAC_CONST(0.70246366611685174), + FRAC_CONST(0.71171960615517138), + FRAC_CONST(0.72085359670291882), + FRAC_CONST(0.7298640726978356), + FRAC_CONST(0.73874949024124625), + FRAC_CONST(0.74750832686259672), + FRAC_CONST(0.75613908178032285), + FRAC_CONST(0.76464027615900032), + FRAC_CONST(0.77301045336273699), + FRAC_CONST(0.78124817920475853), + FRAC_CONST(0.78935204219315003), + FRAC_CONST(0.79732065377270711), + FRAC_CONST(0.80515264856285829), + FRAC_CONST(0.81284668459161513), + FRAC_CONST(0.82040144352551359), + FRAC_CONST(0.82781563089550203), + FRAC_CONST(0.83508797631874299), + FRAC_CONST(0.84221723371628654), + FRAC_CONST(0.84920218152657889), + FRAC_CONST(0.85604162291477137), + FRAC_CONST(0.86273438597779184), + FRAC_CONST(0.86927932394514362), + FRAC_CONST(0.87567531537539967), + FRAC_CONST(0.88192126434835494), + FRAC_CONST(0.88801610065280734), + FRAC_CONST(0.89395877996993212), + FRAC_CONST(0.8997482840522214), + FRAC_CONST(0.90538362089795521), + FRAC_CONST(0.91086382492117568), + FRAC_CONST(0.91618795711713596), + FRAC_CONST(0.92135510522319242), + FRAC_CONST(0.9263643838751181), + FRAC_CONST(0.93121493475880346), + FRAC_CONST(0.93590592675732565), + FRAC_CONST(0.94043655609335486), + FRAC_CONST(0.94480604646687805), + FRAC_CONST(0.94901364918821385), + FRAC_CONST(0.95305864330629697), + FRAC_CONST(0.95694033573220882), + FRAC_CONST(0.9606580613579353), + FRAC_CONST(0.96421118317032928), + FRAC_CONST(0.96759909236025976), + FRAC_CONST(0.9708212084269281), + FRAC_CONST(0.97387697927733363), + FRAC_CONST(0.97676588132087239), + FRAC_CONST(0.97948741955905139), + FRAC_CONST(0.98204112767030394), + FRAC_CONST(0.98442656808989171), + FRAC_CONST(0.98664333208487898), + FRAC_CONST(0.98869103982416728), + FRAC_CONST(0.99056934044357725), + FRAC_CONST(0.99227791210596705), + FRAC_CONST(0.99381646205637808), + FRAC_CONST(0.99518472667219682), + FRAC_CONST(0.99638247150832537), + FRAC_CONST(0.99740949133735191), + FRAC_CONST(0.99826561018471593), + FRAC_CONST(0.99895068135886012), + FRAC_CONST(0.99946458747636568), + FRAC_CONST(0.99980724048206482), + FRAC_CONST(0.99997858166412923) +}; +#endif + +#ifdef LD_DEC +ALIGN static const real_t sine_mid_512[] = +{ + FRAC_CONST(0.0015339801862847655), + FRAC_CONST(0.0046019261204485705), + FRAC_CONST(0.007669828739531097), + FRAC_CONST(0.010737659167264491), + FRAC_CONST(0.013805388528060391), + FRAC_CONST(0.01687298794728171), + FRAC_CONST(0.019940428551514441), + FRAC_CONST(0.023007681468839369), + FRAC_CONST(0.026074717829103901), + FRAC_CONST(0.029141508764193722), + FRAC_CONST(0.032208025408304586), + FRAC_CONST(0.035274238898213947), + FRAC_CONST(0.038340120373552694), + FRAC_CONST(0.041405640977076739), + FRAC_CONST(0.044470771854938668), + FRAC_CONST(0.047535484156959303), + FRAC_CONST(0.050599749036899282), + FRAC_CONST(0.05366353765273052), + FRAC_CONST(0.056726821166907748), + FRAC_CONST(0.059789570746639868), + FRAC_CONST(0.062851757564161406), + FRAC_CONST(0.065913352797003805), + FRAC_CONST(0.068974327628266746), + FRAC_CONST(0.072034653246889332), + FRAC_CONST(0.075094300847921305), + FRAC_CONST(0.078153241632794232), + FRAC_CONST(0.081211446809592441), + FRAC_CONST(0.084268887593324071), + FRAC_CONST(0.087325535206192059), + FRAC_CONST(0.090381360877864983), + FRAC_CONST(0.093436335845747787), + FRAC_CONST(0.096490431355252593), + FRAC_CONST(0.099543618660069319), + FRAC_CONST(0.10259586902243628), + FRAC_CONST(0.10564715371341062), + FRAC_CONST(0.10869744401313872), + FRAC_CONST(0.11174671121112659), + FRAC_CONST(0.11479492660651008), + FRAC_CONST(0.11784206150832498), + FRAC_CONST(0.12088808723577708), + FRAC_CONST(0.12393297511851216), + FRAC_CONST(0.12697669649688587), + FRAC_CONST(0.13001922272223335), + FRAC_CONST(0.13306052515713906), + FRAC_CONST(0.1361005751757062), + FRAC_CONST(0.1391393441638262), + FRAC_CONST(0.14217680351944803), + FRAC_CONST(0.14521292465284746), + FRAC_CONST(0.14824767898689603), + FRAC_CONST(0.15128103795733022), + FRAC_CONST(0.1543129730130201), + FRAC_CONST(0.15734345561623825), + FRAC_CONST(0.16037245724292828), + FRAC_CONST(0.16339994938297323), + FRAC_CONST(0.1664259035404641), + FRAC_CONST(0.16945029123396796), + FRAC_CONST(0.17247308399679595), + FRAC_CONST(0.17549425337727143), + FRAC_CONST(0.17851377093899751), + FRAC_CONST(0.18153160826112497), + FRAC_CONST(0.18454773693861962), + FRAC_CONST(0.1875621285825296), + FRAC_CONST(0.19057475482025274), + FRAC_CONST(0.19358558729580361), + FRAC_CONST(0.19659459767008022), + FRAC_CONST(0.19960175762113097), + FRAC_CONST(0.20260703884442113), + FRAC_CONST(0.20561041305309924), + FRAC_CONST(0.20861185197826349), + FRAC_CONST(0.21161132736922755), + FRAC_CONST(0.21460881099378676), + FRAC_CONST(0.21760427463848364), + FRAC_CONST(0.22059769010887351), + FRAC_CONST(0.22358902922978999), + FRAC_CONST(0.22657826384561), + FRAC_CONST(0.22956536582051887), + FRAC_CONST(0.23255030703877524), + FRAC_CONST(0.23553305940497549), + FRAC_CONST(0.23851359484431842), + FRAC_CONST(0.24149188530286933), + FRAC_CONST(0.24446790274782415), + FRAC_CONST(0.24744161916777327), + FRAC_CONST(0.25041300657296522), + FRAC_CONST(0.25338203699557016), + FRAC_CONST(0.25634868248994291), + FRAC_CONST(0.25931291513288623), + FRAC_CONST(0.26227470702391359), + FRAC_CONST(0.26523403028551179), + FRAC_CONST(0.26819085706340318), + FRAC_CONST(0.27114515952680801), + FRAC_CONST(0.27409690986870638), + FRAC_CONST(0.2770460803060999), + FRAC_CONST(0.27999264308027322), + FRAC_CONST(0.28293657045705539), + FRAC_CONST(0.28587783472708062), + FRAC_CONST(0.28881640820604948), + FRAC_CONST(0.29175226323498926), + FRAC_CONST(0.29468537218051433), + FRAC_CONST(0.2976157074350862), + FRAC_CONST(0.30054324141727345), + FRAC_CONST(0.30346794657201132), + FRAC_CONST(0.30638979537086092), + FRAC_CONST(0.30930876031226873), + FRAC_CONST(0.31222481392182488), + FRAC_CONST(0.31513792875252244), + FRAC_CONST(0.31804807738501495), + FRAC_CONST(0.32095523242787521), + FRAC_CONST(0.32385936651785285), + FRAC_CONST(0.32676045232013173), + FRAC_CONST(0.32965846252858749), + FRAC_CONST(0.33255336986604422), + FRAC_CONST(0.3354451470845316), + FRAC_CONST(0.33833376696554113), + FRAC_CONST(0.34121920232028236), + FRAC_CONST(0.34410142598993881), + FRAC_CONST(0.34698041084592368), + FRAC_CONST(0.34985612979013492), + FRAC_CONST(0.35272855575521073), + FRAC_CONST(0.35559766170478385), + FRAC_CONST(0.35846342063373654), + FRAC_CONST(0.36132580556845428), + FRAC_CONST(0.36418478956707989), + FRAC_CONST(0.36704034571976718), + FRAC_CONST(0.3698924471489341), + FRAC_CONST(0.37274106700951576), + FRAC_CONST(0.37558617848921722), + FRAC_CONST(0.37842775480876556), + FRAC_CONST(0.38126576922216238), + FRAC_CONST(0.38410019501693504), + FRAC_CONST(0.38693100551438858), + FRAC_CONST(0.38975817406985641), + FRAC_CONST(0.39258167407295147), + FRAC_CONST(0.39540147894781635), + FRAC_CONST(0.39821756215337356), + FRAC_CONST(0.40102989718357562), + FRAC_CONST(0.40383845756765407), + FRAC_CONST(0.40664321687036903), + FRAC_CONST(0.40944414869225759), + FRAC_CONST(0.41224122666988289), + FRAC_CONST(0.41503442447608163), + FRAC_CONST(0.41782371582021227), + FRAC_CONST(0.42060907444840251), + FRAC_CONST(0.42339047414379605), + FRAC_CONST(0.42616788872679962), + FRAC_CONST(0.42894129205532949), + FRAC_CONST(0.43171065802505726), + FRAC_CONST(0.43447596056965565), + FRAC_CONST(0.43723717366104409), + FRAC_CONST(0.43999427130963326), + FRAC_CONST(0.44274722756457002), + FRAC_CONST(0.44549601651398174), + FRAC_CONST(0.44824061228521989), + FRAC_CONST(0.45098098904510386), + FRAC_CONST(0.45371712100016387), + FRAC_CONST(0.45644898239688392), + FRAC_CONST(0.45917654752194409), + FRAC_CONST(0.46189979070246273), + FRAC_CONST(0.46461868630623782), + FRAC_CONST(0.46733320874198842), + FRAC_CONST(0.47004333245959562), + FRAC_CONST(0.47274903195034279), + FRAC_CONST(0.47545028174715587), + FRAC_CONST(0.47814705642484301), + FRAC_CONST(0.48083933060033396), + FRAC_CONST(0.48352707893291874), + FRAC_CONST(0.48621027612448642), + FRAC_CONST(0.48888889691976317), + FRAC_CONST(0.4915629161065499), + FRAC_CONST(0.49423230851595967), + FRAC_CONST(0.49689704902265447), + FRAC_CONST(0.49955711254508184), + FRAC_CONST(0.50221247404571079), + FRAC_CONST(0.50486310853126759), + FRAC_CONST(0.50750899105297087), + FRAC_CONST(0.51015009670676681), + FRAC_CONST(0.51278640063356296), + FRAC_CONST(0.51541787801946293), + FRAC_CONST(0.51804450409599934), + FRAC_CONST(0.52066625414036716), + FRAC_CONST(0.52328310347565643), + FRAC_CONST(0.52589502747108463), + FRAC_CONST(0.52850200154222848), + FRAC_CONST(0.531104001151255), + FRAC_CONST(0.53370100180715296), + FRAC_CONST(0.53629297906596318), + FRAC_CONST(0.53887990853100842), + FRAC_CONST(0.54146176585312344), + FRAC_CONST(0.54403852673088382), + FRAC_CONST(0.54661016691083486), + FRAC_CONST(0.54917666218771966), + FRAC_CONST(0.55173798840470734), + FRAC_CONST(0.55429412145362), + FRAC_CONST(0.5568450372751601), + FRAC_CONST(0.55939071185913614), + FRAC_CONST(0.56193112124468947), + FRAC_CONST(0.5644662415205195), + FRAC_CONST(0.56699604882510868), + FRAC_CONST(0.56952051934694714), + FRAC_CONST(0.57203962932475705), + FRAC_CONST(0.57455335504771576), + FRAC_CONST(0.57706167285567944), + FRAC_CONST(0.57956455913940563), + FRAC_CONST(0.58206199034077544), + FRAC_CONST(0.58455394295301533), + FRAC_CONST(0.58704039352091797), + FRAC_CONST(0.58952131864106394), + FRAC_CONST(0.59199669496204099), + FRAC_CONST(0.59446649918466443), + FRAC_CONST(0.5969307080621965), + FRAC_CONST(0.59938929840056454), + FRAC_CONST(0.60184224705858003), + FRAC_CONST(0.60428953094815596), + FRAC_CONST(0.60673112703452448), + FRAC_CONST(0.60916701233645321), + FRAC_CONST(0.61159716392646191), + FRAC_CONST(0.61402155893103838), + FRAC_CONST(0.61644017453085365), + FRAC_CONST(0.61885298796097632), + FRAC_CONST(0.62125997651108755), + FRAC_CONST(0.62366111752569453), + FRAC_CONST(0.62605638840434352), + FRAC_CONST(0.62844576660183271), + FRAC_CONST(0.63082922962842447), + FRAC_CONST(0.63320675505005719), + FRAC_CONST(0.63557832048855611), + FRAC_CONST(0.63794390362184406), + FRAC_CONST(0.64030348218415167), + FRAC_CONST(0.64265703396622686), + FRAC_CONST(0.64500453681554393), + FRAC_CONST(0.64734596863651206), + FRAC_CONST(0.64968130739068319), + FRAC_CONST(0.6520105310969595), + FRAC_CONST(0.65433361783180044), + FRAC_CONST(0.65665054572942894), + FRAC_CONST(0.65896129298203732), + FRAC_CONST(0.66126583783999227), + FRAC_CONST(0.66356415861203977), + FRAC_CONST(0.66585623366550972), + FRAC_CONST(0.66814204142651845), + FRAC_CONST(0.67042156038017309), + FRAC_CONST(0.67269476907077286), + FRAC_CONST(0.67496164610201193), + FRAC_CONST(0.67722217013718033), + FRAC_CONST(0.67947631989936497), + FRAC_CONST(0.68172407417164971), + FRAC_CONST(0.6839654117973154), + FRAC_CONST(0.68620031168003859), + FRAC_CONST(0.68842875278409044), + FRAC_CONST(0.6906507141345346), + FRAC_CONST(0.69286617481742463), + FRAC_CONST(0.69507511398000088), + FRAC_CONST(0.69727751083088652), + FRAC_CONST(0.69947334464028377), + FRAC_CONST(0.70166259474016845), + FRAC_CONST(0.70384524052448494), + FRAC_CONST(0.70602126144933974), + FRAC_CONST(0.70819063703319529), + FRAC_CONST(0.71035334685706231), + FRAC_CONST(0.71250937056469232), + FRAC_CONST(0.71465868786276898), + FRAC_CONST(0.71680127852109954), + FRAC_CONST(0.71893712237280438), + FRAC_CONST(0.72106619931450811), + FRAC_CONST(0.72318848930652735), + FRAC_CONST(0.72530397237306066), + FRAC_CONST(0.72741262860237577), + FRAC_CONST(0.7295144381469969), + FRAC_CONST(0.73160938122389252), + FRAC_CONST(0.73369743811466026), + FRAC_CONST(0.73577858916571348), + FRAC_CONST(0.73785281478846598), + FRAC_CONST(0.73992009545951609), + FRAC_CONST(0.74198041172083096), + FRAC_CONST(0.74403374417992918), + FRAC_CONST(0.74608007351006378), + FRAC_CONST(0.74811938045040349), + FRAC_CONST(0.75015164580621496), + FRAC_CONST(0.7521768504490427), + FRAC_CONST(0.75419497531688917), + FRAC_CONST(0.75620600141439454), + FRAC_CONST(0.75820990981301528), + FRAC_CONST(0.76020668165120242), + FRAC_CONST(0.7621962981345789), + FRAC_CONST(0.76417874053611667), + FRAC_CONST(0.76615399019631281), + FRAC_CONST(0.76812202852336531), + FRAC_CONST(0.7700828369933479), + FRAC_CONST(0.77203639715038441), + FRAC_CONST(0.77398269060682279), + FRAC_CONST(0.77592169904340758), + FRAC_CONST(0.77785340420945304), + FRAC_CONST(0.77977778792301444), + FRAC_CONST(0.78169483207105939), + FRAC_CONST(0.7836045186096382), + FRAC_CONST(0.78550682956405393), + FRAC_CONST(0.78740174702903132), + FRAC_CONST(0.78928925316888565), + FRAC_CONST(0.79116933021769009), + FRAC_CONST(0.79304196047944364), + FRAC_CONST(0.79490712632823701), + FRAC_CONST(0.79676481020841872), + FRAC_CONST(0.79861499463476082), + FRAC_CONST(0.80045766219262271), + FRAC_CONST(0.80229279553811572), + FRAC_CONST(0.8041203773982657), + FRAC_CONST(0.80594039057117628), + FRAC_CONST(0.80775281792619036), + FRAC_CONST(0.80955764240405126), + FRAC_CONST(0.81135484701706373), + FRAC_CONST(0.81314441484925359), + FRAC_CONST(0.81492632905652662), + FRAC_CONST(0.81670057286682785), + FRAC_CONST(0.81846712958029866), + FRAC_CONST(0.82022598256943469), + FRAC_CONST(0.82197711527924155), + FRAC_CONST(0.82372051122739132), + FRAC_CONST(0.82545615400437744), + FRAC_CONST(0.82718402727366902), + FRAC_CONST(0.82890411477186487), + FRAC_CONST(0.8306164003088462), + FRAC_CONST(0.83232086776792968), + FRAC_CONST(0.83401750110601813), + FRAC_CONST(0.8357062843537526), + FRAC_CONST(0.83738720161566194), + FRAC_CONST(0.83906023707031263), + FRAC_CONST(0.84072537497045807), + FRAC_CONST(0.84238259964318596), + FRAC_CONST(0.84403189549006641), + FRAC_CONST(0.84567324698729907), + FRAC_CONST(0.84730663868585832), + FRAC_CONST(0.84893205521163961), + FRAC_CONST(0.85054948126560337), + FRAC_CONST(0.85215890162391983), + FRAC_CONST(0.8537603011381113), + FRAC_CONST(0.85535366473519603), + FRAC_CONST(0.85693897741782865), + FRAC_CONST(0.85851622426444274), + FRAC_CONST(0.86008539042939014), + FRAC_CONST(0.8616464611430813), + FRAC_CONST(0.86319942171212416), + FRAC_CONST(0.86474425751946238), + FRAC_CONST(0.86628095402451299), + FRAC_CONST(0.86780949676330321), + FRAC_CONST(0.86932987134860673), + FRAC_CONST(0.87084206347007886), + FRAC_CONST(0.87234605889439154), + FRAC_CONST(0.87384184346536675), + FRAC_CONST(0.87532940310411078), + FRAC_CONST(0.87680872380914576), + FRAC_CONST(0.87827979165654146), + FRAC_CONST(0.87974259280004741), + FRAC_CONST(0.88119711347122198), + FRAC_CONST(0.88264333997956279), + FRAC_CONST(0.88408125871263499), + FRAC_CONST(0.88551085613619995), + FRAC_CONST(0.88693211879434208), + FRAC_CONST(0.88834503330959624), + FRAC_CONST(0.88974958638307289), + FRAC_CONST(0.89114576479458318), + FRAC_CONST(0.89253355540276469), + FRAC_CONST(0.89391294514520325), + FRAC_CONST(0.89528392103855758), + FRAC_CONST(0.89664647017868015), + FRAC_CONST(0.89800057974073988), + FRAC_CONST(0.89934623697934146), + FRAC_CONST(0.90068342922864686), + FRAC_CONST(0.90201214390249307), + FRAC_CONST(0.90333236849451182), + FRAC_CONST(0.90464409057824624), + FRAC_CONST(0.90594729780726846), + FRAC_CONST(0.90724197791529593), + FRAC_CONST(0.90852811871630612), + FRAC_CONST(0.90980570810465222), + FRAC_CONST(0.91107473405517625), + FRAC_CONST(0.91233518462332275), + FRAC_CONST(0.91358704794525081), + FRAC_CONST(0.91483031223794609), + FRAC_CONST(0.91606496579933161), + FRAC_CONST(0.91729099700837791), + FRAC_CONST(0.91850839432521225), + FRAC_CONST(0.91971714629122736), + FRAC_CONST(0.92091724152918952), + FRAC_CONST(0.92210866874334507), + FRAC_CONST(0.92329141671952764), + FRAC_CONST(0.9244654743252626), + FRAC_CONST(0.92563083050987272), + FRAC_CONST(0.92678747430458175), + FRAC_CONST(0.92793539482261789), + FRAC_CONST(0.92907458125931575), + FRAC_CONST(0.93020502289221907), + FRAC_CONST(0.93132670908118043), + FRAC_CONST(0.93243962926846236), + FRAC_CONST(0.93354377297883617), + FRAC_CONST(0.93463912981968078), + FRAC_CONST(0.93572568948108037), + FRAC_CONST(0.93680344173592156), + FRAC_CONST(0.93787237643998989), + FRAC_CONST(0.93893248353206449), + FRAC_CONST(0.93998375303401394), + FRAC_CONST(0.94102617505088926), + FRAC_CONST(0.94205973977101731), + FRAC_CONST(0.94308443746609349), + FRAC_CONST(0.94410025849127266), + FRAC_CONST(0.94510719328526061), + FRAC_CONST(0.94610523237040334), + FRAC_CONST(0.94709436635277722), + FRAC_CONST(0.94807458592227623), + FRAC_CONST(0.94904588185270056), + FRAC_CONST(0.950008245001843), + FRAC_CONST(0.95096166631157508), + FRAC_CONST(0.95190613680793223), + FRAC_CONST(0.95284164760119872), + FRAC_CONST(0.95376818988599033), + FRAC_CONST(0.95468575494133834), + FRAC_CONST(0.95559433413077111), + FRAC_CONST(0.95649391890239499), + FRAC_CONST(0.95738450078897586), + FRAC_CONST(0.95826607140801767), + FRAC_CONST(0.95913862246184189), + FRAC_CONST(0.96000214573766585), + FRAC_CONST(0.96085663310767966), + FRAC_CONST(0.96170207652912254), + FRAC_CONST(0.96253846804435916), + FRAC_CONST(0.96336579978095405), + FRAC_CONST(0.96418406395174572), + FRAC_CONST(0.96499325285492032), + FRAC_CONST(0.96579335887408357), + FRAC_CONST(0.96658437447833312), + FRAC_CONST(0.96736629222232851), + FRAC_CONST(0.96813910474636233), + FRAC_CONST(0.96890280477642887), + FRAC_CONST(0.96965738512429245), + FRAC_CONST(0.9704028386875555), + FRAC_CONST(0.97113915844972509), + FRAC_CONST(0.9718663374802794), + FRAC_CONST(0.97258436893473221), + FRAC_CONST(0.97329324605469825), + FRAC_CONST(0.97399296216795583), + FRAC_CONST(0.97468351068851067), + FRAC_CONST(0.97536488511665687), + FRAC_CONST(0.97603707903903902), + FRAC_CONST(0.97670008612871184), + FRAC_CONST(0.97735390014519996), + FRAC_CONST(0.97799851493455714), + FRAC_CONST(0.9786339244294231), + FRAC_CONST(0.97926012264908202), + FRAC_CONST(0.97987710369951764), + FRAC_CONST(0.98048486177346938), + FRAC_CONST(0.98108339115048659), + FRAC_CONST(0.98167268619698311), + FRAC_CONST(0.98225274136628937), + FRAC_CONST(0.98282355119870524), + FRAC_CONST(0.98338511032155118), + FRAC_CONST(0.98393741344921892), + FRAC_CONST(0.98448045538322093), + FRAC_CONST(0.98501423101223984), + FRAC_CONST(0.98553873531217606), + FRAC_CONST(0.98605396334619544), + FRAC_CONST(0.98655991026477541), + FRAC_CONST(0.98705657130575097), + FRAC_CONST(0.98754394179435923), + FRAC_CONST(0.98802201714328353), + FRAC_CONST(0.98849079285269659), + FRAC_CONST(0.98895026451030299), + FRAC_CONST(0.98940042779138038), + FRAC_CONST(0.98984127845882053), + FRAC_CONST(0.99027281236316911), + FRAC_CONST(0.99069502544266463), + FRAC_CONST(0.99110791372327678), + FRAC_CONST(0.9915114733187439), + FRAC_CONST(0.99190570043060933), + FRAC_CONST(0.99229059134825737), + FRAC_CONST(0.99266614244894802), + FRAC_CONST(0.99303235019785141), + FRAC_CONST(0.99338921114808065), + FRAC_CONST(0.9937367219407246), + FRAC_CONST(0.99407487930487937), + FRAC_CONST(0.9944036800576791), + FRAC_CONST(0.9947231211043257), + FRAC_CONST(0.99503319943811863), + FRAC_CONST(0.99533391214048228), + FRAC_CONST(0.99562525638099431), + FRAC_CONST(0.99590722941741172), + FRAC_CONST(0.99617982859569687), + FRAC_CONST(0.99644305135004263), + FRAC_CONST(0.99669689520289606), + FRAC_CONST(0.99694135776498216), + FRAC_CONST(0.99717643673532619), + FRAC_CONST(0.9974021299012753), + FRAC_CONST(0.99761843513851955), + FRAC_CONST(0.99782535041111164), + FRAC_CONST(0.99802287377148624), + FRAC_CONST(0.99821100336047819), + FRAC_CONST(0.99838973740734016), + FRAC_CONST(0.99855907422975931), + FRAC_CONST(0.99871901223387294), + FRAC_CONST(0.99886954991428356), + FRAC_CONST(0.99901068585407338), + FRAC_CONST(0.99914241872481691), + FRAC_CONST(0.99926474728659442), + FRAC_CONST(0.99937767038800285), + FRAC_CONST(0.99948118696616695), + FRAC_CONST(0.99957529604674922), + FRAC_CONST(0.99965999674395922), + FRAC_CONST(0.99973528826056168), + FRAC_CONST(0.99980116988788426), + FRAC_CONST(0.99985764100582386), + FRAC_CONST(0.9999047010828529), + FRAC_CONST(0.99994234967602391), + FRAC_CONST(0.99997058643097414), + FRAC_CONST(0.9999894110819284), + FRAC_CONST(0.99999882345170188) +}; + +#ifdef ALLOW_SMALL_FRAMELENGTH +ALIGN static const real_t sine_mid_480[] = +{ + FRAC_CONST(0.0016362454436240478), + FRAC_CONST(0.00490871880799799), + FRAC_CONST(0.0081811396039371282), + FRAC_CONST(0.011453472786443779), + FRAC_CONST(0.014725683311458524), + FRAC_CONST(0.017997736136235509), + FRAC_CONST(0.021269596219717739), + FRAC_CONST(0.024541228522912285), + FRAC_CONST(0.027812598009265607), + FRAC_CONST(0.03108366964503869), + FRAC_CONST(0.034354408399682276), + FRAC_CONST(0.037624779246211978), + FRAC_CONST(0.04089474716158345), + FRAC_CONST(0.044164277127067358), + FRAC_CONST(0.047433334128624507), + FRAC_CONST(0.050701883157280733), + FRAC_CONST(0.053969889209501881), + FRAC_CONST(0.057237317287568618), + FRAC_CONST(0.060504132399951269), + FRAC_CONST(0.063770299561684493), + FRAC_CONST(0.06703578379474201), + FRAC_CONST(0.070300550128411174), + FRAC_CONST(0.073564563599667426), + FRAC_CONST(0.076827789253548759), + FRAC_CONST(0.080090192143530081), + FRAC_CONST(0.083351737331897449), + FRAC_CONST(0.086612389890122182), + FRAC_CONST(0.089872114899234967), + FRAC_CONST(0.093130877450199795), + FRAC_CONST(0.096388642644287828), + FRAC_CONST(0.09964537559345106), + FRAC_CONST(0.1029010414206961), + FRAC_CONST(0.10615560526045748), + FRAC_CONST(0.10940903225897117), + FRAC_CONST(0.11266128757464781), + FRAC_CONST(0.11591233637844581), + FRAC_CONST(0.11916214385424433), + FRAC_CONST(0.1224106751992162), + FRAC_CONST(0.12565789562420052), + FRAC_CONST(0.12890377035407541), + FRAC_CONST(0.13214826462813015), + FRAC_CONST(0.13539134370043773), + FRAC_CONST(0.13863297284022669), + FRAC_CONST(0.14187311733225325), + FRAC_CONST(0.14511174247717309), + FRAC_CONST(0.14834881359191271), + FRAC_CONST(0.15158429601004111), + FRAC_CONST(0.15481815508214106), + FRAC_CONST(0.1580503561761798), + FRAC_CONST(0.16128086467788047), + FRAC_CONST(0.16450964599109233), + FRAC_CONST(0.16773666553816149), + FRAC_CONST(0.17096188876030122), + FRAC_CONST(0.17418528111796186), + FRAC_CONST(0.17740680809120093), + FRAC_CONST(0.18062643518005275), + FRAC_CONST(0.18384412790489776), + FRAC_CONST(0.18705985180683199), + FRAC_CONST(0.19027357244803589), + FRAC_CONST(0.19348525541214331), + FRAC_CONST(0.19669486630460997), + FRAC_CONST(0.19990237075308173), + FRAC_CONST(0.20310773440776286), + FRAC_CONST(0.20631092294178383), + FRAC_CONST(0.20951190205156878), + FRAC_CONST(0.21271063745720317), + FRAC_CONST(0.21590709490280058), + FRAC_CONST(0.2191012401568698), + FRAC_CONST(0.22229303901268133), + FRAC_CONST(0.22548245728863364), + FRAC_CONST(0.22866946082861941), + FRAC_CONST(0.23185401550239115), + FRAC_CONST(0.23503608720592667), + FRAC_CONST(0.23821564186179459), + FRAC_CONST(0.24139264541951888), + FRAC_CONST(0.24456706385594387), + FRAC_CONST(0.24773886317559846), + FRAC_CONST(0.25090800941106001), + FRAC_CONST(0.25407446862331851), + FRAC_CONST(0.25723820690213967), + FRAC_CONST(0.26039919036642817), + FRAC_CONST(0.26355738516459076), + FRAC_CONST(0.26671275747489837), + FRAC_CONST(0.2698652735058486), + FRAC_CONST(0.27301489949652735), + FRAC_CONST(0.27616160171697068), + FRAC_CONST(0.27930534646852595), + FRAC_CONST(0.28244610008421245), + FRAC_CONST(0.2855838289290823), + FRAC_CONST(0.28871849940058025), + FRAC_CONST(0.29185007792890405), + FRAC_CONST(0.29497853097736348), + FRAC_CONST(0.2981038250427398), + FRAC_CONST(0.30122592665564446), + FRAC_CONST(0.30434480238087736), + FRAC_CONST(0.30746041881778519), + FRAC_CONST(0.31057274260061901), + FRAC_CONST(0.31368174039889146), + FRAC_CONST(0.31678737891773395), + FRAC_CONST(0.31988962489825296), + FRAC_CONST(0.32298844511788638), + FRAC_CONST(0.32608380639075912), + FRAC_CONST(0.32917567556803889), + FRAC_CONST(0.33226401953829071), + FRAC_CONST(0.33534880522783189), + FRAC_CONST(0.33842999960108583), + FRAC_CONST(0.34150756966093632), + FRAC_CONST(0.34458148244908043), + FRAC_CONST(0.34765170504638188), + FRAC_CONST(0.35071820457322322), + FRAC_CONST(0.35378094818985806), + FRAC_CONST(0.35683990309676283), + FRAC_CONST(0.35989503653498811), + FRAC_CONST(0.36294631578650921), + FRAC_CONST(0.36599370817457672), + FRAC_CONST(0.36903718106406647), + FRAC_CONST(0.37207670186182878), + FRAC_CONST(0.37511223801703802), + FRAC_CONST(0.37814375702154046), + FRAC_CONST(0.38117122641020335), + FRAC_CONST(0.38419461376126157), + FRAC_CONST(0.38721388669666562), + FRAC_CONST(0.39022901288242801), + FRAC_CONST(0.39323996002896966), + FRAC_CONST(0.39624669589146555), + FRAC_CONST(0.39924918827019029), + FRAC_CONST(0.40224740501086254), + FRAC_CONST(0.40524131400498986), + FRAC_CONST(0.40823088319021217), + FRAC_CONST(0.41121608055064529), + FRAC_CONST(0.41419687411722372), + FRAC_CONST(0.41717323196804335), + FRAC_CONST(0.42014512222870243), + FRAC_CONST(0.42311251307264408), + FRAC_CONST(0.42607537272149631), + FRAC_CONST(0.4290336694454126), + FRAC_CONST(0.43198737156341183), + FRAC_CONST(0.43493644744371707), + FRAC_CONST(0.43788086550409511), + FRAC_CONST(0.44082059421219388), + FRAC_CONST(0.44375560208588088), + FRAC_CONST(0.44668585769357955), + FRAC_CONST(0.4496113296546066), + FRAC_CONST(0.45253198663950756), + FRAC_CONST(0.45544779737039259), + FRAC_CONST(0.45835873062127125), + FRAC_CONST(0.46126475521838717), + FRAC_CONST(0.46416584004055156), + FRAC_CONST(0.46706195401947659), + FRAC_CONST(0.46995306614010829), + FRAC_CONST(0.47283914544095862), + FRAC_CONST(0.47572016101443682), + FRAC_CONST(0.47859608200718085), + FRAC_CONST(0.4814668776203872), + FRAC_CONST(0.48433251711014125), + FRAC_CONST(0.4871929697877464), + FRAC_CONST(0.49004820502005247), + FRAC_CONST(0.49289819222978404), + FRAC_CONST(0.49574290089586776), + FRAC_CONST(0.49858230055375902), + FRAC_CONST(0.50141636079576901), + FRAC_CONST(0.50424505127138919), + FRAC_CONST(0.50706834168761705), + FRAC_CONST(0.50988620180928057), + FRAC_CONST(0.51269860145936175), + FRAC_CONST(0.51550551051931948), + FRAC_CONST(0.51830689892941317), + FRAC_CONST(0.5211027366890234), + FRAC_CONST(0.52389299385697385), + FRAC_CONST(0.52667764055185196), + FRAC_CONST(0.52945664695232897), + FRAC_CONST(0.53222998329747884), + FRAC_CONST(0.53499761988709726), + FRAC_CONST(0.53775952708201991), + FRAC_CONST(0.54051567530443978), + FRAC_CONST(0.54326603503822357), + FRAC_CONST(0.54601057682922816), + FRAC_CONST(0.54874927128561579), + FRAC_CONST(0.55148208907816942), + FRAC_CONST(0.55420900094060566), + FRAC_CONST(0.55692997766988939), + FRAC_CONST(0.559644990126546), + FRAC_CONST(0.56235400923497314), + FRAC_CONST(0.56505700598375252), + FRAC_CONST(0.56775395142596052), + FRAC_CONST(0.57044481667947822), + FRAC_CONST(0.57312957292730071), + FRAC_CONST(0.57580819141784534), + FRAC_CONST(0.57848064346525996), + FRAC_CONST(0.58114690044973039), + FRAC_CONST(0.58380693381778626), + FRAC_CONST(0.58646071508260733), + FRAC_CONST(0.58910821582432815), + FRAC_CONST(0.5917494076903429), + FRAC_CONST(0.5943842623956086), + FRAC_CONST(0.59701275172294799), + FRAC_CONST(0.59963484752335228), + FRAC_CONST(0.60225052171628191), + FRAC_CONST(0.60485974628996786), + FRAC_CONST(0.60746249330171098), + FRAC_CONST(0.61005873487818185), + FRAC_CONST(0.61264844321571899), + FRAC_CONST(0.61523159058062682), + FRAC_CONST(0.61780814930947225), + FRAC_CONST(0.62037809180938108), + FRAC_CONST(0.62294139055833397), + FRAC_CONST(0.6254980181054608), + FRAC_CONST(0.62804794707133416), + FRAC_CONST(0.63059115014826372), + FRAC_CONST(0.63312760010058777), + FRAC_CONST(0.63565726976496484), + FRAC_CONST(0.63818013205066515), + FRAC_CONST(0.64069615993986073), + FRAC_CONST(0.64320532648791406), + FRAC_CONST(0.64570760482366729), + FRAC_CONST(0.64820296814972966), + FRAC_CONST(0.65069138974276486), + FRAC_CONST(0.65317284295377676), + FRAC_CONST(0.65564730120839498), + FRAC_CONST(0.65811473800715958), + FRAC_CONST(0.660575126925805), + FRAC_CONST(0.66302844161554231), + FRAC_CONST(0.6654746558033422), + FRAC_CONST(0.66791374329221598), + FRAC_CONST(0.67034567796149647), + FRAC_CONST(0.67277043376711676), + FRAC_CONST(0.67518798474189046), + FRAC_CONST(0.67759830499578866), + FRAC_CONST(0.68000136871621808), + FRAC_CONST(0.68239715016829683), + FRAC_CONST(0.6847856236951303), + FRAC_CONST(0.68716676371808583), + FRAC_CONST(0.68954054473706683), + FRAC_CONST(0.69190694133078579), + FRAC_CONST(0.69426592815703603), + FRAC_CONST(0.69661747995296419), + FRAC_CONST(0.69896157153533944), + FRAC_CONST(0.70129817780082437), + FRAC_CONST(0.7036272737262429), + FRAC_CONST(0.70594883436884903), + FRAC_CONST(0.70826283486659336), + FRAC_CONST(0.71056925043838959), + FRAC_CONST(0.71286805638437978), + FRAC_CONST(0.71515922808619936), + FRAC_CONST(0.71744274100723993), + FRAC_CONST(0.71971857069291278), + FRAC_CONST(0.7219866927709101), + FRAC_CONST(0.72424708295146689), + FRAC_CONST(0.72649971702762028), + FRAC_CONST(0.72874457087546896), + FRAC_CONST(0.73098162045443171), + FRAC_CONST(0.73321084180750484), + FRAC_CONST(0.73543221106151868), + FRAC_CONST(0.73764570442739286), + FRAC_CONST(0.73985129820039208), + FRAC_CONST(0.74204896876037885), + FRAC_CONST(0.7442386925720671), + FRAC_CONST(0.74642044618527381), + FRAC_CONST(0.74859420623517081), + FRAC_CONST(0.75075994944253421), + FRAC_CONST(0.75291765261399446), + FRAC_CONST(0.75506729264228367), + FRAC_CONST(0.75720884650648446), + FRAC_CONST(0.75934229127227548), + FRAC_CONST(0.76146760409217706), + FRAC_CONST(0.76358476220579641), + FRAC_CONST(0.7656937429400712), + FRAC_CONST(0.76779452370951196), + FRAC_CONST(0.76988708201644451), + FRAC_CONST(0.77197139545125026), + FRAC_CONST(0.7740474416926072), + FRAC_CONST(0.77611519850772781), + FRAC_CONST(0.77817464375259782), + FRAC_CONST(0.78022575537221317), + FRAC_CONST(0.78226851140081632), + FRAC_CONST(0.78430288996213138), + FRAC_CONST(0.78632886926959822), + FRAC_CONST(0.78834642762660623), + FRAC_CONST(0.79035554342672631), + FRAC_CONST(0.79235619515394229), + FRAC_CONST(0.79434836138288134), + FRAC_CONST(0.79633202077904397), + FRAC_CONST(0.79830715209903147), + FRAC_CONST(0.8002737341907743), + FRAC_CONST(0.80223174599375802), + FRAC_CONST(0.80418116653924954), + FRAC_CONST(0.80612197495052085), + FRAC_CONST(0.80805415044307316), + FRAC_CONST(0.80997767232485907), + FRAC_CONST(0.81189251999650469), + FRAC_CONST(0.81379867295152986), + FRAC_CONST(0.81569611077656778), + FRAC_CONST(0.81758481315158371), + FRAC_CONST(0.81946475985009259), + FRAC_CONST(0.82133593073937561), + FRAC_CONST(0.82319830578069586), + FRAC_CONST(0.82505186502951278), + FRAC_CONST(0.82689658863569615), + FRAC_CONST(0.82873245684373809), + FRAC_CONST(0.83055944999296494), + FRAC_CONST(0.83237754851774781), + FRAC_CONST(0.83418673294771239), + FRAC_CONST(0.83598698390794668), + FRAC_CONST(0.83777828211920935), + FRAC_CONST(0.83956060839813562), + FRAC_CONST(0.84133394365744296), + FRAC_CONST(0.84309826890613537), + FRAC_CONST(0.84485356524970701), + FRAC_CONST(0.84659981389034411), + FRAC_CONST(0.84833699612712676), + FRAC_CONST(0.85006509335622882), + FRAC_CONST(0.8517840870711173), + FRAC_CONST(0.85349395886275037), + FRAC_CONST(0.85519469041977514), + FRAC_CONST(0.85688626352872277), + FRAC_CONST(0.85856866007420429), + FRAC_CONST(0.86024186203910447), + FRAC_CONST(0.86190585150477417), + FRAC_CONST(0.86356061065122347), + FRAC_CONST(0.86520612175731115), + FRAC_CONST(0.86684236720093533), + FRAC_CONST(0.86846932945922151), + FRAC_CONST(0.87008699110871135), + FRAC_CONST(0.87169533482554817), + FRAC_CONST(0.87329434338566281), + FRAC_CONST(0.87488399966495822), + FRAC_CONST(0.87646428663949283), + FRAC_CONST(0.87803518738566277), + FRAC_CONST(0.87959668508038291), + FRAC_CONST(0.88114876300126743), + FRAC_CONST(0.88269140452680916), + FRAC_CONST(0.8842245931365561), + FRAC_CONST(0.88574831241129048), + FRAC_CONST(0.88726254603320276), + FRAC_CONST(0.88876727778606746), + FRAC_CONST(0.89026249155541637), + FRAC_CONST(0.8917481713287112), + FRAC_CONST(0.89322430119551532), + FRAC_CONST(0.89469086534766362), + FRAC_CONST(0.89614784807943237), + FRAC_CONST(0.89759523378770689), + FRAC_CONST(0.89903300697214927), + FRAC_CONST(0.9004611522353636), + FRAC_CONST(0.90187965428306172), + FRAC_CONST(0.90328849792422594), + FRAC_CONST(0.90468766807127299), + FRAC_CONST(0.90607714974021469), + FRAC_CONST(0.90745692805081868), + FRAC_CONST(0.90882698822676755), + FRAC_CONST(0.91018731559581767), + FRAC_CONST(0.91153789558995579), + FRAC_CONST(0.91287871374555518), + FRAC_CONST(0.91420975570353069), + FRAC_CONST(0.9155310072094921), + FRAC_CONST(0.91684245411389753), + FRAC_CONST(0.91814408237220391), + FRAC_CONST(0.91943587804501858), + FRAC_CONST(0.92071782729824769), + FRAC_CONST(0.92198991640324446), + FRAC_CONST(0.92325213173695675), + FRAC_CONST(0.92450445978207241), + FRAC_CONST(0.92574688712716402), + FRAC_CONST(0.92697940046683291), + FRAC_CONST(0.92820198660185149), + FRAC_CONST(0.92941463243930444), + FRAC_CONST(0.93061732499272909), + FRAC_CONST(0.93181005138225426), + FRAC_CONST(0.93299279883473885), + FRAC_CONST(0.93416555468390772), + FRAC_CONST(0.93532830637048769), + FRAC_CONST(0.93648104144234268), + FRAC_CONST(0.93762374755460598), + FRAC_CONST(0.93875641246981323), + FRAC_CONST(0.93987902405803303), + FRAC_CONST(0.94099157029699743), + FRAC_CONST(0.94209403927222979), + FRAC_CONST(0.94318641917717327), + FRAC_CONST(0.9442686983133165), + FRAC_CONST(0.94534086509031956), + FRAC_CONST(0.9464029080261378), + FRAC_CONST(0.94745481574714419), + FRAC_CONST(0.94849657698825252), + FRAC_CONST(0.94952818059303667), + FRAC_CONST(0.95054961551385087), + FRAC_CONST(0.95156087081194762), + FRAC_CONST(0.95256193565759528), + FRAC_CONST(0.95355279933019343), + FRAC_CONST(0.9545334512183884), + FRAC_CONST(0.95550388082018611), + FRAC_CONST(0.95646407774306541), + FRAC_CONST(0.95741403170408834), + FRAC_CONST(0.95835373253001133), + FRAC_CONST(0.95928317015739362), + FRAC_CONST(0.96020233463270466), + FRAC_CONST(0.96111121611243155), + FRAC_CONST(0.96200980486318388), + FRAC_CONST(0.96289809126179782), + FRAC_CONST(0.96377606579543984), + FRAC_CONST(0.96464371906170809), + FRAC_CONST(0.96550104176873297), + FRAC_CONST(0.96634802473527726), + FRAC_CONST(0.96718465889083372), + FRAC_CONST(0.96801093527572268), + FRAC_CONST(0.96882684504118799), + FRAC_CONST(0.96963237944949143), + FRAC_CONST(0.97042752987400682), + FRAC_CONST(0.97121228779931179), + FRAC_CONST(0.97198664482127939), + FRAC_CONST(0.97275059264716823), + FRAC_CONST(0.97350412309571066), + FRAC_CONST(0.97424722809720088), + FRAC_CONST(0.97497989969358168), + FRAC_CONST(0.97570213003852857), + FRAC_CONST(0.97641391139753486), + FRAC_CONST(0.97711523614799412), + FRAC_CONST(0.97780609677928154), + FRAC_CONST(0.97848648589283505), + FRAC_CONST(0.97915639620223371), + FRAC_CONST(0.9798158205332762), + FRAC_CONST(0.98046475182405801), + FRAC_CONST(0.98110318312504607), + FRAC_CONST(0.98173110759915416), + FRAC_CONST(0.98234851852181571), + FRAC_CONST(0.98295540928105563), + FRAC_CONST(0.9835517733775615), + FRAC_CONST(0.98413760442475307), + FRAC_CONST(0.98471289614885038), + FRAC_CONST(0.98527764238894122), + FRAC_CONST(0.98583183709704714), + FRAC_CONST(0.98637547433818806), + FRAC_CONST(0.98690854829044583), + FRAC_CONST(0.98743105324502667), + FRAC_CONST(0.98794298360632238), + FRAC_CONST(0.98844433389196995), + FRAC_CONST(0.98893509873291074), + FRAC_CONST(0.98941527287344755), + FRAC_CONST(0.98988485117130098), + FRAC_CONST(0.99034382859766479), + FRAC_CONST(0.99079220023725967), + FRAC_CONST(0.99122996128838525), + FRAC_CONST(0.9916571070629725), + FRAC_CONST(0.99207363298663342), + FRAC_CONST(0.99247953459870997), + FRAC_CONST(0.99287480755232194), + FRAC_CONST(0.99325944761441354), + FRAC_CONST(0.99363345066579889), + FRAC_CONST(0.99399681270120555), + FRAC_CONST(0.99434952982931812), + FRAC_CONST(0.9946915982728195), + FRAC_CONST(0.99502301436843166), + FRAC_CONST(0.99534377456695422), + FRAC_CONST(0.9956538754333033), + FRAC_CONST(0.99595331364654771), + FRAC_CONST(0.99624208599994479), + FRAC_CONST(0.99652018940097464), + FRAC_CONST(0.99678762087137318), + FRAC_CONST(0.99704437754716424), + FRAC_CONST(0.99729045667869021), + FRAC_CONST(0.99752585563064111), + FRAC_CONST(0.99775057188208349), + FRAC_CONST(0.9979646030264866), + FRAC_CONST(0.99816794677174903), + FRAC_CONST(0.9983606009402225), + FRAC_CONST(0.99854256346873571), + FRAC_CONST(0.99871383240861611), + FRAC_CONST(0.99887440592571108), + FRAC_CONST(0.99902428230040718), + FRAC_CONST(0.99916345992764877), + FRAC_CONST(0.99929193731695531), + FRAC_CONST(0.99940971309243731), + FRAC_CONST(0.99951678599281069), + FRAC_CONST(0.99961315487141078), + FRAC_CONST(0.99969881869620425), + FRAC_CONST(0.99977377654980037), + FRAC_CONST(0.99983802762946083), + FRAC_CONST(0.99989157124710804), + FRAC_CONST(0.9999344068293331), + FRAC_CONST(0.99996653391740109), + FRAC_CONST(0.99998795216725689), + FRAC_CONST(0.99999866134952808) +}; +#endif + +ALIGN static const real_t ld_mid_512[] = +{ + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0.0061358846491544753), + FRAC_CONST(0.01840672990580482), + FRAC_CONST(0.030674803176636626), + FRAC_CONST(0.04293825693494082), + FRAC_CONST(0.055195244349689934), + FRAC_CONST(0.067443919563664051), + FRAC_CONST(0.079682437971430126), + FRAC_CONST(0.091908956497132724), + FRAC_CONST(0.10412163387205459), + FRAC_CONST(0.11631863091190475), + FRAC_CONST(0.12849811079379317), + FRAC_CONST(0.14065823933284921), + FRAC_CONST(0.15279718525844344), + FRAC_CONST(0.16491312048996989), + FRAC_CONST(0.17700422041214875), + FRAC_CONST(0.18906866414980619), + FRAC_CONST(0.2011046348420919), + FRAC_CONST(0.21311031991609136), + FRAC_CONST(0.22508391135979283), + FRAC_CONST(0.2370236059943672), + FRAC_CONST(0.24892760574572015), + FRAC_CONST(0.26079411791527551), + FRAC_CONST(0.27262135544994898), + FRAC_CONST(0.28440753721127188), + FRAC_CONST(0.29615088824362379), + FRAC_CONST(0.30784964004153487), + FRAC_CONST(0.31950203081601569), + FRAC_CONST(0.33110630575987643), + FRAC_CONST(0.34266071731199438), + FRAC_CONST(0.35416352542049034), + FRAC_CONST(0.36561299780477385), + FRAC_CONST(0.37700741021641826), + FRAC_CONST(0.38834504669882625), + FRAC_CONST(0.39962419984564679), + FRAC_CONST(0.41084317105790391), + FRAC_CONST(0.42200027079979968), + FRAC_CONST(0.43309381885315196), + FRAC_CONST(0.4441221445704292), + FRAC_CONST(0.45508358712634384), + FRAC_CONST(0.46597649576796618), + FRAC_CONST(0.47679923006332209), + FRAC_CONST(0.487550160148436), + FRAC_CONST(0.49822766697278187), + FRAC_CONST(0.50883014254310699), + FRAC_CONST(0.51935599016558964), + FRAC_CONST(0.52980362468629461), + FRAC_CONST(0.54017147272989285), + FRAC_CONST(0.55045797293660481), + FRAC_CONST(0.56066157619733603), + FRAC_CONST(0.57078074588696726), + FRAC_CONST(0.58081395809576453), + FRAC_CONST(0.59075970185887416), + FRAC_CONST(0.60061647938386897), + FRAC_CONST(0.61038280627630948), + FRAC_CONST(0.6200572117632891), + FRAC_CONST(0.62963823891492698), + FRAC_CONST(0.63912444486377573), + FRAC_CONST(0.64851440102211244), + FRAC_CONST(0.65780669329707864), + FRAC_CONST(0.66699992230363747), + FRAC_CONST(0.67609270357531592), + FRAC_CONST(0.68508366777270036), + FRAC_CONST(0.693971460889654), + FRAC_CONST(0.7027547444572253), + FRAC_CONST(0.71143219574521643), + FRAC_CONST(0.72000250796138165), + FRAC_CONST(0.7284643904482252), + FRAC_CONST(0.73681656887736979), + FRAC_CONST(0.74505778544146595), + FRAC_CONST(0.75318679904361241), + FRAC_CONST(0.76120238548426178), + FRAC_CONST(0.76910333764557959), + FRAC_CONST(0.77688846567323244), + FRAC_CONST(0.78455659715557524), + FRAC_CONST(0.79210657730021239), + FRAC_CONST(0.79953726910790501), + FRAC_CONST(0.80684755354379922), + FRAC_CONST(0.8140363297059483), + FRAC_CONST(0.82110251499110465), + FRAC_CONST(0.8280450452577558), + FRAC_CONST(0.83486287498638001), + FRAC_CONST(0.84155497743689833), + FRAC_CONST(0.84812034480329712), + FRAC_CONST(0.85455798836540053), + FRAC_CONST(0.86086693863776731), + FRAC_CONST(0.86704624551569265), + FRAC_CONST(0.87309497841829009), + FRAC_CONST(0.87901222642863341), + FRAC_CONST(0.88479709843093779), + FRAC_CONST(0.89044872324475788), + FRAC_CONST(0.89596624975618511), + FRAC_CONST(0.90134884704602203), + FRAC_CONST(0.90659570451491533), + FRAC_CONST(0.91170603200542988), + FRAC_CONST(0.9166790599210427), + FRAC_CONST(0.9215140393420419), + FRAC_CONST(0.92621024213831127), + FRAC_CONST(0.93076696107898371), + FRAC_CONST(0.9351835099389475), + FRAC_CONST(0.93945922360218992), + FRAC_CONST(0.94359345816196039), + FRAC_CONST(0.94758559101774109), + FRAC_CONST(0.95143502096900834), + FRAC_CONST(0.95514116830577067), + FRAC_CONST(0.9587034748958716), + FRAC_CONST(0.96212140426904158), + FRAC_CONST(0.9653944416976894), + FRAC_CONST(0.96852209427441727), + FRAC_CONST(0.97150389098625178), + FRAC_CONST(0.97433938278557586), + FRAC_CONST(0.97702814265775439), + FRAC_CONST(0.97956976568544052), + FRAC_CONST(0.98196386910955524), + FRAC_CONST(0.98421009238692903), + FRAC_CONST(0.98630809724459867), + FRAC_CONST(0.98825756773074946), + FRAC_CONST(0.99005821026229712), + FRAC_CONST(0.99170975366909953), + FRAC_CONST(0.9932119492347945), + FRAC_CONST(0.99456457073425542), + FRAC_CONST(0.99576741446765982), + FRAC_CONST(0.99682029929116567), + FRAC_CONST(0.99772306664419164), + FRAC_CONST(0.99847558057329477), + FRAC_CONST(0.99907772775264536), + FRAC_CONST(0.99952941750109314), + FRAC_CONST(0.9998305817958234), + FRAC_CONST(0.99998117528260111), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1) +}; + +#ifdef ALLOW_SMALL_FRAMELENGTH +ALIGN static const real_t ld_mid_480[] = +{ + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0), + FRAC_CONST(0.0065449379673518581), + FRAC_CONST(0.019633692460628301), + FRAC_CONST(0.032719082821776137), + FRAC_CONST(0.045798866936520771), + FRAC_CONST(0.058870803651189033), + FRAC_CONST(0.071932653156719387), + FRAC_CONST(0.084982177372441667), + FRAC_CONST(0.09801714032956059), + FRAC_CONST(0.11103530855427769), + FRAC_CONST(0.12403445145048532), + FRAC_CONST(0.13701234168196802), + FRAC_CONST(0.14996675555404498), + FRAC_CONST(0.16289547339458874), + FRAC_CONST(0.17579627993435451), + FRAC_CONST(0.18866696468655525), + FRAC_CONST(0.2015053223256171), + FRAC_CONST(0.21430915306505074), + FRAC_CONST(0.2270762630343732), + FRAC_CONST(0.23980446465501654), + FRAC_CONST(0.25249157701515795), + FRAC_CONST(0.26513542624340797), + FRAC_CONST(0.27773384588129219), + FRAC_CONST(0.29028467725446233), + FRAC_CONST(0.3027857698425746), + FRAC_CONST(0.31523498164776964), + FRAC_CONST(0.32763017956169349), + FRAC_CONST(0.33996923973099424), + FRAC_CONST(0.35225004792123354), + FRAC_CONST(0.36447049987914965), + FRAC_CONST(0.37662850169321077), + FRAC_CONST(0.38872197015239557), + FRAC_CONST(0.40074883310314097), + FRAC_CONST(0.41270702980439467), + FRAC_CONST(0.42459451128071307), + FRAC_CONST(0.43640924067334208), + FRAC_CONST(0.44814919358922256), + FRAC_CONST(0.45981235844785984), + FRAC_CONST(0.47139673682599764), + FRAC_CONST(0.48290034380003727), + FRAC_CONST(0.49432120828614462), + FRAC_CONST(0.50565737337798455), + FRAC_CONST(0.51690689668202761), + FRAC_CONST(0.52806785065036799), + FRAC_CONST(0.53913832291100017), + FRAC_CONST(0.55011641659549337), + FRAC_CONST(0.56100025066400983), + FRAC_CONST(0.57178796022761225), + FRAC_CONST(0.58247769686780215), + FRAC_CONST(0.59306762895323706), + FRAC_CONST(0.60355594195357143), + FRAC_CONST(0.61394083875036642), + FRAC_CONST(0.62422053994501758), + FRAC_CONST(0.63439328416364549), + FRAC_CONST(0.64445732835889735), + FRAC_CONST(0.65441094810861034), + FRAC_CONST(0.66425243791128175), + FRAC_CONST(0.67398011147829784), + FRAC_CONST(0.68359230202287125), + FRAC_CONST(0.69308736254563585), + FRAC_CONST(0.70246366611685174), + FRAC_CONST(0.71171960615517138), + FRAC_CONST(0.72085359670291882), + FRAC_CONST(0.7298640726978356), + FRAC_CONST(0.73874949024124625), + FRAC_CONST(0.74750832686259672), + FRAC_CONST(0.75613908178032285), + FRAC_CONST(0.76464027615900032), + FRAC_CONST(0.77301045336273699), + FRAC_CONST(0.78124817920475853), + FRAC_CONST(0.78935204219315003), + FRAC_CONST(0.79732065377270711), + FRAC_CONST(0.80515264856285829), + FRAC_CONST(0.81284668459161513), + FRAC_CONST(0.82040144352551359), + FRAC_CONST(0.82781563089550203), + FRAC_CONST(0.83508797631874299), + FRAC_CONST(0.84221723371628654), + FRAC_CONST(0.84920218152657889), + FRAC_CONST(0.85604162291477137), + FRAC_CONST(0.86273438597779184), + FRAC_CONST(0.86927932394514362), + FRAC_CONST(0.87567531537539967), + FRAC_CONST(0.88192126434835494), + FRAC_CONST(0.88801610065280734), + FRAC_CONST(0.89395877996993212), + FRAC_CONST(0.8997482840522214), + FRAC_CONST(0.90538362089795521), + FRAC_CONST(0.91086382492117568), + FRAC_CONST(0.91618795711713596), + FRAC_CONST(0.92135510522319242), + FRAC_CONST(0.9263643838751181), + FRAC_CONST(0.93121493475880346), + FRAC_CONST(0.93590592675732565), + FRAC_CONST(0.94043655609335486), + FRAC_CONST(0.94480604646687805), + FRAC_CONST(0.94901364918821385), + FRAC_CONST(0.95305864330629697), + FRAC_CONST(0.95694033573220882), + FRAC_CONST(0.9606580613579353), + FRAC_CONST(0.96421118317032928), + FRAC_CONST(0.96759909236025976), + FRAC_CONST(0.9708212084269281), + FRAC_CONST(0.97387697927733363), + FRAC_CONST(0.97676588132087239), + FRAC_CONST(0.97948741955905139), + FRAC_CONST(0.98204112767030394), + FRAC_CONST(0.98442656808989171), + FRAC_CONST(0.98664333208487898), + FRAC_CONST(0.98869103982416728), + FRAC_CONST(0.99056934044357725), + FRAC_CONST(0.99227791210596705), + FRAC_CONST(0.99381646205637808), + FRAC_CONST(0.99518472667219682), + FRAC_CONST(0.99638247150832537), + FRAC_CONST(0.99740949133735191), + FRAC_CONST(0.99826561018471593), + FRAC_CONST(0.99895068135886012), + FRAC_CONST(0.99946458747636568), + FRAC_CONST(0.99980724048206482), + FRAC_CONST(0.99997858166412923), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1), + FRAC_CONST(1) +}; +#endif +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/specrec.c b/third_party/faad_hdc/upstream/libfaad/specrec.c new file mode 100644 index 000000000..94cee17bb --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/specrec.c @@ -0,0 +1,1365 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: specrec.c,v 1.63 2010/06/04 20:47:56 menno Exp $ +**/ + +/* + Spectral reconstruction: + - grouping/sectioning + - inverse quantization + - applying scalefactors +*/ + +#include "common.h" +#include "structs.h" + +#include +#include "specrec.h" +#include "filtbank.h" +#include "syntax.h" +#include "iq_table.h" +#include "ms.h" +#include "is.h" +#include "pns.h" +#include "tns.h" +#include "drc.h" +#include "lt_predict.h" +#include "ic_predict.h" +#ifdef SSR_DEC +#include "ssr.h" +#include "ssr_fb.h" +#endif + + +/* static function declarations */ +static uint8_t quant_to_spec(NeAACDecStruct *hDecoder, + ic_stream *ics, int16_t *quant_data, + real_t *spec_data, uint16_t frame_len); + + +#ifdef LD_DEC +ALIGN static const uint8_t num_swb_512_window[] = +{ + 0, 0, 0, 36, 36, 37, 31, 31, 0, 0, 0, 0 +}; +ALIGN static const uint8_t num_swb_480_window[] = +{ + 0, 0, 0, 35, 35, 37, 30, 30, 0, 0, 0, 0 +}; +#endif + +ALIGN static const uint8_t num_swb_960_window[] = +{ + 40, 40, 45, 49, 49, 49, 46, 46, 42, 42, 42, 40 +}; + +ALIGN static const uint8_t num_swb_1024_window[] = +{ + 41, 41, 47, 49, 49, 51, 47, 47, 43, 43, 43, 40 +}; + +ALIGN static const uint8_t num_swb_128_window[] = +{ + 12, 12, 12, 14, 14, 14, 15, 15, 15, 15, 15, 15 +}; + +ALIGN static const uint16_t swb_offset_1024_96[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, + 64, 72, 80, 88, 96, 108, 120, 132, 144, 156, 172, 188, 212, 240, + 276, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024 +}; + +ALIGN static const uint16_t swb_offset_128_96[] = +{ + 0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 92, 128 +}; + +ALIGN static const uint16_t swb_offset_1024_64[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, + 64, 72, 80, 88, 100, 112, 124, 140, 156, 172, 192, 216, 240, 268, + 304, 344, 384, 424, 464, 504, 544, 584, 624, 664, 704, 744, 784, 824, + 864, 904, 944, 984, 1024 +}; + +ALIGN static const uint16_t swb_offset_128_64[] = +{ + 0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 92, 128 +}; + +ALIGN static const uint16_t swb_offset_1024_48[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48, 56, 64, 72, + 80, 88, 96, 108, 120, 132, 144, 160, 176, 196, 216, 240, 264, 292, + 320, 352, 384, 416, 448, 480, 512, 544, 576, 608, 640, 672, 704, 736, + 768, 800, 832, 864, 896, 928, 1024 +}; + +#ifdef LD_DEC +ALIGN static const uint16_t swb_offset_512_48[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 68, 76, 84, + 92, 100, 112, 124, 136, 148, 164, 184, 208, 236, 268, 300, 332, 364, 396, + 428, 460, 512 +}; + +ALIGN static const uint16_t swb_offset_480_48[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 64, 72 ,80 ,88, + 96, 108, 120, 132, 144, 156, 172, 188, 212, 240, 272, 304, 336, 368, 400, + 432, 480 +}; +#endif + +ALIGN static const uint16_t swb_offset_128_48[] = +{ + 0, 4, 8, 12, 16, 20, 28, 36, 44, 56, 68, 80, 96, 112, 128 +}; + +ALIGN static const uint16_t swb_offset_1024_32[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48, 56, 64, 72, + 80, 88, 96, 108, 120, 132, 144, 160, 176, 196, 216, 240, 264, 292, + 320, 352, 384, 416, 448, 480, 512, 544, 576, 608, 640, 672, 704, 736, + 768, 800, 832, 864, 896, 928, 960, 992, 1024 +}; + +#ifdef LD_DEC +ALIGN static const uint16_t swb_offset_512_32[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 64, 72, 80, + 88, 96, 108, 120, 132, 144, 160, 176, 192, 212, 236, 260, 288, 320, 352, + 384, 416, 448, 480, 512 +}; + +ALIGN static const uint16_t swb_offset_480_32[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, + 88, 96, 104, 112, 124, 136, 148, 164, 180, 200, 224, 256, 288, 320, 352, + 384, 416, 448, 480 +}; +#endif + +ALIGN static const uint16_t swb_offset_1024_24[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 52, 60, 68, + 76, 84, 92, 100, 108, 116, 124, 136, 148, 160, 172, 188, 204, 220, + 240, 260, 284, 308, 336, 364, 396, 432, 468, 508, 552, 600, 652, 704, + 768, 832, 896, 960, 1024 +}; + +#ifdef LD_DEC +ALIGN static const uint16_t swb_offset_512_24[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 52, 60, 68, + 80, 92, 104, 120, 140, 164, 192, 224, 256, 288, 320, 352, 384, 416, + 448, 480, 512 +}; + +ALIGN static const uint16_t swb_offset_480_24[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 52, 60, 68, 80, 92, 104, 120, + 140, 164, 192, 224, 256, 288, 320, 352, 384, 416, 448, 480 +}; +#endif + +ALIGN static const uint16_t swb_offset_128_24[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 36, 44, 52, 64, 76, 92, 108, 128 +}; + +ALIGN static const uint16_t swb_offset_1024_16[] = +{ + 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 100, 112, 124, + 136, 148, 160, 172, 184, 196, 212, 228, 244, 260, 280, 300, 320, 344, + 368, 396, 424, 456, 492, 532, 572, 616, 664, 716, 772, 832, 896, 960, 1024 +}; + +ALIGN static const uint16_t swb_offset_128_16[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 40, 48, 60, 72, 88, 108, 128 +}; + +ALIGN static const uint16_t swb_offset_1024_8[] = +{ + 0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 172, + 188, 204, 220, 236, 252, 268, 288, 308, 328, 348, 372, 396, 420, 448, + 476, 508, 544, 580, 620, 664, 712, 764, 820, 880, 944, 1024 +}; + +ALIGN static const uint16_t swb_offset_128_8[] = +{ + 0, 4, 8, 12, 16, 20, 24, 28, 36, 44, 52, 60, 72, 88, 108, 128 +}; + +ALIGN static const uint16_t *swb_offset_1024_window[] = +{ + swb_offset_1024_96, /* 96000 */ + swb_offset_1024_96, /* 88200 */ + swb_offset_1024_64, /* 64000 */ + swb_offset_1024_48, /* 48000 */ + swb_offset_1024_48, /* 44100 */ + swb_offset_1024_32, /* 32000 */ + swb_offset_1024_24, /* 24000 */ + swb_offset_1024_24, /* 22050 */ + swb_offset_1024_16, /* 16000 */ + swb_offset_1024_16, /* 12000 */ + swb_offset_1024_16, /* 11025 */ + swb_offset_1024_8 /* 8000 */ +}; + +#ifdef LD_DEC +ALIGN static const uint16_t *swb_offset_512_window[] = +{ + 0, /* 96000 */ + 0, /* 88200 */ + 0, /* 64000 */ + swb_offset_512_48, /* 48000 */ + swb_offset_512_48, /* 44100 */ + swb_offset_512_32, /* 32000 */ + swb_offset_512_24, /* 24000 */ + swb_offset_512_24, /* 22050 */ + 0, /* 16000 */ + 0, /* 12000 */ + 0, /* 11025 */ + 0 /* 8000 */ +}; + +ALIGN static const uint16_t *swb_offset_480_window[] = +{ + 0, /* 96000 */ + 0, /* 88200 */ + 0, /* 64000 */ + swb_offset_480_48, /* 48000 */ + swb_offset_480_48, /* 44100 */ + swb_offset_480_32, /* 32000 */ + swb_offset_480_24, /* 24000 */ + swb_offset_480_24, /* 22050 */ + 0, /* 16000 */ + 0, /* 12000 */ + 0, /* 11025 */ + 0 /* 8000 */ +}; +#endif + +ALIGN static const uint16_t *swb_offset_128_window[] = +{ + swb_offset_128_96, /* 96000 */ + swb_offset_128_96, /* 88200 */ + swb_offset_128_64, /* 64000 */ + swb_offset_128_48, /* 48000 */ + swb_offset_128_48, /* 44100 */ + swb_offset_128_48, /* 32000 */ + swb_offset_128_24, /* 24000 */ + swb_offset_128_24, /* 22050 */ + swb_offset_128_16, /* 16000 */ + swb_offset_128_16, /* 12000 */ + swb_offset_128_16, /* 11025 */ + swb_offset_128_8 /* 8000 */ +}; + +#define bit_set(A, B) ((A) & (1<<(B))) + +/* 4.5.2.3.4 */ +/* + - determine the number of windows in a window_sequence named num_windows + - determine the number of window_groups named num_window_groups + - determine the number of windows in each group named window_group_length[g] + - determine the total number of scalefactor window bands named num_swb for + the actual window type + - determine swb_offset[swb], the offset of the first coefficient in + scalefactor window band named swb of the window actually used + - determine sect_sfb_offset[g][section],the offset of the first coefficient + in section named section. This offset depends on window_sequence and + scale_factor_grouping and is needed to decode the spectral_data(). +*/ +uint8_t window_grouping_info(NeAACDecStruct *hDecoder, ic_stream *ics) +{ + uint8_t i, g; + + uint8_t sf_index = hDecoder->sf_index; + + if (sf_index >= 12) + return 32; + + switch (ics->window_sequence) { + case ONLY_LONG_SEQUENCE: + case LONG_START_SEQUENCE: + case LONG_STOP_SEQUENCE: + ics->num_windows = 1; + ics->num_window_groups = 1; + ics->window_group_length[ics->num_window_groups-1] = 1; +#ifdef LD_DEC + if (hDecoder->object_type == LD) + { + if (hDecoder->frameLength == 512) + ics->num_swb = num_swb_512_window[sf_index]; + else /* if (hDecoder->frameLength == 480) */ + ics->num_swb = num_swb_480_window[sf_index]; + } else { +#endif + if (hDecoder->frameLength == 1024) + ics->num_swb = num_swb_1024_window[sf_index]; + else /* if (hDecoder->frameLength == 960) */ + ics->num_swb = num_swb_960_window[sf_index]; +#ifdef LD_DEC + } +#endif + + if (ics->max_sfb > ics->num_swb) + { + return 32; + } + + /* preparation of sect_sfb_offset for long blocks */ + /* also copy the last value! */ +#ifdef LD_DEC + if (hDecoder->object_type == LD) + { + if (hDecoder->frameLength == 512) + { + for (i = 0; i < ics->num_swb; i++) + { + ics->sect_sfb_offset[0][i] = swb_offset_512_window[sf_index][i]; + ics->swb_offset[i] = swb_offset_512_window[sf_index][i]; + } + } else /* if (hDecoder->frameLength == 480) */ { + for (i = 0; i < ics->num_swb; i++) + { + ics->sect_sfb_offset[0][i] = swb_offset_480_window[sf_index][i]; + ics->swb_offset[i] = swb_offset_480_window[sf_index][i]; + } + } + ics->sect_sfb_offset[0][ics->num_swb] = hDecoder->frameLength; + ics->swb_offset[ics->num_swb] = hDecoder->frameLength; + ics->swb_offset_max = hDecoder->frameLength; + } else { +#endif + for (i = 0; i < ics->num_swb; i++) + { + ics->sect_sfb_offset[0][i] = swb_offset_1024_window[sf_index][i]; + ics->swb_offset[i] = swb_offset_1024_window[sf_index][i]; + } + ics->sect_sfb_offset[0][ics->num_swb] = hDecoder->frameLength; + ics->swb_offset[ics->num_swb] = hDecoder->frameLength; + ics->swb_offset_max = hDecoder->frameLength; +#ifdef LD_DEC + } +#endif + return 0; + case EIGHT_SHORT_SEQUENCE: + ics->num_windows = 8; + ics->num_window_groups = 1; + ics->window_group_length[ics->num_window_groups-1] = 1; + ics->num_swb = num_swb_128_window[sf_index]; + + if (ics->max_sfb > ics->num_swb) + { + return 32; + } + + for (i = 0; i < ics->num_swb; i++) + ics->swb_offset[i] = swb_offset_128_window[sf_index][i]; + ics->swb_offset[ics->num_swb] = hDecoder->frameLength/8; + ics->swb_offset_max = hDecoder->frameLength/8; + + for (i = 0; i < ics->num_windows-1; i++) { + if (bit_set(ics->scale_factor_grouping, 6-i) == 0) + { + ics->num_window_groups += 1; + ics->window_group_length[ics->num_window_groups-1] = 1; + } else { + ics->window_group_length[ics->num_window_groups-1] += 1; + } + } + + /* preparation of sect_sfb_offset for short blocks */ + for (g = 0; g < ics->num_window_groups; g++) + { + uint16_t width; + uint8_t sect_sfb = 0; + uint16_t offset = 0; + + for (i = 0; i < ics->num_swb; i++) + { + if (i+1 == ics->num_swb) + { + width = (hDecoder->frameLength/8) - swb_offset_128_window[sf_index][i]; + } else { + width = swb_offset_128_window[sf_index][i+1] - + swb_offset_128_window[sf_index][i]; + } + width *= ics->window_group_length[g]; + ics->sect_sfb_offset[g][sect_sfb++] = offset; + offset += width; + } + ics->sect_sfb_offset[g][sect_sfb] = offset; + } + return 0; + default: + return 32; + } +} + +/* iquant() * output = sign(input)*abs(input)^(4/3) */ +static INLINE real_t iquant(int16_t q, const real_t *tab, uint8_t *error) +{ +#ifdef FIXED_POINT +/* For FIXED_POINT the iq_table is prescaled by 3 bits (iq_table[]/8) */ +/* BIG_IQ_TABLE allows you to use the full 8192 value table, if this is not + * defined a 1026 value table and interpolation will be used + */ +#ifndef BIG_IQ_TABLE + static const real_t errcorr[] = { + REAL_CONST(0), REAL_CONST(1.0/8.0), REAL_CONST(2.0/8.0), REAL_CONST(3.0/8.0), + REAL_CONST(4.0/8.0), REAL_CONST(5.0/8.0), REAL_CONST(6.0/8.0), REAL_CONST(7.0/8.0), + REAL_CONST(0) + }; + real_t x1, x2; +#endif + int16_t sgn = 1; + + if (q < 0) + { + q = -q; + sgn = -1; + } + + if (q < IQ_TABLE_SIZE) + { +//#define IQUANT_PRINT +#ifdef IQUANT_PRINT + //printf("0x%.8X\n", sgn * tab[q]); + printf("%d\n", sgn * tab[q]); +#endif + return sgn * tab[q]; + } + +#ifndef BIG_IQ_TABLE + if (q >= 8192) + { + *error = 17; + return 0; + } + + /* linear interpolation */ + x1 = tab[q>>3]; + x2 = tab[(q>>3) + 1]; + return sgn * 16 * (MUL_R(errcorr[q&7],(x2-x1)) + x1); +#else + *error = 17; + return 0; +#endif + +#else + if (q < 0) + { + /* tab contains a value for all possible q [0,8192] */ + if (-q < IQ_TABLE_SIZE) + return -tab[-q]; + + *error = 17; + return 0; + } else { + /* tab contains a value for all possible q [0,8192] */ + if (q < IQ_TABLE_SIZE) + return tab[q]; + + *error = 17; + return 0; + } +#endif +} + +#ifndef FIXED_POINT +ALIGN static const real_t pow2sf_tab[] = { + 2.9802322387695313E-008, 5.9604644775390625E-008, 1.1920928955078125E-007, + 2.384185791015625E-007, 4.76837158203125E-007, 9.5367431640625E-007, + 1.9073486328125E-006, 3.814697265625E-006, 7.62939453125E-006, + 1.52587890625E-005, 3.0517578125E-005, 6.103515625E-005, + 0.0001220703125, 0.000244140625, 0.00048828125, + 0.0009765625, 0.001953125, 0.00390625, + 0.0078125, 0.015625, 0.03125, + 0.0625, 0.125, 0.25, + 0.5, 1.0, 2.0, + 4.0, 8.0, 16.0, 32.0, + 64.0, 128.0, 256.0, + 512.0, 1024.0, 2048.0, + 4096.0, 8192.0, 16384.0, + 32768.0, 65536.0, 131072.0, + 262144.0, 524288.0, 1048576.0, + 2097152.0, 4194304.0, 8388608.0, + 16777216.0, 33554432.0, 67108864.0, + 134217728.0, 268435456.0, 536870912.0, + 1073741824.0, 2147483648.0, 4294967296.0, + 8589934592.0, 17179869184.0, 34359738368.0, + 68719476736.0, 137438953472.0, 274877906944.0 +}; +#endif + +/* quant_to_spec: perform dequantisation and scaling + * and in case of short block it also does the deinterleaving + */ +/* + For ONLY_LONG_SEQUENCE windows (num_window_groups = 1, + window_group_length[0] = 1) the spectral data is in ascending spectral + order. + For the EIGHT_SHORT_SEQUENCE window, the spectral order depends on the + grouping in the following manner: + - Groups are ordered sequentially + - Within a group, a scalefactor band consists of the spectral data of all + grouped SHORT_WINDOWs for the associated scalefactor window band. To + clarify via example, the length of a group is in the range of one to eight + SHORT_WINDOWs. + - If there are eight groups each with length one (num_window_groups = 8, + window_group_length[0..7] = 1), the result is a sequence of eight spectra, + each in ascending spectral order. + - If there is only one group with length eight (num_window_groups = 1, + window_group_length[0] = 8), the result is that spectral data of all eight + SHORT_WINDOWs is interleaved by scalefactor window bands. + - Within a scalefactor window band, the coefficients are in ascending + spectral order. +*/ +static uint8_t quant_to_spec(NeAACDecStruct *hDecoder, + ic_stream *ics, int16_t *quant_data, + real_t *spec_data, uint16_t frame_len) +{ + ALIGN static const real_t pow2_table[] = + { + COEF_CONST(1.0), + COEF_CONST(1.1892071150027210667174999705605), /* 2^0.25 */ + COEF_CONST(1.4142135623730950488016887242097), /* 2^0.5 */ + COEF_CONST(1.6817928305074290860622509524664) /* 2^0.75 */ + }; + const real_t *tab = iq_table; + + uint8_t g, sfb, win; + uint16_t width, bin, k, gindex; + uint8_t error = 0; /* Init error flag */ +#ifndef FIXED_POINT + real_t scf; +#else + int32_t sat_shift_mask = 0; +#endif + + k = 0; + gindex = 0; + + /* In this case quant_to_spec is no-op and spec_data remains undefined. + * Without peeking into AAC specification, there is no strong evidence if + * such streams are invalid -> just calm down MSAN. */ + if (ics->num_swb == 0) + memset(spec_data, 0, frame_len * sizeof(real_t)); + + for (g = 0; g < ics->num_window_groups; g++) + { + uint16_t j = 0; + uint16_t gincrease = 0; + uint16_t win_inc = ics->swb_offset[ics->num_swb]; + + for (sfb = 0; sfb < ics->num_swb; sfb++) + { + int32_t exp, frac; + uint16_t wa = gindex + j; + int16_t scale_factor = ics->scale_factors[g][sfb]; + + width = ics->swb_offset[sfb+1] - ics->swb_offset[sfb]; + +#ifdef FIXED_POINT + scale_factor -= 100; + /* IMDCT pre-scaling */ + if (hDecoder->object_type == LD) + { + scale_factor -= 24 /*9*/; + } else { + if (ics->window_sequence == EIGHT_SHORT_SEQUENCE) + scale_factor -= 16 /*7*/; + else + scale_factor -= 28 /*10*/; + } + if (scale_factor > 120) + scale_factor = 120; /* => exp <= 30 */ +#else + (void)hDecoder; +#endif + + /* scale_factor for IS or PNS, has different meaning; fill with almost zeroes */ + if (is_intensity(ics, g, sfb) || is_noise(ics, g, sfb)) + { + scale_factor = 0; + } + + /* scale_factor must be between 0 and 255 */ + exp = (scale_factor /* - 100 */) >> 2; + /* frac must always be > 0 */ + frac = (scale_factor /* - 100 */) & 3; + +#ifndef FIXED_POINT + scf = pow2sf_tab[exp/*+25*/] * pow2_table[frac]; +#else + if (exp > 0) + sat_shift_mask = SAT_SHIFT_MASK(exp); +#endif + + for (win = 0; win < ics->window_group_length[g]; win++) + { + for (bin = 0; bin < width; bin += 4) + { + uint16_t wb = wa + bin; +#ifndef FIXED_POINT + spec_data[wb+0] = iquant(quant_data[k+0], tab, &error) * scf; + spec_data[wb+1] = iquant(quant_data[k+1], tab, &error) * scf; + spec_data[wb+2] = iquant(quant_data[k+2], tab, &error) * scf; + spec_data[wb+3] = iquant(quant_data[k+3], tab, &error) * scf; +#else + real_t iq0 = iquant(quant_data[k+0], tab, &error); + real_t iq1 = iquant(quant_data[k+1], tab, &error); + real_t iq2 = iquant(quant_data[k+2], tab, &error); + real_t iq3 = iquant(quant_data[k+3], tab, &error); + + if (exp == -32) + { + spec_data[wb+0] = 0; + spec_data[wb+1] = 0; + spec_data[wb+2] = 0; + spec_data[wb+3] = 0; + } else if (exp <= 0) { + spec_data[wb+0] = iq0 >> -exp; + spec_data[wb+1] = iq1 >> -exp; + spec_data[wb+2] = iq2 >> -exp; + spec_data[wb+3] = iq3 >> -exp; + } else { /* exp > 0 */ + spec_data[wb+0] = SAT_SHIFT(iq0, exp, sat_shift_mask); + spec_data[wb+1] = SAT_SHIFT(iq1, exp, sat_shift_mask); + spec_data[wb+2] = SAT_SHIFT(iq2, exp, sat_shift_mask); + spec_data[wb+3] = SAT_SHIFT(iq3, exp, sat_shift_mask); + } + if (frac != 0) + { + spec_data[wb+0] = MUL_C(spec_data[wb+0],pow2_table[frac]); + spec_data[wb+1] = MUL_C(spec_data[wb+1],pow2_table[frac]); + spec_data[wb+2] = MUL_C(spec_data[wb+2],pow2_table[frac]); + spec_data[wb+3] = MUL_C(spec_data[wb+3],pow2_table[frac]); + } + +//#define SCFS_PRINT +#ifdef SCFS_PRINT + printf("%d\n", spec_data[gindex+(win*win_inc)+j+bin+0]); + printf("%d\n", spec_data[gindex+(win*win_inc)+j+bin+1]); + printf("%d\n", spec_data[gindex+(win*win_inc)+j+bin+2]); + printf("%d\n", spec_data[gindex+(win*win_inc)+j+bin+3]); + //printf("0x%.8X\n", spec_data[gindex+(win*win_inc)+j+bin+0]); + //printf("0x%.8X\n", spec_data[gindex+(win*win_inc)+j+bin+1]); + //printf("0x%.8X\n", spec_data[gindex+(win*win_inc)+j+bin+2]); + //printf("0x%.8X\n", spec_data[gindex+(win*win_inc)+j+bin+3]); +#endif +#endif + + gincrease += 4; + k += 4; + } + wa += win_inc; + } + j += width; + } + gindex += gincrease; + } + + return error; +} + +static uint8_t allocate_single_channel(NeAACDecStruct *hDecoder, uint8_t channel, + uint8_t output_channels) +{ + int mul = 1; + +#ifdef MAIN_DEC + /* MAIN object type prediction */ + if (hDecoder->object_type == MAIN) + { + /* allocate the state only when needed */ + if (hDecoder->pred_stat[channel] != NULL) + { + faad_free(hDecoder->pred_stat[channel]); + hDecoder->pred_stat[channel] = NULL; + } + + hDecoder->pred_stat[channel] = (pred_state*)faad_malloc(hDecoder->frameLength * sizeof(pred_state)); + reset_all_predictors(hDecoder->pred_stat[channel], hDecoder->frameLength); + } +#endif + +#ifdef LTP_DEC + if (is_ltp_ot(hDecoder->object_type)) + { + /* allocate the state only when needed */ + if (hDecoder->lt_pred_stat[channel] != NULL) + { + faad_free(hDecoder->lt_pred_stat[channel]); + hDecoder->lt_pred_stat[channel] = NULL; + } + + hDecoder->lt_pred_stat[channel] = (int16_t*)faad_malloc(hDecoder->frameLength*4 * sizeof(int16_t)); + memset(hDecoder->lt_pred_stat[channel], 0, hDecoder->frameLength*4 * sizeof(int16_t)); + } +#endif + + if (hDecoder->time_out[channel] != NULL) + { + faad_free(hDecoder->time_out[channel]); + hDecoder->time_out[channel] = NULL; + } + + { + mul = 1; +#ifdef SBR_DEC + hDecoder->sbr_alloced[hDecoder->fr_ch_ele] = 0; + if ((hDecoder->sbr_present_flag == 1) || (hDecoder->forceUpSampling == 1)) + { + /* SBR requires 2 times as much output data */ + mul = 2; + hDecoder->sbr_alloced[hDecoder->fr_ch_ele] = 1; + } +#endif + hDecoder->time_out[channel] = (real_t*)faad_malloc(mul*hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->time_out[channel], 0, mul*hDecoder->frameLength*sizeof(real_t)); + } + +#if (defined(PS_DEC) || defined(DRM_PS)) + if (output_channels == 2) + { + if (hDecoder->time_out[channel+1] != NULL) + { + faad_free(hDecoder->time_out[channel+1]); + hDecoder->time_out[channel+1] = NULL; + } + + hDecoder->time_out[channel+1] = (real_t*)faad_malloc(mul*hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->time_out[channel+1], 0, mul*hDecoder->frameLength*sizeof(real_t)); + } +#endif + + if (hDecoder->fb_intermed[channel] != NULL) + { + faad_free(hDecoder->fb_intermed[channel]); + hDecoder->fb_intermed[channel] = NULL; + } + + hDecoder->fb_intermed[channel] = (real_t*)faad_malloc(hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->fb_intermed[channel], 0, hDecoder->frameLength*sizeof(real_t)); + +#ifdef SSR_DEC + if (hDecoder->object_type == SSR) + { + if (hDecoder->ssr_overlap[channel] == NULL) + { + hDecoder->ssr_overlap[channel] = (real_t*)faad_malloc(2*hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->ssr_overlap[channel], 0, 2*hDecoder->frameLength*sizeof(real_t)); + } + if (hDecoder->prev_fmd[channel] == NULL) + { + uint16_t k; + hDecoder->prev_fmd[channel] = (real_t*)faad_malloc(2*hDecoder->frameLength*sizeof(real_t)); + for (k = 0; k < 2*hDecoder->frameLength; k++) + hDecoder->prev_fmd[channel][k] = REAL_CONST(-1); + } + } +#endif + + return 0; +} + +static uint8_t allocate_channel_pair(NeAACDecStruct *hDecoder, + uint8_t channel, uint8_t paired_channel) +{ + int mul = 1; + +#ifdef MAIN_DEC + /* MAIN object type prediction */ + if (hDecoder->object_type == MAIN) + { + /* allocate the state only when needed */ + if (hDecoder->pred_stat[channel] == NULL) + { + hDecoder->pred_stat[channel] = (pred_state*)faad_malloc(hDecoder->frameLength * sizeof(pred_state)); + reset_all_predictors(hDecoder->pred_stat[channel], hDecoder->frameLength); + } + if (hDecoder->pred_stat[paired_channel] == NULL) + { + hDecoder->pred_stat[paired_channel] = (pred_state*)faad_malloc(hDecoder->frameLength * sizeof(pred_state)); + reset_all_predictors(hDecoder->pred_stat[paired_channel], hDecoder->frameLength); + } + } +#endif + +#ifdef LTP_DEC + if (is_ltp_ot(hDecoder->object_type)) + { + /* allocate the state only when needed */ + if (hDecoder->lt_pred_stat[channel] == NULL) + { + hDecoder->lt_pred_stat[channel] = (int16_t*)faad_malloc(hDecoder->frameLength*4 * sizeof(int16_t)); + memset(hDecoder->lt_pred_stat[channel], 0, hDecoder->frameLength*4 * sizeof(int16_t)); + } + if (hDecoder->lt_pred_stat[paired_channel] == NULL) + { + hDecoder->lt_pred_stat[paired_channel] = (int16_t*)faad_malloc(hDecoder->frameLength*4 * sizeof(int16_t)); + memset(hDecoder->lt_pred_stat[paired_channel], 0, hDecoder->frameLength*4 * sizeof(int16_t)); + } + } +#endif + + { + mul = 1; +#ifdef SBR_DEC + hDecoder->sbr_alloced[hDecoder->fr_ch_ele] = 0; + if ((hDecoder->sbr_present_flag == 1) || (hDecoder->forceUpSampling == 1)) + { + /* SBR requires 2 times as much output data */ + mul = 2; + hDecoder->sbr_alloced[hDecoder->fr_ch_ele] = 1; + } +#endif + } + if (hDecoder->time_out[channel] == NULL) + { + hDecoder->time_out[channel] = (real_t*)faad_malloc(mul*hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->time_out[channel], 0, mul*hDecoder->frameLength*sizeof(real_t)); + } + if (hDecoder->time_out[paired_channel] == NULL) + { + hDecoder->time_out[paired_channel] = (real_t*)faad_malloc(mul*hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->time_out[paired_channel], 0, mul*hDecoder->frameLength*sizeof(real_t)); + } + + if (hDecoder->fb_intermed[channel] == NULL) + { + hDecoder->fb_intermed[channel] = (real_t*)faad_malloc(hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->fb_intermed[channel], 0, hDecoder->frameLength*sizeof(real_t)); + } + if (hDecoder->fb_intermed[paired_channel] == NULL) + { + hDecoder->fb_intermed[paired_channel] = (real_t*)faad_malloc(hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->fb_intermed[paired_channel], 0, hDecoder->frameLength*sizeof(real_t)); + } + +#ifdef SSR_DEC + if (hDecoder->object_type == SSR) + { + if (hDecoder->ssr_overlap[cpe->channel] == NULL) + { + hDecoder->ssr_overlap[cpe->channel] = (real_t*)faad_malloc(2*hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->ssr_overlap[cpe->channel], 0, 2*hDecoder->frameLength*sizeof(real_t)); + } + if (hDecoder->ssr_overlap[cpe->paired_channel] == NULL) + { + hDecoder->ssr_overlap[cpe->paired_channel] = (real_t*)faad_malloc(2*hDecoder->frameLength*sizeof(real_t)); + memset(hDecoder->ssr_overlap[cpe->paired_channel], 0, 2*hDecoder->frameLength*sizeof(real_t)); + } + if (hDecoder->prev_fmd[cpe->channel] == NULL) + { + uint16_t k; + hDecoder->prev_fmd[cpe->channel] = (real_t*)faad_malloc(2*hDecoder->frameLength*sizeof(real_t)); + for (k = 0; k < 2*hDecoder->frameLength; k++) + hDecoder->prev_fmd[cpe->channel][k] = REAL_CONST(-1); + } + if (hDecoder->prev_fmd[cpe->paired_channel] == NULL) + { + uint16_t k; + hDecoder->prev_fmd[cpe->paired_channel] = (real_t*)faad_malloc(2*hDecoder->frameLength*sizeof(real_t)); + for (k = 0; k < 2*hDecoder->frameLength; k++) + hDecoder->prev_fmd[cpe->paired_channel][k] = REAL_CONST(-1); + } + } +#endif + + return 0; +} + +uint8_t reconstruct_single_channel(NeAACDecStruct *hDecoder, ic_stream *ics, + element *sce, int16_t *spec_data) +{ + uint8_t retval; + uint8_t output_channels; + ALIGN real_t spec_coef[1024]; + +#ifdef PROFILE + int64_t count = faad_get_ts(); +#endif + + + /* always allocate 2 channels, PS can always "suddenly" turn up */ +#if ( (defined(DRM) && defined(DRM_PS)) ) + output_channels = 2; +#elif defined(PS_DEC) + if (hDecoder->ps_used[hDecoder->fr_ch_ele]) + output_channels = 2; + else + output_channels = 1; +#else + output_channels = 1; +#endif + + if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 0) + { + /* element_output_channels not set yet */ + hDecoder->element_output_channels[hDecoder->fr_ch_ele] = output_channels; + } else if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] != output_channels) { + /* element inconsistency */ + + /* this only happens if PS is actually found but not in the first frame + * this means that there is only 1 bitstream element! + */ + + /* The simplest way to fix the accounting, + * is to reallocate this and all the following channels. + */ + memset(&hDecoder->element_alloced[hDecoder->fr_ch_ele], 0, + sizeof(uint8_t) * (MAX_SYNTAX_ELEMENTS - hDecoder->fr_ch_ele)); + + hDecoder->element_output_channels[hDecoder->fr_ch_ele] = output_channels; + + //return 21; + } + + if (hDecoder->element_alloced[hDecoder->fr_ch_ele] == 0) + { + retval = allocate_single_channel(hDecoder, sce->channel, output_channels); + if (retval > 0) + return retval; + + hDecoder->element_alloced[hDecoder->fr_ch_ele] = 1; + } + + /* sanity check, CVE-2018-20199, CVE-2018-20360 */ + if(!hDecoder->time_out[sce->channel]) + return 15; + if(output_channels > 1 && !hDecoder->time_out[sce->channel+1]) + return 15; + if(!hDecoder->fb_intermed[sce->channel]) + return 15; + + /* dequantisation and scaling */ + retval = quant_to_spec(hDecoder, ics, spec_data, spec_coef, hDecoder->frameLength); + if (retval > 0) + return retval; + +#ifdef PROFILE + count = faad_get_ts() - count; + hDecoder->requant_cycles += count; +#endif + + + /* pns decoding */ + pns_decode(ics, NULL, spec_coef, NULL, hDecoder->frameLength, 0, hDecoder->object_type, + &(hDecoder->__r1), &(hDecoder->__r2)); + +#ifdef MAIN_DEC + /* MAIN object type prediction */ + if (hDecoder->object_type == MAIN) + { + if (!hDecoder->pred_stat[sce->channel]) + return 33; + + /* intra channel prediction */ + ic_prediction(ics, spec_coef, hDecoder->pred_stat[sce->channel], hDecoder->frameLength, + hDecoder->sf_index); + + /* In addition, for scalefactor bands coded by perceptual + noise substitution the predictors belonging to the + corresponding spectral coefficients are reset. + */ + pns_reset_pred_state(ics, hDecoder->pred_stat[sce->channel]); + } +#endif + +#ifdef LTP_DEC + if (is_ltp_ot(hDecoder->object_type)) + { +#ifdef LD_DEC + if (hDecoder->object_type == LD) + { + if (ics->ltp.data_present) + { + if (ics->ltp.lag_update) + hDecoder->ltp_lag[sce->channel] = ics->ltp.lag; + } + ics->ltp.lag = hDecoder->ltp_lag[sce->channel]; + } +#endif + + /* long term prediction */ + lt_prediction(ics, &(ics->ltp), spec_coef, hDecoder->lt_pred_stat[sce->channel], hDecoder->fb, + ics->window_shape, hDecoder->window_shape_prev[sce->channel], + hDecoder->sf_index, hDecoder->object_type, hDecoder->frameLength); + } +#endif + + /* tns decoding */ + tns_decode_frame(ics, &(ics->tns), hDecoder->sf_index, hDecoder->object_type, + spec_coef, hDecoder->frameLength); + + /* drc decoding */ +#ifdef APPLY_DRC + if (hDecoder->drc->present) + { + if (!hDecoder->drc->exclude_mask[sce->channel] || !hDecoder->drc->excluded_chns_present) + drc_decode(hDecoder->drc, spec_coef); + } +#endif + /* filter bank */ +#ifdef SSR_DEC + if (hDecoder->object_type != SSR) + { +#endif + ifilter_bank(hDecoder->fb, ics->window_sequence, ics->window_shape, + hDecoder->window_shape_prev[sce->channel], spec_coef, + hDecoder->time_out[sce->channel], hDecoder->fb_intermed[sce->channel], + hDecoder->object_type, hDecoder->frameLength); +#ifdef SSR_DEC + } else { + ssr_decode(&(ics->ssr), hDecoder->fb, ics->window_sequence, ics->window_shape, + hDecoder->window_shape_prev[sce->channel], spec_coef, hDecoder->time_out[sce->channel], + hDecoder->ssr_overlap[sce->channel], hDecoder->ipqf_buffer[sce->channel], hDecoder->prev_fmd[sce->channel], + hDecoder->frameLength); + } +#endif + + /* save window shape for next frame */ + hDecoder->window_shape_prev[sce->channel] = ics->window_shape; + +#ifdef LTP_DEC + if (is_ltp_ot(hDecoder->object_type)) + { + lt_update_state(hDecoder->lt_pred_stat[sce->channel], hDecoder->time_out[sce->channel], + hDecoder->fb_intermed[sce->channel], hDecoder->frameLength, hDecoder->object_type); + } +#endif + +#ifdef SBR_DEC + if (((hDecoder->sbr_present_flag == 1) || (hDecoder->forceUpSampling == 1)) + && hDecoder->sbr_alloced[hDecoder->fr_ch_ele]) + { + int ele = hDecoder->fr_ch_ele; + int ch = sce->channel; + + /* following case can happen when forceUpSampling == 1 */ + if (hDecoder->sbr[ele] == NULL) + { + hDecoder->sbr[ele] = sbrDecodeInit(hDecoder->frameLength, + hDecoder->element_id[ele], 2*get_sample_rate(hDecoder->sf_index), + hDecoder->downSampledSBR +#ifdef DRM + , 0 +#endif + ); + } + if (!hDecoder->sbr[ele]) + return 19; + + if (sce->ics1.window_sequence == EIGHT_SHORT_SEQUENCE) + hDecoder->sbr[ele]->maxAACLine = 8*min(sce->ics1.swb_offset[max(sce->ics1.max_sfb-1, 0)], sce->ics1.swb_offset_max); + else + hDecoder->sbr[ele]->maxAACLine = min(sce->ics1.swb_offset[max(sce->ics1.max_sfb-1, 0)], sce->ics1.swb_offset_max); + + /* check if any of the PS tools is used */ +#if (defined(PS_DEC) || defined(DRM_PS)) + if (hDecoder->ps_used[ele] == 0) + { +#endif + retval = sbrDecodeSingleFrame(hDecoder->sbr[ele], hDecoder->time_out[ch], + hDecoder->postSeekResetFlag, hDecoder->downSampledSBR); +#if (defined(PS_DEC) || defined(DRM_PS)) + } else { + retval = sbrDecodeSingleFramePS(hDecoder->sbr[ele], hDecoder->time_out[ch], + hDecoder->time_out[ch+1], hDecoder->postSeekResetFlag, + hDecoder->downSampledSBR); + } +#endif + if (retval > 0) + return retval; + } else if (((hDecoder->sbr_present_flag == 1) || (hDecoder->forceUpSampling == 1)) + && !hDecoder->sbr_alloced[hDecoder->fr_ch_ele]) + { + return 23; + } +#endif + + /* copy L to R when no PS is used */ +#if (defined(PS_DEC) || defined(DRM_PS)) + if ((hDecoder->ps_used[hDecoder->fr_ch_ele] == 0) && + (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 2)) + { + int ele = hDecoder->fr_ch_ele; + int ch = sce->channel; + int frame_size = (hDecoder->sbr_alloced[ele]) ? 2 : 1; + frame_size *= hDecoder->frameLength*sizeof(real_t); + + memcpy(hDecoder->time_out[ch+1], hDecoder->time_out[ch], frame_size); + } +#endif + + return 0; +} + +uint8_t reconstruct_channel_pair(NeAACDecStruct *hDecoder, ic_stream *ics1, ic_stream *ics2, + element *cpe, int16_t *spec_data1, int16_t *spec_data2) +{ + uint8_t retval; + ALIGN real_t spec_coef1[1024]; + ALIGN real_t spec_coef2[1024]; + +#ifdef PROFILE + int64_t count = faad_get_ts(); +#endif + if (hDecoder->element_alloced[hDecoder->fr_ch_ele] != 2) + { + retval = allocate_channel_pair(hDecoder, cpe->channel, (uint8_t)cpe->paired_channel); + if (retval > 0) + return retval; + + hDecoder->element_alloced[hDecoder->fr_ch_ele] = 2; + } + + /* sanity check, CVE-2018-20199, CVE-2018-20360 */ + if(!hDecoder->time_out[cpe->channel] || !hDecoder->time_out[cpe->paired_channel]) + return 15; + if(!hDecoder->fb_intermed[cpe->channel] || !hDecoder->fb_intermed[cpe->paired_channel]) + return 15; + + /* dequantisation and scaling */ + retval = quant_to_spec(hDecoder, ics1, spec_data1, spec_coef1, hDecoder->frameLength); + if (retval > 0) + return retval; + retval = quant_to_spec(hDecoder, ics2, spec_data2, spec_coef2, hDecoder->frameLength); + if (retval > 0) + return retval; + +#ifdef PROFILE + count = faad_get_ts() - count; + hDecoder->requant_cycles += count; +#endif + + /* pns decoding */ + if (ics1->ms_mask_present) + { + pns_decode(ics1, ics2, spec_coef1, spec_coef2, hDecoder->frameLength, 1, hDecoder->object_type, + &(hDecoder->__r1), &(hDecoder->__r2)); + } else { + pns_decode(ics1, ics2, spec_coef1, spec_coef2, hDecoder->frameLength, 0, hDecoder->object_type, + &(hDecoder->__r1), &(hDecoder->__r2)); + } + + /* mid/side decoding */ + ms_decode(ics1, ics2, spec_coef1, spec_coef2, hDecoder->frameLength); + +#if 0 + { + int i; + for (i = 0; i < 1024; i++) + { + //printf("%d\n", spec_coef1[i]); + printf("0x%.8X\n", spec_coef1[i]); + } + for (i = 0; i < 1024; i++) + { + //printf("%d\n", spec_coef2[i]); + printf("0x%.8X\n", spec_coef2[i]); + } + } +#endif + + /* intensity stereo decoding */ + is_decode(ics1, ics2, spec_coef1, spec_coef2, hDecoder->frameLength); + +#if 0 + { + int i; + for (i = 0; i < 1024; i++) + { + printf("%d\n", spec_coef1[i]); + //printf("0x%.8X\n", spec_coef1[i]); + } + for (i = 0; i < 1024; i++) + { + printf("%d\n", spec_coef2[i]); + //printf("0x%.8X\n", spec_coef2[i]); + } + } +#endif + +#ifdef MAIN_DEC + /* MAIN object type prediction */ + if (hDecoder->object_type == MAIN) + { + /* intra channel prediction */ + ic_prediction(ics1, spec_coef1, hDecoder->pred_stat[cpe->channel], hDecoder->frameLength, + hDecoder->sf_index); + ic_prediction(ics2, spec_coef2, hDecoder->pred_stat[cpe->paired_channel], hDecoder->frameLength, + hDecoder->sf_index); + + /* In addition, for scalefactor bands coded by perceptual + noise substitution the predictors belonging to the + corresponding spectral coefficients are reset. + */ + pns_reset_pred_state(ics1, hDecoder->pred_stat[cpe->channel]); + pns_reset_pred_state(ics2, hDecoder->pred_stat[cpe->paired_channel]); + } +#endif + +#ifdef LTP_DEC + if (is_ltp_ot(hDecoder->object_type)) + { + ltp_info *ltp1 = &(ics1->ltp); + ltp_info *ltp2 = (cpe->common_window) ? &(ics2->ltp2) : &(ics2->ltp); +#ifdef LD_DEC + if (hDecoder->object_type == LD) + { + if (ltp1->data_present) + { + if (ltp1->lag_update) + hDecoder->ltp_lag[cpe->channel] = ltp1->lag; + } + ltp1->lag = hDecoder->ltp_lag[cpe->channel]; + if (ltp2->data_present) + { + if (ltp2->lag_update) + hDecoder->ltp_lag[cpe->paired_channel] = ltp2->lag; + } + ltp2->lag = hDecoder->ltp_lag[cpe->paired_channel]; + } +#endif + + /* long term prediction */ + lt_prediction(ics1, ltp1, spec_coef1, hDecoder->lt_pred_stat[cpe->channel], hDecoder->fb, + ics1->window_shape, hDecoder->window_shape_prev[cpe->channel], + hDecoder->sf_index, hDecoder->object_type, hDecoder->frameLength); + lt_prediction(ics2, ltp2, spec_coef2, hDecoder->lt_pred_stat[cpe->paired_channel], hDecoder->fb, + ics2->window_shape, hDecoder->window_shape_prev[cpe->paired_channel], + hDecoder->sf_index, hDecoder->object_type, hDecoder->frameLength); + } +#endif + + /* tns decoding */ + tns_decode_frame(ics1, &(ics1->tns), hDecoder->sf_index, hDecoder->object_type, + spec_coef1, hDecoder->frameLength); + tns_decode_frame(ics2, &(ics2->tns), hDecoder->sf_index, hDecoder->object_type, + spec_coef2, hDecoder->frameLength); + + /* drc decoding */ +#if APPLY_DRC + if (hDecoder->drc->present) + { + if (!hDecoder->drc->exclude_mask[cpe->channel] || !hDecoder->drc->excluded_chns_present) + drc_decode(hDecoder->drc, spec_coef1); + if (!hDecoder->drc->exclude_mask[cpe->paired_channel] || !hDecoder->drc->excluded_chns_present) + drc_decode(hDecoder->drc, spec_coef2); + } +#endif + /* filter bank */ +#ifdef SSR_DEC + if (hDecoder->object_type != SSR) + { +#endif + ifilter_bank(hDecoder->fb, ics1->window_sequence, ics1->window_shape, + hDecoder->window_shape_prev[cpe->channel], spec_coef1, + hDecoder->time_out[cpe->channel], hDecoder->fb_intermed[cpe->channel], + hDecoder->object_type, hDecoder->frameLength); + ifilter_bank(hDecoder->fb, ics2->window_sequence, ics2->window_shape, + hDecoder->window_shape_prev[cpe->paired_channel], spec_coef2, + hDecoder->time_out[cpe->paired_channel], hDecoder->fb_intermed[cpe->paired_channel], + hDecoder->object_type, hDecoder->frameLength); +#ifdef SSR_DEC + } else { + ssr_decode(&(ics1->ssr), hDecoder->fb, ics1->window_sequence, ics1->window_shape, + hDecoder->window_shape_prev[cpe->channel], spec_coef1, hDecoder->time_out[cpe->channel], + hDecoder->ssr_overlap[cpe->channel], hDecoder->ipqf_buffer[cpe->channel], + hDecoder->prev_fmd[cpe->channel], hDecoder->frameLength); + ssr_decode(&(ics2->ssr), hDecoder->fb, ics2->window_sequence, ics2->window_shape, + hDecoder->window_shape_prev[cpe->paired_channel], spec_coef2, hDecoder->time_out[cpe->paired_channel], + hDecoder->ssr_overlap[cpe->paired_channel], hDecoder->ipqf_buffer[cpe->paired_channel], + hDecoder->prev_fmd[cpe->paired_channel], hDecoder->frameLength); + } +#endif + + /* save window shape for next frame */ + hDecoder->window_shape_prev[cpe->channel] = ics1->window_shape; + hDecoder->window_shape_prev[cpe->paired_channel] = ics2->window_shape; + +#ifdef LTP_DEC + if (is_ltp_ot(hDecoder->object_type)) + { + lt_update_state(hDecoder->lt_pred_stat[cpe->channel], hDecoder->time_out[cpe->channel], + hDecoder->fb_intermed[cpe->channel], hDecoder->frameLength, hDecoder->object_type); + lt_update_state(hDecoder->lt_pred_stat[cpe->paired_channel], hDecoder->time_out[cpe->paired_channel], + hDecoder->fb_intermed[cpe->paired_channel], hDecoder->frameLength, hDecoder->object_type); + } +#endif + +#ifdef SBR_DEC + if (((hDecoder->sbr_present_flag == 1) || (hDecoder->forceUpSampling == 1)) + && hDecoder->sbr_alloced[hDecoder->fr_ch_ele]) + { + int ele = hDecoder->fr_ch_ele; + int ch0 = cpe->channel; + int ch1 = cpe->paired_channel; + + /* following case can happen when forceUpSampling == 1 */ + if (hDecoder->sbr[ele] == NULL) + { + hDecoder->sbr[ele] = sbrDecodeInit(hDecoder->frameLength, + hDecoder->element_id[ele], 2*get_sample_rate(hDecoder->sf_index), + hDecoder->downSampledSBR +#ifdef DRM + , 0 +#endif + ); + } + if (!hDecoder->sbr[ele]) + return 19; + + if (cpe->ics1.window_sequence == EIGHT_SHORT_SEQUENCE) + hDecoder->sbr[ele]->maxAACLine = 8*min(cpe->ics1.swb_offset[max(cpe->ics1.max_sfb-1, 0)], cpe->ics1.swb_offset_max); + else + hDecoder->sbr[ele]->maxAACLine = min(cpe->ics1.swb_offset[max(cpe->ics1.max_sfb-1, 0)], cpe->ics1.swb_offset_max); + + retval = sbrDecodeCoupleFrame(hDecoder->sbr[ele], + hDecoder->time_out[ch0], hDecoder->time_out[ch1], + hDecoder->postSeekResetFlag, hDecoder->downSampledSBR); + if (retval > 0) + return retval; + } else if (((hDecoder->sbr_present_flag == 1) || (hDecoder->forceUpSampling == 1)) + && !hDecoder->sbr_alloced[hDecoder->fr_ch_ele]) + { + return 23; + } +#endif + + return 0; +} diff --git a/third_party/faad_hdc/upstream/libfaad/specrec.h b/third_party/faad_hdc/upstream/libfaad/specrec.h new file mode 100644 index 000000000..8464ca931 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/specrec.h @@ -0,0 +1,49 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: specrec.h,v 1.33 2009/01/26 23:51:15 menno Exp $ +**/ + +#ifndef __SPECREC_H__ +#define __SPECREC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "syntax.h" + +uint8_t window_grouping_info(NeAACDecStruct *hDecoder, ic_stream *ics); +uint8_t reconstruct_channel_pair(NeAACDecStruct *hDecoder, ic_stream *ics1, ic_stream *ics2, + element *cpe, int16_t *spec_data1, int16_t *spec_data2); +uint8_t reconstruct_single_channel(NeAACDecStruct *hDecoder, ic_stream *ics, element *sce, + int16_t *spec_data); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ssr.c b/third_party/faad_hdc/upstream/libfaad/ssr.c new file mode 100644 index 000000000..296c42b8c --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ssr.c @@ -0,0 +1,175 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ssr.c,v 1.19 2007/11/01 12:33:36 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#ifdef SSR_DEC + +#include "syntax.h" +#include "filtbank.h" +#include "ssr.h" +#include "ssr_fb.h" + +void ssr_decode(ssr_info *ssr, fb_info *fb, uint8_t window_sequence, + uint8_t window_shape, uint8_t window_shape_prev, + real_t *freq_in, real_t *time_out, real_t *overlap, + real_t ipqf_buffer[SSR_BANDS][96/4], + real_t *prev_fmd, uint16_t frame_len) +{ + uint8_t band; + uint16_t ssr_frame_len = frame_len/SSR_BANDS; + real_t time_tmp[2048] = {0}; + real_t output[1024] = {0}; + + for (band = 0; band < SSR_BANDS; band++) + { + int16_t j; + + /* uneven bands have inverted frequency scale */ + if (band == 1 || band == 3) + { + for (j = 0; j < ssr_frame_len/2; j++) + { + real_t tmp; + tmp = freq_in[j + ssr_frame_len*band]; + freq_in[j + ssr_frame_len*band] = + freq_in[ssr_frame_len - j - 1 + ssr_frame_len*band]; + freq_in[ssr_frame_len - j - 1 + ssr_frame_len*band] = tmp; + } + } + + /* non-overlapping inverse filterbank for SSR */ + ssr_ifilter_bank(fb, window_sequence, window_shape, window_shape_prev, + freq_in + band*ssr_frame_len, time_tmp + band*ssr_frame_len, + ssr_frame_len); + + /* gain control */ + ssr_gain_control(ssr, time_tmp, output, overlap, prev_fmd, + band, window_sequence, ssr_frame_len); + } + + /* inverse pqf to bring subbands together again */ + ssr_ipqf(ssr, output, time_out, ipqf_buffer, frame_len, SSR_BANDS); +} + +static void ssr_gain_control(ssr_info *ssr, real_t *data, real_t *output, + real_t *overlap, real_t *prev_fmd, uint8_t band, + uint8_t window_sequence, uint16_t frame_len) +{ + uint16_t i; + real_t gc_function[2*1024/SSR_BANDS]; + + if (window_sequence != EIGHT_SHORT_SEQUENCE) + { + ssr_gc_function(ssr, &prev_fmd[band * frame_len*2], + gc_function, window_sequence, band, frame_len); + + for (i = 0; i < frame_len*2; i++) + data[band * frame_len*2 + i] *= gc_function[i]; + for (i = 0; i < frame_len; i++) + { + output[band*frame_len + i] = overlap[band*frame_len + i] + + data[band*frame_len*2 + i]; + } + for (i = 0; i < frame_len; i++) + { + overlap[band*frame_len + i] = + data[band*frame_len*2 + frame_len + i]; + } + } else { + uint8_t w; + for (w = 0; w < 8; w++) + { + uint16_t frame_len8 = frame_len/8; + uint16_t frame_len16 = frame_len/16; + + ssr_gc_function(ssr, &prev_fmd[band*frame_len*2 + w*frame_len*2/8], + gc_function, window_sequence, frame_len); + + for (i = 0; i < frame_len8*2; i++) + data[band*frame_len*2 + w*frame_len8*2+i] *= gc_function[i]; + for (i = 0; i < frame_len8; i++) + { + overlap[band*frame_len + i + 7*frame_len16 + w*frame_len8] += + data[band*frame_len*2 + 2*w*frame_len8 + i]; + } + for (i = 0; i < frame_len8; i++) + { + overlap[band*frame_len + i + 7*frame_len16 + (w+1)*frame_len8] = + data[band*frame_len*2 + 2*w*frame_len8 + frame_len8 + i]; + } + } + for (i = 0; i < frame_len; i++) + output[band*frame_len + i] = overlap[band*frame_len + i]; + for (i = 0; i < frame_len; i++) + overlap[band*frame_len + i] = overlap[band*frame_len + i + frame_len]; + } +} + +static void ssr_gc_function(ssr_info *ssr, real_t *prev_fmd, + real_t *gc_function, uint8_t window_sequence, + uint8_t band, uint16_t frame_len) +{ + uint16_t i; + uint16_t len_area1, len_area2; + int32_t aloc[10]; + real_t alev[10]; + + switch (window_sequence) + { + case ONLY_LONG_SEQUENCE: + len_area1 = frame_len/SSR_BANDS; + len_area2 = 0; + break; + case LONG_START_SEQUENCE: + len_area1 = (frame_len/SSR_BANDS)*7/32; + len_area2 = (frame_len/SSR_BANDS)/16; + break; + case EIGHT_SHORT_SEQUENCE: + len_area1 = (frame_len/8)/SSR_BANDS; + len_area2 = 0; + break; + case LONG_STOP_SEQUENCE: + len_area1 = (frame_len/SSR_BANDS); + len_area2 = 0; + break; + } + + /* decode bitstream information */ + + /* build array M */ + + + for (i = 0; i < frame_len*2; i++) + gc_function[i] = 1; +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ssr.h b/third_party/faad_hdc/upstream/libfaad/ssr.h new file mode 100644 index 000000000..0be2aed0d --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ssr.h @@ -0,0 +1,59 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ssr.h,v 1.19 2007/11/01 12:33:36 menno Exp $ +**/ + +#ifndef __SSR_H__ +#define __SSR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SSR_BANDS 4 +#define PQFTAPS 96 + +void ssr_decode(ssr_info *ssr, fb_info *fb, uint8_t window_sequence, + uint8_t window_shape, uint8_t window_shape_prev, + real_t *freq_in, real_t *time_out, real_t *overlap, + real_t ipqf_buffer[SSR_BANDS][96/4], + real_t *prev_fmd, uint16_t frame_len); + + +static void ssr_gain_control(ssr_info *ssr, real_t *data, real_t *output, + real_t *overlap, real_t *prev_fmd, uint8_t band, + uint8_t window_sequence, uint16_t frame_len); +static void ssr_gc_function(ssr_info *ssr, real_t *prev_fmd, + real_t *gc_function, uint8_t window_sequence, + uint16_t frame_len); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ssr_fb.c b/third_party/faad_hdc/upstream/libfaad/ssr_fb.c new file mode 100644 index 000000000..fdcdc75ef --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ssr_fb.c @@ -0,0 +1,184 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ssr_fb.c,v 1.17 2007/11/01 12:33:36 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#ifdef SSR_DEC + +#include +#include "syntax.h" +#include "filtbank.h" +#include "mdct.h" +#include "ssr_fb.h" +#include "ssr_win.h" + +fb_info *ssr_filter_bank_init(uint16_t frame_len) +{ + uint16_t nshort = frame_len/8; + + fb_info *fb = (fb_info*)faad_malloc(sizeof(fb_info)); + memset(fb, 0, sizeof(fb_info)); + + /* normal */ + fb->mdct256 = faad_mdct_init(2*nshort); + fb->mdct2048 = faad_mdct_init(2*frame_len); + + fb->long_window[0] = sine_long_256; + fb->short_window[0] = sine_short_32; + fb->long_window[1] = kbd_long_256; + fb->short_window[1] = kbd_short_32; + + return fb; +} + +void ssr_filter_bank_end(fb_info *fb) +{ + faad_mdct_end(fb->mdct256); + faad_mdct_end(fb->mdct2048); + + if (fb) faad_free(fb); +} + +static INLINE void imdct_ssr(fb_info *fb, real_t *in_data, + real_t *out_data, uint16_t len) +{ + mdct_info *mdct; + + switch (len) + { + case 512: + mdct = fb->mdct2048; + break; + case 64: + mdct = fb->mdct256; + break; + } + + faad_imdct(mdct, in_data, out_data); +} + +/* NON-overlapping inverse filterbank for use with SSR */ +void ssr_ifilter_bank(fb_info *fb, uint8_t window_sequence, uint8_t window_shape, + uint8_t window_shape_prev, real_t *freq_in, + real_t *time_out, uint16_t frame_len) +{ + int16_t i; + real_t *transf_buf; + + real_t *window_long; + real_t *window_long_prev; + real_t *window_short; + real_t *window_short_prev; + + uint16_t nlong = frame_len; + uint16_t nshort = frame_len/8; + uint16_t trans = nshort/2; + + uint16_t nflat_ls = (nlong-nshort)/2; + + transf_buf = (real_t*)faad_malloc(2*nlong*sizeof(real_t)); + + window_long = fb->long_window[window_shape]; + window_long_prev = fb->long_window[window_shape_prev]; + window_short = fb->short_window[window_shape]; + window_short_prev = fb->short_window[window_shape_prev]; + + switch (window_sequence) + { + case ONLY_LONG_SEQUENCE: + imdct_ssr(fb, freq_in, transf_buf, 2*nlong); + for (i = nlong-1; i >= 0; i--) + { + time_out[i] = MUL_R_C(transf_buf[i],window_long_prev[i]); + time_out[nlong+i] = MUL_R_C(transf_buf[nlong+i],window_long[nlong-1-i]); + } + break; + + case LONG_START_SEQUENCE: + imdct_ssr(fb, freq_in, transf_buf, 2*nlong); + for (i = 0; i < nlong; i++) + time_out[i] = MUL_R_C(transf_buf[i],window_long_prev[i]); + for (i = 0; i < nflat_ls; i++) + time_out[nlong+i] = transf_buf[nlong+i]; + for (i = 0; i < nshort; i++) + time_out[nlong+nflat_ls+i] = MUL_R_C(transf_buf[nlong+nflat_ls+i],window_short[nshort-i-1]); + for (i = 0; i < nflat_ls; i++) + time_out[nlong+nflat_ls+nshort+i] = 0; + break; + + case EIGHT_SHORT_SEQUENCE: + imdct_ssr(fb, freq_in+0*nshort, transf_buf+2*nshort*0, 2*nshort); + imdct_ssr(fb, freq_in+1*nshort, transf_buf+2*nshort*1, 2*nshort); + imdct_ssr(fb, freq_in+2*nshort, transf_buf+2*nshort*2, 2*nshort); + imdct_ssr(fb, freq_in+3*nshort, transf_buf+2*nshort*3, 2*nshort); + imdct_ssr(fb, freq_in+4*nshort, transf_buf+2*nshort*4, 2*nshort); + imdct_ssr(fb, freq_in+5*nshort, transf_buf+2*nshort*5, 2*nshort); + imdct_ssr(fb, freq_in+6*nshort, transf_buf+2*nshort*6, 2*nshort); + imdct_ssr(fb, freq_in+7*nshort, transf_buf+2*nshort*7, 2*nshort); + for(i = nshort-1; i >= 0; i--) + { + time_out[i+0*nshort] = MUL_R_C(transf_buf[nshort*0+i],window_short_prev[i]); + time_out[i+1*nshort] = MUL_R_C(transf_buf[nshort*1+i],window_short[i]); + time_out[i+2*nshort] = MUL_R_C(transf_buf[nshort*2+i],window_short_prev[i]); + time_out[i+3*nshort] = MUL_R_C(transf_buf[nshort*3+i],window_short[i]); + time_out[i+4*nshort] = MUL_R_C(transf_buf[nshort*4+i],window_short_prev[i]); + time_out[i+5*nshort] = MUL_R_C(transf_buf[nshort*5+i],window_short[i]); + time_out[i+6*nshort] = MUL_R_C(transf_buf[nshort*6+i],window_short_prev[i]); + time_out[i+7*nshort] = MUL_R_C(transf_buf[nshort*7+i],window_short[i]); + time_out[i+8*nshort] = MUL_R_C(transf_buf[nshort*8+i],window_short_prev[i]); + time_out[i+9*nshort] = MUL_R_C(transf_buf[nshort*9+i],window_short[i]); + time_out[i+10*nshort] = MUL_R_C(transf_buf[nshort*10+i],window_short_prev[i]); + time_out[i+11*nshort] = MUL_R_C(transf_buf[nshort*11+i],window_short[i]); + time_out[i+12*nshort] = MUL_R_C(transf_buf[nshort*12+i],window_short_prev[i]); + time_out[i+13*nshort] = MUL_R_C(transf_buf[nshort*13+i],window_short[i]); + time_out[i+14*nshort] = MUL_R_C(transf_buf[nshort*14+i],window_short_prev[i]); + time_out[i+15*nshort] = MUL_R_C(transf_buf[nshort*15+i],window_short[i]); + } + break; + + case LONG_STOP_SEQUENCE: + imdct_ssr(fb, freq_in, transf_buf, 2*nlong); + for (i = 0; i < nflat_ls; i++) + time_out[i] = 0; + for (i = 0; i < nshort; i++) + time_out[nflat_ls+i] = MUL_R_C(transf_buf[nflat_ls+i],window_short_prev[i]); + for (i = 0; i < nflat_ls; i++) + time_out[nflat_ls+nshort+i] = transf_buf[nflat_ls+nshort+i]; + for (i = 0; i < nlong; i++) + time_out[nlong+i] = MUL_R_C(transf_buf[nlong+i],window_long[nlong-1-i]); + break; + } + + faad_free(transf_buf); +} + + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ssr_fb.h b/third_party/faad_hdc/upstream/libfaad/ssr_fb.h new file mode 100644 index 000000000..98fac2ed9 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ssr_fb.h @@ -0,0 +1,53 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id: ssr_fb.h,v 1.16 2007/11/01 12:33:36 menno Exp $ +**/ + +#ifndef __SSR_FB_H__ +#define __SSR_FB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +fb_info *ssr_filter_bank_init(uint16_t frame_len); +void ssr_filter_bank_end(fb_info *fb); + +/*non overlapping inverse filterbank */ +void ssr_ifilter_bank(fb_info *fb, + uint8_t window_sequence, + uint8_t window_shape, + uint8_t window_shape_prev, + real_t *freq_in, + real_t *time_out, + uint16_t frame_len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ssr_ipqf.c b/third_party/faad_hdc/upstream/libfaad/ssr_ipqf.c new file mode 100644 index 000000000..8dcb73ae9 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ssr_ipqf.c @@ -0,0 +1,191 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: ssr_ipqf.c,v 1.18 2007/11/01 12:33:39 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#ifdef SSR_DEC + +#include "ssr.h" +#include "ssr_ipqf.h" + +static real_t **app_pqfbuf; +static real_t **pp_q0, **pp_t0, **pp_t1; + +void gc_set_protopqf(real_t *p_proto) +{ + int j; + static real_t a_half[48] = + { + 1.2206911375946939E-05, 1.7261986723798209E-05, 1.2300093657077942E-05, + -1.0833943097791965E-05, -5.7772498639901686E-05, -1.2764767618947719E-04, + -2.0965186675013334E-04, -2.8166673689263850E-04, -3.1234860429017460E-04, + -2.6738519958452353E-04, -1.1949424681824722E-04, 1.3965139412648678E-04, + 4.8864136409185725E-04, 8.7044629275148344E-04, 1.1949430269934793E-03, + 1.3519708175026700E-03, 1.2346314373964412E-03, 7.6953209114159191E-04, + -5.2242432579537141E-05, -1.1516092887213454E-03, -2.3538469841711277E-03, + -3.4033123072127277E-03, -4.0028551071986133E-03, -3.8745415659693259E-03, + -2.8321073426874310E-03, -8.5038892323704195E-04, 1.8856751185350931E-03, + 4.9688741735340923E-03, 7.8056704536795926E-03, 9.7027909685901654E-03, + 9.9960423120166159E-03, 8.2019366335594487E-03, 4.1642072876103365E-03, + -1.8364453822737758E-03, -9.0384863094167686E-03, -1.6241528177129844E-02, + -2.1939551286300665E-02, -2.4533179947088161E-02, -2.2591663337768787E-02, + -1.5122066420044672E-02, -1.7971713448186293E-03, 1.6903413428575379E-02, + 3.9672315874127042E-02, 6.4487527248102796E-02, 8.8850025474701726E-02, + 0.1101132906105560 , 0.1258540205143761 , 0.1342239368467012 + }; + + for (j = 0; j < 48; ++j) + { + p_proto[j] = p_proto[95-j] = a_half[j]; + } +} + +void gc_setcoef_eff_pqfsyn(int mm, + int kk, + real_t *p_proto, + real_t ***ppp_q0, + real_t ***ppp_t0, + real_t ***ppp_t1) +{ + int i, k, n; + real_t w; + + /* Set 1st Mul&Acc Coef's */ + *ppp_q0 = (real_t **) calloc(mm, sizeof(real_t *)); + for (n = 0; n < mm; ++n) + { + (*ppp_q0)[n] = (real_t *) calloc(mm, sizeof(real_t)); + } + for (n = 0; n < mm/2; ++n) + { + for (i = 0; i < mm; ++i) + { + w = (2*i+1)*(2*n+1-mm)*M_PI/(4*mm); + (*ppp_q0)[n][i] = 2.0 * cos((real_t) w); + + w = (2*i+1)*(2*(mm+n)+1-mm)*M_PI/(4*mm); + (*ppp_q0)[n + mm/2][i] = 2.0 * cos((real_t) w); + } + } + + /* Set 2nd Mul&Acc Coef's */ + *ppp_t0 = (real_t **) calloc(mm, sizeof(real_t *)); + *ppp_t1 = (real_t **) calloc(mm, sizeof(real_t *)); + for (n = 0; n < mm; ++n) + { + (*ppp_t0)[n] = (real_t *) calloc(kk, sizeof(real_t)); + (*ppp_t1)[n] = (real_t *) calloc(kk, sizeof(real_t)); + } + for (n = 0; n < mm; ++n) + { + for (k = 0; k < kk; ++k) + { + (*ppp_t0)[n][k] = mm * p_proto[2*k *mm + n]; + (*ppp_t1)[n][k] = mm * p_proto[(2*k+1)*mm + n]; + + if (k%2 != 0) + { + (*ppp_t0)[n][k] = -(*ppp_t0)[n][k]; + (*ppp_t1)[n][k] = -(*ppp_t1)[n][k]; + } + } + } +} + +void ssr_ipqf(ssr_info *ssr, real_t *in_data, real_t *out_data, + real_t buffer[SSR_BANDS][96/4], + uint16_t frame_len, uint8_t bands) +{ + static int initFlag = 0; + real_t a_pqfproto[PQFTAPS]; + + int i; + + if (initFlag == 0) + { + gc_set_protopqf(a_pqfproto); + gc_setcoef_eff_pqfsyn(SSR_BANDS, PQFTAPS/(2*SSR_BANDS), a_pqfproto, + &pp_q0, &pp_t0, &pp_t1); + initFlag = 1; + } + + for (i = 0; i < frame_len / SSR_BANDS; i++) + { + int l, n, k; + int mm = SSR_BANDS; + int kk = PQFTAPS/(2*SSR_BANDS); + + for (n = 0; n < mm; n++) + { + for (k = 0; k < 2*kk-1; k++) + { + buffer[n][k] = buffer[n][k+1]; + } + } + + for (n = 0; n < mm; n++) + { + real_t acc = 0.0; + for (l = 0; l < mm; l++) + { + acc += pp_q0[n][l] * in_data[l*frame_len/SSR_BANDS + i]; + } + buffer[n][2*kk-1] = acc; + } + + for (n = 0; n < mm/2; n++) + { + real_t acc = 0.0; + for (k = 0; k < kk; k++) + { + acc += pp_t0[n][k] * buffer[n][2*kk-1-2*k]; + } + for (k = 0; k < kk; ++k) + { + acc += pp_t1[n][k] * buffer[n + mm/2][2*kk-2-2*k]; + } + out_data[i*SSR_BANDS + n] = acc; + + acc = 0.0; + for (k = 0; k < kk; k++) + { + acc += pp_t0[mm-1-n][k] * buffer[n][2*kk-1-2*k]; + } + for (k = 0; k < kk; k++) + { + acc -= pp_t1[mm-1-n][k] * buffer[n + mm/2][2*kk-2-2*k]; + } + out_data[i*SSR_BANDS + mm-1-n] = acc; + } + } +} + +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ssr_ipqf.h b/third_party/faad_hdc/upstream/libfaad/ssr_ipqf.h new file mode 100644 index 000000000..323a54b70 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ssr_ipqf.h @@ -0,0 +1,46 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id: ssr_ipqf.h,v 1.17 2007/11/01 12:33:39 menno Exp $ +**/ + +#ifndef __SSR_IPQF_H__ +#define __SSR_IPQF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void ssr_ipqf(ssr_info *ssr, real_t *in_data, real_t *out_data, + real_t buffer[SSR_BANDS][96/4], + uint16_t frame_len, uint8_t bands); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/ssr_win.h b/third_party/faad_hdc/upstream/libfaad/ssr_win.h new file mode 100644 index 000000000..9abbac4da --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/ssr_win.h @@ -0,0 +1,635 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Ahead Software AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Ahead Software through Mpeg4AAClicense@nero.com. +** +** $Id: ssr_win.h,v 1.16 2007/11/01 12:33:39 menno Exp $ +**/ + +#ifndef __SSR_WIN_H__ +#define __SSR_WIN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4305) +#pragma warning(disable:4244) +#endif + +static real_t sine_short_32[] = { + 0.0245412290, + 0.0735645667, + 0.1224106774, + 0.1709618866, + 0.2191012502, + 0.2667127550, + 0.3136817515, + 0.3598950505, + 0.4052413106, + 0.4496113360, + 0.4928981960, + 0.5349976420, + 0.5758082271, + 0.6152316332, + 0.6531728506, + 0.6895405650, + 0.7242470980, + 0.7572088838, + 0.7883464694, + 0.8175848126, + 0.8448535800, + 0.8700870275, + 0.8932242990, + 0.9142097831, + 0.9329928160, + 0.9495282173, + 0.9637760520, + 0.9757021666, + 0.9852776527, + 0.9924795628, + 0.9972904325, + 0.9996988177 +}; + +static real_t sine_long_256[] = { + 0.0030679568, + 0.0092037553, + 0.0153392069, + 0.0214740802, + 0.0276081469, + 0.0337411724, + 0.0398729295, + 0.0460031852, + 0.0521317050, + 0.0582582653, + 0.0643826351, + 0.0705045760, + 0.0766238645, + 0.0827402696, + 0.0888535529, + 0.0949634984, + 0.1010698676, + 0.1071724296, + 0.1132709533, + 0.1193652153, + 0.1254549921, + 0.1315400302, + 0.1376201212, + 0.1436950415, + 0.1497645378, + 0.1558284014, + 0.1618863940, + 0.1679383069, + 0.1739838719, + 0.1800229102, + 0.1860551536, + 0.1920804083, + 0.1980984211, + 0.2041089684, + 0.2101118416, + 0.2161068022, + 0.2220936269, + 0.2280720919, + 0.2340419590, + 0.2400030345, + 0.2459550500, + 0.2518978119, + 0.2578310966, + 0.2637546957, + 0.2696683407, + 0.2755718231, + 0.2814649343, + 0.2873474658, + 0.2932191789, + 0.2990798354, + 0.3049292266, + 0.3107671738, + 0.3165933788, + 0.3224076927, + 0.3282098472, + 0.3339996636, + 0.3397769034, + 0.3455413282, + 0.3512927592, + 0.3570309579, + 0.3627557456, + 0.3684668541, + 0.3741640747, + 0.3798472285, + 0.3855160773, + 0.3911703825, + 0.3968099952, + 0.4024346471, + 0.4080441594, + 0.4136383235, + 0.4192169011, + 0.4247796834, + 0.4303264916, + 0.4358570874, + 0.4413712919, + 0.4468688369, + 0.4523496032, + 0.4578133225, + 0.4632597864, + 0.4686888456, + 0.4741002321, + 0.4794937670, + 0.4848692715, + 0.4902265072, + 0.4955652654, + 0.5008853674, + 0.5061866641, + 0.5114688873, + 0.5167317986, + 0.5219752789, + 0.5271991491, + 0.5324031115, + 0.5375871062, + 0.5427507758, + 0.5478940606, + 0.5530167222, + 0.5581185222, + 0.5631993413, + 0.5682589412, + 0.5732972026, + 0.5783138275, + 0.5833086967, + 0.5882815719, + 0.5932323337, + 0.5981607437, + 0.6030666232, + 0.6079497933, + 0.6128100753, + 0.6176473498, + 0.6224613190, + 0.6272518039, + 0.6320187449, + 0.6367619038, + 0.6414810419, + 0.6461760402, + 0.6508467197, + 0.6554928422, + 0.6601143479, + 0.6647109985, + 0.6692826152, + 0.6738290191, + 0.6783500314, + 0.6828455329, + 0.6873153448, + 0.6917592883, + 0.6961771250, + 0.7005687952, + 0.7049341202, + 0.7092728615, + 0.7135848999, + 0.7178700566, + 0.7221282125, + 0.7263591886, + 0.7305628061, + 0.7347388864, + 0.7388873696, + 0.7430079579, + 0.7471006513, + 0.7511651516, + 0.7552013993, + 0.7592092156, + 0.7631884217, + 0.7671388984, + 0.7710605264, + 0.7749531269, + 0.7788165212, + 0.7826505899, + 0.7864552140, + 0.7902302146, + 0.7939754725, + 0.7976908684, + 0.8013761640, + 0.8050313592, + 0.8086562157, + 0.8122506142, + 0.8158144355, + 0.8193475604, + 0.8228498101, + 0.8263210654, + 0.8297612667, + 0.8331701756, + 0.8365477324, + 0.8398938179, + 0.8432082534, + 0.8464909792, + 0.8497417569, + 0.8529606462, + 0.8561473489, + 0.8593018055, + 0.8624239564, + 0.8655136228, + 0.8685707450, + 0.8715950847, + 0.8745866418, + 0.8775452971, + 0.8804709315, + 0.8833633661, + 0.8862225413, + 0.8890483975, + 0.8918406963, + 0.8945994973, + 0.8973246217, + 0.9000158906, + 0.9026733041, + 0.9052967429, + 0.9078861475, + 0.9104412794, + 0.9129621983, + 0.9154487252, + 0.9179008007, + 0.9203183055, + 0.9227011204, + 0.9250492454, + 0.9273625612, + 0.9296408892, + 0.9318842888, + 0.9340925813, + 0.9362657070, + 0.9384035468, + 0.9405061007, + 0.9425731897, + 0.9446048737, + 0.9466009140, + 0.9485613704, + 0.9504860640, + 0.9523749948, + 0.9542281032, + 0.9560452700, + 0.9578264356, + 0.9595715404, + 0.9612805247, + 0.9629532695, + 0.9645897746, + 0.9661900401, + 0.9677538276, + 0.9692812562, + 0.9707721472, + 0.9722265005, + 0.9736442566, + 0.9750253558, + 0.9763697386, + 0.9776773453, + 0.9789481759, + 0.9801821709, + 0.9813792109, + 0.9825392962, + 0.9836624265, + 0.9847484827, + 0.9857975245, + 0.9868094325, + 0.9877841473, + 0.9887216687, + 0.9896219969, + 0.9904850721, + 0.9913108945, + 0.9920993447, + 0.9928504229, + 0.9935641289, + 0.9942404628, + 0.9948793054, + 0.9954807758, + 0.9960446954, + 0.9965711236, + 0.9970600605, + 0.9975114465, + 0.9979252815, + 0.9983015656, + 0.9986402392, + 0.9989413023, + 0.9992047548, + 0.9994305968, + 0.9996188283, + 0.9997693896, + 0.9998823404, + 0.9999576211, + 0.9999952912 +}; + +static real_t kbd_short_32[] = { + 0.0000875914060105, + 0.0009321760265333, + 0.0032114611466596, + 0.0081009893216786, + 0.0171240286619181, + 0.0320720743527833, + 0.0548307856028528, + 0.0871361822564870, + 0.1302923415174603, + 0.1848955425508276, + 0.2506163195331889, + 0.3260874142923209, + 0.4089316830907141, + 0.4959414909423747, + 0.5833939894958904, + 0.6674601983218376, + 0.7446454751465113, + 0.8121892962974020, + 0.8683559394406505, + 0.9125649996381605, + 0.9453396205809574, + 0.9680864942677585, + 0.9827581789763112, + 0.9914756203467121, + 0.9961964092194694, + 0.9984956609571091, + 0.9994855586984285, + 0.9998533730714648, + 0.9999671864476404, + 0.9999948432453556, + 0.9999995655238333, + 0.9999999961638728 +}; + + +static real_t kbd_long_256[] = { + 0.0005851230124487, + 0.0009642149851497, + 0.0013558207534965, + 0.0017771849644394, + 0.0022352533849672, + 0.0027342299070304, + 0.0032773001022195, + 0.0038671998069216, + 0.0045064443384152, + 0.0051974336885144, + 0.0059425050016407, + 0.0067439602523141, + 0.0076040812644888, + 0.0085251378135895, + 0.0095093917383048, + 0.0105590986429280, + 0.0116765080854300, + 0.0128638627792770, + 0.0141233971318631, + 0.0154573353235409, + 0.0168678890600951, + 0.0183572550877256, + 0.0199276125319803, + 0.0215811201042484, + 0.0233199132076965, + 0.0251461009666641, + 0.0270617631981826, + 0.0290689473405856, + 0.0311696653515848, + 0.0333658905863535, + 0.0356595546648444, + 0.0380525443366107, + 0.0405466983507029, + 0.0431438043376910, + 0.0458455957104702, + 0.0486537485902075, + 0.0515698787635492, + 0.0545955386770205, + 0.0577322144743916, + 0.0609813230826460, + 0.0643442093520723, + 0.0678221432558827, + 0.0714163171546603, + 0.0751278431308314, + 0.0789577503982528, + 0.0829069827918993, + 0.0869763963425241, + 0.0911667569410503, + 0.0954787380973307, + 0.0999129187977865, + 0.1044697814663005, + 0.1091497100326053, + 0.1139529881122542, + 0.1188797973021148, + 0.1239302155951605, + 0.1291042159181728, + 0.1344016647957880, + 0.1398223211441467, + 0.1453658351972151, + 0.1510317475686540, + 0.1568194884519144, + 0.1627283769610327, + 0.1687576206143887, + 0.1749063149634756, + 0.1811734433685097, + 0.1875578769224857, + 0.1940583745250518, + 0.2006735831073503, + 0.2074020380087318, + 0.2142421635060113, + 0.2211922734956977, + 0.2282505723293797, + 0.2354151558022098, + 0.2426840122941792, + 0.2500550240636293, + 0.2575259686921987, + 0.2650945206801527, + 0.2727582531907993, + 0.2805146399424422, + 0.2883610572460804, + 0.2962947861868143, + 0.3043130149466800, + 0.3124128412663888, + 0.3205912750432127, + 0.3288452410620226, + 0.3371715818562547, + 0.3455670606953511, + 0.3540283646950029, + 0.3625521080463003, + 0.3711348353596863, + 0.3797730251194006, + 0.3884630932439016, + 0.3972013967475546, + 0.4059842374986933, + 0.4148078660689724, + 0.4236684856687616, + 0.4325622561631607, + 0.4414852981630577, + 0.4504336971855032, + 0.4594035078775303, + 0.4683907582974173, + 0.4773914542472655, + 0.4864015836506502, + 0.4954171209689973, + 0.5044340316502417, + 0.5134482766032377, + 0.5224558166913167, + 0.5314526172383208, + 0.5404346525403849, + 0.5493979103766972, + 0.5583383965124314, + 0.5672521391870222, + 0.5761351935809411, + 0.5849836462541291, + 0.5937936195492526, + 0.6025612759529649, + 0.6112828224083939, + 0.6199545145721097, + 0.6285726610088878, + 0.6371336273176413, + 0.6456338401819751, + 0.6540697913388968, + 0.6624380414593221, + 0.6707352239341151, + 0.6789580485595255, + 0.6871033051160131, + 0.6951678668345944, + 0.7031486937449871, + 0.7110428359000029, + 0.7188474364707993, + 0.7265597347077880, + 0.7341770687621900, + 0.7416968783634273, + 0.7491167073477523, + 0.7564342060337386, + 0.7636471334404891, + 0.7707533593446514, + 0.7777508661725849, + 0.7846377507242818, + 0.7914122257259034, + 0.7980726212080798, + 0.8046173857073919, + 0.8110450872887550, + 0.8173544143867162, + 0.8235441764639875, + 0.8296133044858474, + 0.8355608512093652, + 0.8413859912867303, + 0.8470880211822968, + 0.8526663589032990, + 0.8581205435445334, + 0.8634502346476508, + 0.8686552113760616, + 0.8737353715068081, + 0.8786907302411250, + 0.8835214188357692, + 0.8882276830575707, + 0.8928098814640207, + 0.8972684835130879, + 0.9016040675058185, + 0.9058173183656508, + 0.9099090252587376, + 0.9138800790599416, + 0.9177314696695282, + 0.9214642831859411, + 0.9250796989403991, + 0.9285789863994010, + 0.9319635019415643, + 0.9352346855155568, + 0.9383940571861993, + 0.9414432135761304, + 0.9443838242107182, + 0.9472176277741918, + 0.9499464282852282, + 0.9525720912004834, + 0.9550965394547873, + 0.9575217494469370, + 0.9598497469802043, + 0.9620826031668507, + 0.9642224303060783, + 0.9662713777449607, + 0.9682316277319895, + 0.9701053912729269, + 0.9718949039986892, + 0.9736024220549734, + 0.9752302180233160, + 0.9767805768831932, + 0.9782557920246753, + 0.9796581613210076, + 0.9809899832703159, + 0.9822535532154261, + 0.9834511596505429, + 0.9845850806232530, + 0.9856575802399989, + 0.9866709052828243, + 0.9876272819448033, + 0.9885289126911557, + 0.9893779732525968, + 0.9901766097569984, + 0.9909269360049311, + 0.9916310308941294, + 0.9922909359973702, + 0.9929086532976777, + 0.9934861430841844, + 0.9940253220113651, + 0.9945280613237534, + 0.9949961852476154, + 0.9954314695504363, + 0.9958356402684387, + 0.9962103726017252, + 0.9965572899760172, + 0.9968779632693499, + 0.9971739102014799, + 0.9974465948831872, + 0.9976974275220812, + 0.9979277642809907, + 0.9981389072844972, + 0.9983321047686901, + 0.9985085513687731, + 0.9986693885387259, + 0.9988157050968516, + 0.9989485378906924, + 0.9990688725744943, + 0.9991776444921379, + 0.9992757396582338, + 0.9993639958299003, + 0.9994432036616085, + 0.9995141079353859, + 0.9995774088586188, + 0.9996337634216871, + 0.9996837868076957, + 0.9997280538466377, + 0.9997671005064359, + 0.9998014254134544, + 0.9998314913952471, + 0.9998577270385304, + 0.9998805282555989, + 0.9999002598526793, + 0.9999172570940037, + 0.9999318272557038, + 0.9999442511639580, + 0.9999547847121726, + 0.9999636603523446, + 0.9999710885561258, + 0.9999772592414866, + 0.9999823431612708, + 0.9999864932503106, + 0.9999898459281599, + 0.9999925223548691, + 0.9999946296375997, + 0.9999962619864214, + 0.9999975018180320, + 0.9999984208055542, + 0.9999990808746198, + 0.9999995351446231, + 0.9999998288155155 +}; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/structs.h b/third_party/faad_hdc/upstream/libfaad/structs.h new file mode 100644 index 000000000..3abc88802 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/structs.h @@ -0,0 +1,446 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: structs.h,v 1.49 2009/01/26 23:51:15 menno Exp $ +**/ + +#ifndef __STRUCTS_H__ +#define __STRUCTS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cfft.h" +#ifdef SBR_DEC +#include "sbr_dec.h" +#endif + +#define MAX_CHANNELS 64 +#define MAX_SYNTAX_ELEMENTS 48 +#define MAX_WINDOW_GROUPS 8 +#define MAX_SFB 51 +#define MAX_LTP_SFB 40 +#define MAX_LTP_SFB_S 8 + +/* used to save the prediction state */ +typedef struct { + int16_t r[2]; + int16_t COR[2]; + int16_t VAR[2]; +} pred_state; + +typedef struct { + uint16_t N; + cfft_info *cfft; + complex_t *sincos; +#ifdef PROFILE + int64_t cycles; + int64_t fft_cycles; +#endif +} mdct_info; + +typedef struct +{ + const real_t *long_window[2]; + const real_t *short_window[2]; +#ifdef LD_DEC + const real_t *ld_window[2]; +#endif + + mdct_info *mdct256; +#ifdef LD_DEC + mdct_info *mdct1024; +#endif + mdct_info *mdct2048; +#ifdef PROFILE + int64_t cycles; +#endif +} fb_info; + +typedef struct +{ + uint8_t present; + + uint8_t num_bands; + uint8_t pce_instance_tag; + uint8_t excluded_chns_present; + uint8_t band_top[17]; + uint8_t prog_ref_level; + uint8_t dyn_rng_sgn[17]; + uint8_t dyn_rng_ctl[17]; + uint8_t exclude_mask[MAX_CHANNELS]; + uint8_t additional_excluded_chns[MAX_CHANNELS]; + + real_t ctrl1; + real_t ctrl2; +} drc_info; + +typedef struct +{ + uint8_t element_instance_tag; + uint8_t object_type; + uint8_t sf_index; + uint8_t num_front_channel_elements; + uint8_t num_side_channel_elements; + uint8_t num_back_channel_elements; + uint8_t num_lfe_channel_elements; + uint8_t num_assoc_data_elements; + uint8_t num_valid_cc_elements; + uint8_t mono_mixdown_present; + uint8_t mono_mixdown_element_number; + uint8_t stereo_mixdown_present; + uint8_t stereo_mixdown_element_number; + uint8_t matrix_mixdown_idx_present; + uint8_t pseudo_surround_enable; + uint8_t matrix_mixdown_idx; + uint8_t front_element_is_cpe[16]; + uint8_t front_element_tag_select[16]; + uint8_t side_element_is_cpe[16]; + uint8_t side_element_tag_select[16]; + uint8_t back_element_is_cpe[16]; + uint8_t back_element_tag_select[16]; + uint8_t lfe_element_tag_select[16]; + uint8_t assoc_data_element_tag_select[16]; + uint8_t cc_element_is_ind_sw[16]; + uint8_t valid_cc_element_tag_select[16]; + + uint8_t channels; + + uint8_t comment_field_bytes; + uint8_t comment_field_data[257]; + + /* extra added values */ + uint8_t num_front_channels; + uint8_t num_side_channels; + uint8_t num_back_channels; + uint8_t num_lfe_channels; + uint8_t sce_channel[16]; + uint8_t cpe_channel[16]; +} program_config; + +typedef struct +{ + uint16_t syncword; + uint8_t id; + uint8_t layer; + uint8_t protection_absent; + uint8_t profile; + uint8_t sf_index; + uint8_t private_bit; + uint8_t channel_configuration; + uint8_t original; + uint8_t home; + uint8_t emphasis; + uint8_t copyright_identification_bit; + uint8_t copyright_identification_start; + uint16_t aac_frame_length; + uint16_t adts_buffer_fullness; + uint16_t crc_check; + uint8_t no_raw_data_blocks_in_frame; + + /* control param */ + uint8_t old_format; +} adts_header; + +typedef struct +{ + uint8_t copyright_id_present; + int8_t copyright_id[10]; + uint8_t original_copy; + uint32_t bitrate; + uint32_t adif_buffer_fullness; + uint8_t num_program_config_elements; + uint8_t home; + uint8_t bitstream_type; + + /* maximum of 16 PCEs */ + program_config pce[16]; +} adif_header; + +#ifdef LTP_DEC +typedef struct +{ + uint8_t last_band; + uint8_t data_present; + uint16_t lag; + uint8_t lag_update; + uint8_t coef; + uint8_t long_used[MAX_SFB]; + uint8_t short_used[8]; + uint8_t short_lag_present[8]; + uint8_t short_lag[8]; +} ltp_info; +#endif + +#ifdef MAIN_DEC +typedef struct +{ + uint8_t limit; + uint8_t predictor_reset; + uint8_t predictor_reset_group_number; + uint8_t prediction_used[MAX_SFB]; +} pred_info; +#endif + +typedef struct +{ + uint8_t number_pulse; + uint8_t pulse_start_sfb; + uint8_t pulse_offset[4]; + uint8_t pulse_amp[4]; +} pulse_info; + +typedef struct +{ + uint8_t n_filt[8]; + uint8_t coef_res[8]; + uint8_t length[8][4]; + uint8_t order[8][4]; + uint8_t direction[8][4]; + uint8_t coef_compress[8][4]; + uint8_t coef[8][4][32]; +} tns_info; + +#ifdef SSR_DEC +typedef struct +{ + uint8_t max_band; + + uint8_t adjust_num[4][8]; + uint8_t alevcode[4][8][8]; + uint8_t aloccode[4][8][8]; +} ssr_info; +#endif + +typedef struct +{ + uint8_t max_sfb; + uint8_t global_gain; + + uint8_t num_swb; + uint8_t num_window_groups; + uint8_t num_windows; + uint8_t window_sequence; + uint8_t window_group_length[8]; + uint8_t window_shape; + uint8_t scale_factor_grouping; + uint16_t sect_sfb_offset[8][15*8]; + uint16_t swb_offset[52]; + uint16_t swb_offset_max; + + uint8_t sect_cb[8][15*8]; + uint16_t sect_start[8][15*8]; + uint16_t sect_end[8][15*8]; + uint8_t sfb_cb[8][8*15]; + uint8_t num_sec[8]; /* number of sections in a group */ + + int16_t scale_factors[8][51]; /* [0..255], except for noise and intensity */ + + uint8_t ms_mask_present; + uint8_t ms_used[MAX_WINDOW_GROUPS][MAX_SFB]; + + uint8_t noise_used; + uint8_t is_used; + + uint8_t pulse_data_present; + uint8_t tns_data_present; + uint8_t gain_control_data_present; + uint8_t predictor_data_present; + + pulse_info pul; + tns_info tns; +#ifdef MAIN_DEC + pred_info pred; +#endif +#ifdef LTP_DEC + ltp_info ltp; + ltp_info ltp2; +#endif +#ifdef SSR_DEC + ssr_info ssr; +#endif + +#ifdef ERROR_RESILIENCE + /* ER HCR data */ + uint16_t length_of_reordered_spectral_data; + uint8_t length_of_longest_codeword; + /* ER RLVC data */ + uint8_t sf_concealment; + uint16_t length_of_rvlc_sf; + uint16_t dpcm_noise_nrg; + uint8_t sf_escapes_present; + uint8_t length_of_rvlc_escapes; + uint16_t dpcm_noise_last_position; + uint8_t rev_global_gain; +#endif +} ic_stream; /* individual channel stream */ + +typedef struct +{ + uint8_t channel; + int16_t paired_channel; + + uint8_t element_instance_tag; + uint8_t common_window; + + ic_stream ics1; + ic_stream ics2; +} element; /* syntax element (SCE, CPE, LFE) */ + +#define MAX_ASC_BYTES 64 +typedef struct { + int inited; + int version, versionA; + int framelen_type; + int useSameStreamMux; + int allStreamsSameTimeFraming; + int numSubFrames; + int numPrograms; + int numLayers; + int otherDataPresent; + uint32_t otherDataLenBits; + uint32_t frameLength; + uint8_t ASC[MAX_ASC_BYTES]; + uint32_t ASCbits; +} latm_header; + +typedef struct +{ + uint8_t adts_header_present; + uint8_t adif_header_present; + uint8_t latm_header_present; + uint8_t sf_index; + uint8_t object_type; + uint8_t channelConfiguration; + uint8_t postSeekResetFlag; +#ifdef ERROR_RESILIENCE + uint8_t aacSectionDataResilienceFlag; + uint8_t aacScalefactorDataResilienceFlag; + uint8_t aacSpectralDataResilienceFlag; +#endif + uint16_t frameLength; + + uint32_t frame; + + uint32_t sample_buffer_size; + + uint8_t downMatrix; + uint8_t upMatrix; + uint8_t first_syn_ele; + uint8_t has_lfe; + /* number of channels in current frame */ + uint8_t fr_channels; + /* number of elements in current frame */ + uint8_t fr_ch_ele; + + /* element_output_channels: + determines the number of channels the element will output + */ + uint8_t element_output_channels[MAX_SYNTAX_ELEMENTS]; + /* element_alloced: + determines whether the data needed for the element is allocated or not + */ + uint8_t element_alloced[MAX_SYNTAX_ELEMENTS]; + /* sample_buffer_size: + (internal) output dara buffer size + */ + + latm_header latm_config; + + /* output data buffer */ + void *sample_buffer; + + uint8_t window_shape_prev[MAX_CHANNELS]; +#ifdef LTP_DEC + uint16_t ltp_lag[MAX_CHANNELS]; +#endif + fb_info *fb; + drc_info *drc; + + real_t *time_out[MAX_CHANNELS]; + real_t *fb_intermed[MAX_CHANNELS]; + +#ifdef SBR_DEC + sbr_info *sbr[MAX_SYNTAX_ELEMENTS]; +#endif + +#ifdef SSR_DEC + real_t *ssr_overlap[MAX_CHANNELS]; + real_t *prev_fmd[MAX_CHANNELS]; + real_t ipqf_buffer[MAX_CHANNELS][4][96/4]; +#endif + +#ifdef MAIN_DEC + pred_state *pred_stat[MAX_CHANNELS]; +#endif +#ifdef LTP_DEC + int16_t *lt_pred_stat[MAX_CHANNELS]; +#endif + + /* RNG states */ + uint32_t __r1; + uint32_t __r2; + +#ifdef SBR_DEC + int8_t sbr_present_flag; + int8_t forceUpSampling; + int8_t downSampledSBR; + /* determines whether SBR data is allocated for the gives element */ + uint8_t sbr_alloced[MAX_SYNTAX_ELEMENTS]; +#endif + +#if (defined(PS_DEC) || defined(DRM_PS)) + uint8_t ps_used[MAX_SYNTAX_ELEMENTS]; + uint8_t ps_used_global; +#endif + + /* Program Config Element */ + uint8_t pce_set; + program_config pce; + uint8_t element_id[MAX_CHANNELS]; + uint8_t internal_channel[MAX_CHANNELS]; + + /* Configuration data */ + NeAACDecConfiguration config; + +#ifdef PROFILE + int64_t cycles; + int64_t spectral_cycles; + int64_t output_cycles; + int64_t scalefac_cycles; + int64_t requant_cycles; +#endif + const unsigned char *cmes; +} NeAACDecStruct; + + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/syntax.c b/third_party/faad_hdc/upstream/libfaad/syntax.c new file mode 100644 index 000000000..118990cc5 --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/syntax.c @@ -0,0 +1,2968 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: syntax.c,v 1.93 2009/01/26 23:51:15 menno Exp $ +**/ + +/* + Reads the AAC bitstream as defined in 14496-3 (MPEG-4 Audio) +*/ + +#include "common.h" +#include "structs.h" + +#include +#include + +#include "syntax.h" +#include "specrec.h" +#include "huffman.h" +#include "bits.h" +#include "pulse.h" +#include "analysis.h" +#include "drc.h" +#ifdef ERROR_RESILIENCE +#include "rvlc.h" +#endif +#ifdef SBR_DEC +#include "sbr_syntax.h" +#endif +#include "mp4.h" + + +/* static function declarations */ +static void decode_sce_lfe(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, bitfile *ld, + uint8_t id_syn_ele); +static void decode_cpe(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, bitfile *ld, + uint8_t id_syn_ele); +static uint8_t single_lfe_channel_element(NeAACDecStruct *hDecoder, bitfile *ld, + uint8_t channel, uint8_t *tag); +static uint8_t channel_pair_element(NeAACDecStruct *hDecoder, bitfile *ld, + uint8_t channel, uint8_t *tag); +#ifdef COUPLING_DEC +static uint8_t coupling_channel_element(NeAACDecStruct *hDecoder, bitfile *ld); +#endif +static uint16_t data_stream_element(NeAACDecStruct *hDecoder, bitfile *ld); +static uint8_t program_config_element(program_config *pce, bitfile *ld); +static uint8_t fill_element(NeAACDecStruct *hDecoder, bitfile *ld, drc_info *drc +#ifdef SBR_DEC + ,uint8_t sbr_ele +#endif + ); +static uint8_t individual_channel_stream(NeAACDecStruct *hDecoder, element *ele, + bitfile *ld, ic_stream *ics, uint8_t scal_flag, + int16_t *spec_data); +static uint8_t ics_info(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld, + uint8_t common_window); +static uint8_t section_data(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld); +static uint8_t scale_factor_data(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld); +#ifdef SSR_DEC +static void gain_control_data(bitfile *ld, ic_stream *ics); +#endif +static uint8_t spectral_data(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld, + int16_t *spectral_data); +static uint8_t pulse_data(ic_stream *ics, pulse_info *pul, bitfile *ld); +static void tns_data(NeAACDecStruct *hDecoder, ic_stream *ics, tns_info *tns, bitfile *ld); +#ifdef LTP_DEC +static uint8_t ltp_data(NeAACDecStruct *hDecoder, ic_stream *ics, ltp_info *ltp, bitfile *ld); +#endif +static uint8_t adts_fixed_header(adts_header *adts, bitfile *ld); +static void adts_variable_header(adts_header *adts, bitfile *ld); +static void adts_error_check(adts_header *adts, bitfile *ld); +static uint8_t side_info(NeAACDecStruct *hDecoder, element *ele, + bitfile *ld, ic_stream *ics, uint8_t scal_flag); +#ifdef DRM +static int8_t DRM_aac_scalable_main_header(NeAACDecStruct *hDecoder, ic_stream *ics1, ic_stream *ics2, + bitfile *ld, uint8_t this_layer_stereo); +#else +static uint16_t extension_payload(bitfile *ld, drc_info *drc, uint16_t count); +static uint8_t dynamic_range_info(bitfile *ld, drc_info *drc); +static uint8_t excluded_channels(bitfile *ld, drc_info *drc); +#endif + + +/* Table 4.4.1 */ +int8_t GASpecificConfig(bitfile *ld, mp4AudioSpecificConfig *mp4ASC, + program_config *pce_out) +{ + program_config pce; + + /* 1024 or 960 */ + mp4ASC->frameLengthFlag = faad_get1bit(ld + DEBUGVAR(1,138,"GASpecificConfig(): FrameLengthFlag")); +#ifndef ALLOW_SMALL_FRAMELENGTH + if (mp4ASC->frameLengthFlag == 1) + return -3; +#endif + + mp4ASC->dependsOnCoreCoder = faad_get1bit(ld + DEBUGVAR(1,139,"GASpecificConfig(): DependsOnCoreCoder")); + if (mp4ASC->dependsOnCoreCoder == 1) + { + mp4ASC->coreCoderDelay = (uint16_t)faad_getbits(ld, 14 + DEBUGVAR(1,140,"GASpecificConfig(): CoreCoderDelay")); + } + + mp4ASC->extensionFlag = faad_get1bit(ld DEBUGVAR(1,141,"GASpecificConfig(): ExtensionFlag")); + if (mp4ASC->channelsConfiguration == 0) + { + if (program_config_element(&pce, ld)) + return -3; + //mp4ASC->channelsConfiguration = pce.channels; + + if (pce_out != NULL) + memcpy(pce_out, &pce, sizeof(program_config)); + + /* + if (pce.num_valid_cc_elements) + return -3; + */ + } + +#ifdef ERROR_RESILIENCE + if (mp4ASC->extensionFlag == 1) + { + /* Error resilience not supported yet */ + if (mp4ASC->objectTypeIndex >= ER_OBJECT_START) + { + mp4ASC->aacSectionDataResilienceFlag = faad_get1bit(ld + DEBUGVAR(1,144,"GASpecificConfig(): aacSectionDataResilienceFlag")); + mp4ASC->aacScalefactorDataResilienceFlag = faad_get1bit(ld + DEBUGVAR(1,145,"GASpecificConfig(): aacScalefactorDataResilienceFlag")); + mp4ASC->aacSpectralDataResilienceFlag = faad_get1bit(ld + DEBUGVAR(1,146,"GASpecificConfig(): aacSpectralDataResilienceFlag")); + } + /* 1 bit: extensionFlag3 */ + faad_getbits(ld, 1); + } +#endif + + return 0; +} + +/* Table 4.4.2 */ +/* An MPEG-4 Audio decoder is only required to follow the Program + Configuration Element in GASpecificConfig(). The decoder shall ignore + any Program Configuration Elements that may occur in raw data blocks. + PCEs transmitted in raw data blocks cannot be used to convey decoder + configuration information. +*/ +static uint8_t program_config_element(program_config *pce, bitfile *ld) +{ + uint8_t i; + + memset(pce, 0, sizeof(program_config)); + + pce->channels = 0; + + pce->element_instance_tag = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,10,"program_config_element(): element_instance_tag")); + + pce->object_type = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,11,"program_config_element(): object_type")); + pce->sf_index = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,12,"program_config_element(): sf_index")); + pce->num_front_channel_elements = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,13,"program_config_element(): num_front_channel_elements")); + pce->num_side_channel_elements = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,14,"program_config_element(): num_side_channel_elements")); + pce->num_back_channel_elements = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,15,"program_config_element(): num_back_channel_elements")); + pce->num_lfe_channel_elements = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,16,"program_config_element(): num_lfe_channel_elements")); + pce->num_assoc_data_elements = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,17,"program_config_element(): num_assoc_data_elements")); + pce->num_valid_cc_elements = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,18,"program_config_element(): num_valid_cc_elements")); + + pce->mono_mixdown_present = faad_get1bit(ld + DEBUGVAR(1,19,"program_config_element(): mono_mixdown_present")); + if (pce->mono_mixdown_present == 1) + { + pce->mono_mixdown_element_number = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,20,"program_config_element(): mono_mixdown_element_number")); + } + + pce->stereo_mixdown_present = faad_get1bit(ld + DEBUGVAR(1,21,"program_config_element(): stereo_mixdown_present")); + if (pce->stereo_mixdown_present == 1) + { + pce->stereo_mixdown_element_number = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,22,"program_config_element(): stereo_mixdown_element_number")); + } + + pce->matrix_mixdown_idx_present = faad_get1bit(ld + DEBUGVAR(1,23,"program_config_element(): matrix_mixdown_idx_present")); + if (pce->matrix_mixdown_idx_present == 1) + { + pce->matrix_mixdown_idx = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,24,"program_config_element(): matrix_mixdown_idx")); + pce->pseudo_surround_enable = faad_get1bit(ld + DEBUGVAR(1,25,"program_config_element(): pseudo_surround_enable")); + } + + for (i = 0; i < pce->num_front_channel_elements; i++) + { + pce->front_element_is_cpe[i] = faad_get1bit(ld + DEBUGVAR(1,26,"program_config_element(): front_element_is_cpe")); + pce->front_element_tag_select[i] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,27,"program_config_element(): front_element_tag_select")); + + if (pce->front_element_is_cpe[i] & 1) + { + pce->cpe_channel[pce->front_element_tag_select[i]] = pce->channels; + pce->num_front_channels += 2; + pce->channels += 2; + } else { + pce->sce_channel[pce->front_element_tag_select[i]] = pce->channels; + pce->num_front_channels++; + pce->channels++; + } + } + + for (i = 0; i < pce->num_side_channel_elements; i++) + { + pce->side_element_is_cpe[i] = faad_get1bit(ld + DEBUGVAR(1,28,"program_config_element(): side_element_is_cpe")); + pce->side_element_tag_select[i] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,29,"program_config_element(): side_element_tag_select")); + + if (pce->side_element_is_cpe[i] & 1) + { + pce->cpe_channel[pce->side_element_tag_select[i]] = pce->channels; + pce->num_side_channels += 2; + pce->channels += 2; + } else { + pce->sce_channel[pce->side_element_tag_select[i]] = pce->channels; + pce->num_side_channels++; + pce->channels++; + } + } + + for (i = 0; i < pce->num_back_channel_elements; i++) + { + pce->back_element_is_cpe[i] = faad_get1bit(ld + DEBUGVAR(1,30,"program_config_element(): back_element_is_cpe")); + pce->back_element_tag_select[i] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,31,"program_config_element(): back_element_tag_select")); + + if (pce->back_element_is_cpe[i] & 1) + { + pce->cpe_channel[pce->back_element_tag_select[i]] = pce->channels; + pce->channels += 2; + pce->num_back_channels += 2; + } else { + pce->sce_channel[pce->back_element_tag_select[i]] = pce->channels; + pce->num_back_channels++; + pce->channels++; + } + } + + for (i = 0; i < pce->num_lfe_channel_elements; i++) + { + pce->lfe_element_tag_select[i] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,32,"program_config_element(): lfe_element_tag_select")); + + pce->sce_channel[pce->lfe_element_tag_select[i]] = pce->channels; + pce->num_lfe_channels++; + pce->channels++; + } + + for (i = 0; i < pce->num_assoc_data_elements; i++) + pce->assoc_data_element_tag_select[i] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,33,"program_config_element(): assoc_data_element_tag_select")); + + for (i = 0; i < pce->num_valid_cc_elements; i++) + { + pce->cc_element_is_ind_sw[i] = faad_get1bit(ld + DEBUGVAR(1,34,"program_config_element(): cc_element_is_ind_sw")); + pce->valid_cc_element_tag_select[i] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,35,"program_config_element(): valid_cc_element_tag_select")); + } + + faad_byte_align(ld); + + pce->comment_field_bytes = (uint8_t)faad_getbits(ld, 8 + DEBUGVAR(1,36,"program_config_element(): comment_field_bytes")); + + for (i = 0; i < pce->comment_field_bytes; i++) + { + pce->comment_field_data[i] = (uint8_t)faad_getbits(ld, 8 + DEBUGVAR(1,37,"program_config_element(): comment_field_data")); + } + pce->comment_field_data[i] = 0; + + if (pce->channels > MAX_CHANNELS) + return 22; + + return 0; +} + +static void decode_sce_lfe(NeAACDecStruct *hDecoder, + NeAACDecFrameInfo *hInfo, bitfile *ld, + uint8_t id_syn_ele) +{ + uint8_t channels = hDecoder->fr_channels; + uint8_t tag = 0; + + /* One or two channels are used; + exact number will be known after single_lfe_channel_element + */ + if (channels+2 > MAX_CHANNELS) + { + hInfo->error = 12; + return; + } + if (hDecoder->fr_ch_ele+1 > MAX_SYNTAX_ELEMENTS) + { + hInfo->error = 13; + return; + } + + /* for SCE hDecoder->element_output_channels[] is not set here because this + can become 2 when some form of Parametric Stereo coding is used + */ + + if (hDecoder->element_id[hDecoder->fr_ch_ele] != INVALID_ELEMENT_ID && + hDecoder->element_id[hDecoder->fr_ch_ele] != id_syn_ele) + { + /* element inconsistency */ + hInfo->error = 21; + return; + } + + /* save the syntax element id */ + hDecoder->element_id[hDecoder->fr_ch_ele] = id_syn_ele; + + /* decode the element */ + hInfo->error = single_lfe_channel_element(hDecoder, ld, channels, &tag); + + /* map output channels position to internal data channels */ + if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 2) + { + /* this might be faulty when pce_set is true */ + hDecoder->internal_channel[channels] = channels; + hDecoder->internal_channel[channels+1] = channels+1; + } else { + if (hDecoder->pce_set) + { + if (hDecoder->pce.channels > MAX_CHANNELS) + { + hInfo->error = 22; + return; + } + hDecoder->internal_channel[hDecoder->pce.sce_channel[tag]] = channels; + } else { + hDecoder->internal_channel[channels] = channels; + } + } + + hDecoder->fr_channels += hDecoder->element_output_channels[hDecoder->fr_ch_ele]; + hDecoder->fr_ch_ele++; +} + +static void decode_cpe(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, bitfile *ld, + uint8_t id_syn_ele) +{ + uint8_t channels = hDecoder->fr_channels; + uint8_t tag = 0; + + if (channels+2 > MAX_CHANNELS) + { + hInfo->error = 12; + return; + } + if (hDecoder->fr_ch_ele+1 > MAX_SYNTAX_ELEMENTS) + { + hInfo->error = 13; + return; + } + if (hDecoder->pce_set && (hDecoder->pce.channels > MAX_CHANNELS)) + { + hInfo->error = 22; + return; + } + + /* for CPE the number of output channels is always 2 */ + if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 0) + { + /* element_output_channels not set yet */ + hDecoder->element_output_channels[hDecoder->fr_ch_ele] = 2; + } else if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] != 2) { + /* element inconsistency */ + hInfo->error = 21; + return; + } + + if (hDecoder->element_id[hDecoder->fr_ch_ele] != INVALID_ELEMENT_ID && + hDecoder->element_id[hDecoder->fr_ch_ele] != id_syn_ele) + { + /* element inconsistency */ + hInfo->error = 21; + return; + } + + /* save the syntax element id */ + hDecoder->element_id[hDecoder->fr_ch_ele] = id_syn_ele; + + /* decode the element */ + hInfo->error = channel_pair_element(hDecoder, ld, channels, &tag); + + /* map output channel position to internal data channels */ + if (hDecoder->pce_set) + { + hDecoder->internal_channel[hDecoder->pce.cpe_channel[tag]] = channels; + hDecoder->internal_channel[hDecoder->pce.cpe_channel[tag]+1] = channels+1; + } else { + hDecoder->internal_channel[channels] = channels; + hDecoder->internal_channel[channels+1] = channels+1; + } + + hDecoder->fr_channels += 2; + hDecoder->fr_ch_ele++; +} + +#ifdef HDC + +static int hdc_data_frame(NeAACDecStruct *hDecoder, ic_stream *ics1, ic_stream *ics2, bitfile *ld, int layer_stereo) +{ + uint8_t retval = 0; + uint8_t ics_reserved_bit; + + ics_reserved_bit = faad_get1bit(ld + DEBUGVAR(1,43,"hdc_data_frame(): ics_reserved_bit")); + if (ics_reserved_bit != 0) + return 32; + ics1->window_shape = faad_get1bit(ld + DEBUGVAR(1,45,"hdc_data_frame(): window_shape")); + ics1->window_sequence = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,44,"hdc_data_frame(): window_sequence")); + + if (ics1->window_sequence == EIGHT_SHORT_SEQUENCE) + { + ics1->max_sfb = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,46,"hdc_data_frame(): max_sfb (short)")); + ics1->scale_factor_grouping = (uint8_t)faad_getbits(ld, 7 + DEBUGVAR(1,47,"hdc_data_frame(): scale_factor_grouping")); + } else { + ics1->max_sfb = (uint8_t)faad_getbits(ld, 6 + DEBUGVAR(1,48,"hdc_data_frame(): max_sfb (long)")); + } + + /* get the grouping information */ + if ((retval = window_grouping_info(hDecoder, ics1)) > 0) + return retval; + + /* should be an error */ + /* check the range of max_sfb */ + if (ics1->max_sfb > ics1->num_swb) + return 16; + + if (layer_stereo) + { + ics1->ms_mask_present = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,306,"hdc_data_frame(): ms_mask_present")); + if (ics1->ms_mask_present == 3) + { + /* bitstream error */ + return 32; + } + if (ics1->ms_mask_present == 1) + { + uint8_t g, sfb; + for (g = 0; g < ics1->num_window_groups; g++) + { + for (sfb = 0; sfb < ics1->max_sfb; sfb++) + { + ics1->ms_used[g][sfb] = faad_get1bit(ld + DEBUGVAR(1,307,"hdc_data_frame(): faad_get1bit")); + } + } + } + memcpy(ics2, ics1, sizeof(ic_stream)); + } + else + { + ics1->ms_mask_present = 0; + } + + return 0; +} + +static uint8_t hdc_sbr_data_block(NeAACDecStruct *hDecoder, bitfile *ld) +{ + uint16_t count; + uint32_t bitsconsumed = faad_get_processed_bits(ld); + uint32_t buffer_size = faad_origbitbuffer_size(ld); + + if (bitsconsumed > buffer_size*8) + { + return 14; + } + + if (!hDecoder->sbr[0]) + { + hDecoder->sbr[0] = sbrDecodeInit(hDecoder->frameLength, + hDecoder->element_id[0], 2*get_sample_rate(hDecoder->sf_index), + hDecoder->downSampledSBR +#ifdef DRM + , 0 +#endif + + ); + } + hDecoder->sbr[0]->Is_HDC_SBR = 1; + + count = (uint16_t)bit2byte(buffer_size*8 - bitsconsumed); + + hDecoder->sbr[0]->ret = sbr_extension_data(ld, hDecoder->sbr[0], count, hDecoder->postSeekResetFlag); +#if (defined(PS_DEC) || defined(DRM_PS)) + if (hDecoder->sbr[0]->ps_used) + { + hDecoder->ps_used[0] = 1; + hDecoder->ps_used_global = 1; + } +#endif + return 0; +} + +static int hdc_is_stereo_layer(int block_type) +{ + switch (block_type) + { + case 0: + return 0; + case 1: + return 0; + case 2: + return 1; + case 5: + case 6: + return 0; + case 7: + return 1; + default: + { + fprintf(stderr, "Unknown HDC block type: %d\n", block_type); + return 0; + } + } +} + +void hdc_data_block(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, + bitfile *ld, program_config *pce, drc_info *drc) +{ + uint8_t channels = hDecoder->fr_channels = 0; + uint8_t block_type; + uint8_t layer_stereo = 0; + element cpe = {0}; + ic_stream *ics1 = &(cpe.ics1); + ic_stream *ics2 = &(cpe.ics2); + ALIGN int16_t spec_data1[1024] = {0}; + ALIGN int16_t spec_data2[1024] = {0}; + + hDecoder->fr_ch_ele = 0; + hDecoder->first_syn_ele = 25; + hDecoder->has_lfe = 0; + + block_type = (uint8_t)faad_getbits(ld, LEN_SE_ID); + layer_stereo = hdc_is_stereo_layer(block_type); + + if (layer_stereo) + { + hDecoder->element_id[hDecoder->fr_ch_ele] = ID_CPE; + if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 0) + hDecoder->element_output_channels[hDecoder->fr_ch_ele] = 2; + } + else + { + hDecoder->element_id[hDecoder->fr_ch_ele] = ID_SCE; + } + + hInfo->error = hdc_data_frame(hDecoder, ics1, ics2, ld, layer_stereo); + if (hInfo->error > 0) + return; + + cpe.common_window = 1; + + if (layer_stereo) + { + cpe.channel = channels; + cpe.paired_channel = channels+1; + } + + ics1->tns_data_present = faad_get1bit(ld); + if (ics1->tns_data_present) + tns_data(hDecoder, ics1, &(ics1->tns), ld); + if (layer_stereo) + { + /* get tns data */ + if ((ics2->tns_data_present = faad_get1bit(ld)) & 1) + tns_data(hDecoder, ics2, &(ics2->tns), ld); + } + + hInfo->error = side_info(hDecoder, &cpe, ld, ics1, 1); + if (hInfo->error > 0) + return; + + /* decode the spectral data */ + if ((hInfo->error = spectral_data(hDecoder, ics1, ld, spec_data1)) > 0) + return; + if (layer_stereo) + { + hInfo->error = side_info(hDecoder, &cpe, ld, ics2, 1); + if (hInfo->error > 0) + return; + + if ((hInfo->error = spectral_data(hDecoder, ics2, ld, spec_data2)) > 0) + return; + } + + /* check if next bitstream element is a fill element */ + /* if so, read it now so SBR decoding can be done in case of a file with SBR */ + if (faad_showbits(ld, LEN_SE_ID) == ID_FIL) + { + faad_flushbits(ld, LEN_SE_ID); + + if (faad_getbits(ld, 1) && (hInfo->error = hdc_sbr_data_block(hDecoder, ld)) > 0) + return; + } + + if (layer_stereo) + { + hInfo->error = reconstruct_channel_pair(hDecoder, ics1, ics2, &cpe, spec_data1, spec_data2); + if (hInfo->error > 0) + return; + } else { + hInfo->error = reconstruct_single_channel(hDecoder, ics1, &cpe, spec_data1); + if (hInfo->error > 0) + return; + } + + /* map output channels position to internal data channels */ + if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 2) + { + /* this might be faulty when pce_set is true */ + hDecoder->internal_channel[channels] = channels; + hDecoder->internal_channel[channels+1] = channels+1; + } else { + hDecoder->internal_channel[channels] = channels; + } + + hDecoder->fr_channels += hDecoder->element_output_channels[hDecoder->fr_ch_ele]; + hDecoder->fr_ch_ele++; +} +#endif + +void raw_data_block(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, + bitfile *ld, program_config *pce, drc_info *drc) +{ + uint8_t id_syn_ele; + uint8_t ele_this_frame = 0; + + hDecoder->fr_channels = 0; + hDecoder->fr_ch_ele = 0; + hDecoder->first_syn_ele = 25; + hDecoder->has_lfe = 0; + +#ifdef ERROR_RESILIENCE + if (hDecoder->object_type < ER_OBJECT_START) + { +#endif + /* Table 4.4.3: raw_data_block() */ + while ((id_syn_ele = (uint8_t)faad_getbits(ld, LEN_SE_ID + DEBUGVAR(1,4,"NeAACDecDecode(): id_syn_ele"))) != ID_END) + { + switch (id_syn_ele) { + case ID_SCE: + ele_this_frame++; + if (hDecoder->first_syn_ele == 25) hDecoder->first_syn_ele = id_syn_ele; + decode_sce_lfe(hDecoder, hInfo, ld, id_syn_ele); + if (hInfo->error > 0) + return; + break; + case ID_CPE: + ele_this_frame++; + if (hDecoder->first_syn_ele == 25) hDecoder->first_syn_ele = id_syn_ele; + decode_cpe(hDecoder, hInfo, ld, id_syn_ele); + if (hInfo->error > 0) + return; + break; + case ID_LFE: +#ifdef DRM + hInfo->error = 32; +#else + ele_this_frame++; + hDecoder->has_lfe++; + decode_sce_lfe(hDecoder, hInfo, ld, id_syn_ele); +#endif + if (hInfo->error > 0) + return; + break; + case ID_CCE: /* not implemented yet, but skip the bits */ +#ifdef DRM + hInfo->error = 32; +#else + ele_this_frame++; +#ifdef COUPLING_DEC + hInfo->error = coupling_channel_element(hDecoder, ld); +#else + hInfo->error = 6; +#endif +#endif + if (hInfo->error > 0) + return; + break; + case ID_DSE: + ele_this_frame++; + data_stream_element(hDecoder, ld); + break; + case ID_PCE: + if (ele_this_frame != 0) + { + hInfo->error = 31; + return; + } + ele_this_frame++; + /* 14496-4: 5.6.4.1.2.1.3: */ + /* program_configuration_element()'s in access units shall be ignored */ + program_config_element(pce, ld); + //if ((hInfo->error = program_config_element(pce, ld)) > 0) + // return; + //hDecoder->pce_set = 1; + break; + case ID_FIL: + ele_this_frame++; + /* one sbr_info describes a channel_element not a channel! */ + /* if we encounter SBR data here: error */ + /* SBR data will be read directly in the SCE/LFE/CPE element */ + if ((hInfo->error = fill_element(hDecoder, ld, drc +#ifdef SBR_DEC + , INVALID_SBR_ELEMENT +#endif + )) > 0) + return; + break; + } + if (ld->error != 0) + { + hInfo->error = 32; + return; + } + } +#ifdef ERROR_RESILIENCE + } else { + /* Table 262: er_raw_data_block() */ + switch (hDecoder->channelConfiguration) + { + case 1: + decode_sce_lfe(hDecoder, hInfo, ld, ID_SCE); + if (hInfo->error > 0) + return; + break; + case 2: + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + break; + case 3: + decode_sce_lfe(hDecoder, hInfo, ld, ID_SCE); + if (hInfo->error > 0) + return; + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + break; + case 4: + decode_sce_lfe(hDecoder, hInfo, ld, ID_SCE); + if (hInfo->error > 0) + return; + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + decode_sce_lfe(hDecoder, hInfo, ld, ID_SCE); + if (hInfo->error > 0) + return; + break; + case 5: + decode_sce_lfe(hDecoder, hInfo, ld, ID_SCE); + if (hInfo->error > 0) + return; + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + break; + case 6: + decode_sce_lfe(hDecoder, hInfo, ld, ID_SCE); + if (hInfo->error > 0) + return; + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + decode_sce_lfe(hDecoder, hInfo, ld, ID_LFE); + if (hInfo->error > 0) + return; + break; + case 7: /* 8 channels */ + decode_sce_lfe(hDecoder, hInfo, ld, ID_SCE); + if (hInfo->error > 0) + return; + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + decode_cpe(hDecoder, hInfo, ld, ID_CPE); + if (hInfo->error > 0) + return; + decode_sce_lfe(hDecoder, hInfo, ld, ID_LFE); + if (hInfo->error > 0) + return; + break; + default: + hInfo->error = 7; + return; + } +#if 0 + cnt = bits_to_decode() / 8; + while (cnt >= 1) + { + cnt -= extension_payload(cnt); + } +#endif + } +#endif + + /* new in corrigendum 14496-3:2002 */ +#ifdef DRM + if (hDecoder->object_type != DRM_ER_LC +#if 0 + && !hDecoder->latm_header_present +#endif + ) +#endif + { + faad_byte_align(ld); + } + + return; +} + +/* Table 4.4.4 and */ +/* Table 4.4.9 */ +static uint8_t single_lfe_channel_element(NeAACDecStruct *hDecoder, bitfile *ld, + uint8_t channel, uint8_t *tag) +{ + uint8_t retval = 0; + element sce = {0}; + ic_stream *ics = &(sce.ics1); + ALIGN int16_t spec_data[1024] = {0}; + + sce.element_instance_tag = (uint8_t)faad_getbits(ld, LEN_TAG + DEBUGVAR(1,38,"single_lfe_channel_element(): element_instance_tag")); + + *tag = sce.element_instance_tag; + sce.channel = channel; + sce.paired_channel = -1; + + retval = individual_channel_stream(hDecoder, &sce, ld, ics, 0, spec_data); + if (retval > 0) + return retval; + + /* IS not allowed in single channel */ + if (ics->is_used) + return 32; + +#ifdef SBR_DEC + /* check if next bitstream element is a fill element */ + /* if so, read it now so SBR decoding can be done in case of a file with SBR */ + if (faad_showbits(ld, LEN_SE_ID) == ID_FIL) + { + faad_flushbits(ld, LEN_SE_ID); + + /* one sbr_info describes a channel_element not a channel! */ + if ((retval = fill_element(hDecoder, ld, hDecoder->drc, hDecoder->fr_ch_ele)) > 0) + { + return retval; + } + } +#endif + + /* noiseless coding is done, spectral reconstruction is done now */ + retval = reconstruct_single_channel(hDecoder, ics, &sce, spec_data); + if (retval > 0) + return retval; + + return 0; +} + +/* Table 4.4.5 */ +static uint8_t channel_pair_element(NeAACDecStruct *hDecoder, bitfile *ld, + uint8_t channels, uint8_t *tag) +{ + ALIGN int16_t spec_data1[1024] = {0}; + ALIGN int16_t spec_data2[1024] = {0}; + element cpe = {0}; + ic_stream *ics1 = &(cpe.ics1); + ic_stream *ics2 = &(cpe.ics2); + uint8_t result; + + cpe.channel = channels; + cpe.paired_channel = channels+1; + + cpe.element_instance_tag = (uint8_t)faad_getbits(ld, LEN_TAG + DEBUGVAR(1,39,"channel_pair_element(): element_instance_tag")); + *tag = cpe.element_instance_tag; + + if ((cpe.common_window = faad_get1bit(ld + DEBUGVAR(1,40,"channel_pair_element(): common_window"))) & 1) + { + /* both channels have common ics information */ + if ((result = ics_info(hDecoder, ics1, ld, cpe.common_window)) > 0) + return result; + + ics1->ms_mask_present = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,41,"channel_pair_element(): ms_mask_present")); + if (ics1->ms_mask_present == 3) + { + /* bitstream error */ + return 32; + } + if (ics1->ms_mask_present == 1) + { + uint8_t g, sfb; + for (g = 0; g < ics1->num_window_groups; g++) + { + for (sfb = 0; sfb < ics1->max_sfb; sfb++) + { + ics1->ms_used[g][sfb] = faad_get1bit(ld + DEBUGVAR(1,42,"channel_pair_element(): faad_get1bit")); + } + } + } + +#ifdef ERROR_RESILIENCE + if ((hDecoder->object_type >= ER_OBJECT_START) && (ics1->predictor_data_present)) + { + if (( +#ifdef LTP_DEC + ics1->ltp.data_present = +#endif + faad_get1bit(ld DEBUGVAR(1,50,"channel_pair_element(): ltp.data_present"))) & 1) + { +#ifdef LTP_DEC + if ((result = ltp_data(hDecoder, ics1, &(ics1->ltp), ld)) > 0) + { + return result; + } +#else + return 26; +#endif + } + } +#endif + + memcpy(ics2, ics1, sizeof(ic_stream)); + } else { + ics1->ms_mask_present = 0; + } + + if ((result = individual_channel_stream(hDecoder, &cpe, ld, ics1, + 0, spec_data1)) > 0) + { + return result; + } + +#ifdef ERROR_RESILIENCE + if (cpe.common_window && (hDecoder->object_type >= ER_OBJECT_START) && + (ics1->predictor_data_present)) + { + if (( +#ifdef LTP_DEC + ics1->ltp2.data_present = +#endif + faad_get1bit(ld DEBUGVAR(1,50,"channel_pair_element(): ltp.data_present"))) & 1) + { +#ifdef LTP_DEC + if ((result = ltp_data(hDecoder, ics1, &(ics1->ltp2), ld)) > 0) + { + return result; + } +#else + return 26; +#endif + } + } +#endif + + if ((result = individual_channel_stream(hDecoder, &cpe, ld, ics2, + 0, spec_data2)) > 0) + { + return result; + } + +#ifdef SBR_DEC + /* check if next bitstream element is a fill element */ + /* if so, read it now so SBR decoding can be done in case of a file with SBR */ + if (faad_showbits(ld, LEN_SE_ID) == ID_FIL) + { + faad_flushbits(ld, LEN_SE_ID); + + /* one sbr_info describes a channel_element not a channel! */ + if ((result = fill_element(hDecoder, ld, hDecoder->drc, hDecoder->fr_ch_ele)) > 0) + { + return result; + } + } +#endif + + /* noiseless coding is done, spectral reconstruction is done now */ + if ((result = reconstruct_channel_pair(hDecoder, ics1, ics2, &cpe, + spec_data1, spec_data2)) > 0) + { + return result; + } + + return 0; +} + +/* Table 4.4.6 */ +static uint8_t ics_info(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld, + uint8_t common_window) +{ + uint8_t retval = 0; + uint8_t ics_reserved_bit; + + ics_reserved_bit = faad_get1bit(ld + DEBUGVAR(1,43,"ics_info(): ics_reserved_bit")); + if (ics_reserved_bit != 0) + return 32; + ics->window_sequence = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,44,"ics_info(): window_sequence")); + ics->window_shape = faad_get1bit(ld + DEBUGVAR(1,45,"ics_info(): window_shape")); + +#ifdef LD_DEC + /* No block switching in LD */ + if ((hDecoder->object_type == LD) && (ics->window_sequence != ONLY_LONG_SEQUENCE)) + return 32; +#endif + + if (ics->window_sequence == EIGHT_SHORT_SEQUENCE) + { + ics->max_sfb = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,46,"ics_info(): max_sfb (short)")); + ics->scale_factor_grouping = (uint8_t)faad_getbits(ld, 7 + DEBUGVAR(1,47,"ics_info(): scale_factor_grouping")); + } else { + ics->max_sfb = (uint8_t)faad_getbits(ld, 6 + DEBUGVAR(1,48,"ics_info(): max_sfb (long)")); + } + + /* get the grouping information */ + if ((retval = window_grouping_info(hDecoder, ics)) > 0) + return retval; + + + /* should be an error */ + /* check the range of max_sfb */ + if (ics->max_sfb > ics->num_swb) + return 16; + + if (ics->window_sequence != EIGHT_SHORT_SEQUENCE) + { + if ((ics->predictor_data_present = faad_get1bit(ld + DEBUGVAR(1,49,"ics_info(): predictor_data_present"))) & 1) + { + if (hDecoder->object_type == MAIN) /* MPEG2 style AAC predictor */ + { + uint8_t sfb; + uint8_t predictor_reset, predictor_reset_group_number, prediction_used; + uint8_t limit = min(ics->max_sfb, max_pred_sfb(hDecoder->sf_index)); + + predictor_reset = faad_get1bit(ld DEBUGVAR(1,53,"ics_info(): pred.predictor_reset")); + if (predictor_reset) + { + predictor_reset_group_number = + (uint8_t)faad_getbits(ld, 5 DEBUGVAR(1,54,"ics_info(): pred.predictor_reset_group_number")); + } else { + predictor_reset_group_number = 0; + } + + for (sfb = 0; sfb < limit; sfb++) + { + prediction_used = faad_get1bit(ld DEBUGVAR(1,55,"ics_info(): pred.prediction_used")); +#ifdef MAIN_DEC + ics->pred.prediction_used[sfb] = prediction_used; +#endif + } +#ifdef MAIN_DEC + ics->pred.limit = limit; + ics->pred.predictor_reset = predictor_reset; + ics->pred.predictor_reset_group_number = predictor_reset_group_number; +#else + (void)predictor_reset_group_number; + (void)prediction_used; +#endif + } +#ifdef LTP_DEC + else { /* Long Term Prediction */ + if (hDecoder->object_type < ER_OBJECT_START) + { + if ((ics->ltp.data_present = faad_get1bit(ld + DEBUGVAR(1,50,"ics_info(): ltp.data_present"))) & 1) + { + if ((retval = ltp_data(hDecoder, ics, &(ics->ltp), ld)) > 0) + { + return retval; + } + } + if (common_window) + { + if ((ics->ltp2.data_present = faad_get1bit(ld + DEBUGVAR(1,51,"ics_info(): ltp2.data_present"))) & 1) + { + if ((retval = ltp_data(hDecoder, ics, &(ics->ltp2), ld)) > 0) + { + return retval; + } + } + } + } +#ifdef ERROR_RESILIENCE + if (!common_window && (hDecoder->object_type >= ER_OBJECT_START)) + { + if ((ics->ltp.data_present = faad_get1bit(ld + DEBUGVAR(1,50,"ics_info(): ltp.data_present"))) & 1) + { + if ((retval = ltp_data(hDecoder, ics, &(ics->ltp), ld)) > 0) + { + return retval; + } + } + } +#endif /* ERROR_RESILIENCE */ + } +#else /* LTP_DEC */ + (void)common_window; +#endif /* LTP_DEC */ + } + } + + return retval; +} + +/* Table 4.4.7 */ +static uint8_t pulse_data(ic_stream *ics, pulse_info *pul, bitfile *ld) +{ + uint8_t i; + + pul->number_pulse = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,56,"pulse_data(): number_pulse")); + pul->pulse_start_sfb = (uint8_t)faad_getbits(ld, 6 + DEBUGVAR(1,57,"pulse_data(): pulse_start_sfb")); + + /* check the range of pulse_start_sfb */ + if (pul->pulse_start_sfb > ics->num_swb) + return 16; + + for (i = 0; i < pul->number_pulse+1; i++) + { + pul->pulse_offset[i] = (uint8_t)faad_getbits(ld, 5 + DEBUGVAR(1,58,"pulse_data(): pulse_offset")); +#if 0 + printf("%d\n", pul->pulse_offset[i]); +#endif + pul->pulse_amp[i] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,59,"pulse_data(): pulse_amp")); +#if 0 + printf("%d\n", pul->pulse_amp[i]); +#endif + } + + return 0; +} + +#ifdef COUPLING_DEC +/* Table 4.4.8: Currently just for skipping the bits... */ +static uint8_t coupling_channel_element(NeAACDecStruct *hDecoder, bitfile *ld) +{ + uint8_t c, result = 0; + uint8_t ind_sw_cce_flag = 0; + uint8_t num_gain_element_lists = 0; + uint8_t num_coupled_elements = 0; + + element el_empty = {0}; + ic_stream ics_empty = {0}; + int16_t sh_data[1024]; + + c = faad_getbits(ld, LEN_TAG + DEBUGVAR(1,900,"coupling_channel_element(): element_instance_tag")); + + ind_sw_cce_flag = faad_get1bit(ld + DEBUGVAR(1,901,"coupling_channel_element(): ind_sw_cce_flag")); + num_coupled_elements = faad_getbits(ld, 3 + DEBUGVAR(1,902,"coupling_channel_element(): num_coupled_elements")); + + for (c = 0; c < num_coupled_elements + 1; c++) + { + uint8_t cc_target_is_cpe, cc_target_tag_select; + + num_gain_element_lists++; + + cc_target_is_cpe = faad_get1bit(ld + DEBUGVAR(1,903,"coupling_channel_element(): cc_target_is_cpe")); + cc_target_tag_select = faad_getbits(ld, 4 + DEBUGVAR(1,904,"coupling_channel_element(): cc_target_tag_select")); + + if (cc_target_is_cpe) + { + uint8_t cc_l = faad_get1bit(ld + DEBUGVAR(1,905,"coupling_channel_element(): cc_l")); + uint8_t cc_r = faad_get1bit(ld + DEBUGVAR(1,906,"coupling_channel_element(): cc_r")); + + if (cc_l && cc_r) + num_gain_element_lists++; + } + } + + faad_get1bit(ld + DEBUGVAR(1,907,"coupling_channel_element(): cc_domain")); + faad_get1bit(ld + DEBUGVAR(1,908,"coupling_channel_element(): gain_element_sign")); + faad_getbits(ld, 2 + DEBUGVAR(1,909,"coupling_channel_element(): gain_element_scale")); + + if ((result = individual_channel_stream(hDecoder, &el_empty, ld, &ics_empty, + 0, sh_data)) > 0) + { + return result; + } + + /* IS not allowed in single channel */ + if (ics->is_used) + return 32; + + for (c = 1; c < num_gain_element_lists; c++) + { + uint8_t cge; + + if (ind_sw_cce_flag) + { + cge = 1; + } else { + cge = faad_get1bit(ld + DEBUGVAR(1,910,"coupling_channel_element(): common_gain_element_present")); + } + + if (cge) + { + huffman_scale_factor(ld); + } else { + uint8_t g, sfb; + + for (g = 0; g < ics_empty.num_window_groups; g++) + { + for (sfb = 0; sfb < ics_empty.max_sfb; sfb++) + { + if (ics_empty.sfb_cb[g][sfb] != ZERO_HCB) + huffman_scale_factor(ld); + } + } + } + } + + return 0; +} +#endif + +/* Table 4.4.10 */ +static uint16_t data_stream_element(NeAACDecStruct *hDecoder, bitfile *ld) +{ + uint8_t byte_aligned; + uint16_t i, count; + (void)hDecoder; /* TODO: remove parameter; rename method; why result is unused? */ + + /* element_instance_tag = */ faad_getbits(ld, LEN_TAG + DEBUGVAR(1,60,"data_stream_element(): element_instance_tag")); + byte_aligned = faad_get1bit(ld + DEBUGVAR(1,61,"data_stream_element(): byte_aligned")); + count = (uint16_t)faad_getbits(ld, 8 + DEBUGVAR(1,62,"data_stream_element(): count")); + if (count == 255) + { + count += (uint16_t)faad_getbits(ld, 8 + DEBUGVAR(1,63,"data_stream_element(): extra count")); + } + if (byte_aligned) + faad_byte_align(ld); + + for (i = 0; i < count; i++) + { + faad_getbits(ld, LEN_BYTE + DEBUGVAR(1,64,"data_stream_element(): data_stream_byte")); + } + + return count; +} + +/* Table 4.4.11 */ +static uint8_t fill_element(NeAACDecStruct *hDecoder, bitfile *ld, drc_info *drc +#ifdef SBR_DEC + ,uint8_t sbr_ele +#endif + ) +{ + uint16_t count; +#ifdef SBR_DEC + uint8_t bs_extension_type; +#endif + + count = (uint16_t)faad_getbits(ld, 4 + DEBUGVAR(1,65,"fill_element(): count")); + if (count == 15) + { + count += (uint16_t)faad_getbits(ld, 8 + DEBUGVAR(1,66,"fill_element(): extra count")) - 1; + } + + if (count > 0) + { +#ifdef SBR_DEC + bs_extension_type = (uint8_t)faad_showbits(ld, 4); + + if ((bs_extension_type == EXT_SBR_DATA) || + (bs_extension_type == EXT_SBR_DATA_CRC)) + { + if (sbr_ele == INVALID_SBR_ELEMENT) + return 24; + + if (!hDecoder->sbr[sbr_ele]) + { + hDecoder->sbr[sbr_ele] = sbrDecodeInit(hDecoder->frameLength, + hDecoder->element_id[sbr_ele], 2*get_sample_rate(hDecoder->sf_index), + hDecoder->downSampledSBR +#ifdef DRM + , 0 +#endif + ); + } + if (!hDecoder->sbr[sbr_ele]) + return 19; + + hDecoder->sbr_present_flag = 1; + + /* parse the SBR data */ + hDecoder->sbr[sbr_ele]->ret = sbr_extension_data(ld, hDecoder->sbr[sbr_ele], count, + hDecoder->postSeekResetFlag); + +#if 0 + if (hDecoder->sbr[sbr_ele]->ret > 0) + { + printf("%s\n", NeAACDecGetErrorMessage(hDecoder->sbr[sbr_ele]->ret)); + } +#endif + +#if (defined(PS_DEC) || defined(DRM_PS)) + if (hDecoder->sbr[sbr_ele]->ps_used) + { + hDecoder->ps_used[sbr_ele] = 1; + + /* set element independent flag to 1 as well */ + hDecoder->ps_used_global = 1; + } +#endif + } else if (bs_extension_type == 0) { + // padding + int i; + for (i = 0; i < count; i++) + { + if ((uint8_t)faad_getbits(ld, 8) != 0) + fprintf(stderr, "sbr_extension_data: zero fil data not zero\n"); + } + } else { +#endif +#ifndef DRM + while (count > 0) + { + uint16_t payload_bytes = extension_payload(ld, drc, count); + if (payload_bytes <= count) { + count -= payload_bytes; + } else { + count = 0; + } + } +#else + (void)drc; + return 30; +#endif +#ifdef SBR_DEC + } +#endif + } + + return 0; +} + +/* Table 4.4.12 */ +#ifdef SSR_DEC +static void gain_control_data(bitfile *ld, ic_stream *ics) +{ + uint8_t bd, wd, ad; + ssr_info *ssr = &(ics->ssr); + + ssr->max_band = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,1000,"gain_control_data(): max_band")); + + if (ics->window_sequence == ONLY_LONG_SEQUENCE) + { + for (bd = 1; bd <= ssr->max_band; bd++) + { + for (wd = 0; wd < 1; wd++) + { + ssr->adjust_num[bd][wd] = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,1001,"gain_control_data(): adjust_num")); + + for (ad = 0; ad < ssr->adjust_num[bd][wd]; ad++) + { + ssr->alevcode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,1002,"gain_control_data(): alevcode")); + ssr->aloccode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 5 + DEBUGVAR(1,1003,"gain_control_data(): aloccode")); + } + } + } + } else if (ics->window_sequence == LONG_START_SEQUENCE) { + for (bd = 1; bd <= ssr->max_band; bd++) + { + for (wd = 0; wd < 2; wd++) + { + ssr->adjust_num[bd][wd] = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,1001,"gain_control_data(): adjust_num")); + + for (ad = 0; ad < ssr->adjust_num[bd][wd]; ad++) + { + ssr->alevcode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,1002,"gain_control_data(): alevcode")); + if (wd == 0) + { + ssr->aloccode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,1003,"gain_control_data(): aloccode")); + } else { + ssr->aloccode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,1003,"gain_control_data(): aloccode")); + } + } + } + } + } else if (ics->window_sequence == EIGHT_SHORT_SEQUENCE) { + for (bd = 1; bd <= ssr->max_band; bd++) + { + for (wd = 0; wd < 8; wd++) + { + ssr->adjust_num[bd][wd] = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,1001,"gain_control_data(): adjust_num")); + + for (ad = 0; ad < ssr->adjust_num[bd][wd]; ad++) + { + ssr->alevcode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,1002,"gain_control_data(): alevcode")); + ssr->aloccode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,1003,"gain_control_data(): aloccode")); + } + } + } + } else if (ics->window_sequence == LONG_STOP_SEQUENCE) { + for (bd = 1; bd <= ssr->max_band; bd++) + { + for (wd = 0; wd < 2; wd++) + { + ssr->adjust_num[bd][wd] = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,1001,"gain_control_data(): adjust_num")); + + for (ad = 0; ad < ssr->adjust_num[bd][wd]; ad++) + { + ssr->alevcode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,1002,"gain_control_data(): alevcode")); + + if (wd == 0) + { + ssr->aloccode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,1003,"gain_control_data(): aloccode")); + } else { + ssr->aloccode[bd][wd][ad] = (uint8_t)faad_getbits(ld, 5 + DEBUGVAR(1,1003,"gain_control_data(): aloccode")); + } + } + } + } + } +} +#endif + +#ifdef DRM +/* Table 4.4.13 ASME */ +void DRM_aac_scalable_main_element(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, + bitfile *ld, program_config *pce, drc_info *drc) +{ + uint8_t channels = hDecoder->fr_channels = 0; + uint8_t this_layer_stereo = (hDecoder->channelConfiguration > 1) ? 1 : 0; + element cpe = {0}; + ic_stream *ics1 = &(cpe.ics1); + ic_stream *ics2 = &(cpe.ics2); + ALIGN int16_t spec_data1[1024] = {0}; + ALIGN int16_t spec_data2[1024] = {0}; + + (void)drc; /* TODO: remove unused parameter? */ + (void)pce; /* TODO: remove unused parameter? */ + + hDecoder->fr_ch_ele = 0; + + hInfo->error = DRM_aac_scalable_main_header(hDecoder, ics1, ics2, ld, this_layer_stereo); + if (hInfo->error > 0) + return; + + cpe.common_window = 1; + if (this_layer_stereo) + { + hDecoder->element_id[0] = ID_CPE; + if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 0) + hDecoder->element_output_channels[hDecoder->fr_ch_ele] = 2; + } else { + hDecoder->element_id[0] = ID_SCE; + } + + if (this_layer_stereo) + { + cpe.channel = 0; + cpe.paired_channel = 1; + } + + + /* Stereo2 / Mono1 */ + ics1->tns_data_present = faad_get1bit(ld); + +#if defined(LTP_DEC) + ics1->ltp.data_present = faad_get1bit(ld); +#elif defined (DRM) + if(faad_get1bit(ld)) { + hInfo->error = 26; + return; + } +#else + faad_get1bit(ld); +#endif + + hInfo->error = side_info(hDecoder, &cpe, ld, ics1, 1); + if (hInfo->error > 0) + return; + if (this_layer_stereo) + { + /* Stereo3 */ + ics2->tns_data_present = faad_get1bit(ld); +#ifdef LTP_DEC + ics1->ltp.data_present = +#endif + faad_get1bit(ld); + hInfo->error = side_info(hDecoder, &cpe, ld, ics2, 1); + if (hInfo->error > 0) + return; + } + /* Stereo4 / Mono2 */ + if (ics1->tns_data_present) + tns_data(hDecoder, ics1, &(ics1->tns), ld); + if (this_layer_stereo) + { + /* Stereo5 */ + if (ics2->tns_data_present) + tns_data(hDecoder, ics2, &(ics2->tns), ld); + } + +#ifdef DRM + /* CRC check */ + if (hDecoder->object_type == DRM_ER_LC) + { + if ((hInfo->error = (uint8_t)faad_check_CRC(ld, (uint16_t)faad_get_processed_bits(ld) - 8)) > 0) + return; + } +#endif + + /* Stereo6 / Mono3 */ + /* error resilient spectral data decoding */ + if ((hInfo->error = reordered_spectral_data(hDecoder, ics1, ld, spec_data1)) > 0) + { + return; + } + if (this_layer_stereo) + { + /* Stereo7 */ + /* error resilient spectral data decoding */ + if ((hInfo->error = reordered_spectral_data(hDecoder, ics2, ld, spec_data2)) > 0) + { + return; + } + } + + +#ifdef DRM +#ifdef SBR_DEC + /* In case of DRM we need to read the SBR info before channel reconstruction */ + if ((hDecoder->sbr_present_flag == 1) && (hDecoder->object_type == DRM_ER_LC)) + { + bitfile ld_sbr = {0}; + uint32_t i; + uint16_t count = 0; + uint8_t *revbuffer; + uint8_t *prevbufstart; + uint8_t *pbufend; + + /* all forward bitreading should be finished at this point */ + uint32_t bitsconsumed = faad_get_processed_bits(ld); + uint32_t buffer_size = faad_origbitbuffer_size(ld); + uint8_t *buffer = (uint8_t*)faad_origbitbuffer(ld); + + if (bitsconsumed + 8 > buffer_size*8) + { + hInfo->error = 14; + return; + } + + if (!hDecoder->sbr[0]) + { + hDecoder->sbr[0] = sbrDecodeInit(hDecoder->frameLength, hDecoder->element_id[0], + 2*get_sample_rate(hDecoder->sf_index), 0 /* ds SBR */, 1); + } + if (!hDecoder->sbr[0]) + { + hInfo->error = 19; + return; + } + + /* Reverse bit reading of SBR data in DRM audio frame */ + revbuffer = (uint8_t*)faad_malloc(buffer_size*sizeof(uint8_t)); + prevbufstart = revbuffer; + pbufend = &buffer[buffer_size - 1]; + for (i = 0; i < buffer_size; i++) + *prevbufstart++ = reverse_byte(*pbufend--); + + /* Set SBR data */ + /* consider 8 bits from AAC-CRC */ + /* SBR buffer size is original buffer size minus AAC buffer size */ + count = (uint16_t)bit2byte(buffer_size*8 - bitsconsumed); + faad_initbits(&ld_sbr, revbuffer, count); + + hDecoder->sbr[0]->sample_rate = get_sample_rate(hDecoder->sf_index); + hDecoder->sbr[0]->sample_rate *= 2; + + faad_getbits(&ld_sbr, 8); /* Skip 8-bit CRC */ + + hDecoder->sbr[0]->ret = sbr_extension_data(&ld_sbr, hDecoder->sbr[0], count, hDecoder->postSeekResetFlag); +#if (defined(PS_DEC) || defined(DRM_PS)) + if (hDecoder->sbr[0]->ps_used) + { + hDecoder->ps_used[0] = 1; + hDecoder->ps_used_global = 1; + } +#endif + + if (ld_sbr.error) + { + hDecoder->sbr[0]->ret = 1; + } + + /* check CRC */ + /* no need to check it if there was already an error */ + if (hDecoder->sbr[0]->ret == 0) + hDecoder->sbr[0]->ret = (uint8_t)faad_check_CRC(&ld_sbr, (uint16_t)faad_get_processed_bits(&ld_sbr) - 8); + + /* SBR data was corrupted, disable it until the next header */ + if (hDecoder->sbr[0]->ret != 0) + { + hDecoder->sbr[0]->header_count = 0; + } + + faad_endbits(&ld_sbr); + + if (revbuffer) + faad_free(revbuffer); + } +#endif +#endif + + if (this_layer_stereo) + { + hInfo->error = reconstruct_channel_pair(hDecoder, ics1, ics2, &cpe, spec_data1, spec_data2); + if (hInfo->error > 0) + return; + } else { + hInfo->error = reconstruct_single_channel(hDecoder, ics1, &cpe, spec_data1); + if (hInfo->error > 0) + return; + } + + /* map output channels position to internal data channels */ + if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 2) + { + /* this might be faulty when pce_set is true */ + hDecoder->internal_channel[channels] = channels; + hDecoder->internal_channel[channels+1] = channels+1; + } else { + hDecoder->internal_channel[channels] = channels; + } + + hDecoder->fr_channels += hDecoder->element_output_channels[hDecoder->fr_ch_ele]; + hDecoder->fr_ch_ele++; + + return; +} + +/* Table 4.4.15 */ +static int8_t DRM_aac_scalable_main_header(NeAACDecStruct *hDecoder, ic_stream *ics1, ic_stream *ics2, + bitfile *ld, uint8_t this_layer_stereo) +{ + uint8_t retval = 0; + uint8_t ics_reserved_bit; + + ics_reserved_bit = faad_get1bit(ld + DEBUGVAR(1,300,"aac_scalable_main_header(): ics_reserved_bits")); + if (ics_reserved_bit != 0) + return 32; + ics1->window_sequence = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,301,"aac_scalable_main_header(): window_sequence")); + ics1->window_shape = faad_get1bit(ld + DEBUGVAR(1,302,"aac_scalable_main_header(): window_shape")); + + if (ics1->window_sequence == EIGHT_SHORT_SEQUENCE) + { + ics1->max_sfb = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,303,"aac_scalable_main_header(): max_sfb (short)")); + ics1->scale_factor_grouping = (uint8_t)faad_getbits(ld, 7 + DEBUGVAR(1,304,"aac_scalable_main_header(): scale_factor_grouping")); + } else { + ics1->max_sfb = (uint8_t)faad_getbits(ld, 6 + DEBUGVAR(1,305,"aac_scalable_main_header(): max_sfb (long)")); + } + + /* get the grouping information */ + if ((retval = window_grouping_info(hDecoder, ics1)) > 0) + return retval; + + /* should be an error */ + /* check the range of max_sfb */ + if (ics1->max_sfb > ics1->num_swb) + return 16; + + if (this_layer_stereo) + { + ics1->ms_mask_present = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,306,"aac_scalable_main_header(): ms_mask_present")); + if (ics1->ms_mask_present == 3) + { + /* bitstream error */ + return 32; + } + if (ics1->ms_mask_present == 1) + { + uint8_t g, sfb; + for (g = 0; g < ics1->num_window_groups; g++) + { + for (sfb = 0; sfb < ics1->max_sfb; sfb++) + { + ics1->ms_used[g][sfb] = faad_get1bit(ld + DEBUGVAR(1,307,"aac_scalable_main_header(): faad_get1bit")); + } + } + } + + memcpy(ics2, ics1, sizeof(ic_stream)); + } else { + ics1->ms_mask_present = 0; + } + + return 0; +} +#endif + +static uint8_t side_info(NeAACDecStruct *hDecoder, element *ele, + bitfile *ld, ic_stream *ics, uint8_t scal_flag) +{ + uint8_t result; + + ics->global_gain = (uint8_t)faad_getbits(ld, 8 + DEBUGVAR(1,67,"individual_channel_stream(): global_gain")); + + if (!ele->common_window && !scal_flag) + { + if ((result = ics_info(hDecoder, ics, ld, ele->common_window)) > 0) + return result; + } + + if ((result = section_data(hDecoder, ics, ld)) > 0) + return result; + + if ((result = scale_factor_data(hDecoder, ics, ld)) > 0) + return result; + + if (!scal_flag) + { + /** + ** NOTE: It could be that pulse data is available in scalable AAC too, + ** as said in Amendment 1, this could be only the case for ER AAC, + ** though. (have to check this out later) + **/ + /* get pulse data */ + if ((ics->pulse_data_present = faad_get1bit(ld + DEBUGVAR(1,68,"individual_channel_stream(): pulse_data_present"))) & 1) + { + if ((result = pulse_data(ics, &(ics->pul), ld)) > 0) + return result; + } + + /* get tns data */ + if ((ics->tns_data_present = faad_get1bit(ld + DEBUGVAR(1,69,"individual_channel_stream(): tns_data_present"))) & 1) + { +#ifdef ERROR_RESILIENCE + if (hDecoder->object_type < ER_OBJECT_START) +#endif + tns_data(hDecoder, ics, &(ics->tns), ld); + } + + /* get gain control data */ + if ((ics->gain_control_data_present = faad_get1bit(ld + DEBUGVAR(1,70,"individual_channel_stream(): gain_control_data_present"))) & 1) + { +#ifdef SSR_DEC + if (hDecoder->object_type != SSR) + return 1; + else + gain_control_data(ld, ics); +#else + return 1; +#endif + } + } + +#ifdef ERROR_RESILIENCE + if (hDecoder->aacSpectralDataResilienceFlag) + { + ics->length_of_reordered_spectral_data = (uint16_t)faad_getbits(ld, 14 + DEBUGVAR(1,147,"individual_channel_stream(): length_of_reordered_spectral_data")); + + if (hDecoder->channelConfiguration == 2) + { + if (ics->length_of_reordered_spectral_data > 6144) + ics->length_of_reordered_spectral_data = 6144; + } else { + if (ics->length_of_reordered_spectral_data > 12288) + ics->length_of_reordered_spectral_data = 12288; + } + + ics->length_of_longest_codeword = (uint8_t)faad_getbits(ld, 6 + DEBUGVAR(1,148,"individual_channel_stream(): length_of_longest_codeword")); + if (ics->length_of_longest_codeword >= 49) + ics->length_of_longest_codeword = 49; + } + + /* RVLC spectral data is put here */ + if (hDecoder->aacScalefactorDataResilienceFlag) + { + if ((result = rvlc_decode_scale_factors(ics, ld)) > 0) + return result; + } +#endif + + return 0; +} + +/* Table 4.4.24 */ +static uint8_t individual_channel_stream(NeAACDecStruct *hDecoder, element *ele, + bitfile *ld, ic_stream *ics, uint8_t scal_flag, + int16_t *spec_data) +{ + uint8_t result; + + result = side_info(hDecoder, ele, ld, ics, scal_flag); + if (result > 0) + return result; + + if (hDecoder->object_type >= ER_OBJECT_START) + { + if (ics->tns_data_present) + tns_data(hDecoder, ics, &(ics->tns), ld); + } + +#ifdef DRM + /* CRC check */ + if (hDecoder->object_type == DRM_ER_LC) + { + if ((result = (uint8_t)faad_check_CRC(ld, (uint16_t)faad_get_processed_bits(ld) - 8)) > 0) + return result; + } +#endif + +#ifdef ERROR_RESILIENCE + if (hDecoder->aacSpectralDataResilienceFlag) + { + /* error resilient spectral data decoding */ + if ((result = reordered_spectral_data(hDecoder, ics, ld, spec_data)) > 0) + { + return result; + } + } else { +#endif + /* decode the spectral data */ + if ((result = spectral_data(hDecoder, ics, ld, spec_data)) > 0) + { + return result; + } +#ifdef ERROR_RESILIENCE + } +#endif + + /* pulse coding reconstruction */ + if (ics->pulse_data_present) + { + if (ics->window_sequence != EIGHT_SHORT_SEQUENCE) + { + if ((result = pulse_decode(ics, spec_data, hDecoder->frameLength)) > 0) + return result; + } else { + return 2; /* pulse coding not allowed for short blocks */ + } + } + + return 0; +} + +/* Table 4.4.25 */ +static uint8_t section_data(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld) +{ + uint8_t g; + uint8_t sect_esc_val, sect_bits; + uint8_t sect_lim; /* 51 or 120, anyways less than 127. */ + + if (ics->window_sequence == EIGHT_SHORT_SEQUENCE) { + sect_bits = 3; + sect_lim = 8 * 15; + } else { + sect_bits = 5; + sect_lim = MAX_SFB; + } + sect_esc_val = (1u<max_sfb); + printf(" sect top cb\n"); +#endif + + for (g = 0; g < ics->num_window_groups; g++) + { + uint8_t k = 0; + uint8_t i = 0; + + while (k < ics->max_sfb) + { +#ifdef ERROR_RESILIENCE + uint8_t vcb11 = 0; +#endif + uint8_t sfb; + uint8_t sect_len_incr; + uint8_t sect_len = 0; + uint8_t sect_cb_bits = 4; + + /* if "faad_getbits" detects error and returns "0", "k" is never + incremented and we cannot leave the while loop */ + if (ld->error != 0) + return 14; + if (i >= sect_lim) + return 15; + +#ifdef ERROR_RESILIENCE + if (hDecoder->aacSectionDataResilienceFlag) + sect_cb_bits = 5; +#endif + + ics->sect_cb[g][i] = (uint8_t)faad_getbits(ld, sect_cb_bits + DEBUGVAR(1,71,"section_data(): sect_cb")); + + if (ics->sect_cb[g][i] == 12) + return 32; + +#if 0 + printf("%d\n", ics->sect_cb[g][i]); +#endif + +#ifndef DRM + if (ics->sect_cb[g][i] == NOISE_HCB) + ics->noise_used = 1; +#else + /* PNS not allowed in DRM */ + if (ics->sect_cb[g][i] == NOISE_HCB) + return 29; +#endif + if (ics->sect_cb[g][i] == INTENSITY_HCB2 || ics->sect_cb[g][i] == INTENSITY_HCB) + ics->is_used = 1; + +#ifdef ERROR_RESILIENCE + if (hDecoder->aacSectionDataResilienceFlag) + { + if ((ics->sect_cb[g][i] == 11) || + ((ics->sect_cb[g][i] >= 16) && (ics->sect_cb[g][i] <= 32))) + { + vcb11 = 1; + } + } + if (vcb11) + { + sect_len_incr = 1; + } else { +#endif + sect_len_incr = (uint8_t)faad_getbits(ld, sect_bits + DEBUGVAR(1,72,"section_data(): sect_len_incr")); +#ifdef ERROR_RESILIENCE + } +#endif + while (sect_len_incr == sect_esc_val /* && + (k+sect_len < ics->max_sfb)*/) + { + sect_len += sect_len_incr; + if (sect_len > sect_lim) + return 15; + sect_len_incr = (uint8_t)faad_getbits(ld, sect_bits + DEBUGVAR(1,72,"section_data(): sect_len_incr")); + } + + sect_len += sect_len_incr; + + ics->sect_start[g][i] = k; + ics->sect_end[g][i] = k + sect_len; + +#if 0 + printf("%d\n", ics->sect_start[g][i]); +#endif +#if 0 + printf("%d\n", ics->sect_end[g][i]); +#endif + + if (sect_len > sect_lim) + return 15; + if (k + sect_len > sect_lim) + return 15; + + for (sfb = k; sfb < k + sect_len; sfb++) + { + ics->sfb_cb[g][sfb] = ics->sect_cb[g][i]; +#if 0 + printf("%d\n", ics->sfb_cb[g][sfb]); +#endif + } + +#if 0 + printf(" %6d %6d %6d\n", + i, + ics->sect_end[g][i], + ics->sect_cb[g][i]); +#endif + + k += sect_len; /* k <= sect_lim */ + i++; + } + ics->num_sec[g] = i; + + /* the sum of all sect_len_incr elements for a given window + * group shall equal max_sfb */ + if (k != ics->max_sfb) + { + return 32; + } +#if 0 + printf("%d\n", ics->num_sec[g]); +#endif + } + +#if 0 + printf("\n"); +#endif + + return 0; +} + +/* + * decode_scale_factors() + * decodes the scalefactors from the bitstream + */ +/* + * All scalefactors (and also the stereo positions and pns energies) are + * transmitted using Huffman coded DPCM relative to the previous active + * scalefactor (respectively previous stereo position or previous pns energy, + * see subclause 4.6.2 and 4.6.3). The first active scalefactor is + * differentially coded relative to the global gain. + */ +static uint8_t decode_scale_factors(ic_stream *ics, bitfile *ld) +{ + uint8_t g, sfb; + int16_t t; + + int16_t scale_factor = ics->global_gain; + int16_t is_position = 0; + int16_t scale_factor_max = 255; +#ifdef FIXED_POINT + /* TODO: consider rolling out to regular build. */ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* The value is inexact, adjusted to current fuzzer findings. */ + scale_factor_max = 165; +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#endif // FIXED_POINT +#ifndef DRM + int8_t noise_pcm_flag = 1; + int16_t noise_energy = ics->global_gain - 90; +#endif + + for (g = 0; g < ics->num_window_groups; g++) + { + for (sfb = 0; sfb < ics->max_sfb; sfb++) + { + switch (ics->sfb_cb[g][sfb]) + { + case ZERO_HCB: /* zero book */ + ics->scale_factors[g][sfb] = 0; +//#define SF_PRINT +#ifdef SF_PRINT + printf("%d\n", ics->scale_factors[g][sfb]); +#endif + break; + case INTENSITY_HCB: /* intensity books */ + case INTENSITY_HCB2: + + /* decode intensity position */ + t = huffman_scale_factor(ld); + is_position += (t - 60); + ics->scale_factors[g][sfb] = is_position; +#ifdef SF_PRINT + printf("%d\n", ics->scale_factors[g][sfb]); +#endif + + break; + case NOISE_HCB: /* noise books */ + +#ifndef DRM + /* decode noise energy */ + if (noise_pcm_flag) + { + noise_pcm_flag = 0; + t = (int16_t)faad_getbits(ld, 9 + DEBUGVAR(1,73,"scale_factor_data(): first noise")) - 256; + } else { + t = huffman_scale_factor(ld); + t -= 60; + } + noise_energy += t; + ics->scale_factors[g][sfb] = noise_energy; +#ifdef SF_PRINT + printf("%d\n", ics->scale_factors[g][sfb]); +#endif +#else + /* PNS not allowed in DRM */ + return 29; +#endif + + break; + default: /* spectral books */ + + /* ics->scale_factors[g][sfb] must be between 0 and 255 */ + + ics->scale_factors[g][sfb] = 0; + + /* decode scale factor */ + t = huffman_scale_factor(ld); + scale_factor += (t - 60); + if (scale_factor < 0 || scale_factor > 255) + return 4; + ics->scale_factors[g][sfb] = min(scale_factor, scale_factor_max); +#ifdef SF_PRINT + printf("%d\n", ics->scale_factors[g][sfb]); +#endif + + break; + } + } + } + + return 0; +} + +/* Table 4.4.26 */ +static uint8_t scale_factor_data(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld) +{ + uint8_t ret = 0; +#ifdef PROFILE + int64_t count = faad_get_ts(); +#endif + +#ifdef ERROR_RESILIENCE + if (!hDecoder->aacScalefactorDataResilienceFlag) + { +#endif + ret = decode_scale_factors(ics, ld); +#ifdef ERROR_RESILIENCE + } else { + /* In ER AAC the parameters for RVLC are seperated from the actual + data that holds the scale_factors. + Strangely enough, 2 parameters for HCR are put inbetween them. + */ + ret = rvlc_scale_factor_data(ics, ld); + } +#endif + +#ifdef PROFILE + count = faad_get_ts() - count; + hDecoder->scalefac_cycles += count; +#endif + + return ret; +} + +/* Table 4.4.27 */ +static void tns_data(NeAACDecStruct *hDecoder, ic_stream *ics, tns_info *tns, bitfile *ld) +{ + uint8_t w, filt, i, coef_bits; + uint8_t n_filt_bits = 2; + uint8_t length_bits = 6; + uint8_t order_bits = 5; + + if (ics->window_sequence == EIGHT_SHORT_SEQUENCE) + { + n_filt_bits = 1; + length_bits = 4; + order_bits = 3; + } + + for (w = 0; w < ics->num_windows; w++) + { + uint8_t start_coef_bits = 3; +#ifdef HDC + if (hDecoder->object_type == HDC_LC && ics->window_sequence != EIGHT_SHORT_SEQUENCE) + tns->n_filt[w] = 1; + else +#endif + tns->n_filt[w] = (uint8_t)faad_getbits(ld, n_filt_bits + DEBUGVAR(1,74,"tns_data(): n_filt")); +#if 0 + printf("%d\n", tns->n_filt[w]); +#endif + + if (tns->n_filt[w]) + { + if ((tns->coef_res[w] = faad_get1bit(ld + DEBUGVAR(1,75,"tns_data(): coef_res"))) & 1) + start_coef_bits = 4; +#if 0 + printf("%d\n", tns->coef_res[w]); +#endif + } + + for (filt = 0; filt < tns->n_filt[w]; filt++) + { + tns->length[w][filt] = (uint8_t)faad_getbits(ld, length_bits + DEBUGVAR(1,76,"tns_data(): length")); +#if 0 + printf("%d\n", tns->length[w][filt]); +#endif + tns->order[w][filt] = (uint8_t)faad_getbits(ld, order_bits + DEBUGVAR(1,77,"tns_data(): order")); +#if 0 + printf("%d\n", tns->order[w][filt]); +#endif + if (tns->order[w][filt]) + { + tns->direction[w][filt] = faad_get1bit(ld + DEBUGVAR(1,78,"tns_data(): direction")); +#if 0 + printf("%d\n", tns->direction[w][filt]); +#endif + tns->coef_compress[w][filt] = faad_get1bit(ld + DEBUGVAR(1,79,"tns_data(): coef_compress")); +#if 0 + printf("%d\n", tns->coef_compress[w][filt]); +#endif + + coef_bits = start_coef_bits - tns->coef_compress[w][filt]; + for (i = 0; i < tns->order[w][filt]; i++) + { + tns->coef[w][filt][i] = (uint8_t)faad_getbits(ld, coef_bits + DEBUGVAR(1,80,"tns_data(): coef")); +#if 0 + printf("%d\n", tns->coef[w][filt][i]); +#endif + } + } + } + } +} + +#ifdef LTP_DEC +/* Table 4.4.28 */ +static uint8_t ltp_data(NeAACDecStruct *hDecoder, ic_stream *ics, ltp_info *ltp, bitfile *ld) +{ + uint8_t sfb, w; + + ltp->lag = 0; + +#ifdef LD_DEC + if (hDecoder->object_type == LD) + { + ltp->lag_update = (uint8_t)faad_getbits(ld, 1 + DEBUGVAR(1,142,"ltp_data(): lag_update")); + + if (ltp->lag_update) + { + ltp->lag = (uint16_t)faad_getbits(ld, 10 + DEBUGVAR(1,81,"ltp_data(): lag")); + } + } else { +#endif + ltp->lag = (uint16_t)faad_getbits(ld, 11 + DEBUGVAR(1,81,"ltp_data(): lag")); +#ifdef LD_DEC + } +#endif + + /* Check length of lag */ + if (ltp->lag > (hDecoder->frameLength << 1)) + return 18; + + ltp->coef = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,82,"ltp_data(): coef")); + + if (ics->window_sequence == EIGHT_SHORT_SEQUENCE) + { + for (w = 0; w < ics->num_windows; w++) + { + if ((ltp->short_used[w] = faad_get1bit(ld + DEBUGVAR(1,83,"ltp_data(): short_used"))) & 1) + { + ltp->short_lag_present[w] = faad_get1bit(ld + DEBUGVAR(1,84,"ltp_data(): short_lag_present")); + if (ltp->short_lag_present[w]) + { + ltp->short_lag[w] = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,85,"ltp_data(): short_lag")); + } + } + } + } else { + ltp->last_band = (ics->max_sfb < MAX_LTP_SFB ? ics->max_sfb : MAX_LTP_SFB); + + for (sfb = 0; sfb < ltp->last_band; sfb++) + { + ltp->long_used[sfb] = faad_get1bit(ld + DEBUGVAR(1,86,"ltp_data(): long_used")); + } + } + + return 0; +} +#endif + +/* Table 4.4.29 */ +static uint8_t spectral_data(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld, + int16_t *spectral_data) +{ + int8_t i; + uint8_t g; + uint16_t inc, k, p = 0; + uint8_t groups = 0; + uint8_t sect_cb; + uint8_t result; + uint16_t nshort = hDecoder->frameLength/8; + +#ifdef PROFILE + int64_t count = faad_get_ts(); +#endif + + for(g = 0; g < ics->num_window_groups; g++) + { + p = groups*nshort; + + for (i = 0; i < ics->num_sec[g]; i++) + { + sect_cb = ics->sect_cb[g][i]; + + inc = (sect_cb >= FIRST_PAIR_HCB) ? 2 : 4; + + switch (sect_cb) + { + case ZERO_HCB: + case NOISE_HCB: + case INTENSITY_HCB: + case INTENSITY_HCB2: +//#define SD_PRINT +#ifdef SD_PRINT + { + int j; + for (j = ics->sect_sfb_offset[g][ics->sect_start[g][i]]; j < ics->sect_sfb_offset[g][ics->sect_end[g][i]]; j++) + { + printf("%d\n", 0); + } + } +#endif +//#define SFBO_PRINT +#ifdef SFBO_PRINT + printf("%d\n", ics->sect_sfb_offset[g][ics->sect_start[g][i]]); +#endif + p += (ics->sect_sfb_offset[g][ics->sect_end[g][i]] - + ics->sect_sfb_offset[g][ics->sect_start[g][i]]); + break; + default: +#ifdef SFBO_PRINT + printf("%d\n", ics->sect_sfb_offset[g][ics->sect_start[g][i]]); +#endif + for (k = ics->sect_sfb_offset[g][ics->sect_start[g][i]]; + k < ics->sect_sfb_offset[g][ics->sect_end[g][i]]; k += inc) + { + if ((result = huffman_spectral_data(sect_cb, ld, &spectral_data[p])) > 0) + return result; +#ifdef SD_PRINT + { + int j; + for (j = p; j < p+inc; j++) + { + printf("%d\n", spectral_data[j]); + } + } +#endif + p += inc; + } + break; + } + } + groups += ics->window_group_length[g]; + } + +#ifdef PROFILE + count = faad_get_ts() - count; + hDecoder->spectral_cycles += count; +#endif + + return 0; +} + +#ifndef DRM +/* Table 4.4.30 */ +static uint16_t extension_payload(bitfile *ld, drc_info *drc, uint16_t count) +{ + uint16_t i, n, dataElementLength; + uint8_t dataElementLengthPart; + uint8_t align = 4, data_element_version, loopCounter; + + uint8_t extension_type = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,87,"extension_payload(): extension_type")); + + switch (extension_type) + { + case EXT_DYNAMIC_RANGE: + drc->present = 1; + n = dynamic_range_info(ld, drc); + return n; + case EXT_FILL_DATA: + /* fill_nibble = */ faad_getbits(ld, 4 + DEBUGVAR(1,136,"extension_payload(): fill_nibble")); /* must be '0000' */ + for (i = 0; i < count-1; i++) + { + /* fill_byte[i] = */ faad_getbits(ld, 8 + DEBUGVAR(1,88,"extension_payload(): fill_byte")); /* must be '10100101' */ + } + return count; + case EXT_DATA_ELEMENT: + data_element_version = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,400,"extension_payload(): data_element_version")); + switch (data_element_version) + { + case ANC_DATA: + loopCounter = 0; + dataElementLength = 0; + do { + dataElementLengthPart = (uint8_t)faad_getbits(ld, 8 + DEBUGVAR(1,401,"extension_payload(): dataElementLengthPart")); + dataElementLength += dataElementLengthPart; + loopCounter++; + } while (dataElementLengthPart == 255); + + for (i = 0; i < dataElementLength; i++) + { + /* data_element_byte[i] = */ faad_getbits(ld, 8 + DEBUGVAR(1,402,"extension_payload(): data_element_byte")); + return (dataElementLength+loopCounter+1); + } + default: + align = 0; + } + case EXT_FIL: + default: + faad_getbits(ld, align + DEBUGVAR(1,88,"extension_payload(): fill_nibble")); + for (i = 0; i < count-1; i++) + { + /* other_bits[i] = */ faad_getbits(ld, 8 + DEBUGVAR(1,89,"extension_payload(): fill_bit")); + } + return count; + } +} + +/* Table 4.4.31 */ +static uint8_t dynamic_range_info(bitfile *ld, drc_info *drc) +{ + uint8_t i, n = 1; + uint8_t band_incr; + + drc->num_bands = 1; + + if (faad_get1bit(ld + DEBUGVAR(1,90,"dynamic_range_info(): has instance_tag")) & 1) + { + drc->pce_instance_tag = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,91,"dynamic_range_info(): pce_instance_tag")); + /* drc->drc_tag_reserved_bits = */ faad_getbits(ld, 4 + DEBUGVAR(1,92,"dynamic_range_info(): drc_tag_reserved_bits")); + n++; + } + + drc->excluded_chns_present = faad_get1bit(ld + DEBUGVAR(1,93,"dynamic_range_info(): excluded_chns_present")); + if (drc->excluded_chns_present == 1) + { + n += excluded_channels(ld, drc); + } + + if (faad_get1bit(ld + DEBUGVAR(1,94,"dynamic_range_info(): has bands data")) & 1) + { + band_incr = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,95,"dynamic_range_info(): band_incr")); + /* drc->drc_bands_reserved_bits = */ faad_getbits(ld, 4 + DEBUGVAR(1,96,"dynamic_range_info(): drc_bands_reserved_bits")); + n++; + drc->num_bands += band_incr; + + for (i = 0; i < drc->num_bands; i++) + { + drc->band_top[i] = (uint8_t)faad_getbits(ld, 8 + DEBUGVAR(1,97,"dynamic_range_info(): band_top")); + n++; + } + } + + if (faad_get1bit(ld + DEBUGVAR(1,98,"dynamic_range_info(): has prog_ref_level")) & 1) + { + drc->prog_ref_level = (uint8_t)faad_getbits(ld, 7 + DEBUGVAR(1,99,"dynamic_range_info(): prog_ref_level")); + /* drc->prog_ref_level_reserved_bits = */ faad_get1bit(ld + DEBUGVAR(1,100,"dynamic_range_info(): prog_ref_level_reserved_bits")); + n++; + } + + for (i = 0; i < drc->num_bands; i++) + { + drc->dyn_rng_sgn[i] = faad_get1bit(ld + DEBUGVAR(1,101,"dynamic_range_info(): dyn_rng_sgn")); + drc->dyn_rng_ctl[i] = (uint8_t)faad_getbits(ld, 7 + DEBUGVAR(1,102,"dynamic_range_info(): dyn_rng_ctl")); + n++; + } + + return n; +} + +/* Table 4.4.32 */ +static uint8_t excluded_channels(bitfile *ld, drc_info *drc) +{ + uint8_t i, n = 0; + uint8_t num_excl_chan = 7; + + for (i = 0; i < 7; i++) + { + drc->exclude_mask[i] = faad_get1bit(ld + DEBUGVAR(1,103,"excluded_channels(): exclude_mask")); + } + n++; + + while ((drc->additional_excluded_chns[n-1] = faad_get1bit(ld + DEBUGVAR(1,104,"excluded_channels(): additional_excluded_chns"))) == 1) + { + if (i >= MAX_CHANNELS - num_excl_chan - 7) + return n; + for (i = num_excl_chan; i < num_excl_chan+7; i++) + { + drc->exclude_mask[i] = faad_get1bit(ld + DEBUGVAR(1,105,"excluded_channels(): exclude_mask")); + } + n++; + num_excl_chan += 7; + } + + return n; +} +#endif + +/* Annex A: Audio Interchange Formats */ + +/* Table 1.A.2 */ +void get_adif_header(adif_header *adif, bitfile *ld) +{ + uint8_t i; + + /* adif_id[0] = */ faad_getbits(ld, 8 + DEBUGVAR(1,106,"get_adif_header(): adif_id[0]")); + /* adif_id[1] = */ faad_getbits(ld, 8 + DEBUGVAR(1,107,"get_adif_header(): adif_id[1]")); + /* adif_id[2] = */ faad_getbits(ld, 8 + DEBUGVAR(1,108,"get_adif_header(): adif_id[2]")); + /* adif_id[3] = */ faad_getbits(ld, 8 + DEBUGVAR(1,109,"get_adif_header(): adif_id[3]")); + adif->copyright_id_present = faad_get1bit(ld + DEBUGVAR(1,110,"get_adif_header(): copyright_id_present")); + if(adif->copyright_id_present) + { + for (i = 0; i < 72/8; i++) + { + adif->copyright_id[i] = (int8_t)faad_getbits(ld, 8 + DEBUGVAR(1,111,"get_adif_header(): copyright_id")); + } + adif->copyright_id[i] = 0; + } + adif->original_copy = faad_get1bit(ld + DEBUGVAR(1,112,"get_adif_header(): original_copy")); + adif->home = faad_get1bit(ld + DEBUGVAR(1,113,"get_adif_header(): home")); + adif->bitstream_type = faad_get1bit(ld + DEBUGVAR(1,114,"get_adif_header(): bitstream_type")); + adif->bitrate = faad_getbits(ld, 23 + DEBUGVAR(1,115,"get_adif_header(): bitrate")); + adif->num_program_config_elements = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,116,"get_adif_header(): num_program_config_elements")); + + for (i = 0; i < adif->num_program_config_elements + 1; i++) + { + if(adif->bitstream_type == 0) + { + adif->adif_buffer_fullness = faad_getbits(ld, 20 + DEBUGVAR(1,117,"get_adif_header(): adif_buffer_fullness")); + } else { + adif->adif_buffer_fullness = 0; + } + + program_config_element(&adif->pce[i], ld); + } +} + +/* Table 1.A.5 */ +uint8_t adts_frame(adts_header *adts, bitfile *ld) +{ + /* faad_byte_align(ld); */ + if (adts_fixed_header(adts, ld)) + return 5; + adts_variable_header(adts, ld); + adts_error_check(adts, ld); + + return 0; +} + +/* Table 1.A.6 */ +static uint8_t adts_fixed_header(adts_header *adts, bitfile *ld) +{ + uint16_t i; + uint8_t sync_err = 1; + + /* try to recover from sync errors */ + for (i = 0; i < 768; i++) + { + adts->syncword = (uint16_t)faad_showbits(ld, 12); + if (adts->syncword != 0xFFF) + { + faad_getbits(ld, 8 + DEBUGVAR(0,0,"")); + } else { + sync_err = 0; + faad_getbits(ld, 12 + DEBUGVAR(1,118,"adts_fixed_header(): syncword")); + break; + } + } + if (sync_err) + return 5; + + adts->id = faad_get1bit(ld + DEBUGVAR(1,119,"adts_fixed_header(): id")); + adts->layer = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,120,"adts_fixed_header(): layer")); + adts->protection_absent = faad_get1bit(ld + DEBUGVAR(1,121,"adts_fixed_header(): protection_absent")); + adts->profile = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,122,"adts_fixed_header(): profile")); + adts->sf_index = (uint8_t)faad_getbits(ld, 4 + DEBUGVAR(1,123,"adts_fixed_header(): sf_index")); + adts->private_bit = faad_get1bit(ld + DEBUGVAR(1,124,"adts_fixed_header(): private_bit")); + adts->channel_configuration = (uint8_t)faad_getbits(ld, 3 + DEBUGVAR(1,125,"adts_fixed_header(): channel_configuration")); + adts->original = faad_get1bit(ld + DEBUGVAR(1,126,"adts_fixed_header(): original")); + adts->home = faad_get1bit(ld + DEBUGVAR(1,127,"adts_fixed_header(): home")); + + if (adts->old_format == 1) + { + /* Removed in corrigendum 14496-3:2002 */ + if (adts->id == 0) + { + adts->emphasis = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,128,"adts_fixed_header(): emphasis")); + } + } + + return 0; +} + +/* Table 1.A.7 */ +static void adts_variable_header(adts_header *adts, bitfile *ld) +{ + adts->copyright_identification_bit = faad_get1bit(ld + DEBUGVAR(1,129,"adts_variable_header(): copyright_identification_bit")); + adts->copyright_identification_start = faad_get1bit(ld + DEBUGVAR(1,130,"adts_variable_header(): copyright_identification_start")); + adts->aac_frame_length = (uint16_t)faad_getbits(ld, 13 + DEBUGVAR(1,131,"adts_variable_header(): aac_frame_length")); + adts->adts_buffer_fullness = (uint16_t)faad_getbits(ld, 11 + DEBUGVAR(1,132,"adts_variable_header(): adts_buffer_fullness")); + adts->no_raw_data_blocks_in_frame = (uint8_t)faad_getbits(ld, 2 + DEBUGVAR(1,133,"adts_variable_header(): no_raw_data_blocks_in_frame")); +} + +/* Table 1.A.8 */ +static void adts_error_check(adts_header *adts, bitfile *ld) +{ + if (adts->protection_absent == 0) + { + adts->crc_check = (uint16_t)faad_getbits(ld, 16 + DEBUGVAR(1,134,"adts_error_check(): crc_check")); + } +} + +/* LATM parsing functions */ +#if 0 + +static uint32_t latm_get_value(bitfile *ld) +{ + uint32_t l, value; + uint8_t bytesForValue; + + bytesForValue = (uint8_t)faad_getbits(ld, 2); + value = 0; + for(l=0; lframelen_type==0) + { + do + { + tmp = (uint8_t)faad_getbits(ld, 8); + framelen += tmp; + } while(tmp==0xff); + } + else if(latm->framelen_type==1) + framelen=latm->frameLength; + + return framelen; +} + +static uint32_t latmAudioMuxElement(latm_header *latm, bitfile *ld) +{ + uint32_t ascLen, asc_bits=0; + uint32_t x1, y1, m, n, i; + program_config pce; + mp4AudioSpecificConfig mp4ASC; + + latm->useSameStreamMux = (uint8_t)faad_getbits(ld, 1); + if(!latm->useSameStreamMux) + { + //parseSameStreamMuxConfig + latm->version = (uint8_t) faad_getbits(ld, 1); + if(latm->version) + latm->versionA = (uint8_t) faad_getbits(ld, 1); + if(latm->versionA) + { + //dunno the payload format for versionA + fprintf(stderr, "versionA not supported\n"); + return 0; + } + if(latm->version) //read taraBufferFullness + latm_get_value(ld); + latm->allStreamsSameTimeFraming = (uint8_t)faad_getbits(ld, 1); + latm->numSubFrames = (uint8_t)faad_getbits(ld, 6) + 1; + latm->numPrograms = (uint8_t)faad_getbits(ld, 4) + 1; + latm->numLayers = faad_getbits(ld, 3) + 1; + if(latm->numPrograms>1 || !latm->allStreamsSameTimeFraming || latm->numSubFrames>1 || latm->numLayers>1) + { + fprintf(stderr, "\r\nUnsupported LATM configuration: %d programs/ %d subframes, %d layers, allstreams: %d\n", + latm->numPrograms, latm->numSubFrames, latm->numLayers, latm->allStreamsSameTimeFraming); + return 0; + } + ascLen = 0; + if(latm->version) + ascLen = latm_get_value(ld); + + x1 = faad_get_processed_bits(ld); + if(AudioSpecificConfigFromBitfile(ld, &mp4ASC, &pce, 0, 1) < 0) + return 0; + + //horrid hack to unread the ASC bits and store them in latm->ASC + //the correct code would rely on an ideal faad_ungetbits() + y1 = faad_get_processed_bits(ld); + if((y1-x1) <= MAX_ASC_BYTES*8) + { + faad_rewindbits(ld); + m = x1; + while(m>0) + { + n = min(m, 32); + faad_getbits(ld, n); + m -= n; + } + + i = 0; + m = latm->ASCbits = y1 - x1; + while(m > 0) + { + n = min(m, 8); + latm->ASC[i++] = (uint8_t) faad_getbits(ld, n); + m -= n; + } + } + + asc_bits = y1-x1; + + if(ascLen>asc_bits) + faad_getbits(ld, ascLen-asc_bits); + + latm->framelen_type = (uint8_t) faad_getbits(ld, 3); + if(latm->framelen_type == 0) + { + latm->frameLength = 0; + faad_getbits(ld, 8); //buffer fullness for frame_len_type==0, useless + } + else if(latm->framelen_type == 1) + { + latm->frameLength = faad_getbits(ld, 9); + if(latm->frameLength==0) + { + fprintf(stderr, "Invalid frameLength: 0\r\n"); + return 0; + } + latm->frameLength = (latm->frameLength+20)*8; + } + else + { //hellish CELP or HCVX stuff, discard + fprintf(stderr, "Unsupported CELP/HCVX framelentype: %d\n", latm->framelen_type); + return 0; + } + + latm->otherDataLenBits = 0; + if(faad_getbits(ld, 1)) + { //other data present + int esc, tmp; + if(latm->version) + latm->otherDataLenBits = latm_get_value(ld); + else do + { + esc = faad_getbits(ld, 1); + tmp = faad_getbits(ld, 8); + latm->otherDataLenBits = (latm->otherDataLenBits << 8) + tmp; + } while(esc); + } + if(faad_getbits(ld, 1)) //crc + faad_getbits(ld, 8); + latm->inited = 1; + } + + //read payload + if(latm->inited) + return latmParsePayload(latm, ld); + else + return 0; +} + +uint32_t faad_latm_frame(latm_header *latm, bitfile *ld) +{ + uint16_t len; + uint32_t initpos, endpos, firstpos, ret; + + firstpos = faad_get_processed_bits(ld); + while (ld->bytes_left) + { + faad_byte_align(ld); + if(faad_showbits(ld, 11) != 0x2B7) + { + faad_getbits(ld, 8); + continue; + } + faad_getbits(ld, 11); + len = faad_getbits(ld, 13); + if(!len) + continue; + initpos = faad_get_processed_bits(ld); + ret = latmAudioMuxElement(latm, ld); + endpos = faad_get_processed_bits(ld); + if(ret>0) + return (len*8)-(endpos-initpos); + //faad_getbits(ld, initpos-endpos); //go back to initpos, but is valid a getbits(-N) ? + } + return 0xFFFFFFFF; +} +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/syntax.h b/third_party/faad_hdc/upstream/libfaad/syntax.h new file mode 100644 index 000000000..ca192926c --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/syntax.h @@ -0,0 +1,138 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: syntax.h,v 1.60 2009/01/26 23:51:17 menno Exp $ +**/ + +#ifndef __SYNTAX_H__ +#define __SYNTAX_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" + +#define MAIN 1 +#define LC 2 +#define SSR 3 +#define LTP 4 +#define HE_AAC 5 +#define LD 23 +#define ER_LC 17 +#define ER_LTP 19 +#define DRM_ER_LC 27 /* special object type for DRM */ +#define HDC_LC 127 /* special object type for HDC */ + +/* header types */ +#define RAW 0 +#define ADIF 1 +#define ADTS 2 +#define LATM 3 + +/* SBR signalling */ +#define NO_SBR 0 +#define SBR_UPSAMPLED 1 +#define SBR_DOWNSAMPLED 2 +#define NO_SBR_UPSAMPLED 3 + +/* DRM channel definitions */ +#define DRMCH_MONO 1 +#define DRMCH_STEREO 2 +#define DRMCH_SBR_MONO 3 +#define DRMCH_SBR_STEREO 4 +#define DRMCH_SBR_PS_STEREO 5 + + +/* First object type that has ER */ +#define ER_OBJECT_START 17 + + +/* Bitstream */ +#define LEN_SE_ID 3 +#define LEN_TAG 4 +#define LEN_BYTE 8 + +#define EXT_FIL 0 +#define EXT_FILL_DATA 1 +#define EXT_DATA_ELEMENT 2 +#define EXT_DYNAMIC_RANGE 11 +#define ANC_DATA 0 + +/* Syntax elements */ +#define ID_SCE 0x0 +#define ID_CPE 0x1 +#define ID_CCE 0x2 +#define ID_LFE 0x3 +#define ID_DSE 0x4 +#define ID_PCE 0x5 +#define ID_FIL 0x6 +#define ID_END 0x7 +#define INVALID_ELEMENT_ID 255 + +#define ONLY_LONG_SEQUENCE 0x0 +#define LONG_START_SEQUENCE 0x1 +#define EIGHT_SHORT_SEQUENCE 0x2 +#define LONG_STOP_SEQUENCE 0x3 + +#define ZERO_HCB 0 +#define FIRST_PAIR_HCB 5 +#define ESC_HCB 11 +#define QUAD_LEN 4 +#define PAIR_LEN 2 +#define NOISE_HCB 13 +#define INTENSITY_HCB2 14 +#define INTENSITY_HCB 15 + +#define INVALID_SBR_ELEMENT 255 + +int8_t GASpecificConfig(bitfile *ld, mp4AudioSpecificConfig *mp4ASC, + program_config *pce); + +uint8_t adts_frame(adts_header *adts, bitfile *ld); +void get_adif_header(adif_header *adif, bitfile *ld); +void raw_data_block(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, + bitfile *ld, program_config *pce, drc_info *drc); +uint8_t reordered_spectral_data(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld, + int16_t *spectral_data); +#ifdef DRM +void DRM_aac_scalable_main_element(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, + bitfile *ld, program_config *pce, drc_info *drc); +#endif +#if 0 +uint32_t faad_latm_frame(latm_header *latm, bitfile *ld); +#endif + +#ifdef HDC +void hdc_data_block(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, + bitfile *ld, program_config *pce, drc_info *drc); +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/libfaad/tns.c b/third_party/faad_hdc/upstream/libfaad/tns.c new file mode 100644 index 000000000..b6989919e --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/tns.c @@ -0,0 +1,339 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: tns.c,v 1.40 2007/11/01 12:33:40 menno Exp $ +**/ + +#include "common.h" +#include "structs.h" + +#include "syntax.h" +#include "tns.h" + + +/* static function declarations */ +/* returns exp */ +static uint8_t tns_decode_coef(uint8_t order, uint8_t coef_res_bits, + uint8_t coef_compress, uint8_t *coef, real_t *a); +static void tns_ar_filter(real_t *spectrum, uint16_t size, int8_t inc, real_t *lpc, + uint8_t order, uint8_t exp); +static void tns_ma_filter(real_t *spectrum, uint16_t size, int8_t inc, real_t *lpc, + uint8_t order, uint8_t exp); + + +#ifdef _MSC_VER +#pragma warning(disable:4305) +#pragma warning(disable:4244) +#endif +static real_t tns_coef_0_3[] = +{ + COEF_CONST(0.0), COEF_CONST(0.4338837391), COEF_CONST(0.7818314825), COEF_CONST(0.9749279122), + COEF_CONST(-0.9848077530), COEF_CONST(-0.8660254038), COEF_CONST(-0.6427876097), COEF_CONST(-0.3420201433), + COEF_CONST(-0.4338837391), COEF_CONST(-0.7818314825), COEF_CONST(-0.9749279122), COEF_CONST(-0.9749279122), + COEF_CONST(-0.9848077530), COEF_CONST(-0.8660254038), COEF_CONST(-0.6427876097), COEF_CONST(-0.3420201433) +}; +static real_t tns_coef_0_4[] = +{ + COEF_CONST(0.0), COEF_CONST(0.2079116908), COEF_CONST(0.4067366431), COEF_CONST(0.5877852523), + COEF_CONST(0.7431448255), COEF_CONST(0.8660254038), COEF_CONST(0.9510565163), COEF_CONST(0.9945218954), + COEF_CONST(-0.9957341763), COEF_CONST(-0.9618256432), COEF_CONST(-0.8951632914), COEF_CONST(-0.7980172273), + COEF_CONST(-0.6736956436), COEF_CONST(-0.5264321629), COEF_CONST(-0.3612416662), COEF_CONST(-0.1837495178) +}; +static real_t tns_coef_1_3[] = +{ + COEF_CONST(0.0), COEF_CONST(0.4338837391), COEF_CONST(-0.6427876097), COEF_CONST(-0.3420201433), + COEF_CONST(0.9749279122), COEF_CONST(0.7818314825), COEF_CONST(-0.6427876097), COEF_CONST(-0.3420201433), + COEF_CONST(-0.4338837391), COEF_CONST(-0.7818314825), COEF_CONST(-0.6427876097), COEF_CONST(-0.3420201433), + COEF_CONST(-0.7818314825), COEF_CONST(-0.4338837391), COEF_CONST(-0.6427876097), COEF_CONST(-0.3420201433) +}; +static real_t tns_coef_1_4[] = +{ + COEF_CONST(0.0), COEF_CONST(0.2079116908), COEF_CONST(0.4067366431), COEF_CONST(0.5877852523), + COEF_CONST(-0.6736956436), COEF_CONST(-0.5264321629), COEF_CONST(-0.3612416662), COEF_CONST(-0.1837495178), + COEF_CONST(0.9945218954), COEF_CONST(0.9510565163), COEF_CONST(0.8660254038), COEF_CONST(0.7431448255), + COEF_CONST(-0.6736956436), COEF_CONST(-0.5264321629), COEF_CONST(-0.3612416662), COEF_CONST(-0.1837495178) +}; + +static real_t* all_tns_coefs[] = {tns_coef_0_3, tns_coef_0_4, tns_coef_1_3, tns_coef_1_4}; + +/* TNS decoding for one channel and frame */ +void tns_decode_frame(ic_stream *ics, tns_info *tns, uint8_t sr_index, + uint8_t object_type, real_t *spec, uint16_t frame_len) +{ + uint8_t w, f, tns_order; + int8_t inc; + int16_t size; + uint16_t bottom, top, start, end; + uint16_t nshort = frame_len/8; + real_t lpc[TNS_MAX_ORDER+1]; + uint8_t exp; + + if (!ics->tns_data_present) + return; + + for (w = 0; w < ics->num_windows; w++) + { + bottom = ics->num_swb; + + for (f = 0; f < tns->n_filt[w]; f++) + { + top = bottom; + bottom = max(top - tns->length[w][f], 0); + tns_order = min(tns->order[w][f], TNS_MAX_ORDER); + if (!tns_order) + continue; + + exp = tns_decode_coef(tns_order, tns->coef_res[w]+3, + tns->coef_compress[w][f], tns->coef[w][f], lpc); + + start = min(bottom, max_tns_sfb(sr_index, object_type, (ics->window_sequence == EIGHT_SHORT_SEQUENCE))); + start = min(start, ics->max_sfb); + start = min(ics->swb_offset[start], ics->swb_offset_max); + + end = min(top, max_tns_sfb(sr_index, object_type, (ics->window_sequence == EIGHT_SHORT_SEQUENCE))); + end = min(end, ics->max_sfb); + end = min(ics->swb_offset[end], ics->swb_offset_max); + + size = end - start; + if (size <= 0) + continue; + + if (tns->direction[w][f]) + { + inc = -1; + start = end - 1; + } else { + inc = 1; + } + + tns_ar_filter(&spec[(w*nshort)+start], size, inc, lpc, tns_order, exp); + } + } +} + +/* TNS encoding for one channel and frame */ +void tns_encode_frame(ic_stream *ics, tns_info *tns, uint8_t sr_index, + uint8_t object_type, real_t *spec, uint16_t frame_len) +{ + uint8_t w, f, tns_order; + int8_t inc; + int16_t size; + uint16_t bottom, top, start, end; + uint16_t nshort = frame_len/8; + real_t lpc[TNS_MAX_ORDER+1]; + uint8_t exp; + + if (!ics->tns_data_present) + return; + + for (w = 0; w < ics->num_windows; w++) + { + bottom = ics->num_swb; + + for (f = 0; f < tns->n_filt[w]; f++) + { + top = bottom; + bottom = max(top - tns->length[w][f], 0); + tns_order = min(tns->order[w][f], TNS_MAX_ORDER); + if (!tns_order) + continue; + + exp = tns_decode_coef(tns_order, tns->coef_res[w]+3, + tns->coef_compress[w][f], tns->coef[w][f], lpc); + + start = min(bottom, max_tns_sfb(sr_index, object_type, (ics->window_sequence == EIGHT_SHORT_SEQUENCE))); + start = min(start, ics->max_sfb); + start = min(ics->swb_offset[start], ics->swb_offset_max); + + end = min(top, max_tns_sfb(sr_index, object_type, (ics->window_sequence == EIGHT_SHORT_SEQUENCE))); + end = min(end, ics->max_sfb); + end = min(ics->swb_offset[end], ics->swb_offset_max); + + size = end - start; + if (size <= 0) + continue; + + if (tns->direction[w][f]) + { + inc = -1; + start = end - 1; + } else { + inc = 1; + } + + tns_ma_filter(&spec[(w*nshort)+start], size, inc, lpc, tns_order, exp); + } + } +} + +/* Decoder transmitted coefficients for one TNS filter */ +static uint8_t tns_decode_coef(uint8_t order, uint8_t coef_res_bits, uint8_t coef_compress, + uint8_t *coef, real_t *a) +{ + uint8_t i, m; + real_t tmp2[TNS_MAX_ORDER+1], b[TNS_MAX_ORDER+1]; + uint8_t table_index = 2 * (coef_compress != 0) + (coef_res_bits != 3); + real_t* tns_coef = all_tns_coefs[table_index]; + uint8_t exp = 0; + + /* Conversion to signed integer */ + for (i = 0; i < order; i++) + tmp2[i] = tns_coef[coef[i]]; + + /* Conversion to LPC coefficients */ + a[0] = COEF_CONST(1.0); + for (m = 1; m <= order; m++) + { + a[m] = tmp2[m-1]; /* changed */ + for (i = 1; i < m; i++) /* loop only while i>= exp; + + /* OK not to check after the last iteration. */ + if (m < order) + { + real_t sum_abs = COEF_CONST(0.0); + for (i = 1; i <= m; ++i) + { + sum_abs += (a[i] >= 0) ? a[i] : -a[i]; + } + /* Next iteration would turn sum to 2*sum + 1; maximal "coef" is 7.999 */ + if (sum_abs >= COEF_CONST(3.5)) + { + exp++; + for (i = 1; i <= m; ++i) + { + a[i] >>= 1; + } + } + } +#endif + } + return exp; +} + +static void tns_ar_filter(real_t *spectrum, uint16_t size, int8_t inc, real_t *lpc, + uint8_t order, uint8_t exp) +{ + /* + - Simple all-pole filter of order "order" defined by + y(n) = x(n) - lpc[1]*y(n-1) - ... - lpc[order]*y(n-order) + - The state variables of the filter are initialized to zero every time + - The output data is written over the input data ("in-place operation") + - An input vector of "size" samples is processed and the index increment + to the next data sample is given by "inc" + */ + + uint8_t j; + uint16_t i; + /* state is stored as a double ringbuffer */ + real_t state[2*TNS_MAX_ORDER] = {0}; + int8_t state_index = 0; + int32_t mul = 1; + +#ifdef FIXED_POINT + if (exp >= 4) + return; + mul = 1 << exp; +#else + (void)exp; +#endif + + for (i = 0; i < size; i++) + { + real_t y = REAL_CONST(0.0); + for (j = 0; j < order; j++) + y += MUL_C(state[state_index+j], lpc[j+1]); + y = *spectrum - (y * mul); + + /* double ringbuffer state */ + state_index--; + if (state_index < 0) + state_index = order-1; + state[state_index] = state[state_index + order] = y; + + *spectrum = y; + spectrum += inc; + +//#define TNS_PRINT +#ifdef TNS_PRINT + //printf("%d\n", y); + printf("0x%.8X\n", y); +#endif + } +} + +static void tns_ma_filter(real_t *spectrum, uint16_t size, int8_t inc, real_t *lpc, + uint8_t order, uint8_t exp) +{ + /* + - Simple all-zero filter of order "order" defined by + y(n) = x(n) + a(2)*x(n-1) + ... + a(order+1)*x(n-order) + - The state variables of the filter are initialized to zero every time + - The output data is written over the input data ("in-place operation") + - An input vector of "size" samples is processed and the index increment + to the next data sample is given by "inc" + */ + + uint8_t j; + uint16_t i; + /* state is stored as a double ringbuffer */ + real_t state[2*TNS_MAX_ORDER] = {0}; + int8_t state_index = 0; + int32_t mul = 1; + +#ifdef FIXED_POINT + if (exp >= 4) + return; + mul = 1 << exp; +#else + (void)exp; +#endif + + for (i = 0; i < size; i++) + { + real_t y = REAL_CONST(0.0); + for (j = 0; j < order; j++) + y += MUL_C(state[state_index+j], lpc[j+1]); + + y = *spectrum + (y * mul); + + /* double ringbuffer state */ + state_index--; + if (state_index < 0) + state_index = order-1; + state[state_index] = state[state_index + order] = *spectrum; + + *spectrum = y; + spectrum += inc; + } +} diff --git a/third_party/faad_hdc/upstream/libfaad/tns.h b/third_party/faad_hdc/upstream/libfaad/tns.h new file mode 100644 index 000000000..22f8a45db --- /dev/null +++ b/third_party/faad_hdc/upstream/libfaad/tns.h @@ -0,0 +1,51 @@ +/* +** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding +** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** Any non-GPL usage of this software or parts of this software is strictly +** forbidden. +** +** The "appropriate copyright message" mentioned in section 2c of the GPLv2 +** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" +** +** Commercial non-GPL licensing of this software is possible. +** For more info contact Nero AG through Mpeg4AAClicense@nero.com. +** +** $Id: tns.h,v 1.23 2007/11/01 12:33:41 menno Exp $ +**/ + +#ifndef __TNS_H__ +#define __TNS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +#define TNS_MAX_ORDER 20 + + +void tns_decode_frame(ic_stream *ics, tns_info *tns, uint8_t sr_index, + uint8_t object_type, real_t *spec, uint16_t frame_len); +void tns_encode_frame(ic_stream *ics, tns_info *tns, uint8_t sr_index, + uint8_t object_type, real_t *spec, uint16_t frame_len); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/faad_hdc/upstream/properties.json b/third_party/faad_hdc/upstream/properties.json new file mode 100644 index 000000000..156e94af6 --- /dev/null +++ b/third_party/faad_hdc/upstream/properties.json @@ -0,0 +1,4 @@ +{ + "//": "This file contains properties used in build / release process", + "PACKAGE_VERSION": "2.11.2" +} diff --git a/third_party/nrsc5/COPYING.GPL-3.0 b/third_party/nrsc5/COPYING.GPL-3.0 new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/third_party/nrsc5/COPYING.GPL-3.0 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/third_party/nrsc5/LICENSE-SOURCE.json b/third_party/nrsc5/LICENSE-SOURCE.json new file mode 100644 index 000000000..2d3275baa --- /dev/null +++ b/third_party/nrsc5/LICENSE-SOURCE.json @@ -0,0 +1,6 @@ +{ + "sourcePath": "LICENSE", + "sourceRevision": "208bc220f5a3f18492c30a1d7c274117a56e18c5", + "destination": "COPYING.GPL-3.0", + "sha256": "3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986" +} diff --git a/third_party/nrsc5/README.aethersdr.md b/third_party/nrsc5/README.aethersdr.md new file mode 100644 index 000000000..d6e71a000 --- /dev/null +++ b/third_party/nrsc5/README.aethersdr.md @@ -0,0 +1,104 @@ +# Embedded nrsc5 in AetherSDR + +This is the bounded source subset of [nrsc5](https://github.com/theori-io/nrsc5) +at commit `0225922b6f68109df39d07391f4d855464598ab8`, with the bounded +`patches/windows-msvc.patch` platform-header changes described below. +`SOURCE-MANIFEST.json` lists every included path, upstream and vendored SHA-256, +and every omitted upstream path. The sample IQ recording, Python/CLI support +tools, CI files and generated documentation are excluded. The retained C CLI +source and upstream CMake files are reference/provenance only and are not built. + +`upstream/LICENSE` declares GPL-3.0-or-later. The complete GPLv3 text is provided +in `COPYING.GPL-3.0`, with its source/hash in `LICENSE-SOURCE.json`. Preserve +per-file notices, including Ettus Research's GPLv3 convolutional decoder, +Phil Karn's GPL Reed-Solomon code, and rxi's MIT notice in `src/log.c` (not built). +The HDC patch has its own author/commit header and is preserved byte-for-byte. +These source licenses are not a patent clearance or a product certification. + +## Build and target use + +The parent includes `cmake/AetherHdFm.cmake` after resolving its RTL dependencies: + +```cmake +include(cmake/AetherHdFm.cmake) +if(TARGET aether_nrsc5) + target_link_libraries(aethercore PRIVATE aether_nrsc5) +endif() +``` + +`ENABLE_HD_FM` defaults OFF. Qualification builds accept Linux/GNU C, +macOS/AppleClang, or Windows x64/MSVC with installed clang-cl and its compiler-rt +builtins. All require an enabled RTL backend and its existing `RTLSDR_TARGET` / +`RTL_FFTW3F_TARGET`. +The module creates static `aether_nrsc5` and `aether_faad_hdc` only. Linking +`aether_nrsc5` supplies `nrsc5.h`, `AETHER_ENABLE_NRSC5=1`, and the transitive +link dependencies. Test targets that compile the wrapper directly must link +this target themselves; no global compile flag enables an unlinked decoder. + +The upstream build systems are not executed. CLI is OFF by construction; +there is no CLI runtime, downloader, libao dependency, shared decoder library, +configure-time patch, install hook, or architecture-specific SIMD flag. +Configuration writes the private generated header to the build directory. +Library stderr logging remains at upstream level 5 (disabled). Other platforms +retain the default OFF setting. These compiler gates are qualification scope; +a successful configure is not evidence of decoding or radio reception. + +On Windows, only nrsc5 C objects use clang-cl, preserving C11 complex arithmetic +and VLAs while matching the application's MSVC ABI and shared CRT. The main +C++ application and HDC library retain the configured MSVC compiler. The private +`compat/msvc/complex.h` maps the six used complex math operations to UCRT, +retains native Clang complex arithmetic, and avoids UCRT's conflicting `normf` +name. LLVM compiler-rt supplies native complex multiplication/division helpers; +it is statically linked, has no runtime DLL, and must be installed already. +The private `pthread.h` maps the pinned subset to Windows SRW locks, condition +variables and `_beginthreadex`; it is not a general POSIX thread implementation. +Pipe mode still creates no nrsc5 thread. The header patch recognizes native +Windows for the retained WinSock path and excludes an unused POSIX time header. +No DSP, frame parsing, resampling, audio, or metadata algorithm is changed. + +The selected Windows build requires x64 and `/MD` (`/MDd` for Debug). No MinGW, +Windows ARM, Intel Mac or Linux ARM qualification follows from x64 Windows and +Apple Silicon test evidence. Per-configuration object directories preserve +Debug/Release separation. Build dependencies include the generated config and +vendored/adapter/dependency headers. `nrsc5_windows_compat_test` exercises actual +complex math (including signed zero), locking, contention and condition wakeups; +it is registered only when Windows HD is enabled. + +## Required owner and lifetime contract + +Aether alone owns the USB device. Its core wrapper uses `nrsc5_open_pipe`, +`nrsc5_set_mode(NRSC5_MODE_FM)`, `nrsc5_set_callback`, +`nrsc5_pipe_samples_cf32`, and `nrsc5_close`; device/file/TCP entrypoints must +never be called. Their retained upstream symbols remain compiled, not exposed +as an Aether feature. Pipe mode creates no nrsc5 worker; input processing and +callbacks are synchronous on the caller, with internal allocations and HDC work. +They belong on the bounded decoder owner, never the capture or GUI thread. + +Hold Aether's process-global `fftwfPlannerLock()` across open and close, +including all FFTW allocations/plans/destruction/frees. nrsc5's own pthread +mutex does not serialize with Aether's other single-precision FFTW users. +Do not hold the planner lock during sample processing (`fftwf_execute`). +One owner serializes all calls and callbacks for each decoder instance. + +The cf32 pipe requires exactly **744187.5 complex samples/second**, passed as +interleaved float I/Q. Its length is a count of scalar floats and must be even; +do not round the rate to 744188. Audio callbacks contain interleaved signed +16-bit stereo at 44100 Hz; their count is scalar samples, not stereo frames. +Callback data is borrowed: validate its bounds/program/flags and copy it during +the callback. Preserve all session/revision/receiver/service fences in Aether. + +This dependency import does not harden upstream allocation failure paths: +`nrsc5_open_pipe` and nested initialization assume successful allocations. +Do not claim that a null result or a wrapper catch makes out-of-memory recovery +safe. Runtime boundedness, malformed-input handling, callback copying, RF +coverage and lifecycle validation belong to the integration and its tests. + +## Reproducing the import + +Use the exact source revision above, then copy only the paths listed in +`SOURCE-MANIFEST.json`. Pristine files must match `upstreamSha256`. Apply the recorded Windows portability patch with `git apply` inside the +upstream subset; verify `vendoredSha256` afterward. The separate `localAdapters` +manifest entries are AetherSDR-authored compatibility headers, not upstream +files. The included +`upstream/support/faad2-hdc-support.patch` is applied to the separately pinned +FAAD subset as documented in `../faad_hdc/README.aethersdr.md`. diff --git a/third_party/nrsc5/SOURCE-MANIFEST.json b/third_party/nrsc5/SOURCE-MANIFEST.json new file mode 100644 index 000000000..a4f8eb3e5 --- /dev/null +++ b/third_party/nrsc5/SOURCE-MANIFEST.json @@ -0,0 +1,326 @@ +{ + "schemaVersion": 1, + "project": "nrsc5", + "sourceUrl": "https://github.com/theori-io/nrsc5", + "sourceRevision": "0225922b6f68109df39d07391f4d855464598ab8", + "upstreamLicense": "GPL-3.0-or-later", + "omittedUpstreamPaths": [ + ".github/workflows/ci.yml", + ".github/workflows/docs.yml", + ".gitignore", + "doc/Doxyfile.in", + "nrsc5.rb", + "support/cli.py", + "support/msys2-build", + "support/nrsc5.py", + "support/sample.xz", + "support/win-cross-compile" + ], + "patches": [ + { + "pathFromRepositoryRoot": "third_party/nrsc5/patches/windows-msvc.patch", + "sha256": "89b7ed3f881182ea9d2b48fe13869ae9a413980234f53a1b448c537409c3f1bd", + "description": "AetherSDR native Windows include/platform guards; no decoder algorithm changes." + } + ], + "files": [ + { + "upstreamPath": "CMakeLists.txt", + "vendoredPath": "upstream/CMakeLists.txt", + "upstreamSha256": "838f7827474ffa00929e9b3573b71175fe770a25bdeb2292d813a947e58516ec", + "vendoredSha256": "838f7827474ffa00929e9b3573b71175fe770a25bdeb2292d813a947e58516ec" + }, + { + "upstreamPath": "LICENSE", + "vendoredPath": "upstream/LICENSE", + "upstreamSha256": "9a2bde859c84b878cd0c70907610b5d72bf81c0ec9f39e7ba25e52c415d7ae2f", + "vendoredSha256": "9a2bde859c84b878cd0c70907610b5d72bf81c0ec9f39e7ba25e52c415d7ae2f" + }, + { + "upstreamPath": "README.md", + "vendoredPath": "upstream/README.md", + "upstreamSha256": "f4cd10781969d8cee02f82ae016cc3ad49882372b285329f07f722d179152ed3", + "vendoredSha256": "f4cd10781969d8cee02f82ae016cc3ad49882372b285329f07f722d179152ed3" + }, + { + "upstreamPath": "include/nrsc5.h", + "vendoredPath": "upstream/include/nrsc5.h", + "upstreamSha256": "ce0856e6ad02113d452aed6c0b1bcd9b37f28821141ed41eadab1ca320bc1e7f", + "vendoredSha256": "ce0856e6ad02113d452aed6c0b1bcd9b37f28821141ed41eadab1ca320bc1e7f" + }, + { + "upstreamPath": "src/CMakeLists.txt", + "vendoredPath": "upstream/src/CMakeLists.txt", + "upstreamSha256": "bf782f6df0a626ca7bcc0a8f0717ed95037241575b02f822fd404c66e757d211", + "vendoredSha256": "bf782f6df0a626ca7bcc0a8f0717ed95037241575b02f822fd404c66e757d211" + }, + { + "upstreamPath": "src/acquire.c", + "vendoredPath": "upstream/src/acquire.c", + "upstreamSha256": "80efcd06dab7ea66a92d364d4a413471b889087d9783a31ff546bbe765d75f81", + "vendoredSha256": "80efcd06dab7ea66a92d364d4a413471b889087d9783a31ff546bbe765d75f81" + }, + { + "upstreamPath": "src/acquire.h", + "vendoredPath": "upstream/src/acquire.h", + "upstreamSha256": "30cacdff7ff1099b220cb66a175097aef5eddffddbd7679f486b965159f2f996", + "vendoredSha256": "30cacdff7ff1099b220cb66a175097aef5eddffddbd7679f486b965159f2f996" + }, + { + "upstreamPath": "src/bitwriter.h", + "vendoredPath": "upstream/src/bitwriter.h", + "upstreamSha256": "ef486785847f11c75672ee37400a47107befa95d61746b49f9ae286f811f337b", + "vendoredSha256": "ef486785847f11c75672ee37400a47107befa95d61746b49f9ae286f811f337b" + }, + { + "upstreamPath": "src/config.h.in", + "vendoredPath": "upstream/src/config.h.in", + "upstreamSha256": "c26746cec8cec73cfd650e88d7d47d954a68604c7645c80346f5241ff9c4c90c", + "vendoredSha256": "c26746cec8cec73cfd650e88d7d47d954a68604c7645c80346f5241ff9c4c90c" + }, + { + "upstreamPath": "src/conv.h", + "vendoredPath": "upstream/src/conv.h", + "upstreamSha256": "dbe73e58aede99de2b8cf6b9bc4ed28f6fbf44f00cf1b819d93e7c280280ef25", + "vendoredSha256": "dbe73e58aede99de2b8cf6b9bc4ed28f6fbf44f00cf1b819d93e7c280280ef25" + }, + { + "upstreamPath": "src/conv_dec.c", + "vendoredPath": "upstream/src/conv_dec.c", + "upstreamSha256": "b7ccfeb73899d738b4f7050cebbb698db41a8ae7ca04eac41b87113c02f4acd8", + "vendoredSha256": "b7ccfeb73899d738b4f7050cebbb698db41a8ae7ca04eac41b87113c02f4acd8" + }, + { + "upstreamPath": "src/conv_gen.h", + "vendoredPath": "upstream/src/conv_gen.h", + "upstreamSha256": "b49a8c21887b70f9dc7f7e27880eab78ddb1fb6da45a83067a6c8a7d2db3b4b9", + "vendoredSha256": "b49a8c21887b70f9dc7f7e27880eab78ddb1fb6da45a83067a6c8a7d2db3b4b9" + }, + { + "upstreamPath": "src/conv_neon.h", + "vendoredPath": "upstream/src/conv_neon.h", + "upstreamSha256": "47081d7a0e80be1a91f845a5210f8609fc2118fb8001472e97efa9407b144dc9", + "vendoredSha256": "47081d7a0e80be1a91f845a5210f8609fc2118fb8001472e97efa9407b144dc9" + }, + { + "upstreamPath": "src/conv_sse.h", + "vendoredPath": "upstream/src/conv_sse.h", + "upstreamSha256": "da39331c31ba735ed849872844891568bfaa34226e0e56ddc9b136f38db57c8e", + "vendoredSha256": "da39331c31ba735ed849872844891568bfaa34226e0e56ddc9b136f38db57c8e" + }, + { + "upstreamPath": "src/decode.c", + "vendoredPath": "upstream/src/decode.c", + "upstreamSha256": "476f322dd29853f657d7c93301e4e6a96295327266a984c2e3778bbeacd5b4d8", + "vendoredSha256": "476f322dd29853f657d7c93301e4e6a96295327266a984c2e3778bbeacd5b4d8" + }, + { + "upstreamPath": "src/decode.h", + "vendoredPath": "upstream/src/decode.h", + "upstreamSha256": "8e2db784e61a2fbb147958919b670a903302c9f91cad5004e96bc715ad2ea43c", + "vendoredSha256": "8e2db784e61a2fbb147958919b670a903302c9f91cad5004e96bc715ad2ea43c" + }, + { + "upstreamPath": "src/defines.h", + "vendoredPath": "upstream/src/defines.h", + "upstreamSha256": "1fe4261bbf2525ef6fd0e05c3947fbadb171833baa3644c694a8c2bc180a2024", + "vendoredSha256": "1fe4261bbf2525ef6fd0e05c3947fbadb171833baa3644c694a8c2bc180a2024" + }, + { + "upstreamPath": "src/firdecim_cf32.c", + "vendoredPath": "upstream/src/firdecim_cf32.c", + "upstreamSha256": "1c7cdda3671c891b7bae2ef2d2628ea4618b5cb2862a295fd09b8c6b91820fb9", + "vendoredSha256": "1c7cdda3671c891b7bae2ef2d2628ea4618b5cb2862a295fd09b8c6b91820fb9" + }, + { + "upstreamPath": "src/firdecim_cf32.h", + "vendoredPath": "upstream/src/firdecim_cf32.h", + "upstreamSha256": "3ba817013b40fd3e377ef80b5247669c28cce34641e4b6726713596c494d1963", + "vendoredSha256": "3ba817013b40fd3e377ef80b5247669c28cce34641e4b6726713596c494d1963" + }, + { + "upstreamPath": "src/frame.c", + "vendoredPath": "upstream/src/frame.c", + "upstreamSha256": "8163ba2dabdba0bd49ecd5b60ac3b744bff4649a8b222183b321b58d8af26783", + "vendoredSha256": "8163ba2dabdba0bd49ecd5b60ac3b744bff4649a8b222183b321b58d8af26783" + }, + { + "upstreamPath": "src/frame.h", + "vendoredPath": "upstream/src/frame.h", + "upstreamSha256": "a6773630db71fd4b144da1d9310867f320287b975b1586efae6b174a004e91c8", + "vendoredSha256": "a6773630db71fd4b144da1d9310867f320287b975b1586efae6b174a004e91c8" + }, + { + "upstreamPath": "src/here_images.c", + "vendoredPath": "upstream/src/here_images.c", + "upstreamSha256": "1933f1d76835888e81dacbd64e199b51500d1959c612fbe3b599e32f631c1688", + "vendoredSha256": "1933f1d76835888e81dacbd64e199b51500d1959c612fbe3b599e32f631c1688" + }, + { + "upstreamPath": "src/here_images.h", + "vendoredPath": "upstream/src/here_images.h", + "upstreamSha256": "ec578ae6b3a9e155ed930acaf68af603fcb2feef156d13f8d49a962f507e3df8", + "vendoredSha256": "ec578ae6b3a9e155ed930acaf68af603fcb2feef156d13f8d49a962f507e3df8" + }, + { + "upstreamPath": "src/input.c", + "vendoredPath": "upstream/src/input.c", + "upstreamSha256": "cf698e1d0fea54f4d0f54bd66d126d0e8c119c5c55e7682050ca8bf4257ed133", + "vendoredSha256": "cf698e1d0fea54f4d0f54bd66d126d0e8c119c5c55e7682050ca8bf4257ed133" + }, + { + "upstreamPath": "src/input.h", + "vendoredPath": "upstream/src/input.h", + "upstreamSha256": "eae3432572ecb2223a7163d680ffc4ef278e91b658e601fa090ad81c0f7ccbf9", + "vendoredSha256": "eae3432572ecb2223a7163d680ffc4ef278e91b658e601fa090ad81c0f7ccbf9" + }, + { + "upstreamPath": "src/libnrsc5.map", + "vendoredPath": "upstream/src/libnrsc5.map", + "upstreamSha256": "54cf06e6c192052efc86ea954c3857cf0b93c141aade10f9159c44355be45928", + "vendoredSha256": "54cf06e6c192052efc86ea954c3857cf0b93c141aade10f9159c44355be45928" + }, + { + "upstreamPath": "src/libnrsc5.sym", + "vendoredPath": "upstream/src/libnrsc5.sym", + "upstreamSha256": "b1f2e16cc75546c233bb915dd8b3e998581888f82f4cc74e1e90c30137cdc8c1", + "vendoredSha256": "b1f2e16cc75546c233bb915dd8b3e998581888f82f4cc74e1e90c30137cdc8c1" + }, + { + "upstreamPath": "src/log.c", + "vendoredPath": "upstream/src/log.c", + "upstreamSha256": "a3124070028d22d00313dcb2b8727cb3437ffdc543037873cc2a6855f26e07f3", + "vendoredSha256": "a3124070028d22d00313dcb2b8727cb3437ffdc543037873cc2a6855f26e07f3" + }, + { + "upstreamPath": "src/log.h", + "vendoredPath": "upstream/src/log.h", + "upstreamSha256": "799c32c7fce03ff8d47eac2701d955261b4cb4b261395c9bdd6ffffc12ce5ce1", + "vendoredSha256": "799c32c7fce03ff8d47eac2701d955261b4cb4b261395c9bdd6ffffc12ce5ce1" + }, + { + "upstreamPath": "src/main.c", + "vendoredPath": "upstream/src/main.c", + "upstreamSha256": "bbf221aad86265c39e0538051d3333c955e2b4f38592b016a09dc2918acbdf91", + "vendoredSha256": "bbf221aad86265c39e0538051d3333c955e2b4f38592b016a09dc2918acbdf91" + }, + { + "upstreamPath": "src/nrsc5.c", + "vendoredPath": "upstream/src/nrsc5.c", + "upstreamSha256": "d3dafffe6ada4317a04298327cb7646257ab16a21eabbb00b9bef3d370d8154e", + "vendoredSha256": "d3dafffe6ada4317a04298327cb7646257ab16a21eabbb00b9bef3d370d8154e" + }, + { + "upstreamPath": "src/output.c", + "vendoredPath": "upstream/src/output.c", + "upstreamSha256": "fa4d4ffa5c09edfdd2334f7029bcd2328174b617c7cbbdb2385f369b0796d999", + "vendoredSha256": "087e3ff34d540a6d2591a788b5daef8cbc5b742d814749e93191603e7eb5eb13" + }, + { + "upstreamPath": "src/output.h", + "vendoredPath": "upstream/src/output.h", + "upstreamSha256": "c13323ec987a85bb5f762ceaa365d196fac9b968d1207bc43721a40f327dd8e1", + "vendoredSha256": "c13323ec987a85bb5f762ceaa365d196fac9b968d1207bc43721a40f327dd8e1" + }, + { + "upstreamPath": "src/pids.c", + "vendoredPath": "upstream/src/pids.c", + "upstreamSha256": "9632638ec25d243d487760523714b4ed0ce2ae8f6fba8d2642b8cffcadacdd37", + "vendoredSha256": "9632638ec25d243d487760523714b4ed0ce2ae8f6fba8d2642b8cffcadacdd37" + }, + { + "upstreamPath": "src/pids.h", + "vendoredPath": "upstream/src/pids.h", + "upstreamSha256": "38c1dc4c399f474eb23e0b89836986fc926145eec1aa24771d48c6dc912032dc", + "vendoredSha256": "38c1dc4c399f474eb23e0b89836986fc926145eec1aa24771d48c6dc912032dc" + }, + { + "upstreamPath": "src/private.h", + "vendoredPath": "upstream/src/private.h", + "upstreamSha256": "ebb7db40b4dee455ca261a9f4b41d54eca09c6807c5a58ecbcceb4dd5bcdf822", + "vendoredSha256": "ebb7db40b4dee455ca261a9f4b41d54eca09c6807c5a58ecbcceb4dd5bcdf822" + }, + { + "upstreamPath": "src/rs_char.h", + "vendoredPath": "upstream/src/rs_char.h", + "upstreamSha256": "96d4257970c9b1f8642d993158ade20a0e18c94af58971b36bab6d73aeb24168", + "vendoredSha256": "96d4257970c9b1f8642d993158ade20a0e18c94af58971b36bab6d73aeb24168" + }, + { + "upstreamPath": "src/rs_decode.c", + "vendoredPath": "upstream/src/rs_decode.c", + "upstreamSha256": "c7dcde9c579a3e09244fc1ba3100149b309b428267301dba0af4ec3a402f2c7a", + "vendoredSha256": "c7dcde9c579a3e09244fc1ba3100149b309b428267301dba0af4ec3a402f2c7a" + }, + { + "upstreamPath": "src/rs_init.c", + "vendoredPath": "upstream/src/rs_init.c", + "upstreamSha256": "32ca4173c1d624efed9f5d52131310f8e6291f67a43dca21d8b7490ecd1f5ca1", + "vendoredSha256": "32ca4173c1d624efed9f5d52131310f8e6291f67a43dca21d8b7490ecd1f5ca1" + }, + { + "upstreamPath": "src/rtltcp.c", + "vendoredPath": "upstream/src/rtltcp.c", + "upstreamSha256": "a0a3db4502a0591814c497d98d22281d43f8d3b2d4467d36f34337b8c537aa4b", + "vendoredSha256": "1936422e824f8d0228ebe0a3a3c103168dde0eb45e0f02d229402767bd1742b6" + }, + { + "upstreamPath": "src/rtltcp.h", + "vendoredPath": "upstream/src/rtltcp.h", + "upstreamSha256": "373ad0cb2d2977c4ad6ef39c8aa845c2cffdc270f164779754bef58958fd058c", + "vendoredSha256": "373ad0cb2d2977c4ad6ef39c8aa845c2cffdc270f164779754bef58958fd058c" + }, + { + "upstreamPath": "src/strndup.c", + "vendoredPath": "upstream/src/strndup.c", + "upstreamSha256": "51e88cc6fccf5b372852517451d060cdb12b6d42297502f5cfd9872b08b7d5fb", + "vendoredSha256": "51e88cc6fccf5b372852517451d060cdb12b6d42297502f5cfd9872b08b7d5fb" + }, + { + "upstreamPath": "src/sync.c", + "vendoredPath": "upstream/src/sync.c", + "upstreamSha256": "5d8f65d40a1df1b7e0a7025482fffb0fce1ca7ed423116ce7f151f78f42fd9a7", + "vendoredSha256": "5d8f65d40a1df1b7e0a7025482fffb0fce1ca7ed423116ce7f151f78f42fd9a7" + }, + { + "upstreamPath": "src/sync.h", + "vendoredPath": "upstream/src/sync.h", + "upstreamSha256": "0067ff9034892c94ef7bdae068e51f7b9c4922e11c188f8fd69d64a429586753", + "vendoredSha256": "0067ff9034892c94ef7bdae068e51f7b9c4922e11c188f8fd69d64a429586753" + }, + { + "upstreamPath": "src/unicode.c", + "vendoredPath": "upstream/src/unicode.c", + "upstreamSha256": "89881655c230bf6c6965307911fd710e429ef6be65105d724fab27766172bfc1", + "vendoredSha256": "89881655c230bf6c6965307911fd710e429ef6be65105d724fab27766172bfc1" + }, + { + "upstreamPath": "src/unicode.h", + "vendoredPath": "upstream/src/unicode.h", + "upstreamSha256": "0f145f6a681c24525217ac3f438911e3d2d471be1f339516c1be0a899d21be13", + "vendoredSha256": "0f145f6a681c24525217ac3f438911e3d2d471be1f339516c1be0a899d21be13" + }, + { + "upstreamPath": "support/faad2-hdc-support.patch", + "vendoredPath": "upstream/support/faad2-hdc-support.patch", + "upstreamSha256": "e25e8d69c20269757c66ba38c730fbde4588393d981d45d86472beea5cffc117", + "vendoredSha256": "e25e8d69c20269757c66ba38c730fbde4588393d981d45d86472beea5cffc117" + } + ], + "fileCount": 47, + "vendoredBytes": 401245, + "localAdapters": [ + { + "pathFromRepositoryRoot": "third_party/nrsc5/compat/msvc/complex.h", + "sha256": "c755f845f66c719326b07ff8cc6feac0240a09d91f35049df78521ff826c6d75", + "license": "GPL-3.0-or-later", + "description": "AetherSDR authored native Windows compatibility adapter, private to nrsc5 C objects." + }, + { + "pathFromRepositoryRoot": "third_party/nrsc5/compat/msvc/pthread.h", + "sha256": "37976fd853ee5376eae317f37319026b21b5fdef677029b41395db91ce671423", + "license": "GPL-3.0-or-later", + "description": "AetherSDR authored native Windows compatibility adapter, private to nrsc5 C objects." + } + ] +} diff --git a/third_party/nrsc5/compat/msvc/complex.h b/third_party/nrsc5/compat/msvc/complex.h new file mode 100644 index 000000000..6ffb0d9fc --- /dev/null +++ b/third_party/nrsc5/compat/msvc/complex.h @@ -0,0 +1,70 @@ +/* AetherSDR native Windows adapter; GPL-3.0-or-later. + * clang-cl implements C complex arithmetic, while the Microsoft CRT exposes + * complex math through _Fcomplex. Keep the pinned decoder's arithmetic intact + * and bridge only the six CRT functions it uses. This header is private to the + * nrsc5 C target; no C complex values cross its public float-array API. + */ +#pragma once + +#if !defined(_WIN32) || !defined(__clang__) +#error "This adapter requires clang-cl targeting the Windows MSVC ABI" +#endif + +/* Read math.h first: its legacy `complex` alias must not overwrite ours on a + * later upstream include. UCRT also declares normf; the decoder has its own. */ +#include +#include_next + +#undef complex +#define complex _Complex +#undef I +#define I (__builtin_complex(0.0f, 1.0f)) +#undef _Complex_I +#define _Complex_I I +#ifndef CMPLXF +#define CMPLXF(real, imag) __builtin_complex((float)(real), (float)(imag)) +#endif + +static inline _Fcomplex aether_nrsc5_to_crt(float complex value) +{ + return _FCbuild(__real__(value), __imag__(value)); +} + +static inline float aether_nrsc5_crealf(float complex value) +{ + return __real__(value); +} + +static inline float aether_nrsc5_cimagf(float complex value) +{ + return __imag__(value); +} + +static inline float aether_nrsc5_cabsf(float complex value) +{ + return cabsf(aether_nrsc5_to_crt(value)); +} + +static inline float aether_nrsc5_cargf(float complex value) +{ + return cargf(aether_nrsc5_to_crt(value)); +} + +static inline float complex aether_nrsc5_conjf(float complex value) +{ + return CMPLXF(__real__(value), -__imag__(value)); +} + +static inline float complex aether_nrsc5_cexpf(float complex value) +{ + const _Fcomplex result = cexpf(aether_nrsc5_to_crt(value)); + return CMPLXF(crealf(result), cimagf(result)); +} + +#define crealf aether_nrsc5_crealf +#define cimagf aether_nrsc5_cimagf +#define cabsf aether_nrsc5_cabsf +#define cargf aether_nrsc5_cargf +#define conjf aether_nrsc5_conjf +#define cexpf aether_nrsc5_cexpf +#define normf aether_nrsc5_normf diff --git a/third_party/nrsc5/compat/msvc/pthread.h b/third_party/nrsc5/compat/msvc/pthread.h new file mode 100644 index 000000000..b005c71ad --- /dev/null +++ b/third_party/nrsc5/compat/msvc/pthread.h @@ -0,0 +1,109 @@ +/* AetherSDR native Windows adapter; GPL-3.0-or-later. + * Private, deliberately limited pthread subset used by pinned nrsc5. Pipe + * mode uses only the FFTW planner mutex and never creates a decoder thread. + * Preserve the retained upstream device/file entrypoints without introducing + * a pthread DLL or exporting this compatibility layer to any other target. + */ +#pragma once + +#ifndef _WIN32 +#error "This adapter requires Windows" +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#include + +typedef SRWLOCK pthread_mutex_t; +typedef CONDITION_VARIABLE pthread_cond_t; +typedef HANDLE pthread_t; +#define PTHREAD_MUTEX_INITIALIZER SRWLOCK_INIT + +static inline int pthread_mutex_init(pthread_mutex_t *mutex, const void *attributes) +{ + (void)attributes; + InitializeSRWLock(mutex); + return 0; +} + +static inline int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + AcquireSRWLockExclusive(mutex); + return 0; +} + +static inline int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + ReleaseSRWLockExclusive(mutex); + return 0; +} + +static inline int pthread_cond_init(pthread_cond_t *condition, const void *attributes) +{ + (void)attributes; + InitializeConditionVariable(condition); + return 0; +} + +static inline int pthread_cond_broadcast(pthread_cond_t *condition) +{ + WakeAllConditionVariable(condition); + return 0; +} + +static inline int pthread_cond_wait(pthread_cond_t *condition, pthread_mutex_t *mutex) +{ + return SleepConditionVariableSRW(condition, mutex, INFINITE, 0) ? 0 : EINVAL; +} + +struct aether_nrsc5_thread_start +{ + void *(*function)(void *); + void *argument; +}; + +static unsigned __stdcall aether_nrsc5_thread_entry(void *opaque) +{ + struct aether_nrsc5_thread_start start = *(struct aether_nrsc5_thread_start *)opaque; + free(opaque); + start.function(start.argument); + return 0; +} + +static inline int pthread_create(pthread_t *thread, const void *attributes, + void *(*function)(void *), void *argument) +{ + (void)attributes; + struct aether_nrsc5_thread_start *start = malloc(sizeof(*start)); + if (!start) { + return ENOMEM; + } + start->function = function; + start->argument = argument; + const uintptr_t handle = _beginthreadex(NULL, 0, aether_nrsc5_thread_entry, start, 0, NULL); + if (!handle) { + free(start); + return EAGAIN; + } + *thread = (HANDLE)handle; + return 0; +} + +static inline int pthread_join(pthread_t thread, void **result) +{ + /* nrsc5 joins only with a null result and its worker returns NULL. */ + if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { + return EINVAL; + } + if (result) { + *result = NULL; + } + return CloseHandle(thread) ? 0 : EINVAL; +} diff --git a/third_party/nrsc5/patches/windows-msvc.patch b/third_party/nrsc5/patches/windows-msvc.patch new file mode 100644 index 000000000..c16353098 --- /dev/null +++ b/third_party/nrsc5/patches/windows-msvc.patch @@ -0,0 +1,56 @@ +diff --git a/src/output.c b/src/output.c +index 6150feec..563c02f0 100644 +--- a/src/output.c ++++ b/src/output.c +@@ -20,7 +20,9 @@ + #include + #include + #include ++#ifndef _WIN32 + #include ++#endif + + #include "defines.h" + #include "output.h" +diff --git a/src/rtltcp.c b/src/rtltcp.c +index 232ee722..b4efedee 100644 +--- a/src/rtltcp.c ++++ b/src/rtltcp.c +@@ -1,7 +1,8 @@ + #include + #include +-#ifdef __MINGW32__ +-#include ++#ifdef _WIN32 ++#include ++#include + #else + #include + #include +@@ -71,7 +72,7 @@ error: + + void rtltcp_close(rtltcp_t *st) + { +-#ifdef __MINGW32__ ++#ifdef _WIN32 + closesocket(st->socket); + #else + close(st->socket); +@@ -158,7 +159,7 @@ int rtltcp_reset_buffer(rtltcp_t *st, size_t cnt) + char buf[1024]; + unsigned int recvd = 0; + int flags = 0; +-#ifdef __MINGW32__ ++#ifdef _WIN32 + unsigned long mode = 1; + ioctlsocket(st->socket, FIONBIO, &mode); + #else +@@ -172,7 +173,7 @@ int rtltcp_reset_buffer(rtltcp_t *st, size_t cnt) + break; + recvd += err; + } +-#ifdef __MINGW32__ ++#ifdef _WIN32 + mode = 0; + ioctlsocket(st->socket, FIONBIO, &mode); + #endif diff --git a/third_party/nrsc5/upstream/CMakeLists.txt b/third_party/nrsc5/upstream/CMakeLists.txt new file mode 100644 index 000000000..e8af56aad --- /dev/null +++ b/third_party/nrsc5/upstream/CMakeLists.txt @@ -0,0 +1,316 @@ +cmake_minimum_required (VERSION 3.10) +cmake_policy (VERSION 3.10...3.28) +include (CheckLibraryExists) +include (CheckSymbolExists) +include (ExternalProject) +include (FindPkgConfig) +project (nrsc5 C) + +execute_process (COMMAND ${CMAKE_C_COMPILER} -dumpmachine OUTPUT_VARIABLE HOST_TRIPLE_DEFAULT OUTPUT_STRIP_TRAILING_WHITESPACE) + +option (USE_NEON "Use NEON instructions") +option (USE_SSE "Use SSE3 instructions") +option (USE_FAAD2 "AAC decoding with FAAD2" ON) +option (USE_STATIC "Link with static libraries") +option (USE_SYSTEM_FFTW "Use system provided fftw" ON) +option (USE_SYSTEM_RTLSDR "Use system provided rtl-sdr" ON) +option (USE_SYSTEM_LIBUSB "Use system provided libusb" ON) +option (USE_SYSTEM_LIBAO "Use system provided libao" ON) +option (INSTALLED_FAAD_IS_PATCHED "Use patched system-provided FAAD2" OFF) +option (BUILD_DOC "Build API documentation" OFF) +option (BUILD_CLI "Build nrsc5 executable" ON) + +set (FAAD2_CMAKE_ARGS "" CACHE STRING "Extra arguments for FAAD2 cmake command") +set (LIBRARY_DEBUG_LEVEL "5" CACHE STRING "Debug logging level for libnrsc5: 1=debug, 2=info, 3=warn, 4=error, 5=none") +set (HOST_TRIPLE "${HOST_TRIPLE_DEFAULT}" CACHE STRING "Override default host triple") +if (HOST_TRIPLE) + set (HOST_TRIPLE_ARG "--host=${HOST_TRIPLE}") +endif() + +message (STATUS "Building for ${HOST_TRIPLE}") + +find_program (AUTOCONF autoconf) +if (NOT AUTOCONF) + message (FATAL_ERROR "Missing autoconf. Install autoconf package and try again.") +endif () + +find_program (AUTOMAKE automake) +if (NOT AUTOMAKE) + message (FATAL_ERROR "Missing automake. Install autoconf package and try again.") +endif () + +find_program (LIBTOOLIZE NAMES libtoolize glibtoolize) +if (NOT LIBTOOLIZE) + message (FATAL_ERROR "Missing libtoolize. Install libtool package and try again.") +endif () + +find_program (PATCH patch) +if (NOT PATCH) + message (FATAL_ERROR "Missing patch. Install patch package and try again.") +endif() + +if (USE_SYSTEM_FFTW) + find_library (FFTW3F_LIBRARIES fftw3f) + pkg_search_module (FFTW3F fftw3f) + if (NOT FFTW3F_LIBRARIES) + message (WARNING "libfftw3f not found. Building from source.") + set (USE_SYSTEM_FFTW OFF) + endif () +endif () +if (USE_SYSTEM_RTLSDR) + find_library (RTL_SDR_LIBRARIES rtlsdr) + pkg_search_module (RTL_SDR librtlsdr) + if (NOT RTL_SDR_LIBRARIES) + message (WARNING "librtlsdr not found. Building from source.") + set (USE_SYSTEM_RTLSDR OFF) + endif () +endif () +if (USE_SYSTEM_LIBUSB) + find_library (LIBUSB_LIBRARIES usb-1.0) + pkg_search_module (LIBUSB libusb-1.0) + if (NOT LIBUSB_LIBRARIES) + message (WARNING "libusb-1.0 not found. Building from source.") + set (USE_SYSTEM_LIBUSB OFF) + endif () +endif () +if (BUILD_CLI AND USE_SYSTEM_LIBAO) + find_library (AO_LIBRARIES ao) + pkg_search_module(AO ao) + if (NOT AO_LIBRARIES) + message (WARNING "libao not found. Building from source.") + set (USE_SYSTEM_LIBAO OFF) + endif () +endif () + +if (CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*") + if (USE_NEON) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4") + add_definitions (-DHAVE_NEON) + endif() +endif() + +if (CMAKE_SYSTEM_PROCESSOR MATCHES "(i[456]|x)86.*") + if (USE_SSE) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2 -msse3 -mssse3") + add_definitions (-DHAVE_SSE2 -DHAVE_SSE3) + endif() +endif() + +set (CMAKE_REQUIRED_FLAGS --std=gnu11) +check_symbol_exists (strndup string.h HAVE_STRNDUP) +check_symbol_exists (CMPLXF complex.h HAVE_CMPLXF) +check_symbol_exists (_Imaginary_I complex.h HAVE_IMAGINARY_I) +check_symbol_exists (_Complex_I complex.h HAVE_COMPLEX_I) + +if (NOT USE_SYSTEM_FFTW) + set (FFTW_PREFIX "${CMAKE_BINARY_DIR}/fftw-prefix") + ExternalProject_Add ( + fftw_external + URL "https://www.fftw.org/fftw-3.3.10.tar.gz" + URL_HASH SHA256=56c932549852cddcfafdab3820b0200c7742675be92179e59e6215b340e26467 + PREFIX ${FFTW_PREFIX} + + CONFIGURE_COMMAND ${FFTW_PREFIX}/src/fftw_external/configure ${HOST_TRIPLE_ARG} --prefix=${FFTW_PREFIX} --enable-float --enable-static --disable-shared --enable-sse2 --enable-avx --enable-avx2 --with-our-malloc + ) + + add_library (fftw3f STATIC IMPORTED) + set_property (TARGET fftw3f PROPERTY IMPORTED_LOCATION "${FFTW_PREFIX}/lib/libfftw3f.a") + add_dependencies (fftw3f fftw_external) + + set (FFTW3F_INCLUDE_DIRS "${FFTW_PREFIX}/include") + set (FFTW3F_LIBRARIES fftw3f) + set (FFTW3F_BUILTIN fftw3f) +endif () + +if (NOT USE_SYSTEM_LIBUSB) + set (LIBUSB_PREFIX "${CMAKE_BINARY_DIR}/libusb-prefix") + ExternalProject_Add ( + libusb_external + TIMEOUT 120 + GIT_REPOSITORY "https://github.com/libusb/libusb.git" + GIT_TAG v1.0.27 + PREFIX ${LIBUSB_PREFIX} + + UPDATE_COMMAND "" + CONFIGURE_COMMAND ${LIBUSB_PREFIX}/src/libusb_external/configure ${HOST_TRIPLE_ARG} --prefix=${LIBUSB_PREFIX} + ) + ExternalProject_Add_Step ( + libusb_external + bootstrap + COMMAND sh ./bootstrap.sh + DEPENDEES patch + DEPENDERS configure + WORKING_DIRECTORY + ) + add_library (libusb STATIC IMPORTED) + set_property (TARGET libusb PROPERTY IMPORTED_LOCATION "${LIBUSB_PREFIX}/lib/libusb-1.0.a") + + set (LIBUSB_BUILTIN libusb_external) + set (LIBUSB_LIBRARIES libusb) + set (RTLSDR_CMAKE_ARGS + -DCMAKE_PREFIX_PATH:STRING=${LIBUSB_PREFIX} + ) +endif () + +if (NOT USE_SYSTEM_RTLSDR) + set (RTLSDR_PREFIX "${CMAKE_BINARY_DIR}/rtlsdr-prefix") + ExternalProject_Add ( + rtlsdr_external + TIMEOUT 120 + GIT_REPOSITORY "https://gitea.osmocom.org/sdr/rtl-sdr.git" + GIT_TAG v2.0.2 + PREFIX ${RTLSDR_PREFIX} + + UPDATE_COMMAND "" + CMAKE_ARGS + -DCMAKE_C_FLAGS:STRING="-std=gnu17" + -DCMAKE_INSTALL_PREFIX:STRING=${RTLSDR_PREFIX} + -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} + -DCMAKE_SYSTEM_NAME:STRING=${CMAKE_SYSTEM_NAME} + ${RTLSDR_CMAKE_ARGS} + ) + + if (LIBUSB_BUILTIN) + add_dependencies (rtlsdr_external ${LIBUSB_BUILTIN}) + endif () + + add_library (rtlsdr STATIC IMPORTED) + set_property (TARGET rtlsdr PROPERTY IMPORTED_LOCATION "${RTLSDR_PREFIX}/lib/librtlsdr_static.a") + add_dependencies (rtlsdr rtlsdr_external) + + set (RTL_SDR_INCLUDE_DIRS "${RTLSDR_PREFIX}/include") + set (RTL_SDR_LIBRARIES rtlsdr ${LIBUSB_LIBRARIES}) + set (RTL_SDR_BUILTIN rtlsdr) +endif () + +if (BUILD_CLI AND NOT USE_SYSTEM_LIBAO) + if (NOT WIN32) + message (FATAL_ERROR "Built-in libao only supported on Windows.") + endif () + + set (LIBAO_PREFIX "${CMAKE_BINARY_DIR}/libao-prefix") + ExternalProject_Add ( + libao_external + TIMEOUT 120 + GIT_REPOSITORY "https://github.com/xiph/libao.git" + GIT_TAG cafce902a73c1050474a62feff83e428bbbee5f4 + PREFIX ${LIBAO_PREFIX} + + UPDATE_COMMAND "" + CONFIGURE_COMMAND ${LIBAO_PREFIX}/src/libao_external/configure ${HOST_TRIPLE_ARG} --prefix=${LIBAO_PREFIX} --enable-static --disable-shared --disable-pulse + ) + ExternalProject_Add_Step ( + libao_external + bootstrap + COMMAND sh ./autogen.sh + DEPENDEES patch + DEPENDERS configure + WORKING_DIRECTORY + ) + + add_library (libao STATIC IMPORTED) + set_property (TARGET libao PROPERTY IMPORTED_LOCATION "${LIBAO_PREFIX}/lib/libao.a") + add_dependencies (libao libao_external) + + set (AO_INCLUDE_DIRS "${LIBAO_PREFIX}/include") + set (AO_LIBRARIES libao ksuser winmm) + set (AO_BUILTIN libao) +endif () + +if (USE_FAAD2) + # use patched faad if installed + if (INSTALLED_FAAD_IS_PATCHED) + message(STATUS "Try using patched system-provided FAAD2 library") + pkg_search_module(PC_FAAD2 QUIET faad2) + + find_path(FAAD2_INCLUDE_DIRS neaacdec.h + HINTS ${PC_FAAD2_INCLUDEDIR} ${PC_FAAD2_INCLUDE_DIRS}) + + # use the libfaad_hdc.a, not libfaad as specified in pkg-config file + find_library(FAAD2_LIBRARIES NAMES faad_hdc + HINTS ${PC_FAAD2_LIBDIR} ${PC_FAAD2_LIBRARY_DIRS} ) + + if (FAAD2_INCLUDE_DIRS AND FAAD2_LIBRARY) + set(FAAD2_FOUND ON) + else () + message (WARNING "FAAD2 patched not found. Building from source.") + endif() + endif() + + if (NOT FAAD2_FOUND) + set (FAAD2_PREFIX "${CMAKE_BINARY_DIR}/faad2-prefix") + ExternalProject_Add ( + faad2_external + TIMEOUT 120 + GIT_REPOSITORY "https://github.com/knik0/faad2.git" + GIT_TAG 2.11.2 + PREFIX ${FAAD2_PREFIX} + + UPDATE_COMMAND "" + PATCH_COMMAND patch -p1 -Ni "${CMAKE_SOURCE_DIR}/support/faad2-hdc-support.patch" || exit 0 + + CMAKE_ARGS + -DBUILD_SHARED_LIBS:BOOL=OFF + -DCMAKE_INSTALL_PREFIX:STRING=${FAAD2_PREFIX} + -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} + -DCMAKE_SYSTEM_NAME:STRING=${CMAKE_SYSTEM_NAME} + "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -O2 -fPIC" + ${FAAD2_CMAKE_ARGS} + ) + + add_library (faad2 STATIC IMPORTED) + set_property (TARGET faad2 PROPERTY IMPORTED_LOCATION "${FAAD2_PREFIX}/lib/libfaad_hdc.a") + add_dependencies (faad2 faad2_external) + + set (FAAD2_INCLUDE_DIRS "${FAAD2_PREFIX}/include") + set (FAAD2_LIBRARIES faad2) + set (FAAD2_BUILTIN faad2) + set (FAAD2_FOUND ON) + endif() + + if (FAAD2_FOUND) + add_definitions (-DHAVE_FAAD2) + endif() +else () + # we only use libao with faad2 + unset (AO_INCLUDE_DIRS) + unset (AO_LIBRARIES) + unset (AO_BUILTIN) +endif() + +set (BUILTIN_LIBRARIES ${FFTW3F_BUILTIN} ${RTL_SDR_BUILTIN} ${FAAD2_BUILTIN} ${AO_BUILTIN}) + +if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + execute_process( + COMMAND git log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +else() + set(GIT_COMMIT_HASH "unknown") +endif() +add_definitions("-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"") + +add_subdirectory (src) + +# optionally generate documentation via Doxygen +if (BUILD_DOC) + find_package(Doxygen) + if (NOT DOXYGEN_FOUND) + message("** Install the Doxygen application to generate API documentation") + else () + message("-- Build of Doxygen API documentation enabled") + set(DOXYGEN_IN ${CMAKE_CURRENT_BINARY_DIR}/../doc/Doxyfile.in) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + # 'ALL' below enables building docs together with the application + add_custom_target( doc_doxygen ALL + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM ) + endif () +else () + message("-- Build of Doxygen API documentation disabled; use -DBUILD_DOC=ON to enable") +endif () diff --git a/third_party/nrsc5/upstream/LICENSE b/third_party/nrsc5/upstream/LICENSE new file mode 100644 index 000000000..9aa03b39b --- /dev/null +++ b/third_party/nrsc5/upstream/LICENSE @@ -0,0 +1,12 @@ +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/third_party/nrsc5/upstream/README.md b/third_party/nrsc5/upstream/README.md new file mode 100644 index 000000000..78e96b96f --- /dev/null +++ b/third_party/nrsc5/upstream/README.md @@ -0,0 +1,164 @@ +# nrsc5 + +This program receives NRSC-5 digital radio stations using an RTL-SDR dongle, or by reading from I/Q files. It offers a command-line interface as well as an API upon which other applications can be built. Before using it, you'll first need to compile the program using the build instructions below. + +## Building on Ubuntu, Debian or Raspbian + + sudo apt install git build-essential cmake autoconf libtool libao-dev libfftw3-dev librtlsdr-dev + git clone https://github.com/theori-io/nrsc5.git + cd nrsc5 + mkdir build + cd build + cmake [options] .. + make + sudo make install + sudo ldconfig + +Available build options: + + -DUSE_NEON=ON Use NEON instructions. [ARM, default=OFF] + -DUSE_SSE=ON Use SSSE3 instructions. [x86, default=OFF] + -DUSE_FAAD2=ON AAC decoding with FAAD2. [default=ON] + -DLIBRARY_DEBUG_LEVEL=1 Debug logging level for libnrsc5. [default=5] + -DBUILD_DOC=ON Generate html API documentation [default=OFF] + +You can test the program using the included sample capture: + + xz -d < ../support/sample.xz | src/nrsc5 -r - 0 + +## Building on Fedora + +Follow the Ubuntu instructions above, but replace the first command with the following: + + sudo dnf install git make patch cmake autoconf libtool libao-devel fftw-devel rtl-sdr-devel libusb1-devel + +## Building on openSUSE + +Follow the Ubuntu instructions above, but replace the first command with the following: + + zypper install -t pattern devel_C_C++ + zypper install git cmake libao-devel fftw3-devel rtl-sdr-devel libusb-1_0-devel + +## Building on macOS using [Homebrew](https://brew.sh) + + curl https://raw.githubusercontent.com/theori-io/nrsc5/master/nrsc5.rb > /tmp/nrsc5.rb + brew install --HEAD -s /tmp/nrsc5.rb + +## Building for Windows + +To build the program for Windows, you can either use [MSYS2](http://www.msys2.org) on Windows, or else use a cross-compiler on an Ubuntu, Debian or macOS machine. Scripts are provided to help with both cases. + +### Building on Windows with MSYS2 + +Install [MSYS2](http://www.msys2.org). Open a terminal using the "MSYS2 MinGW 64-bit" shortcut. (Or use the 32-bit shortcut if you prefer a 32-bit build.) + + pacman -Syu + +If this is the first time running pacman, you will be told to close the terminal window. After doing so, reopen using the same shortcut as before. + + pacman -Su + pacman -S git + git clone https://github.com/theori-io/nrsc5.git + nrsc5/support/msys2-build -j4 + +You can test your installation using the included sample file: + + cd ~/nrsc5/support + xz -d sample.xz + nrsc5.exe -r sample 0 + +If the sample file does not work, make sure you followed all of the instructions. If it still doesn't work, file an issue with the error message. Please put "[Windows]" in the title of the issue. + +Once everything is built, you can run nrsc5 independently of MSYS2. Copy the following files from your MSYS2 mingw64 (or mingw32) directory (e.g. C:\\msys64\\mingw64\\bin): + +* libnrsc5.dll +* nrsc5.exe + +### Cross-compiling for Windows from Ubuntu / Debian + + sudo apt install cmake autoconf libtool pkgconf git mingw-w64 + git clone https://github.com/theori-io/nrsc5.git + cd nrsc5 + support/win-cross-compile 64 --cmake-args="-DUSE_SSE=ON" -j4 + +Replace `64` with `32` if you want a 32-bit build. Once the build is complete, copy `*.dll` and `nrsc5.exe` from the `build-win64/bin` (or `build-win32/bin`) folder to your Windows machine. + +### Cross-compiling for Windows from macOS + + brew install cmake autoconf automake libtool pkgconf git mingw-w64 + git clone https://github.com/theori-io/nrsc5.git + cd nrsc5 + support/win-cross-compile 64 --cmake-args="-DUSE_SSE=ON" -j4 + +Replace `64` with `32` if you want a 32-bit build. Once the build is complete, copy `*.dll` and `nrsc5.exe` from the `build-win64/bin` (or `build-win32/bin`) folder to your Windows machine. + +## Usage + +### Command-line options: + + frequency center frequency in MHz or Hz + (do not provide frequency when reading from file) + program audio program to decode + (0, 1, 2, or 3) + -g gain gain + (example: 49.6) + (automatic gain selection if not specified) + -d device-index rtl-sdr device + -p ppm-error rtl-sdr ppm error + -H rtltcp-host rtl_tcp host with optional port + (example: localhost:1234) + -r iq-input read IQ samples from input file + --iq-input-format {cu8,cs16,cf32} IQ input format (complex unsigned 8-bit @ 1488375 SPS, + complex signed 16-bit @ 744188 SPS (FM) / 46512 SPS (AM) or + complex float 32-bit @ 744188 SPS (FM) / 46512 SPS (AM). + default is cu8.) + -w iq-output write IQ samples to output file + -o audio-output write audio to output file + -t audio-type type of audio output (wav or raw) + (default is wav. used in conjunction with -o) + -q disable log output + -l log-level set log level + (1 = DEBUG, 2 = INFO, 3 = WARN) + -v print the version number and exit + --am receive AM signals + (default is FM) + -T enable bias-T + -D direct-sampling-mode enable direct sampling + (1 = I-ADC input, 2 = Q-ADC input) + --dump-aas-files dir-name dump AAS files + (WARNING: insecure) + --dump-hdc file-name dump HDC packets + +### Examples: + +Tune to 107.1 MHz and play audio program 0: + + nrsc5 107.1 0 + +Tune to 107.1 MHz and play audio program 0. Manually set gain to 49.0 dB and save raw IQ samples to a file: + + nrsc5 -g 49.0 -w samples1071 107.1 0 + +Read raw IQ samples from a file and play back audio program 0: + + nrsc5 -r samples1071 0 + +Tune to 90.5 MHz and convert audio program 0 to WAV format for playback in an external media player: + + nrsc5 -o - 90.5 0 | mplayer - + +### Keyboard commands: + +To switch between audio programs at runtime, press 0 through 7. + +To quit, press Q. + +### RTL-SDR drivers on Windows + +If you get errors trying to access your RTL-SDR device, then you may need to use [Zadig](http://zadig.akeo.ie/) to change the USB driver. Once you download and run Zadig, select your RTL-SDR device, ensure the driver is set to WinUSB, and then click "Replace Driver". If your device is not listed, enable "Options" -> "List All Devices". + +### Application Programming Interface (API) + +If you would like to build an application that makes use of nrsc5's functionality, you can use the [C API](include/nrsc5.h) ([documentation](https://theori-io.github.io/nrsc5/c-api/)) or [Python API](support/nrsc5.py). The [`nrsc5` command-line application](src/main.c) is built on top of the C API, and an equivalent [Python command-line application](support/cli.py) is built on top of the Python API. These applications serve as examples of how to use the API. + +Note: When using the Python API or the Python command-line application on Windows, place `libnrsc5.dll` in the same folder as `nrsc5.py`. diff --git a/third_party/nrsc5/upstream/include/nrsc5.h b/third_party/nrsc5/upstream/include/nrsc5.h new file mode 100644 index 000000000..47526a750 --- /dev/null +++ b/third_party/nrsc5/upstream/include/nrsc5.h @@ -0,0 +1,914 @@ +#ifndef NRSC5_H_ +#define NRSC5_H_ + +/** \file nrsc5.h + * External API for the nrsc5 library. + */ + +/*! \mainpage Quick API Reference + * + * The library API is documented in nrsc5.h -- see the *Functions* section. + * + * Useful information about the protocol is provided in standards + * documents available from https://www.nrscstandards.org/ + * + * Note: this documentation was *not* written by the authors of **nrsc5**; + * its accuracy cannot be guaranteed. + */ + +#include +#include +#include +#include + +/* + * Definitions. + */ +#define NRSC5_SCAN_BEGIN 87.9e6 +#define NRSC5_SCAN_END 107.9e6 +#define NRSC5_SCAN_SKIP 0.2e6 + +#define NRSC5_MIME_PRIMARY_IMAGE 0xBE4B7536 /**< MIME type for album art */ +#define NRSC5_MIME_STATION_LOGO 0xD9C72536 /**< MIME type for station logo */ +#define NRSC5_MIME_NAVTEQ 0x2D42AC3E +#define NRSC5_MIME_HERE_TPEG 0x82F03DFC +#define NRSC5_MIME_HERE_IMAGE 0xB7F03DFC +#define NRSC5_MIME_HD_TMC 0xEECB55B6 +#define NRSC5_MIME_HDC 0x4DC66C5A /**< MIME type for HD Codec audio */ +#define NRSC5_MIME_TEXT 0xBB492AAC /**< MIME type for text file */ +#define NRSC5_MIME_JPEG 0x1E653E9C /**< MIME type for JPEG image */ +#define NRSC5_MIME_PNG 0x4F328CA0 /**< MIME type for PNG image */ +#define NRSC5_MIME_TTN_TPEG_1 0xB39EBEB2 +#define NRSC5_MIME_TTN_TPEG_2 0x4EB03469 +#define NRSC5_MIME_TTN_TPEG_3 0x52103469 +#define NRSC5_MIME_TTN_STM_TRAFFIC 0xFF8422D7 +#define NRSC5_MIME_TTN_STM_WEATHER 0xEF042E96 +#define NRSC5_MIME_UNKNOWN_00000000 0x00000000 +#define NRSC5_MIME_UNKNOWN_1C7D0E29 0x1C7D0E29 +#define NRSC5_MIME_UNKNOWN_B81FFAA8 0xB81FFAA8 +#define NRSC5_MIME_UNKNOWN_FFFFFFFF 0xFFFFFFFF + +#define NRSC5_AUDIO_FRAME_SAMPLES 2048 /**< Number of audio samples per HDC frame */ + +#define NRSC5_SAMPLE_RATE_CU8 1488375 /**< Sample rate at which nrsc5_pipe_samples_cu8() expects samples (FM or AM) */ +#define NRSC5_SAMPLE_RATE_NATIVE_FM 744187.5 /**< Sample rate at which nrsc5_pipe_samples_cs16() & nrsc5_pipe_samples_cf32() expects samples (FM only) */ +#define NRSC5_SAMPLE_RATE_NATIVE_AM 46511.71875 /**< Sample rate at which nrsc5_pipe_samples_cs16() & nrsc5_pipe_samples_cf32() expects samples (AM only) */ +#define NRSC5_SAMPLE_RATE_CS16_FM NRSC5_SAMPLE_RATE_NATIVE_FM /**< DEPRECATED: use `NRSC5_SAMPLE_RATE_NATIVE_FM` instead */ +#define NRSC5_SAMPLE_RATE_CS16_AM NRSC5_SAMPLE_RATE_NATIVE_AM /**< DEPRECATED: use `NRSC5_SAMPLE_RATE_NATIVE_AM` instead */ +#define NRSC5_SAMPLE_RATE_AUDIO 44100 /**< Sample rate of outgoing audio */ + +#define NRSC5_DEVICE_VERSION_LENGTH 4 /**< Length of Core Version & Manufacturer Version in SIS Parameter messages. */ + +#ifdef NRSC5_EXPORTS +#ifdef __MINGW32__ +#define NRSC5_API __declspec(dllexport) +#else +#define NRSC5_API +#endif +#else +#define NRSC5_API +#endif + +enum +{ + NRSC5_MODE_FM, + NRSC5_MODE_AM +}; + +/* + * Data types. + */ +enum +{ + NRSC5_SIG_COMPONENT_AUDIO, + NRSC5_SIG_COMPONENT_DATA +}; + +enum +{ + NRSC5_AAS_TYPE_STREAM = 0, + NRSC5_AAS_TYPE_PACKET = 1, + NRSC5_AAS_TYPE_LOT = 3 +}; + +/** Represents a service component. + * + * An element of a linked list that accompanies a nrsc5_sig_service_t + * describing a component of a SIG record. This provides further + * information about the audio or data in the channel. + */ +struct nrsc5_sig_component_t +{ + struct nrsc5_sig_component_t *next; /**< Pointer to next element or NULL */ + uint8_t type; /**< NRSC5_SIG_SERVICE_AUDIO or NRSC5_SIG_SERVICE_DATA */ + uint8_t id; /**< Component identifier, 0, 1, 2,... */ + union + { + /*! Data service information + */ + struct { + uint16_t port; /**< distinguishes packets for this service */ + /** e.g. NRSC5_SERVICE_DATA_TYPE_AUDIO_RELATED_DATA */ + uint16_t service_data_type; + uint8_t type; /**< NRSC5_AAS_TYPE_STREAM, NRSC5_AAS_TYPE_PACKET, or NRSC5_AAS_TYPE_LOT */ + uint32_t mime; /**< content, e.g. NRSC5_MIME_STATION_LOGO */ + } data; + /*! Audio service information + */ + struct { + uint8_t port; /**< distinguishes packets for this service */ + uint8_t type; /**< program type, e.g. NRSC5_PROGRAM_TYPE_JAZZ */ + uint32_t mime; /**< content, e.g. NRSC5_MIME_HDC */ + } audio; + }; +}; +/** + * Defines a typename for struct nrsc5_sig_component_t + */ +typedef struct nrsc5_sig_component_t nrsc5_sig_component_t; + +enum +{ + NRSC5_SIG_SERVICE_AUDIO, + NRSC5_SIG_SERVICE_DATA +}; + +/** Represents Station Information Guide (SIG) records + * + * Element of a linked list that will accompany an NRSC5_EVENT_SIG type event. + * Each element describes a channel (audio or data), with a name, e.g. "MPS" + * is used to indicate main program service, "SPS1" to indicate + * supplemental program service 1. Each service may include a linked + * list of type nrsc5_sig_component_t describing its components, which + * may include both audio and other data. + * + * Note: Stations which have only a single audio program and no data services + * may not broadcast a SIG table. Applications should not assume that a SIG + * table will be present. Information about audio services can be obtained + * from PDU headers (reported in NRSC5_EVENT_AUDIO_SERVICE events) instead. + */ +struct nrsc5_sig_service_t +{ + struct nrsc5_sig_service_t *next; /**< Pointer to next element or NULL */ + uint8_t type; /**< NRSC5_SIG_SERVICE_AUDIO or NRSC5_SIG_SERVICE_DATA */ + uint16_t number; /**< Channel number: 1,2,3,4 */ + const char *name; /**< Channel name, e.g. "MPS" or "SPS1" */ + nrsc5_sig_component_t *components; /**< Head of linked list of components */ + nrsc5_sig_component_t *audio_component; /**< Direct link to the audio component of an audio service, or NULL for a data service */ +}; +/** + * Defines a typename for struct nrsc5_sig_service_t + */ +typedef struct nrsc5_sig_service_t nrsc5_sig_service_t; + +enum +{ + NRSC5_EVENT_LOST_DEVICE, + NRSC5_EVENT_IQ, + NRSC5_EVENT_SYNC, + NRSC5_EVENT_LOST_SYNC, + NRSC5_EVENT_MER, + NRSC5_EVENT_BER, + NRSC5_EVENT_HDC, + NRSC5_EVENT_AUDIO, + NRSC5_EVENT_ID3, + NRSC5_EVENT_SIG, + NRSC5_EVENT_LOT, + NRSC5_EVENT_SIS, + NRSC5_EVENT_STREAM, + NRSC5_EVENT_PACKET, + NRSC5_EVENT_AUDIO_SERVICE, + NRSC5_EVENT_STATION_ID, + NRSC5_EVENT_STATION_NAME, + NRSC5_EVENT_STATION_SLOGAN, + NRSC5_EVENT_STATION_MESSAGE, + NRSC5_EVENT_STATION_LOCATION, + NRSC5_EVENT_AUDIO_SERVICE_DESCRIPTOR, + NRSC5_EVENT_DATA_SERVICE_DESCRIPTOR, + NRSC5_EVENT_EMERGENCY_ALERT, + NRSC5_EVENT_HERE_IMAGE, + NRSC5_EVENT_LOT_HEADER, + NRSC5_EVENT_LOT_FRAGMENT, + NRSC5_EVENT_AGC, + NRSC5_EVENT_EXCITER_INFO, + NRSC5_EVENT_IMPORTER_INFO, + NRSC5_EVENT_LEAP_SECOND_OFFSET, + NRSC5_EVENT_LOCAL_TIME, +}; + +enum +{ + NRSC5_ACCESS_PUBLIC, + NRSC5_ACCESS_RESTRICTED +}; + +enum +{ + NRSC5_PROGRAM_TYPE_UNDEFINED = 0, + NRSC5_PROGRAM_TYPE_NEWS = 1, + NRSC5_PROGRAM_TYPE_INFORMATION = 2, + NRSC5_PROGRAM_TYPE_SPORTS = 3, + NRSC5_PROGRAM_TYPE_TALK = 4, + NRSC5_PROGRAM_TYPE_ROCK = 5, + NRSC5_PROGRAM_TYPE_CLASSIC_ROCK = 6, + NRSC5_PROGRAM_TYPE_ADULT_HITS = 7, + NRSC5_PROGRAM_TYPE_SOFT_ROCK = 8, + NRSC5_PROGRAM_TYPE_TOP_40 = 9, + NRSC5_PROGRAM_TYPE_COUNTRY = 10, + NRSC5_PROGRAM_TYPE_OLDIES = 11, + NRSC5_PROGRAM_TYPE_SOFT = 12, + NRSC5_PROGRAM_TYPE_NOSTALGIA = 13, + NRSC5_PROGRAM_TYPE_JAZZ = 14, + NRSC5_PROGRAM_TYPE_CLASSICAL = 15, + NRSC5_PROGRAM_TYPE_RHYTHM_AND_BLUES = 16, + NRSC5_PROGRAM_TYPE_SOFT_RHYTHM_AND_BLUES = 17, + NRSC5_PROGRAM_TYPE_FOREIGN_LANGUAGE = 18, + NRSC5_PROGRAM_TYPE_RELIGIOUS_MUSIC = 19, + NRSC5_PROGRAM_TYPE_RELIGIOUS_TALK = 20, + NRSC5_PROGRAM_TYPE_PERSONALITY = 21, + NRSC5_PROGRAM_TYPE_PUBLIC = 22, + NRSC5_PROGRAM_TYPE_COLLEGE = 23, + NRSC5_PROGRAM_TYPE_SPANISH_TALK = 24, + NRSC5_PROGRAM_TYPE_SPANISH_MUSIC = 25, + NRSC5_PROGRAM_TYPE_HIP_HOP = 26, + NRSC5_PROGRAM_TYPE_WEATHER = 29, + NRSC5_PROGRAM_TYPE_EMERGENCY_TEST = 30, + NRSC5_PROGRAM_TYPE_EMERGENCY = 31, + NRSC5_PROGRAM_TYPE_TRAFFIC = 65, + NRSC5_PROGRAM_TYPE_SPECIAL_READING_SERVICES = 76 +}; + +enum +{ + NRSC5_BLEND_DISABLE, + NRSC5_BLEND_SELECT, + NRSC5_BLEND_ENABLE +}; + +enum +{ + NRSC5_LOCATION_FORMAT_SAME, + NRSC5_LOCATION_FORMAT_FIPS, + NRSC5_LOCATION_FORMAT_ZIP +}; + +enum +{ + NRSC5_ALERT_CATEGORY_NON_SPECIFIC = 1, + NRSC5_ALERT_CATEGORY_GEOPHYSICAL = 2, + NRSC5_ALERT_CATEGORY_WEATHER = 3, + NRSC5_ALERT_CATEGORY_SAFETY = 4, + NRSC5_ALERT_CATEGORY_SECURITY = 5, + NRSC5_ALERT_CATEGORY_RESCUE = 6, + NRSC5_ALERT_CATEGORY_FIRE = 7, + NRSC5_ALERT_CATEGORY_HEALTH = 8, + NRSC5_ALERT_CATEGORY_ENVIRONMENTAL = 9, + NRSC5_ALERT_CATEGORY_TRANSPORTATION = 10, + NRSC5_ALERT_CATEGORY_UTILITIES = 11, + NRSC5_ALERT_CATEGORY_HAZMAT = 12, + NRSC5_ALERT_CATEGORY_TEST = 30 +}; + +enum +{ + NRSC5_HERE_IMAGE_TRAFFIC = 8, + NRSC5_HERE_IMAGE_WEATHER = 13 +}; + +/** + * Station Information Service *Audio* service descriptor. This is a + * linked list element that may point to further audio service + * descriptor elements. Refer to NRSC-5 document SY_IDD_1020s. + * + * Note: Not all stations broadcast SIS audio service descriptors, so + * applications should not assume that they will be present. Audio service + * data from PDU headers (reported in NRSC5_EVENT_AUDIO_SERVICE events) can + * be used instead. + */ +struct nrsc5_sis_asd_t +{ + struct nrsc5_sis_asd_t *next; /**< Pointer to next element or NULL */ + unsigned int program; /**< program number 0, 1, ..., 7 */ + unsigned int access; /**< NRSC5_ACCESS_PUBLIC or NRSC5_ACCESS_RESTRICTED */ + unsigned int type; /**< program type, e.g. NRSC5_PROGRAM_TYPE_JAZZ */ + unsigned int sound_exp; /**< 0 is none, 2 is Dolby Pro Logic II Surround */ +}; +/** + * Defines a typename for struct nrsc5_sis_asd_t + */ +typedef struct nrsc5_sis_asd_t nrsc5_sis_asd_t; + +enum +{ + NRSC5_SERVICE_DATA_TYPE_NON_SPECIFIC = 0, + NRSC5_SERVICE_DATA_TYPE_NEWS = 1, + NRSC5_SERVICE_DATA_TYPE_SPORTS = 3, + NRSC5_SERVICE_DATA_TYPE_WEATHER = 29, + NRSC5_SERVICE_DATA_TYPE_EMERGENCY = 31, + NRSC5_SERVICE_DATA_TYPE_TRAFFIC = 65, + NRSC5_SERVICE_DATA_TYPE_IMAGE_MAPS = 66, + NRSC5_SERVICE_DATA_TYPE_TEXT = 80, + NRSC5_SERVICE_DATA_TYPE_ADVERTISING = 256, + NRSC5_SERVICE_DATA_TYPE_FINANCIAL = 257, + NRSC5_SERVICE_DATA_TYPE_STOCK_TICKER = 258, + NRSC5_SERVICE_DATA_TYPE_NAVIGATION = 259, + NRSC5_SERVICE_DATA_TYPE_ELECTRONIC_PROGRAM_GUIDE = 260, + NRSC5_SERVICE_DATA_TYPE_AUDIO = 261, + NRSC5_SERVICE_DATA_TYPE_PRIVATE_DATA_NETWORK = 262, + NRSC5_SERVICE_DATA_TYPE_SERVICE_MAINTENANCE = 263, + NRSC5_SERVICE_DATA_TYPE_HD_RADIO_SYSTEM_SERVICES = 264, + NRSC5_SERVICE_DATA_TYPE_AUDIO_RELATED_DATA = 265, + NRSC5_SERVICE_DATA_TYPE_RESERVED_FOR_SPECIAL_TESTS = 511 +}; + +/** + * Station Information Service *Data* service descriptor. This is a + * linked list element that may point to further data service descriptors + * via `next` member if not `NULL`. See + * nrsc5_service_data_type_name() for named types of data. + * Refer to NRSC-5 document SY_IDD_1020s. + */ +struct nrsc5_sis_dsd_t +{ + struct nrsc5_sis_dsd_t *next; /**< Pointer to next element or NULL */ + unsigned int access; /**< NRSC5_ACCESS_PUBLIC or NRSC5_ACCESS_RESTRICTED */ + unsigned int type; /**< data service type, e.g. NRSC5_SERVICE_DATA_TYPE_TEXT */ + uint32_t mime_type; /**< MIME type, e.g. `NRSC5_MIME_TEXT` */ +}; +/** + * Defines a typename for struct nrsc5_sis_dsd_t + */ +typedef struct nrsc5_sis_dsd_t nrsc5_sis_dsd_t; + +/** +* ID3 comment descriptor. This is a linked list element that may point to further +* comments via `next` member if not `NULL`. +* Refer to HD-Radio document SY_IDD_1028s. +*/ +struct nrsc5_id3_comment_t { + struct nrsc5_id3_comment_t *next; /**< Pointer to next element or NULL */ + char *lang; /**< language code, e.g. "eng" */ + char *short_content_desc; /**< short content description */ + char *full_text; /**< full text */ +}; +/** + * Defines a typename for struct nrsc5_id3_comment_t + */ +typedef struct nrsc5_id3_comment_t nrsc5_id3_comment_t; + +enum +{ + NRSC5_PKT_FLAGS_NONE = 0, + NRSC5_PKT_FLAGS_CRC_ERROR = 1 << 0, /** Failed the CRC check. Could be corrupted packet. */ +}; + +enum +{ + NRSC5_AUDIO_FLAGS_NONE = 0, + NRSC5_AUDIO_FLAGS_UNAVAILABLE = 1 << 0 /** Digital audio was currently unavailable. */, + NRSC5_AUDIO_FLAGS_DECODING_ERROR = 1 << 1, /** Failed audio decoding. */ +}; + +/** Incoming event from receiver. + * + * This event structure is passed to your application supplied + * callback function--see nrsc5_set_callback(). It is a union of + * various structs, keyed by member `event`. + */ +struct nrsc5_event_t +{ +/*! Type of event. + * The member `event` determines which sort of event occurred: + * - `NRSC5_EVENT_LOST_DEVICE` : RTL-SDR device was disconnected + * - `NRSC5_EVENT_IQ` : IQ data, see the `iq` union member + * - `NRSC5_EVENT_SYNC` : indicates synchronization achieved, see the `sync` union member + * - `NRSC5_EVENT_LOST_SYNC` : indicates synchronization lost + * - `NRSC5_EVENT_MER` : modulation error ratio, see the `mer` union member, and NRSC5 document SY_TN_2646s + * - `NRSC5_EVENT_BER` : Bit Error Ratio data, see the `ber` union member + * - `NRSC5_EVENT_HDC` : HDC audio packet, see the `hdc` union member + * - `NRSC5_EVENT_AUDIO` : audio buffer, see the `audio` union member + * - `NRSC5_EVENT_ID3` : ID3 information packet arrived, see `id3` member and information in HD-Radio document SY_IDD_1028s. + * - `NRSC5_EVENT_SIG` : service information arrived, see `sig` member + * - `NRSC5_EVENT_LOT` : LOT file data available, see `lot` member + * - `NRSC5_EVENT_LOT_HEADER` : LOT file header metadata available, see `lot` member + * - `NRSC5_EVENT_LOT_FRAGMENT` : fragment of a LOT file received, see `lot_fragment` member + * - `NRSC5_EVENT_SIS` : DEPRECATED. Use `NRSC5_EVENT_STATION_ID`, `NRSC5_EVENT_STATION_NAME`, `NRSC5_EVENT_STATION_SLOGAN`, `NRSC5_EVENT_STATION_MESSAGE`, `NRSC5_EVENT_STATION_LOCATION`, `NRSC5_EVENT_AUDIO_SERVICE_DESCRIPTOR`, `NRSC5_EVENT_DATA_SERVICE_DESCRIPTOR`, and `NRSC5_EVENT_EMERGENCY_ALERT` instead. + * - `NRSC5_EVENT_STREAM` : stream data available, see `stream` member + * - `NRSC5_EVENT_PACKET` : packet data available, see `packet` member + * - `NRSC5_EVENT_AUDIO_SERVICE` : audio service available, see `audio_service` member + * - `NRSC5_EVENT_STATION_ID` : station ID number, see `station_id` member + * - `NRSC5_EVENT_STATION_NAME` : station name, see `station_name` member + * - `NRSC5_EVENT_STATION_SLOGAN` : station slogan, see `station_slogan` member + * - `NRSC5_EVENT_STATION_MESSAGE` : station message, see `station_message` member + * - `NRSC5_EVENT_STATION_LOCATION` : station location, see `station_location` member + * - `NRSC5_EVENT_AUDIO_SERVICE_DESCRIPTOR` : SIS audio service descriptor, see `asd` member + * - `NRSC5_EVENT_DATA_SERVICE_DESCRIPTOR` : SIS data service descriptor, see `dsd` member + * - `NRSC5_EVENT_EMERGENCY_ALERT` : emergency alert, see `emergency_alert` member + * - `NRSC5_EVENT_HERE_IMAGE` : HERE Images traffic/weather map, see `here_image` member + * - `NRSC5_EVENT_AGC` : automatic gain control status, see `agc` member + * - `NRSC5_EVENT_EXCITER_INFO` : exciter data, see `exciter_info` member + * - `NRSC5_EVENT_IMPORTER_INFO` : importer data, see `importer_info` member + * - `NRSC5_EVENT_LEAP_SECOND_OFFSET` : leap second offset, see `leap_second_offset` member + * - `NRSC5_EVENT_LOCAL_TIME` : local time data, see `local_time` member + */ + unsigned int event; + union + { + struct { + const void *data; + size_t count; + } iq; + struct { + float freq_offset; /**< Frequency offset in Hz */ + int psmi; /**< Primary Service Mode Indicator (1, 2, 3, 5, 6, or 11 for FM; 1 or 2 for AM) */ + int pli; /**< Power Level Indicator (AM only; set to -1 for FM) */ + int hppi; /**< High-Power PIDS Indicator (AM only; set to -1 for FM) */ + int aabi; /**< Analog Audio Bandwidth Indicator (AM only; set to -1 for FM) */ + int rdbi; /**< Reduced Digital Bandwidth Indicator (AM only; set to -1 for FM) */ + } sync; + struct { + float cber; + } ber; + struct { + float lower; /**< Modulation error ratio of the lower sideband in dB. Note that the NRSC-5 standard defines FM signals to be spectrally inverted, so the lower sideband is the one that is higher in frequency. AM signals are not inverted. */ + float upper; /**< Modulation error ratio of the upper sideband in dB. Note that the NRSC-5 standard defines FM signals to be spectrally inverted, so the upper sideband is the one that is lower in frequency. AM signals are not inverted. */ + } mer; + struct { + unsigned int program; + const uint8_t *data; + size_t count; + unsigned int flags; /** The specific status of the hdc packet. Example `NRSC5_PKT_FLAGS_CRC_ERROR` **/ + } hdc; + struct { + unsigned int program; + const int16_t *data; + size_t count; + unsigned int flags; /** The specific status of the audio sample. Example `NRSC5_AUDIO_FLAGS_DECODING_ERROR` **/ + } audio; + struct { + unsigned int program; + const char *title; + const char *artist; + const char *album; + const char *genre; + struct { + const char *owner; + const char *id; + } ufid; + struct { + uint32_t mime; + int param; + int lot; + } xhdr; + nrsc5_id3_comment_t *comments; + struct + { + char *price; /** price of the product */ + char *contact_url; /** URL for contacting the seller */ + char *seller; /** Name of the seller */ + char *description; /** Short description of the product */ + uint8_t received_as; /** How audio is delivered when bought */ + struct tm *valid_until; /** Date when the price expires. Note that only the `tm_year`, `tm_mon`, and `tm_mday` fields are set. */ + } commercial; + } id3; + struct { + uint16_t port; /**< DEPRECATED: Use `component->data.port` instead */ + uint16_t seq; + unsigned int size; + uint32_t mime; /**< DEPRECATED: Use `component->data.mime` instead */ + const uint8_t *data; + nrsc5_sig_service_t *service; + nrsc5_sig_component_t *component; + } stream; + struct { + uint16_t port; /**< DEPRECATED: Use `component->data.port` instead */ + uint16_t seq; + unsigned int size; + uint32_t mime; /**< DEPRECATED: Use `component->data.mime` instead */ + const uint8_t *data; + nrsc5_sig_service_t *service; + nrsc5_sig_component_t *component; + } packet; + struct { + uint16_t port; /**< DEPRECATED: Use `component->data.port` instead */ + unsigned int lot; /**< LOT id of the file */ + unsigned int size; /**< number of bytes in the file */ + uint32_t mime; /**< MIME type of the file, e.g. NRSC5_MIME_PNG */ + const char *name; /**< filename */ + const uint8_t *data; /**< contents of the file (if the event type is NRSC5_EVENT_LOT), or NULL (if the event type is NRSC5_EVENT_LOT_HEADER) */ + struct tm *expiry_utc; /**< time after which the file should be deleted */ + nrsc5_sig_service_t *service; /**< pointer to the associated SIG service */ + nrsc5_sig_component_t *component; /**< pointer to the associated SIG component */ + } lot; + struct { + unsigned int lot; /**< LOT id of the file this fragment belongs to */ + unsigned int seq; /**< sequence number of this fragment within the LOT file */ + unsigned int repeat; /**< number of repetitions remaining */ + unsigned int size; /**< number of bytes in this fragment */ + unsigned int bytes_so_far; /**< total number of bytes received for this LOT file, across all received fragments */ + int is_duplicate; /**< 1 if this fragment was previously received, otherwise 0 */ + const uint8_t *data; /**< pointer to the data bytes of this fragment */ + nrsc5_sig_service_t *service; /**< pointer to the associated SIG service */ + nrsc5_sig_component_t *component; /**< pointer to the associated SIG component */ + } lot_fragment; + struct { + unsigned int program; /**< program number 0, 1, ..., 7 */ + unsigned int access; /**< NRSC5_ACCESS_PUBLIC or NRSC5_ACCESS_RESTRICTED */ + unsigned int type; /**< program type, e.g. NRSC5_PROGRAM_TYPE_JAZZ */ + unsigned int codec_mode; /**< audio codec mode. See SY_IDD_1017s Table 5-2. */ + unsigned int blend_control; /**< blend control (NRSC5_BLEND_DISABLE, NRSC5_BLEND_SELECT, or NRSC5_BLEND_ENABLE). See SY_IDD_1017s section 5.2.1.2. */ + int digital_audio_gain; /**< TX digital audio gain, in dB. See SY_IDD_1017s section 5.2.1.3. */ + unsigned int common_delay; /**< post-decoded common delay, in audio frame periods. See SY_IDD_1017s Table 5-1. */ + unsigned int latency; /**< audio codec latency, in audio frame periods. See SY_IDD_1017s Table 5-1. */ + } audio_service; + struct { + nrsc5_sig_service_t *services; + } sig; + struct { + const char *country_code; + int fcc_facility_id; + const char *name; + const char *slogan; + const char *message; + const char *alert; + float latitude; + float longitude; + int altitude; + nrsc5_sis_asd_t *audio_services; + nrsc5_sis_dsd_t *data_services; + const uint8_t *alert_cnt; + int alert_cnt_length; + int alert_category1; + int alert_category2; + int alert_location_format; + int alert_num_locations; + const int *alert_locations; + } sis; + struct { + const char *country_code; + int fcc_facility_id; + } station_id; + struct { + const char *name; + } station_name; + struct { + const char *slogan; + } station_slogan; + struct { + const char *message; + } station_message; + struct { + float latitude; + float longitude; + int altitude; + } station_location; + struct { + unsigned int program; /**< program number 0, 1, ..., 7 */ + unsigned int access; /**< NRSC5_ACCESS_PUBLIC or NRSC5_ACCESS_RESTRICTED */ + unsigned int type; /**< program type, e.g. NRSC5_PROGRAM_TYPE_JAZZ */ + unsigned int sound_exp; /**< 0 is none, 2 is Dolby Pro Logic II Surround */ + } asd; + struct { + unsigned int access; /**< NRSC5_ACCESS_PUBLIC or NRSC5_ACCESS_RESTRICTED */ + unsigned int type; /**< data service type, e.g. NRSC5_SERVICE_DATA_TYPE_TEXT */ + uint32_t mime_type; /**< MIME type, e.g. `NRSC5_MIME_TEXT` */ + } dsd; + struct { + const char *message; + const uint8_t *control_data; + int control_data_length; + int category1; + int category2; + int location_format; + int num_locations; + const int *locations; + } emergency_alert; + struct { + int image_type; /**< NRSC5_HERE_IMAGE_TRAFFIC or NRSC5_HERE_IMAGE_WEATHER */ + int seq; /**< sequence number (1-15); increments when traffic/weather image changes */ + int n1; /**< part number (1-9) for traffic, or incrementing sequence number for weather */ + int n2; /**< number of parts (9) for traffic, or incrementing sequence number for weather */ + struct tm *time_utc; /**< UTC time of traffic or weather image */ + float latitude1; /**< latitude of north map edge */ + float longitude1; /**< longitude of west map edge */ + float latitude2; /**< latitude of south map edge */ + float longitude2; /**< longitude of east map edge */ + const char *name; /**< filename, e.g. "trafficMap_1_2_rdhs.png" or "WeatherImage_0_0_rdhs.png" */ + unsigned int size; /**< size of image file, in bytes */ + const uint8_t *data; /**< contents of image file */ + } here_image; + struct { + float gain_db; /**< SDR gain value in dB */ + float peak_dbfs; /**< peak signal amplitude in dB, relative to full scale */ + int is_final; /**< 1 if this is the final (best) gain value, otherwise 0 */ + } agc; + struct { + const char *manufacturer_id; /**< Manufacturer ID, e.g. "GG" or "L7" */ + int core_version[NRSC5_DEVICE_VERSION_LENGTH]; /**< Core Version number. */ + int core_status; /**< Core Version status. 0 (Commercial Release), 1 (Engineering Release), 2 (Patch). */ + int manufacturer_version[NRSC5_DEVICE_VERSION_LENGTH]; /**< Manufacturer-assigned Version number. */ + int manufacturer_status; /**< Manufacturer Version status. 0 (Commercial Release), 1 (Engineering Release), 2 (Patch). */ + int importer_connected; /**< 1 if an importer is connected, otherwise 0. */ + } exciter_info; + struct { + const char *manufacturer_id; /**< Manufacturer ID, e.g. "GG" or "L7" */ + int core_version[NRSC5_DEVICE_VERSION_LENGTH]; /**< Core Version number. */ + int core_status; /**< Core Version status. 0 (Commercial Release), 1 (Engineering Release), 2 (Patch). */ + int manufacturer_version[NRSC5_DEVICE_VERSION_LENGTH]; /**< Manufacturer-assigned Version number. */ + int manufacturer_status; /**< Manufacturer Version status. 0 (Commercial Release), 1 (Engineering Release), 2 (Patch). */ + } importer_info; + struct { + int pending_offset; /**< Future GPS-UTC offset in seconds. Meant to be broadcasted months before the leap seconds and a few hours afterward. */ + int current_offset; /**< Current GPS-UTC offset in seconds. */ + unsigned int pending_alfn; /**< ALFN representing the GPS time of a pending leap second adjustment, or 0 if a leap second is not pending.*/ + } leap_second_offset; + struct { + int utc_offset; /**< Local Time Zone UTC Offset in minutes. */ + int dst_regional; /**< 1 if DST is currently in effect regionally, otherwise 0. */ + int dst_local; /**< 1 if DST is practiced locally, otherwise 0. */ + int dst_schedule; /**< DST Schedule. 0 means Daylight Saving Time is not practiced. 1 means U.S./Canada schedule. 2 means EU schedule. */ + } local_time; + }; +}; +/** + * Defines a typename for struct nrsc5_event_t + */ +typedef struct nrsc5_event_t nrsc5_event_t; + +/** + * Prototype for a user event handling callback function. + * The function will be invoked with a pointer to the nrsc5_event_t + * and a pointer to a second contextual argument, established by + * nrsc5_set_callback(). + */ +typedef void (*nrsc5_callback_t)(const nrsc5_event_t *evt, void *opaque); + +/** + * An opaque data type used by API functions to represent session information. + * Applications should acquire a pointer to one via the `open_` functions: + * nrsc5_open(), nrsc5_open_file(), nrsc5_open_pipe(), or nrsc5_open_rtltcp(). + */ +typedef struct nrsc5_t nrsc5_t; + + +/* ============================================================================ + * Public functions. All functions return void or an error code (0 == success). + */ + +/** + * Retrieves the version string of the library. + * @param[out] version character pointer that will reference the version string. + */ +NRSC5_API void nrsc5_get_version(const char **version); + +/** + * Retrieves a string corresponding to a service data type. + * @param[in] type a service data type integer. + * @param[out] name character pointer to a string naming the service type + * + * This name will be quite short, e.g. "News" or "Weather". If the type is + * not recognized, it will be the string "Unknown". + */ +NRSC5_API void nrsc5_service_data_type_name(unsigned int type, const char **name); + +/** + * Retrieves a string corresponding to a program type. + * @param[in] type a program data type integer. + * @param[out] name character pointer to a string naming the service type + * + * This name will be quite short, e.g. "News" or "Rock". If the type is + * not recognized, it will be the string "Unknown". + */ +NRSC5_API void nrsc5_program_type_name(unsigned int type, const char **name); + +/** + * Retrieves a string corresponding to an alert category. + * @param[in] type an alert category integer. + * @param[out] name character pointer to a string naming the alert category + * + * This name will be quite short, e.g. "Weather" or "Safety". If the type is + * not recognized, it will be the string "Unknown". + */ + NRSC5_API void nrsc5_alert_category_name(unsigned int category, const char **name); + + /** + * Initializes a session for a particular RTLSDR radio dongle. + * @param[out] st handle for an nrsc5_t + * @param[in] device_index the RTLSDR device index + * @return 0 on success, nonzero on error + * + * The `device_index` is commonly 0 on systems with a single radio dongle. + * This, or another `open_` command should be issued before using the other API + * functions that need an `nrsc5_t` session handle. + * This function will allocate and initialize an opaque `nsrc5_t` struct and set + * the pointer `result` to it on success. It uses librtlsdr to: + * + * - open the RTLSDR device indicated by `device_index` + * - set the sample rate to constant SAMPLE_RATE + * - set the tuner gain mode to 1 (auto) + * - set offset tuning to 1 + * + * Other session options are initialized to defaults, e.g. mode to FM. + * It creates (but does not start) a worker thread. + */ +NRSC5_API int nrsc5_open(nrsc5_t **st, int device_index); + +/** + * Initializes a session given an open `FILE` pointer. + * @param[out] st handle for an `nrsc5_t` + * @param[in] fp FILE pointer handle with nrsc5 data + * @return 0 on success, nonzero on error + * + */ +NRSC5_API int nrsc5_open_file(nrsc5_t **st, FILE *fp); + +/** + * Initializes a session for use with a pipe. + * @param[out] st handle for an `nrsc5_t` + * @return 0 on success, nonzero on error + * + */ +NRSC5_API int nrsc5_open_pipe(nrsc5_t **st); + +/** + * Initializes a session given a TCP socket file descriptor. + * @param[out] st handle for an `nrsc5_t` + * @param[in] socket the TCP socket with nrsc5 data + * @return 0 on success, nonzero on error + * + */ +NRSC5_API int nrsc5_open_rtltcp(nrsc5_t **st, int socket); + +/** + * Closes an nrsc5 session. + * @param[in] st pointer to an `nrsc5_t` + * + * Any worker thread is signalled to exit, files and sockets are closed, + * and I/O buffers are freed. + */ +NRSC5_API void nrsc5_close(nrsc5_t *st); + +/** + * Signals the worker to *start* demodulation. + * @param[in] st pointer to an `nrsc5_t` session object + * + */ +NRSC5_API void nrsc5_start(nrsc5_t *st); + +/** + * Signals the worker to *stop* demodulation. + * @param[in] st pointer to an `nrsc5_t` session object + * + * This function will block until the worker is stopped. + */ +NRSC5_API void nrsc5_stop(nrsc5_t *st); + +/** + * Set the session mode to AM or FM. + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] mode either `NRSC5_MODE_FM` or `NRSC5_MODE_AM` + * @return 0 on success or nonzero on error. + * + */ +NRSC5_API int nrsc5_set_mode(nrsc5_t *st, int mode); + +/** + * Enable or disable the bias-T for the radio. + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] on 1 indicates enabled, and 0 indicates disabled + * @return Return 0 on success or nonzero on error. + * + * This works with both a local SDR and over a TCP connection. + * A Bias-T is often used to power a low noise amplifier by injecting + * DC on the antenna cable. Do not enable this unless you know you + * have an LNA and compatible antenna. + */ +NRSC5_API int nrsc5_set_bias_tee(nrsc5_t *st, int on); + +/** + * Enable or disable direct sampling. + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] on 1 indicates enabled, and 0 indicates disabled + * @return 0 on success or nonzero on error. + * + * This works with both a local SDR and over a TCP connection. + */ +NRSC5_API int nrsc5_set_direct_sampling(nrsc5_t *st, int on); + +/** + * Adjust the radio frequency correction. + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] ppm_error correction offset in parts-per-million + * @return Return 0 on success or nonzero on error. + * + * This works with both a local SDR and over a TCP connection. + */ +NRSC5_API int nrsc5_set_freq_correction(nrsc5_t *st, int ppm_error); + +/** + * Retrieve the frequency to which the device is currently tuned + * if initialized to a local dongle, otherwise the cached frequency. + * + * @param[in] st pointer to an `nrsc5_t` session object + * @param[out] freq frequency in Hz + */ +NRSC5_API void nrsc5_get_frequency(nrsc5_t *st, float *freq); + +/** + * Sets the frequency to which the receiver is tuned. + * + * @param[in] st pointer to an `nrsc5_t` session object + * @param[out] freq frequency in Hz + * @return 0 on success or nonzero on error + * + * This function may only be called when the device is **stopped**. + * Input, output are reset. Gain is reset if auto-gain is enabled. + * Works with both a local SDR and over a TCP connection. + */ +NRSC5_API int nrsc5_set_frequency(nrsc5_t *st, float freq); + +/** + * Retrieve the actual receiver gain. + * + * @param[in] st pointer to an `nrsc5_t` session object + * @param[out] gain in dB + * + */ +NRSC5_API void nrsc5_get_gain(nrsc5_t *st, float *gain); + +/** + * Set the receiver gain. + * + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] gain in dB + * @return 0 on success or nonzero on error + * + */ +NRSC5_API int nrsc5_set_gain(nrsc5_t *st, float gain); + +/** + * Enable or disable receiver auto-gain control. + * + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] enabled set to 1 to enable auto gain, 0 to disable + * + */ +NRSC5_API void nrsc5_set_auto_gain(nrsc5_t *st, int enabled); + +/** + * Establish a callback function. + * + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] callback pointer to an event handling function of two arguments + * @param[in] opaque pointer to the function's intended 2nd argument + * + */ +NRSC5_API void nrsc5_set_callback(nrsc5_t *st, nrsc5_callback_t callback, void *opaque); + +/** + * Push an IQ array of 8-bit unsigned samples into the demodulator. + * + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] samples pointer to an array 8-bit unsigned samples + * @param[in] length the number of samples in the array + * @see NRSC5_SAMPLE_RATE_CU8 for required sample rate + * @return 0 on success, nonzero on error + * + */ +NRSC5_API int nrsc5_pipe_samples_cu8(nrsc5_t *st, const uint8_t *samples, unsigned int length); + +/** + * Push an IQ input array of 16-bit signed samples into the demodulator. + * + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] samples pointer to an array 16-bit signed samples + * @param[in] length the number of samples in the array + * @see NRSC5_SAMPLE_RATE_NATIVE_FM & NRSC5_SAMPLE_RATE_NATIVE_AM for required sample rate + * @return 0 on success, nonzero on error + * + */ +NRSC5_API int nrsc5_pipe_samples_cs16(nrsc5_t *st, const int16_t *samples, unsigned int length); + +/** + * Push an IQ input array of complex float samples into the demodulator. + * + * @param[in] st pointer to an `nrsc5_t` session object + * @param[in] samples pointer to an array of complex float samples + * @param[in] length the number of samples in the array. Must be a multiple of two. + * @see NRSC5_SAMPLE_RATE_NATIVE_FM & NRSC5_SAMPLE_RATE_NATIVE_AM for required sample rate + * @return 0 on success, nonzero on error + * + */ +NRSC5_API int nrsc5_pipe_samples_cf32(nrsc5_t *st, const float *samples, unsigned int length); + +/** + * Resets the current session. + * Gain is reset if auto-gain is enabled + * + * @param[in] st pointer to an `nrsc5_t` session object + * @return 0 on success, nonzero on error + * + */ +NRSC5_API int nrsc5_reset(nrsc5_t *st); + +#endif /* NRSC5_H_ */ diff --git a/third_party/nrsc5/upstream/src/CMakeLists.txt b/third_party/nrsc5/upstream/src/CMakeLists.txt new file mode 100644 index 000000000..c97736d82 --- /dev/null +++ b/third_party/nrsc5/upstream/src/CMakeLists.txt @@ -0,0 +1,123 @@ +add_compile_options (--std=gnu11 -O3 -Wall -Wextra) + +add_definitions (-D_GNU_SOURCE) +set (THREAD_LIBRARY pthread) +if (CMAKE_SYSTEM_NAME MATCHES Linux) + set (EXPORTS_LINKER_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libnrsc5.map") +elseif (CMAKE_SYSTEM_NAME MATCHES Darwin) + set (CMAKE_MACOSX_RPATH ON) + set (EXPORTS_LINKER_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/libnrsc5.sym") +elseif (CMAKE_SYSTEM_NAME MATCHES Windows) + set (EXPORTS_LINKER_FLAGS "-Wl,--exclude-all-symbols") + set (SOCKET_LIBRARY ws2_32) +endif() +if (USE_STATIC) + set (STATIC_LINKER_FLAGS -static) +endif() + +configure_file (config.h.in config.h) +include_directories ( + ${FAAD2_INCLUDE_DIRS} + ${AO_INCLUDE_DIRS} + ${RTL_SDR_INCLUDE_DIRS} + ${FFTW3F_INCLUDE_DIRS} + "${CMAKE_CURRENT_BINARY_DIR}" + "${CMAKE_SOURCE_DIR}/include" +) +link_directories ( + ${FAAD2_LIBRARY_DIRS} + ${AO_LIBRARY_DIRS} + ${RTL_SDR_LIBRARY_DIRS} + ${FFTW3F_LIBRARY_DIRS} + ${LIBUSB_LIBRARY_DIRS} +) + +set (LIBRARY_FILES + acquire.c + decode.c + frame.c + here_images.c + input.c + nrsc5.c + output.c + pids.c + rtltcp.c + sync.c + + firdecim_cf32.c + + conv_dec.c + + rs_init.c + rs_decode.c + + unicode.c + + strndup.c +) + +set ( + LibraryDependencies + ${FAAD2_LIBRARIES} + ${FFTW3F_LIBRARIES} + ${RTL_SDR_LIBRARIES} + ${THREAD_LIBRARY} + ${SOCKET_LIBRARY} + m +) + +add_library ( + nrsc5 SHARED + ${LIBRARY_FILES} +) +target_link_libraries ( + nrsc5 + ${LibraryDependencies} +) +if (BUILTIN_LIBRARIES) + add_dependencies(nrsc5 ${BUILTIN_LIBRARIES}) +endif () +set_target_properties(nrsc5 PROPERTIES PUBLIC_HEADER "../include/nrsc5.h") +set_target_properties(nrsc5 PROPERTIES LINK_FLAGS "${STATIC_LINKER_FLAGS} ${EXPORTS_LINKER_FLAGS}") +target_compile_definitions(nrsc5 PUBLIC "-DNRSC5_EXPORTS=1") + +add_library ( + nrsc5_static + ${LIBRARY_FILES} +) +target_link_libraries ( + nrsc5_static + ${LibraryDependencies} +) +if (BUILTIN_LIBRARIES) + add_dependencies(nrsc5_static ${BUILTIN_LIBRARIES}) +endif () + +if (BUILD_CLI) + add_executable ( + app + main.c + log.c + ) + set_property (TARGET app PROPERTY OUTPUT_NAME nrsc5) + set_target_properties(app PROPERTIES LINK_FLAGS "${STATIC_LINKER_FLAGS}") + target_link_libraries ( + app + nrsc5_static + ${AO_LIBRARIES} + ${THREAD_LIBRARY} + ${SOCKET_LIBRARY} + ) + install ( + TARGETS app + RUNTIME DESTINATION bin + ) +endif () + +install ( + TARGETS nrsc5 nrsc5_static + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + PUBLIC_HEADER DESTINATION include +) diff --git a/third_party/nrsc5/upstream/src/acquire.c b/third_party/nrsc5/upstream/src/acquire.c new file mode 100644 index 000000000..ce63c0bfb --- /dev/null +++ b/third_party/nrsc5/upstream/src/acquire.c @@ -0,0 +1,380 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "acquire.h" +#include "defines.h" +#include "input.h" +#include "private.h" + +#define FILTER_DELAY 15 +#define DECIMATION_FACTOR_FM 2 +#define DECIMATION_FACTOR_AM 32 + +static float filter_taps_fm[] = { + -0.000685643230099231, + 0.005636964458972216, + 0.009015781804919243, + -0.015486305579543114, + -0.035108357667922974, + 0.017446253448724747, + 0.08155813068151474, + 0.007995186373591423, + -0.13311293721199036, + -0.0727422907948494, + 0.15914097428321838, + 0.16498781740665436, + -0.1324498951435089, + -0.2484012246131897, + 0.051773931831121445, + 0.2821577787399292, + 0.051773931831121445, + -0.2484012246131897, + -0.1324498951435089, + 0.16498781740665436, + 0.15914097428321838, + -0.0727422907948494, + -0.13311293721199036, + 0.007995186373591423, + 0.08155813068151474, + 0.017446253448724747, + -0.035108357667922974, + -0.015486305579543114, + 0.009015781804919243, + 0.005636964458972216, + -0.000685643230099231, + 0 +}; + +static float filter_taps_am[] = { + -0.00038464731187559664, + -0.00021618751634377986, + 0.0026779419276863337, + -0.00029802651260979474, + -0.0012626448879018426, + -0.0013182522961869836, + -0.012252614833414555, + 0.015980124473571777, + 0.037112727761268616, + -0.05451361835002899, + -0.05804193392395973, + 0.11320608854293823, + 0.055298302322626114, + -0.16878043115139008, + -0.022917453199625015, + 0.19178225100040436, + -0.022917453199625015, + -0.16878043115139008, + 0.055298302322626114, + 0.11320608854293823, + -0.05804193392395973, + -0.05451361835002899, + 0.037112727761268616, + 0.015980124473571777, + -0.012252614833414555, + -0.0013182522961869836, + -0.0012626448879018426, + -0.00029802651260979474, + 0.0026779419276863337, + -0.00021618751634377986, + -0.00038464731187559664, + 0 +}; + +void acquire_process(acquire_t *st) +{ + float complex max_v = 0, phase_increment; + float angle, angle_diff, angle_factor, max_mag = -1.0f; + int samperr = 0; + int i, j, keep; + + if (st->idx != (unsigned int)st->fftcp * (ACQUIRE_SYMBOLS + 1)) + return; + + output_advance(st->input->output); + + if (st->input->sync_state == SYNC_STATE_FINE) + { + samperr = st->fftcp / 2 + st->input->sync.samperr; + st->input->sync.samperr = 0; + + angle_diff = -st->input->sync.angle; + st->input->sync.angle = 0; + angle = st->prev_angle + angle_diff; + st->prev_angle = angle; + } + else + { + float complex y; + for (i = 0; i < st->fftcp * (ACQUIRE_SYMBOLS + 1); i++) + { + fir_cf32_execute((st->mode == NRSC5_MODE_FM) ? st->filter_fm : st->filter_am, &st->in_buffer[i], &y); + st->buffer[i] = (st->mode == NRSC5_MODE_FM) ? conjf(y) : y; + } + + memset(st->sums, 0, sizeof(float complex) * st->fftcp); + for (i = 0; i < st->fftcp; ++i) + { + for (j = 0; j < ACQUIRE_SYMBOLS; ++j) + st->sums[i] += st->buffer[i + j * st->fftcp] * conjf(st->buffer[i + j * st->fftcp + st->fft]); + } + + for (i = 0; i < st->fftcp; ++i) + { + float mag; + float complex v = 0; + + for (j = 0; j < st->cp; ++j) + v += st->sums[(i + j) % st->fftcp] * st->shape[j] * st->shape[j + st->fft]; + + mag = normf(v); + if (mag > max_mag) + { + max_mag = mag; + max_v = v; + samperr = (i + st->fftcp - FILTER_DELAY) % st->fftcp; + } + } + + angle_diff = cargf(max_v * cexpf(I * -st->prev_angle)); + angle_factor = (st->prev_angle) ? 0.25 : 1.0; + angle = st->prev_angle + (angle_diff * angle_factor); + st->prev_angle = angle; + input_set_sync_state(st->input, SYNC_STATE_COARSE); + } + + for (i = 0; i < st->fftcp * (ACQUIRE_SYMBOLS + 1); i++) + st->buffer[i] = (st->mode == NRSC5_MODE_FM) ? conjf(st->in_buffer[i]) : st->in_buffer[i]; + + sync_adjust(&st->input->sync, st->fftcp / 2 - samperr); + angle -= 2 * M_PI * st->cfo; + + st->phase *= cexpf(-(st->fftcp / 2 - samperr) * angle / st->fft * I); + + phase_increment = cexpf(angle / st->fft * I); + + if (st->mode == NRSC5_MODE_AM) + { + float y, sum_y = 0, sum_xy = 0, sum_x2 = 0; + float complex last_carrier; + float complex temp_phase = st->phase; + float mag_sums[FFT_AM] = {0}; + + for (i = 0; i < ACQUIRE_SYMBOLS; ++i) + { + int offset = (st->mode == NRSC5_MODE_FM) ? 0 : (FFT_AM - CP_AM) / 2; + for (j = 0; j < st->fftcp; ++j) + { + float complex sample = temp_phase * st->buffer[i * st->fftcp + j + samperr]; + if (j < st->cp) + st->fftin[(j + offset) % st->fft] = st->shape[j] * sample; + else if (j < st->fft) + st->fftin[(j + offset) % st->fft] = sample; + else + st->fftin[(j + offset) % st->fft] += st->shape[j] * sample; + + temp_phase *= phase_increment; + } + temp_phase /= cabsf(temp_phase); + + fftwf_execute((st->mode == NRSC5_MODE_FM) ? st->fft_plan_fm : st->fft_plan_am); + fftshift(st->fftout, st->fft); + + float x = st->fftcp * (i - (float) (ACQUIRE_SYMBOLS - 1) / 2); + if (i == 0) + y = cargf(st->fftout[CENTER_AM]); + else + y += cargf(st->fftout[CENTER_AM] / last_carrier); + last_carrier = st->fftout[CENTER_AM]; + + sum_y += y; + sum_xy += x * y; + sum_x2 += x * x; + + if (st->input->sync_state != SYNC_STATE_FINE) + { + for (j = CENTER_AM - PIDS_OUTER_INDEX_AM; j <= CENTER_AM + PIDS_OUTER_INDEX_AM; j++) + { + mag_sums[j] += cabsf(st->fftout[j]); + } + } + } + + if (st->input->sync_state != SYNC_STATE_FINE) + { + float max_mag = -1.0f; + int max_index = -1; + for (j = CENTER_AM - PIDS_OUTER_INDEX_AM; j <= CENTER_AM + PIDS_OUTER_INDEX_AM; j++) + { + if (mag_sums[j] > max_mag) + { + max_mag = mag_sums[j]; + max_index = j; + } + } + acquire_cfo_adjust(st, max_index - CENTER_AM); + } + + phase_increment *= cexpf(-sum_xy / sum_x2 * I); + // TODO: Investigate why 0.06 is needed below + st->phase *= cexpf((-sum_y / ACQUIRE_SYMBOLS + (sum_xy / sum_x2)*(ACQUIRE_SYMBOLS)*st->fftcp/2 - 0.06) * I); + } + + for (i = 0; i < ACQUIRE_SYMBOLS; ++i) + { + int offset = (st->mode == NRSC5_MODE_FM) ? 0 : (FFT_AM - CP_AM) / 2; + for (j = 0; j < st->fftcp; ++j) + { + float complex sample = st->phase * st->buffer[i * st->fftcp + j + samperr]; + if (j < st->cp) + st->fftin[(j + offset) % st->fft] = st->shape[j] * sample; + else if (j < st->fft) + st->fftin[(j + offset) % st->fft] = sample; + else + st->fftin[(j + offset) % st->fft] += st->shape[j] * sample; + + st->phase *= phase_increment; + } + st->phase /= cabsf(st->phase); + + fftwf_execute((st->mode == NRSC5_MODE_FM) ? st->fft_plan_fm : st->fft_plan_am); + fftshift(st->fftout, st->fft); + sync_push(&st->input->sync, st->fftout); + } + + keep = st->fftcp + (st->fftcp / 2 - samperr) + st->keep_extra; + st->keep_extra = 0; + memmove(&st->in_buffer[0], &st->in_buffer[st->idx - keep], sizeof(float complex) * keep); + st->idx = keep; +} + +void acquire_keep_extra(acquire_t *st, int extra) +{ + st->keep_extra = extra; +} + +void acquire_cfo_adjust(acquire_t *st, int cfo) +{ + st->cfo += cfo; +} + +unsigned int acquire_push(acquire_t *st, const float complex *buf, const unsigned int length) +{ + const unsigned int size = st->fftcp * (ACQUIRE_SYMBOLS + 1); + const unsigned int needed = size - st->idx; + + unsigned int pushed = length; + if (pushed > needed) + pushed = needed; + + memcpy(&st->in_buffer[st->idx], buf, sizeof(float complex) * pushed); + st->idx += pushed; + + return pushed; +} + +void acquire_reset(acquire_t *st) +{ + firdecim_cf32_reset(st->filter_fm); + firdecim_cf32_reset(st->filter_am); + st->idx = 0; + st->prev_angle = 0; + st->phase = 1; + st->keep_extra = 0; + st->cfo = 0; +} + +void acquire_init(acquire_t *st, input_t *input) +{ + int i; + + st->mode = NRSC5_MODE_FM; + st->fft = FFT_FM; + st->fftcp = FFTCP_FM; + st->cp = CP_FM; + + st->input = input; + + st->filter_fm = firdecim_cf32_create(filter_taps_fm, sizeof(filter_taps_fm) / sizeof(filter_taps_fm[0])); + st->filter_am = firdecim_cf32_create(filter_taps_am, sizeof(filter_taps_am) / sizeof(filter_taps_am[0])); + + pthread_mutex_lock(&fftw_mutex); + st->fftin = fftwf_alloc_complex(FFT_FM); + st->fftout = fftwf_alloc_complex(FFT_FM); + st->fft_plan_fm = fftwf_plan_dft_1d(FFT_FM, st->fftin, st->fftout, FFTW_FORWARD, FFTW_ESTIMATE); + st->fft_plan_am = fftwf_plan_dft_1d(FFT_AM, st->fftin, st->fftout, FFTW_FORWARD, FFTW_ESTIMATE); + pthread_mutex_unlock(&fftw_mutex); + + for (i = 0; i < FFTCP_FM; ++i) + { + // Pulse shaping window function for FM + if (i < CP_FM) + st->shape_fm[i] = sinf(M_PI / 2 * i / CP_FM); + else if (i < FFT_FM) + st->shape_fm[i] = 1; + else + st->shape_fm[i] = cosf(M_PI / 2 * (i - FFT_FM) / CP_FM); + } + + for (i = 0; i < FFTCP_AM; ++i) + { + // Pulse shaping window function for AM + if (i < CP_AM) + st->shape_am[i] = sinf(M_PI / 2 * i / CP_AM); + else if (i < FFT_AM) + st->shape_am[i] = 1; + else + st->shape_am[i] = cosf(M_PI / 2 * (i - FFT_AM) / CP_AM); + } + + st->shape = st->shape_fm; + + acquire_reset(st); +} + +void acquire_set_mode(acquire_t *st, int mode) +{ + st->mode = mode; + + if (st->mode == NRSC5_MODE_FM) + { + st->fft = FFT_FM; + st->fftcp = FFTCP_FM; + st->cp = CP_FM; + st->shape = st->shape_fm; + } + else + { + st->fft = FFT_AM; + st->fftcp = FFTCP_AM; + st->cp = CP_AM; + st->shape = st->shape_am; + } +} + +void acquire_free(acquire_t *st) +{ + firdecim_cf32_free(st->filter_fm); + firdecim_cf32_free(st->filter_am); + + pthread_mutex_lock(&fftw_mutex); + fftwf_destroy_plan(st->fft_plan_fm); + fftwf_destroy_plan(st->fft_plan_am); + fftwf_free(st->fftin); + fftwf_free(st->fftout); + pthread_mutex_unlock(&fftw_mutex); +} diff --git a/third_party/nrsc5/upstream/src/acquire.h b/third_party/nrsc5/upstream/src/acquire.h new file mode 100644 index 000000000..52bb1b42e --- /dev/null +++ b/third_party/nrsc5/upstream/src/acquire.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include "firdecim_cf32.h" + +typedef struct +{ + struct input_t *input; + firdecim_cf32 filter_fm; + firdecim_cf32 filter_am; + float complex in_buffer[FFTCP_FM * (ACQUIRE_SYMBOLS + 1)]; + float complex buffer[FFTCP_FM * (ACQUIRE_SYMBOLS + 1)]; + float complex sums[FFTCP_FM]; + float complex *fftin; + float complex *fftout; + float *shape; + float shape_fm[FFTCP_FM]; + float shape_am[FFTCP_AM]; + fftwf_plan fft_plan_fm; + fftwf_plan fft_plan_am; + + unsigned int idx; + float prev_angle; + float complex phase; + int keep_extra; + int cfo; + + int mode; + int fft; + int fftcp; + int cp; +} acquire_t; + +void acquire_process(acquire_t *st); +void acquire_keep_extra(acquire_t *st, int extra); +void acquire_cfo_adjust(acquire_t *st, int cfo); +unsigned int acquire_push(acquire_t *st, const float complex *buf, unsigned int length); +void acquire_reset(acquire_t *st); +void acquire_init(acquire_t *st, struct input_t *input); +void acquire_set_mode(acquire_t *st, int mode); +void acquire_free(acquire_t *st); diff --git a/third_party/nrsc5/upstream/src/bitwriter.h b/third_party/nrsc5/upstream/src/bitwriter.h new file mode 100644 index 000000000..7ac5de49b --- /dev/null +++ b/third_party/nrsc5/upstream/src/bitwriter.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +typedef struct +{ + unsigned int byte; + unsigned int bits; + uint8_t *buf, *begin; +} bitwriter_t; + +static inline void bw_init(bitwriter_t *bw, uint8_t *buf) +{ + bw->byte = 0; + bw->bits = 0; + bw->buf = buf; + bw->begin = buf; +} + +static inline void bw_add1bit(bitwriter_t *bw, unsigned int bit) +{ + bw->byte = (bw->byte << 1) | (!!bit); + if (++bw->bits == 8) + { + *bw->buf++ = bw->byte; + bw->byte = 0; + bw->bits = 0; + } +} + +static inline void bw_addbits(bitwriter_t *bw, unsigned int value, unsigned int bits) +{ + unsigned int i; + for (i = 0; i < bits; ++i) + bw_add1bit(bw, value & (1 << (bits - i - 1))); +} + +static inline unsigned int bw_flush(bitwriter_t *bw) +{ + if (bw->bits) + bw_addbits(bw, 0, 8 - bw->bits); + return bw->buf - bw->begin; +} diff --git a/third_party/nrsc5/upstream/src/config.h.in b/third_party/nrsc5/upstream/src/config.h.in new file mode 100644 index 000000000..5226d7048 --- /dev/null +++ b/third_party/nrsc5/upstream/src/config.h.in @@ -0,0 +1,25 @@ +#pragma once + +#cmakedefine USE_FAAD2 + +#cmakedefine HAVE_STRNDUP +#cmakedefine HAVE_CMPLXF +#cmakedefine HAVE_IMAGINARY_I +#cmakedefine HAVE_COMPLEX_I + +#ifndef HAVE_CMPLXF +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) + #define CMPLXF(x,y) __builtin_complex((float)(x), (float)(y)) +#elif defined(HAVE_IMAGINARY_I) + #define CMPLXF(x,y) ((float complex)((float)(x) + _Imaginary_I * (float)(y))) +#elif defined(HAVE_COMPLEX_I) + #define CMPLXF(x,y) ((float complex)((float)(x) + _Complex_I * (float)(y))) +#endif +#endif /* HAVE_CMPLXF */ + +#cmakedefine LIBRARY_DEBUG_LEVEL @LIBRARY_DEBUG_LEVEL@ + +#ifndef HAVE_STRNDUP +#include +char *strndup(char const *s, size_t n); +#endif diff --git a/third_party/nrsc5/upstream/src/conv.h b/third_party/nrsc5/upstream/src/conv.h new file mode 100644 index 000000000..5a8b6dc84 --- /dev/null +++ b/third_party/nrsc5/upstream/src/conv.h @@ -0,0 +1,37 @@ +#ifndef _CONV_H_ +#define _CONV_H_ + +#include + +enum { + CONV_TERM_FLUSH, + CONV_TERM_TAIL_BITING, +}; + +/* + * Convolutional code descriptor + * + * n - Rate 2, 3, 4 (1/2, 1/3, 1/4) + * k - Constraint length (5 or 7) + * rgen - Recursive generator polynomial in octal + * gen - Generator polynomials in octal + * punc - Puncturing matrix (-1 terminated) + * term - Termination type (zero flush default) + */ +struct lte_conv_code { + int n; + int k; + int len; + unsigned rgen; + unsigned gen[4]; + int *punc; + int term; +}; + +int nrsc5_conv_decode_p1(const int8_t *in, uint8_t *out); +int nrsc5_conv_decode_pids(const int8_t *in, uint8_t *out); +int nrsc5_conv_decode_p3_p4(const int8_t *in, uint8_t *out, int len); +int nrsc5_conv_decode_e1(const int8_t *in, uint8_t *out, int len); +int nrsc5_conv_decode_e2_e3(const int8_t *in, uint8_t *out, int len); + +#endif /* _CONV_H_ */ diff --git a/third_party/nrsc5/upstream/src/conv_dec.c b/third_party/nrsc5/upstream/src/conv_dec.c new file mode 100644 index 000000000..429efbd8a --- /dev/null +++ b/third_party/nrsc5/upstream/src/conv_dec.c @@ -0,0 +1,478 @@ +/* + * Viterbi decoder for convolutional codes + * + * Copyright (C) 2015 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Author: Tom Tsou + */ + +#include "config.h" + +#include +#ifndef __APPLE__ +#include +#endif +#include +#include +#include + +#include "defines.h" +#include "conv.h" + +#include "conv_gen.h" +#if defined(HAVE_SSE3) +#include "conv_sse.h" +#elif defined(HAVE_NEON) +#include "conv_neon.h" +#endif + +#define PARITY(X) __builtin_parity(X) +#define TAIL_BITING_EXTRA 32 + +/* + * Trellis State + * + * state - Internal shift register value + * prev - Register values of previous 0 and 1 states + */ +struct vstate { + unsigned state; + unsigned prev[2]; +}; + +/* + * Trellis Object + * + * num_states - Number of states in the trellis + * sums - Accumulated path metrics + * outputs - Trellis ouput values + * vals - Input value that led to each state + */ +struct vtrellis { + int num_states; + int16_t *sums; + int16_t *outputs; + uint8_t *vals; +}; + +/* + * Viterbi Decoder + * + * n - Code order + * k - Constraint length + * len - Horizontal length of trellis + * recursive - Set to '1' if the code is recursive + * intrvl - Normalization interval + * trellis - Trellis object + * punc - Puncturing sequence + * paths - Trellis paths + */ +struct vdecoder { + int n; + int k; + int len; + int recursive; + int intrvl; + struct vtrellis *trellis; + int *punc; + int16_t **paths; + + void (*metric_func)(const int8_t *, const int16_t *, + int16_t *, int16_t *, int); +}; + +/* + * Aligned Memory Allocator + * + * SSE requires 16-byte memory alignment. We store relevant trellis values + * (accumulated sums, outputs, and path decisions) as 16 bit signed integers + * so the allocated memory is casted as such. + */ +#define SSE_ALIGN 16 + +static int16_t *vdec_malloc(size_t n) +{ +#if defined(HAVE_SSE3) && !defined(__APPLE__) + return (int16_t *) memalign(SSE_ALIGN, sizeof(int16_t) * n); +#else + return (int16_t *) malloc(sizeof(int16_t) * n); +#endif +} + +/* Left shift and mask for finding the previous state */ +static unsigned vstate_lshift(unsigned reg, int k, int val) +{ + unsigned mask; + + if (k == 5) + mask = 0x0e; + else if (k == 7) + mask = 0x3e; + else if (k == 9) + mask = 0xfe; + else + mask = 0; + + return ((reg << 1) & mask) | val; +} + +/* + * Populate non-recursive trellis state + * + * For a given state defined by the k-1 length shift register, find the + * value of the input bit that drove the trellis to that state. Then + * generate the N outputs of the generator polynomial at that state. + */ +static void gen_state_info(const struct lte_conv_code *code, + uint8_t *val, unsigned reg, int16_t *out) +{ + int i; + unsigned prev; + + /* Previous '0' state */ + prev = vstate_lshift(reg, code->k, 0); + + /* Compute output and unpack to NRZ */ + *val = (reg >> (code->k - 2)) & 0x01; + prev = prev | (unsigned) *val << (code->k - 1); + + for (i = 0; i < code->n; i++) + out[i] = PARITY(prev & code->gen[i]) * 2 - 1; +} + +/* + * Populate recursive trellis state + */ +static void gen_rec_state_info(const struct lte_conv_code *code, + uint8_t *val, unsigned reg, int16_t *out) +{ + int i; + unsigned prev, rec, mask; + + /* Previous '0' and '1' states */ + prev = vstate_lshift(reg, code->k, 0); + + /* Compute recursive input value (not the value shifted into register) */ + rec = (reg >> (code->k - 2)) & 0x01; + + if ((unsigned int) PARITY(prev & code->rgen) == rec) + *val = 0; + else + *val = 1; + + /* Compute outputs and unpack to NRZ */ + prev = prev | rec << (code->k - 1); + + if (code->k == 5) + mask = 0x0f; + else if (code->k == 7) + mask = 0x3f; + else + mask = 0xff; + + /* Check for recursive outputs */ + for (i = 0; i < code->n; i++) { + if (code->gen[i] & mask) + out[i] = PARITY(prev & code->gen[i]) * 2 - 1; + else + out[i] = *val * 2 - 1; + } +} + +/* Release the trellis */ +static void free_trellis(struct vtrellis *trellis) +{ + if (!trellis) + return; + + free(trellis->vals); + free(trellis->outputs); + free(trellis->sums); + free(trellis); +} + +#define NUM_STATES(K) (K == 9 ? 256 : (K == 7 ? 64 : 16)) + +/* + * Allocate and initialize the trellis object + * + * Initialization consists of generating the outputs and output value of a + * given state. Due to trellis symmetry, only one of the transition paths + * is used by the butterfly operation in the forward recursion, so only one + * set of N outputs is required per state variable. + */ +static struct vtrellis *generate_trellis(const struct lte_conv_code *code) +{ + int i; + struct vtrellis *trellis; + int16_t *out; + + int ns = NUM_STATES(code->k); + int olen = (code->n == 2) ? 2 : 4; + + trellis = (struct vtrellis *) calloc(1, sizeof(struct vtrellis)); + trellis->num_states = ns; + trellis->sums = vdec_malloc(ns); + trellis->outputs = vdec_malloc(ns * olen); + trellis->vals = (uint8_t *) malloc(ns * sizeof(uint8_t)); + + if (!trellis->sums || !trellis->outputs) + goto fail; + + /* Populate the trellis state objects */ + for (i = 0; i < ns; i++) { + out = &trellis->outputs[olen * i]; + + if (code->rgen) + gen_rec_state_info(code, &trellis->vals[i], i, out); + else + gen_state_info(code, &trellis->vals[i], i, out); + } + + return trellis; +fail: + free_trellis(trellis); + return NULL; +} + +/* + * Reset decoder + * + * Set accumulated path metrics to zero. For termination other than + * tail-biting, initialize the zero state as the encoder starting state. + * Intialize with the maximum accumulated sum at length equal to the + * constraint length. + */ +static void reset_decoder(struct vdecoder *dec, int term) +{ + int ns = dec->trellis->num_states; + + memset(dec->trellis->sums, 0, sizeof(int16_t) * ns); + + if (term != CONV_TERM_TAIL_BITING) + dec->trellis->sums[0] = INT8_MAX * dec->n * dec->k; +} + +static int _traceback(struct vdecoder *dec, + unsigned state, uint8_t *out, int len, int offset) +{ + int i; + unsigned path; + + for (i = len - 1; i >= 0; i--) { + path = dec->paths[i + offset][state] + 1; + out[i] = dec->trellis->vals[state]; + state = vstate_lshift(state, dec->k, path); + } + + return state; +} + +static void _traceback_rec(struct vdecoder *dec, + unsigned state, uint8_t *out, int len) +{ + int i; + unsigned path; + + for (i = len - 1; i >= 0; i--) { + path = dec->paths[i][state] + 1; + out[i] = path ^ dec->trellis->vals[state]; + state = vstate_lshift(state, dec->k, path); + } +} + +/* + * Traceback and generate decoded output + * + * For tail biting, find the largest accumulated path metric at the final state + * followed by two trace back passes. For zero flushing the final state is + * always zero with a single traceback path. + */ +static int traceback(struct vdecoder *dec, uint8_t *out, int term, int len) +{ + int i, sum, max_p = -1, max = -1; + unsigned path, state = 0; + + if (term == CONV_TERM_TAIL_BITING) { + for (i = 0; i < dec->trellis->num_states; i++) { + sum = dec->trellis->sums[i]; + if (sum > max) { + max_p = max; + max = sum; + state = i; + } + } + if (max < 0) + return -EPROTO; + for (i = dec->len - 1; i >= len + TAIL_BITING_EXTRA; i--) { + path = dec->paths[i][state] + 1; + state = vstate_lshift(state, dec->k, path); + } + } else { + for (i = dec->len - 1; i >= len; i--) { + path = dec->paths[i][state] + 1; + state = vstate_lshift(state, dec->k, path); + } + } + + if (dec->recursive) + _traceback_rec(dec, state, out, len); + else + state =_traceback(dec, state, out, len, term == CONV_TERM_TAIL_BITING ? TAIL_BITING_EXTRA : 0); + + /* Don't handle the odd case of recursize tail-biting codes */ + + return max - max_p; +} + +/* Release decoder object */ +static void free_vdec(struct vdecoder *dec) +{ + if (!dec) + return; + + free(dec->paths[0]); + free(dec->paths); + free_trellis(dec->trellis); + free(dec); +} + +/* + * Allocate decoder object + * + * Subtract the constraint length K on the normalization interval to + * accommodate the initialization path metric at state zero. + */ +static struct vdecoder *alloc_vdec(const struct lte_conv_code *code) +{ + int i, ns; + struct vdecoder *dec; + + ns = NUM_STATES(code->k); + + dec = (struct vdecoder *) calloc(1, sizeof(struct vdecoder)); + dec->n = code->n; + dec->k = code->k; + dec->recursive = code->rgen ? 1 : 0; + dec->intrvl = INT16_MAX / (dec->n * INT8_MAX) - dec->k; + + assert(dec->n == 3); + assert(dec->k == 7 || dec->k == 9); + + if (code->term == CONV_TERM_FLUSH) + dec->len = code->len + code->k - 1; + else + dec->len = code->len + TAIL_BITING_EXTRA * 2; + + dec->trellis = generate_trellis(code); + if (!dec->trellis) + goto fail; + + dec->paths = (int16_t **) malloc(sizeof(int16_t *) * dec->len); + dec->paths[0] = vdec_malloc(ns * dec->len); + for (i = 1; i < dec->len; i++) + dec->paths[i] = &dec->paths[0][i * ns]; + + return dec; +fail: + free_vdec(dec); + return NULL; +} + +/* + * Forward trellis recursion + * + * Generate branch metrics and path metrics with a combined function. Only + * accumulated path metric sums and path selections are stored. Normalize on + * the interval specified by the decoder. + */ +static void _conv_decode(struct vdecoder *dec, const int8_t *seq, int term, int len) +{ + int i, j = 0; + struct vtrellis *trellis = dec->trellis; + + if (term == CONV_TERM_TAIL_BITING) + j = len - TAIL_BITING_EXTRA; + + for (i = 0; i < dec->len; i++, j++) { + if (term == CONV_TERM_TAIL_BITING && j == len) + j = 0; + + if (dec->k == 7) + gen_metrics_k7_n3(&seq[dec->n * j], + trellis->outputs, + trellis->sums, + dec->paths[i], + !(i % dec->intrvl)); + else if (dec->k == 9) + gen_metrics_k9_n3(&seq[dec->n * j], + trellis->outputs, + trellis->sums, + dec->paths[i], + !(i % dec->intrvl)); + } +} + +static int nrsc5_conv_decode(const int8_t *in, uint8_t *out, int k, int len, unsigned int g1, unsigned int g2, unsigned int g3) +{ + const struct lte_conv_code code = { + .n = 3, + .k = k, + .len = len, + .gen = { g1, g2, g3 }, + .term = CONV_TERM_TAIL_BITING, + }; + int rc; + + struct vdecoder *vdec = alloc_vdec(&code); + if (!vdec) + return -EFAULT; + + reset_decoder(vdec, code.term); + + /* Propagate through the trellis with interval normalization */ + _conv_decode(vdec, in, code.term, code.len); + + rc = traceback(vdec, out, code.term, code.len); + + free_vdec(vdec); + return rc; +} + +int nrsc5_conv_decode_p1(const int8_t *in, uint8_t *out) +{ + return nrsc5_conv_decode(in, out, 7, P1_FRAME_LEN_FM, 0133, 0171, 0165); +} + +int nrsc5_conv_decode_pids(const int8_t *in, uint8_t *out) +{ + return nrsc5_conv_decode(in, out, 7, PIDS_FRAME_LEN, 0133, 0171, 0165); +} + +int nrsc5_conv_decode_p3_p4(const int8_t *in, uint8_t *out, int len) +{ + return nrsc5_conv_decode(in, out, 7, len, 0133, 0171, 0165); +} + +int nrsc5_conv_decode_e1(const int8_t *in, uint8_t *out, int len) +{ + return nrsc5_conv_decode(in, out, 9, len, 0561, 0657, 0711); +} + +int nrsc5_conv_decode_e2_e3(const int8_t *in, uint8_t *out, int len) +{ + return nrsc5_conv_decode(in, out, 9, len, 0561, 0753, 0711); +} diff --git a/third_party/nrsc5/upstream/src/conv_gen.h b/third_party/nrsc5/upstream/src/conv_gen.h new file mode 100644 index 000000000..8026fd778 --- /dev/null +++ b/third_party/nrsc5/upstream/src/conv_gen.h @@ -0,0 +1,123 @@ +/* + * Viterbi decoder for convolutional codes + * + * Copyright (C) 2015 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Author: Tom Tsou + */ + +#include +#include + +/* + * Add-Compare-Select (ACS-Butterfly) + * + * Compute 4 accumulated path metrics and 4 path selections. Note that path + * selections are store as -1 and 0 rather than 0 and 1. This is to match + * the output format of the sse packed compare instruction 'pmaxuw'. + */ +static void acs_butterfly(int state, int num_states, + int16_t metric, int16_t *sum, + int16_t *new_sum, int16_t *path) +{ + int state0, state1; + int sum0, sum1, sum2, sum3; + + state0 = *(sum + (2 * state + 0)); + state1 = *(sum + (2 * state + 1)); + + sum0 = state0 + metric; + sum1 = state1 - metric; + sum2 = state0 - metric; + sum3 = state1 + metric; + + if (sum0 > sum1) { + *new_sum = sum0; + *path = -1; + } else { + *new_sum = sum1; + *path = 0; + } + + if (sum2 > sum3) { + *(new_sum + num_states / 2) = sum2; + *(path + num_states / 2) = -1; + } else { + *(new_sum + num_states / 2) = sum3; + *(path + num_states / 2) = 0; + } +} + +/* Branch metrics unit N=3 */ +static void _gen_branch_metrics_n3(int num_states, const int8_t *seq, + const int16_t *out, int16_t *metrics) +{ + int i; + + for (i = 0; i < num_states / 2; i++) + metrics[i] = seq[0] * out[4 * i + 0] + + seq[1] * out[4 * i + 1] + + seq[2] * out[4 * i + 2]; +} + +/* Path metric unit */ +static void _gen_path_metrics(int num_states, int16_t *sums, + int16_t *metrics, int16_t *paths, int norm) +{ + int i; + int16_t min; + int16_t new_sums[num_states]; + + for (i = 0; i < num_states / 2; i++) { + acs_butterfly(i, num_states, metrics[i], + sums, &new_sums[i], &paths[i]); + } + + if (norm) { + min = new_sums[0]; + for (i = 1; i < num_states; i++) { + if (new_sums[i] < min) + min = new_sums[i]; + } + + for (i = 0; i < num_states; i++) + new_sums[i] -= min; + } + + memcpy(sums, new_sums, num_states * sizeof(int16_t)); +} + +#if !defined(HAVE_SSE3) && !defined(HAVE_NEON) +static void gen_metrics_k7_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + int16_t metrics[32]; + + _gen_branch_metrics_n3(64, seq, out, metrics); + _gen_path_metrics(64, sums, metrics, paths, norm); + +} +#endif + +static void gen_metrics_k9_n3(const int8_t *seq, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + int16_t metrics[128]; + + _gen_branch_metrics_n3(256, seq, out, metrics); + _gen_path_metrics(256, sums, metrics, paths, norm); + +} diff --git a/third_party/nrsc5/upstream/src/conv_neon.h b/third_party/nrsc5/upstream/src/conv_neon.h new file mode 100644 index 000000000..c7f1914f4 --- /dev/null +++ b/third_party/nrsc5/upstream/src/conv_neon.h @@ -0,0 +1,173 @@ +/* + * Viterbi decoder for convolutional codes - ARM NEON + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Author: Andrew Wesie + * + * Based on conv_sse.h written by Tom Tsou . + */ + +#include +#include + +#define NEON_DEINTERLEAVE_K7(M0,M1,M2,M3,M4,M5,M6,M7, \ + M8,M9,M10,M11,M12,M13,M14,M15) \ +{ \ + int16x8x2_t tmp; \ + tmp = vuzpq_s16(M0, M1); \ + M8 = tmp.val[0]; M9 = tmp.val[1]; \ + tmp = vuzpq_s16(M2, M3); \ + M10 = tmp.val[0]; M11 = tmp.val[1]; \ + tmp = vuzpq_s16(M4, M5); \ + M12 = tmp.val[0]; M13 = tmp.val[1]; \ + tmp = vuzpq_s16(M6, M7); \ + M14 = tmp.val[0]; M15 = tmp.val[1]; \ +} +#define NEON_BRANCH_METRIC_N4(M0,M1,M2,M3,M4,M5) \ +{ \ + M0 = vmulq_s16(M4, M0); \ + M1 = vmulq_s16(M4, M1); \ + M2 = vmulq_s16(M4, M2); \ + M3 = vmulq_s16(M4, M3); \ + int16x4_t t1 = vpadd_s16(vpadd_s16(vget_low_s16(M0), vget_high_s16(M0)), vpadd_s16(vget_low_s16(M1), vget_high_s16(M1))); \ + int16x4_t t2 = vpadd_s16(vpadd_s16(vget_low_s16(M2), vget_high_s16(M2)), vpadd_s16(vget_low_s16(M3), vget_high_s16(M3))); \ + M5 = vcombine_s16(t1, t2); \ +} +#define NEON_BUTTERFLY(M0,M1,M2,M3,M4) \ +{ \ + M3 = vqaddq_s16(M0, M2); \ + M4 = vqsubq_s16(M1, M2); \ + M0 = vqsubq_s16(M0, M2); \ + M1 = vqaddq_s16(M1, M2); \ + M2 = vmaxq_s16(M3, M4); \ + M3 = vreinterpretq_s16_u16(vcgtq_s16(M3, M4)); \ + M4 = vmaxq_s16(M0, M1); \ + M1 = vreinterpretq_s16_u16(vcgtq_s16(M0, M1)); \ +} +#define NEON_NORMALIZE_K7(M0,M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11) \ +{ \ + M8 = vminq_s16(M0, M1); \ + M9 = vminq_s16(M2, M3); \ + M10 = vminq_s16(M4, M5); \ + M11 = vminq_s16(M6, M7); \ + M8 = vminq_s16(M8, M9); \ + M10 = vminq_s16(M10, M11); \ + M8 = vminq_s16(M8, M10); \ + int16x4_t t = vpmin_s16(vget_low_s16(M8), vget_high_s16(M8)); \ + t = vpmin_s16(t, t); \ + t = vpmin_s16(t, t); \ + M8 = vdupq_lane_s16(t, 0); \ + M0 = vqsubq_s16(M0, M8); \ + M1 = vqsubq_s16(M1, M8); \ + M2 = vqsubq_s16(M2, M8); \ + M3 = vqsubq_s16(M3, M8); \ + M4 = vqsubq_s16(M4, M8); \ + M5 = vqsubq_s16(M5, M8); \ + M6 = vqsubq_s16(M6, M8); \ + M7 = vqsubq_s16(M7, M8); \ +} +static inline void _neon_metrics_k7_n4(const int16_t *val, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + int16x8_t m0, m1, m2, m3, m4, m5, m6, m7; + int16x8_t m8, m9, m10, m11, m12, m13, m14, m15; + int16x4_t input; + + /* (PMU) Load accumulated path matrics */ + m0 = vld1q_s16(&sums[0]); + m1 = vld1q_s16(&sums[8]); + m2 = vld1q_s16(&sums[16]); + m3 = vld1q_s16(&sums[24]); + m4 = vld1q_s16(&sums[32]); + m5 = vld1q_s16(&sums[40]); + m6 = vld1q_s16(&sums[48]); + m7 = vld1q_s16(&sums[56]); + + /* (PMU) Deinterleave into even and odd packed registers */ + NEON_DEINTERLEAVE_K7(m0, m1, m2, m3 ,m4 ,m5, m6, m7, + m8, m9, m10, m11, m12, m13, m14, m15) + + /* (BMU) Load and expand 8-bit input out to 16-bits */ + input = vld1_s16(val); + m7 = vcombine_s16(input, input); + + /* (BMU) Load and compute branch metrics */ + m0 = vld1q_s16(&out[0]); + m1 = vld1q_s16(&out[8]); + m2 = vld1q_s16(&out[16]); + m3 = vld1q_s16(&out[24]); + + NEON_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m4) + + m0 = vld1q_s16(&out[32]); + m1 = vld1q_s16(&out[40]); + m2 = vld1q_s16(&out[48]); + m3 = vld1q_s16(&out[56]); + + NEON_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m5) + + m0 = vld1q_s16(&out[64]); + m1 = vld1q_s16(&out[72]); + m2 = vld1q_s16(&out[80]); + m3 = vld1q_s16(&out[88]); + + NEON_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m6) + + m0 = vld1q_s16(&out[96]); + m1 = vld1q_s16(&out[104]); + m2 = vld1q_s16(&out[112]); + m3 = vld1q_s16(&out[120]); + + NEON_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m7) + + /* (PMU) Butterflies: 0-15 */ + NEON_BUTTERFLY(m8, m9, m4, m0, m1) + NEON_BUTTERFLY(m10, m11, m5, m2, m3) + + vst1q_s16(&paths[0], m0); + vst1q_s16(&paths[8], m2); + vst1q_s16(&paths[32], m9); + vst1q_s16(&paths[40], m11); + + /* (PMU) Butterflies: 17-31 */ + NEON_BUTTERFLY(m12, m13, m6, m0, m2) + NEON_BUTTERFLY(m14, m15, m7, m9, m11) + + vst1q_s16(&paths[16], m0); + vst1q_s16(&paths[24], m9); + vst1q_s16(&paths[48], m13); + vst1q_s16(&paths[56], m15); + + if (norm) + NEON_NORMALIZE_K7(m4, m1, m5, m3, m6, m2, + m7, m11, m0, m8, m9, m10) + + vst1q_s16(&sums[0], m4); + vst1q_s16(&sums[8], m5); + vst1q_s16(&sums[16], m6); + vst1q_s16(&sums[24], m7); + vst1q_s16(&sums[32], m1); + vst1q_s16(&sums[40], m3); + vst1q_s16(&sums[48], m2); + vst1q_s16(&sums[56], m11); +} + +static inline void gen_metrics_k7_n3(const int8_t *val, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + const int16_t _val[4] = { val[0], val[1], val[2], 0 }; + + _neon_metrics_k7_n4(_val, out, sums, paths, norm); +} diff --git a/third_party/nrsc5/upstream/src/conv_sse.h b/third_party/nrsc5/upstream/src/conv_sse.h new file mode 100644 index 000000000..98f961998 --- /dev/null +++ b/third_party/nrsc5/upstream/src/conv_sse.h @@ -0,0 +1,323 @@ +/* + * Viterbi decoder for convolutional codes - Intel SSE/AVX + * + * Copyright (C) 2015 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Author: Tom Tsou + */ + +#include "config.h" + +#include +#include +#include + +#if defined(HAVE_SSE4_1) || defined(HAVE_SSE41) +#include +#endif + +#ifdef HAVE_AVX2 +#include +#endif + +/* + * Octo-Viterbi butterfly + * + * Compute 8-wide butterfly generating 16 path decisions and 16 accumulated + * sums. Inputs all packed 16-bit integers in three 128-bit XMM registers. + * Two intermediate registers are used and results are set in the upper 4 + * registers. + * + * Input: + * M0 - Path metrics 0 (packed 16-bit integers) + * M1 - Path metrics 1 (packed 16-bit integers) + * M2 - Branch metrics (packed 16-bit integers) + * + * Output: + * M2 - Selected and accumulated path metrics 0 + * M4 - Selected and accumulated path metrics 1 + * M3 - Path selections 0 + * M1 - Path selections 1 + */ +#define SSE_BUTTERFLY(M0,M1,M2,M3,M4) \ +{ \ + M3 = _mm_adds_epi16(M0, M2); \ + M4 = _mm_subs_epi16(M1, M2); \ + M0 = _mm_subs_epi16(M0, M2); \ + M1 = _mm_adds_epi16(M1, M2); \ + M2 = _mm_max_epi16(M3, M4); \ + M3 = _mm_cmpgt_epi16(M3, M4); \ + M4 = _mm_max_epi16(M0, M1); \ + M1 = _mm_cmpgt_epi16(M0, M1); \ +} + +#define _I8_SHUFFLE_MASK 15, 14, 11, 10, 7, 6, 3, 2, 13, 12, 9, 8, 5, 4, 1, 0 + +/* + * Two lane deinterleaving K = 7 + * + * Take 64 interleaved 16-bit integers and deinterleave to 8 packed 128-bit + * registers. The operation summarized below. 16 registers are used with the + * lower 8 as input and upper 8 as output. + * + * In - 10101010 10101010 10101010 10101010 ... + * Out - 00000000 11111111 00000000 11111111 ... + * + * Input: + * M0:7 - Packed 16-bit integers + * + * Output: + * M8:15 - Deinterleaved packed 16-bit integers + */ +#define SSE_DEINTERLEAVE_K7(M0,M1,M2,M3,M4,M5,M6,M7, \ + M8,M9,M10,M11,M12,M13,M14,M15) \ +{ \ + M8 = _mm_set_epi8(_I8_SHUFFLE_MASK); \ + M0 = _mm_shuffle_epi8(M0, M8); \ + M1 = _mm_shuffle_epi8(M1, M8); \ + M2 = _mm_shuffle_epi8(M2, M8); \ + M3 = _mm_shuffle_epi8(M3, M8); \ + M4 = _mm_shuffle_epi8(M4, M8); \ + M5 = _mm_shuffle_epi8(M5, M8); \ + M6 = _mm_shuffle_epi8(M6, M8); \ + M7 = _mm_shuffle_epi8(M7, M8); \ + M8 = _mm_unpacklo_epi64(M0, M1); \ + M9 = _mm_unpackhi_epi64(M0, M1); \ + M10 = _mm_unpacklo_epi64(M2, M3); \ + M11 = _mm_unpackhi_epi64(M2, M3); \ + M12 = _mm_unpacklo_epi64(M4, M5); \ + M13 = _mm_unpackhi_epi64(M4, M5); \ + M14 = _mm_unpacklo_epi64(M6, M7); \ + M15 = _mm_unpackhi_epi64(M6, M7); \ +} + +/* + * Generate branch metrics N = 4: + * + * Compute 8 branch metrics from trellis outputs and input values. This + * macro is reused for N less than 4 where the extra soft input bits are + * padded. + * + * Input: + * M0:3 - 8 x 4 packed 16-bit trellis outputs + * M4 - Expanded and packed 16-bit input value + * + * Output: + * M5 - 8 computed 16-bit branch metrics + */ +#define SSE_BRANCH_METRIC_N4(M0,M1,M2,M3,M4,M5) \ +{ \ + M0 = _mm_sign_epi16(M4, M0); \ + M1 = _mm_sign_epi16(M4, M1); \ + M2 = _mm_sign_epi16(M4, M2); \ + M3 = _mm_sign_epi16(M4, M3); \ + M0 = _mm_hadds_epi16(M0, M1); \ + M1 = _mm_hadds_epi16(M2, M3); \ + M5 = _mm_hadds_epi16(M0, M1); \ +} + +/* + * Broadcast 16-bit integer + * + * Repeat the low 16-bit integer to all elements of the 128-bit SSE + * register. Only AVX2 has a dedicated broadcast instruction; use repeat + * unpacks for SSE only architectures. This is a destructive operation and + * the source register is overwritten. + * + * Input: + * M0 - Low 16-bit element is read + * + * Output: + * M0 - Contains broadcasted values + */ +#ifdef HAVE_AVX2 +#define SSE_BROADCAST(M0) \ +{ \ + M0 = _mm_broadcastw_epi16(M0); \ +} +#else +#define SSE_BROADCAST(M0) \ +{ \ + M0 = _mm_unpacklo_epi16(M0, M0); \ + M0 = _mm_unpacklo_epi32(M0, M0); \ + M0 = _mm_unpacklo_epi64(M0, M0); \ +} +#endif + +/* + * Horizontal minimum + * + * Compute horizontal minimum of packed unsigned 16-bit integers and place + * result in the low 16-bit element of the source register. Only SSE 4.1 + * has a dedicated minpos instruction. One internmediate register is used + * if SSE 4.1 is not available. This is a destructive operation and the + * source register is overwritten. + * + * Input: + * M0 - Packed unsigned 16-bit integers + * + * Output: + * M0 - Minimum value placed in low 16-bit element + */ +#if defined(HAVE_SSE4_1) || defined(HAVE_SSE41) +#define SSE_MINPOS(M0,M1) \ +{ \ + M0 = _mm_minpos_epu16(M0); \ +} +#else +#define SSE_MINPOS(M0,M1) \ +{ \ + M1 = _mm_shuffle_epi32(M0, _MM_SHUFFLE(0, 0, 3, 2)); \ + M0 = _mm_min_epi16(M0, M1); \ + M1 = _mm_shufflelo_epi16(M0, _MM_SHUFFLE(0, 0, 3, 2)); \ + M0 = _mm_min_epi16(M0, M1); \ + M1 = _mm_shufflelo_epi16(M0, _MM_SHUFFLE(0, 0, 0, 1)); \ + M0 = _mm_min_epi16(M0, M1); \ +} +#endif + +/* + * Normalize state metrics K = 7: + * + * Compute 64-wide normalization by subtracting the smallest value from + * all values. Inputs are 8 registers of accumulated sums and 4 temporary + * registers. Normalized results are returned in the originating locations. + * + * Input: + * M0:7 - Path metricss 0:7 (packed 16-bit integers) + * + * Output: + * M0:7 - Normalized path metrics 0:7 + */ +#define SSE_NORMALIZE_K7(M0,M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11) \ +{ \ + M8 = _mm_min_epi16(M0, M1); \ + M9 = _mm_min_epi16(M2, M3); \ + M10 = _mm_min_epi16(M4, M5); \ + M11 = _mm_min_epi16(M6, M7); \ + M8 = _mm_min_epi16(M8, M9); \ + M10 = _mm_min_epi16(M10, M11); \ + M8 = _mm_min_epi16(M8, M10); \ + SSE_MINPOS(M8, M9) \ + SSE_BROADCAST(M8) \ + M0 = _mm_subs_epi16(M0, M8); \ + M1 = _mm_subs_epi16(M1, M8); \ + M2 = _mm_subs_epi16(M2, M8); \ + M3 = _mm_subs_epi16(M3, M8); \ + M4 = _mm_subs_epi16(M4, M8); \ + M5 = _mm_subs_epi16(M5, M8); \ + M6 = _mm_subs_epi16(M6, M8); \ + M7 = _mm_subs_epi16(M7, M8); \ +} + +/* + * Combined BMU/PMU (K=7, N=3 and N=4) + * + * Compute branch metrics followed by path metrics for half rate 64-state + * trellis. 32 butterfly operations are computed. Deinterleave path + * metrics before computing branch metrics as in the half rate case. + */ +static inline void _sse_metrics_k7_n4(const int16_t *val, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + __m128i m0, m1, m2, m3, m4, m5, m6, m7; + __m128i m8, m9, m10, m11, m12, m13, m14, m15; + + /* (PMU) Load accumulated path matrics */ + m0 = _mm_load_si128((__m128i *) &sums[0]); + m1 = _mm_load_si128((__m128i *) &sums[8]); + m2 = _mm_load_si128((__m128i *) &sums[16]); + m3 = _mm_load_si128((__m128i *) &sums[24]); + m4 = _mm_load_si128((__m128i *) &sums[32]); + m5 = _mm_load_si128((__m128i *) &sums[40]); + m6 = _mm_load_si128((__m128i *) &sums[48]); + m7 = _mm_load_si128((__m128i *) &sums[56]); + + /* (PMU) Deinterleave into even and odd packed registers */ + SSE_DEINTERLEAVE_K7(m0, m1, m2, m3 ,m4 ,m5, m6, m7, + m8, m9, m10, m11, m12, m13, m14, m15) + + /* (BMU) Load and expand 8-bit input out to 16-bits */ + m7 = _mm_loadu_si128((__m128i *) val); + + /* (BMU) Load and compute branch metrics */ + m0 = _mm_load_si128((__m128i *) &out[0]); + m1 = _mm_load_si128((__m128i *) &out[8]); + m2 = _mm_load_si128((__m128i *) &out[16]); + m3 = _mm_load_si128((__m128i *) &out[24]); + + SSE_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m4) + + m0 = _mm_load_si128((__m128i *) &out[32]); + m1 = _mm_load_si128((__m128i *) &out[40]); + m2 = _mm_load_si128((__m128i *) &out[48]); + m3 = _mm_load_si128((__m128i *) &out[56]); + + SSE_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m5) + + m0 = _mm_load_si128((__m128i *) &out[64]); + m1 = _mm_load_si128((__m128i *) &out[72]); + m2 = _mm_load_si128((__m128i *) &out[80]); + m3 = _mm_load_si128((__m128i *) &out[88]); + + SSE_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m6) + + m0 = _mm_load_si128((__m128i *) &out[96]); + m1 = _mm_load_si128((__m128i *) &out[104]); + m2 = _mm_load_si128((__m128i *) &out[112]); + m3 = _mm_load_si128((__m128i *) &out[120]); + + SSE_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m7) + + /* (PMU) Butterflies: 0-15 */ + SSE_BUTTERFLY(m8, m9, m4, m0, m1) + SSE_BUTTERFLY(m10, m11, m5, m2, m3) + + _mm_store_si128((__m128i *) &paths[0], m0); + _mm_store_si128((__m128i *) &paths[8], m2); + _mm_store_si128((__m128i *) &paths[32], m9); + _mm_store_si128((__m128i *) &paths[40], m11); + + /* (PMU) Butterflies: 17-31 */ + SSE_BUTTERFLY(m12, m13, m6, m0, m2) + SSE_BUTTERFLY(m14, m15, m7, m9, m11) + + _mm_store_si128((__m128i *) &paths[16], m0); + _mm_store_si128((__m128i *) &paths[24], m9); + _mm_store_si128((__m128i *) &paths[48], m13); + _mm_store_si128((__m128i *) &paths[56], m15); + + if (norm) + SSE_NORMALIZE_K7(m4, m1, m5, m3, m6, m2, + m7, m11, m0, m8, m9, m10) + + _mm_store_si128((__m128i *) &sums[0], m4); + _mm_store_si128((__m128i *) &sums[8], m5); + _mm_store_si128((__m128i *) &sums[16], m6); + _mm_store_si128((__m128i *) &sums[24], m7); + _mm_store_si128((__m128i *) &sums[32], m1); + _mm_store_si128((__m128i *) &sums[40], m3); + _mm_store_si128((__m128i *) &sums[48], m2); + _mm_store_si128((__m128i *) &sums[56], m11); +} + +static void gen_metrics_k7_n3(const int8_t *val, const int16_t *out, + int16_t *sums, int16_t *paths, int norm) +{ + const int16_t _val[8] = { val[0], val[1], val[2], 0, val[0], val[1], val[2], 0 }; + + _sse_metrics_k7_n4(_val, out, sums, paths, norm); +} diff --git a/third_party/nrsc5/upstream/src/decode.c b/third_party/nrsc5/upstream/src/decode.c new file mode 100644 index 000000000..9abcba2d4 --- /dev/null +++ b/third_party/nrsc5/upstream/src/decode.c @@ -0,0 +1,579 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "conv.h" +#include "decode.h" +#include "input.h" +#include "pids.h" +#include "private.h" + +#define PM_V_SIZE 20 + +/* 1012s.pdf figure 10-4 */ +static const int bl_delay[] = { 2, 1, 5 }; +static const int ml_delay[] = { 11, 6, 7 }; +static const int bu_delay[] = { 10, 8, 9 }; +static const int mu_delay[] = { 4, 3, 0 }; +static const int el_delay[] = { 0, 1 }; +static const int eu_delay[] = { 2, 3, 5, 4 }; + +static const int8_t PM_V[PM_V_SIZE] = { + 10, 2, 18, 6, 14, 8, 16, 0, 12, 4, + 11, 3, 19, 7, 15, 9, 17, 1, 13, 5 +}; + +static const struct lte_conv_code conv_code_k7 = { + .n = 3, + .k = 7, + .len = P1_FRAME_LEN_FM, + .gen = { 0133, 0171, 0165 }, + .term = CONV_TERM_TAIL_BITING, +}; + +static const struct lte_conv_code conv_code_e1 = { + .n = 3, + .k = 9, + .len = P3_FRAME_LEN_MA3, + .gen = { 0561, 0657, 0711 }, + .term = CONV_TERM_TAIL_BITING, +}; + +static const struct lte_conv_code conv_code_e2_e3 = { + .n = 3, + .k = 9, + .len = P3_FRAME_LEN_MA1, + .gen = { 0561, 0753, 0711 }, + .term = CONV_TERM_TAIL_BITING, +}; + +/* 1012s.pdf figure 10-5 */ +static const int pids_il_delay[] = { 0, 1, 12, 13, 6, 5, 18, 17, 11, 7, 23, 19 }; +static const int pids_iu_delay[] = { 2, 4, 14, 16, 3, 8, 15, 20, 9, 10, 21, 22 }; + +static int bit_map(const unsigned char matrix[PARTITION_WIDTH_AM * BLKSZ * 8], const int b, const int k, const int p) +{ + const int col = (9*k) % 25; + const int row = (11*col + 16*(k/25) + 11*(k/50)) % 32; + return (matrix[PARTITION_WIDTH_AM * (b*BLKSZ + row) + col] >> p) & 1; +} + +static void interleaver_ma1(decode_t *st) +{ + int b, k, p; + for (int n = 0; n < 18000; n++) + { + b = n/2250; + k = (n + n/750 + 1) % 750; + p = n % 3; + st->bl[n] = bit_map(st->buffer_pl, b, k, p); + + b = (3*n + 3) % 8; + k = (n + n/3000 + 3) % 750; + p = 3 + (n % 3); + st->ml[DIVERSITY_DELAY_AM + n] = bit_map(st->buffer_pl, b, k, p); + + b = n/2250; + k = (n + n/750) % 750; + p = n % 3; + st->bu[n] = bit_map(st->buffer_pu, b, k, p); + + b = (3*n) % 8; + k = (n + n/3000 + 2) % 750; + p = 3 + (n % 3); + st->mu[DIVERSITY_DELAY_AM + n] = bit_map(st->buffer_pu, b, k, p); + } + + if (st->input->sync.psmi != SERVICE_MODE_MA3) + { + for (int n = 0; n < 12000; n++) + { + b = (3*n + n/3000) % 8; + k = (n + (n/6000)) % 750; + p = n % 2; + st->el[n] = bit_map(st->buffer_t, b, k, p); + } + for (int n = 0; n < 24000; n++) + { + b = (3*n + n/3000 + 2*(n/12000)) % 8; + k = (n + (n/6000)) % 750; + p = n % 4; + st->eu[n] = bit_map(st->buffer_s, b, k, p); + } + } + else + { + for (int n = 0; n < 18000; n++) + { + b = (3*n + 3) % 8; + k = (n + n/3000 + 3) % 750; + p = n % 3; + st->ebl[n] = bit_map(st->buffer_t, b, k, p); + + b = (3*n + 3) % 8; + k = (n + n/3000 + 3) % 750; + p = 3 + (n % 3); + st->eml[DIVERSITY_DELAY_AM + n] = bit_map(st->buffer_t, b, k, p); + + b = (3*n) % 8; + k = (n + n/3000 + 2) % 750; + p = n % 3; + st->ebu[n] = bit_map(st->buffer_s, b, k, p); + + b = (3*n) % 8; + k = (n + n/3000 + 2) % 750; + p = 3 + (n % 3); + st->emu[DIVERSITY_DELAY_AM + n] = bit_map(st->buffer_s, b, k, p); + } + } + + for (int i = 0; i < 6000; i++) + { + for (int j = 0; j < 3; j++) + { + st->p1_am[i*12 + bl_delay[j]] = st->bl[i*3 + j]; + st->p1_am[i*12 + ml_delay[j]] = st->ml[i*3 + j]; + st->p1_am[i*12 + bu_delay[j]] = st->bu[i*3 + j]; + st->p1_am[i*12 + mu_delay[j]] = st->mu[i*3 + j]; + } + if (st->input->sync.psmi != SERVICE_MODE_MA3) + { + for (int j = 0; j < 2; j++) + { + st->p3_am[i*6 + el_delay[j]] = st->el[i*2 + j]; + } + for (int j = 0; j < 4; j++) + { + st->p3_am[i*6 + eu_delay[j]] = st->eu[i*4 + j]; + } + } + else + { + for (int j = 0; j < 3; j++) + { + st->p3_am[i*12 + bl_delay[j]] = st->ebl[i*3 + j]; + st->p3_am[i*12 + ml_delay[j]] = st->eml[i*3 + j]; + st->p3_am[i*12 + bu_delay[j]] = st->ebu[i*3 + j]; + st->p3_am[i*12 + mu_delay[j]] = st->emu[i*3 + j]; + } + } + } + + memmove(st->ml, st->ml + 18000, DIVERSITY_DELAY_AM); + memmove(st->mu, st->mu + 18000, DIVERSITY_DELAY_AM); + if (st->input->sync.psmi == SERVICE_MODE_MA3) + { + memmove(st->eml, st->eml + 18000, DIVERSITY_DELAY_AM); + memmove(st->emu, st->emu + 18000, DIVERSITY_DELAY_AM); + } + + int offset = 0; + for (int i = 0; i < 8 * P1_FRAME_LEN_AM * 3; i++) + { + switch (i % 15) + { + case 1: + case 4: + case 7: + st->viterbi_p1_am[i] = 0; + break; + default: + st->viterbi_p1_am[i] = st->p1_am[offset++] ? 1 : -1; + } + } + + offset = 0; + if (st->input->sync.psmi != SERVICE_MODE_MA3) + { + for (int i = 0; i < P3_FRAME_LEN_MA1 * 3; i++) + { + switch (i % 6) + { + case 1: + case 4: + case 5: + st->viterbi_p3_am[i] = 0; + break; + default: + st->viterbi_p3_am[i] = st->p3_am[offset++] ? 1 : -1; + } + } + } + else + { + for (int i = 0; i < P3_FRAME_LEN_MA3 * 3; i++) + { + switch (i % 15) + { + case 1: + case 4: + case 7: + st->viterbi_p3_am[i] = 0; + break; + default: + st->viterbi_p3_am[i] = st->p3_am[offset++] ? 1 : -1; + } + } + } +} + +// calculate number of bit errors by re-encoding and comparing to the input +static int bit_errors(int8_t *coded, uint8_t *decoded, const unsigned int k, unsigned int frame_len, + const unsigned int gens[3], + const uint8_t *puncture, const int puncture_len) +{ + uint16_t r = 0; + unsigned int i, j, errors = 0; + + // tail biting + for (i = 0; i < (k-1); i++) + r = (r >> 1) | (decoded[frame_len - (k-1) + i] << (k-1)); + + for (i = 0, j = 0; i < frame_len; i++, j += 3) + { + // shift in new bit + r = (r >> 1) | (decoded[i] << (k-1)); + + if (puncture[j % puncture_len] && ((coded[j] > 0) != __builtin_parity(r & gens[0]))) + errors++; + if (puncture[(j+1) % puncture_len] && ((coded[j+1] > 0) != __builtin_parity(r & gens[1]))) + errors++; + if (puncture[(j+2) % puncture_len] && ((coded[j+2] > 0) != __builtin_parity(r & gens[2]))) + errors++; + } + + return errors; +} + +static int bit_errors_2_5_fm(int8_t *coded, uint8_t *decoded, const int len) +{ + const uint8_t puncture[] = {1, 1, 1, 1, 1, 0}; + return bit_errors(coded, decoded, conv_code_k7.k, len, conv_code_k7.gen, puncture, 6); +} + +static int bit_errors_e1(int8_t *coded, uint8_t *decoded, const int len) +{ + const uint8_t puncture[] = {1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1}; + return bit_errors(coded, decoded, conv_code_e1.k, len, conv_code_e1.gen, puncture, 15); +} + +static int bit_errors_e2(int8_t *coded, uint8_t *decoded, const int len) +{ + const uint8_t puncture[] = {1, 0, 1, 1, 0, 0}; + return bit_errors(coded, decoded, conv_code_e2_e3.k, len, conv_code_e2_e3.gen, puncture, 6); +} + +static void descramble(uint8_t *buf, unsigned int length) +{ + const unsigned int width = 11; + unsigned int i, val = 0x3ff; + for (i = 0; i < length; i += 8) + { + unsigned int j; + for (j = 0; j < 8; ++j) + { + int bit = ((val >> 9) ^ val) & 1; + val |= bit << width; + val >>= 1; + buf[i + j] ^= bit; + } + } +} + +static void interleaver_i(const int8_t* in, + int8_t* viterbi, + const int J, + const int B, + const int C, + const int M, + const int8_t* V, + const unsigned int length_v, + const unsigned int N) +{ + unsigned int out = 0; + for (unsigned int i = 0; i < N; i++) + { + const int8_t partition = V[((i + 2 * (M / 4)) / M) % length_v]; + unsigned int block; + if (M == 1) + block = ((i / J) + (partition * 7)) % B; + else + block = (i + (i / (J * B))) % B; + const unsigned int k = i / (J * B); + const unsigned int row = (k * 11) % 32; + const unsigned int column = (k * 11 + k / (32 * 9)) % C; + viterbi[out++] = in[(block * 32 + row) * (J * C) + partition * C + column]; + if ((out % 6) == 5) // depuncture, [1, 1, 1, 1, 1, 0] + viterbi[out++] = 0; + } +} + +static void interleaver_ii(const int8_t* in, int8_t* viterbi, const unsigned int bc, + const int J, const int B, const int C, + const int8_t* V, const unsigned int length_v, + const int b, const int I0) +{ + int out = 0; + + for (unsigned int i = bc * b; i < (bc+1) * b; i++) + { + const int8_t partition = V[i % length_v]; + const unsigned int block = i / b; + const unsigned int k = ((i / J) % (b / J)) + (I0 / (J * B)); + const unsigned int row = (k * 11) % 32; + const unsigned int column = (k * 11 + k / (32 * 9)) % C; + viterbi[out++] = in[(block * 32 + row) * (J * C) + partition * C + column]; + if ((out % 6) == 5) // depuncture, [1, 1, 1, 1, 1, 0] + viterbi[out++] = 0; + }; +} + +void interleaver_iv(interleaver_iv_t* interleaver, int8_t* viterbi, const unsigned int frame_len) +{ + const unsigned int J = (frame_len == P3_FRAME_LEN_MP3_MP11) ? 4 : 2; + const unsigned int B = 32; + const unsigned int C = 36; + const unsigned int M = (frame_len == P3_FRAME_LEN_MP3_MP11) ? 2 : 4; + const unsigned int N = (frame_len == P3_FRAME_LEN_MP3_MP11) ? 147456 : 73728; + const unsigned int bk_bits = 32 * C; + const unsigned int bk_adj = 32 * C - 1; + + if (interleaver->i == N) + { + interleaver->i = 0; + memset(interleaver->pt, 0, sizeof(unsigned int) * 4); + interleaver->ready = 1; + } + + unsigned int out = 0; + for (unsigned int i = 0; i < frame_len * 2; i++) + { + const unsigned int partition = ((interleaver->i + 2 * (M / 4)) / M) % J; + const unsigned int pti = interleaver->pt[partition]++; + const unsigned int block = (pti + (partition * 7) - (bk_adj * (pti / bk_bits))) % B; + const unsigned int row = ((11 * pti) % bk_bits) / C; + const unsigned int column = (pti * 11) % C; + viterbi[out++] = interleaver->internal[(block * 32 + row) * (J * C) + partition * C + column]; + if ((out % 6) == 1 || (out % 6) == 4) // depuncture, [1, 0, 1, 1, 0, 1] + viterbi[out++] = 0; + + interleaver->internal[interleaver->i] = interleaver->buffer[i]; + interleaver->i++; + } +} + +void decode_push_pm(decode_t *st, const int8_t* sbit, const unsigned int bc) +{ + memcpy(st->buffer_pm + PM_BLOCK_SIZE * bc, sbit, PM_BLOCK_SIZE * sizeof(int8_t)); + decode_process_pids(st, bc); + + if (bc == 0) + st->started_pm = 1; + + if (st->started_pm) + { + if (bc == 15) + decode_process_p1(st); + } +} + +void decode_push_px1(decode_t *st, const int8_t* sbit, const unsigned int len, const unsigned int bc) +{ + if (bc % 2 == 0) + st->interleaver_px1.started = 1; + + if (st->interleaver_px1.started) + { + memcpy(st->interleaver_px1.buffer + len * (bc % 2), sbit,len * sizeof(int8_t)); + + if (bc % 2 == 1) + { + interleaver_iv(&st->interleaver_px1, st->viterbi_p3, len); + + if (st->interleaver_px1.ready) + { + nrsc5_conv_decode_p3_p4(st->viterbi_p3, st->scrambler_p3, len); + descramble(st->scrambler_p3, len); + frame_push(&st->input->frame, st->scrambler_p3, len, P3_LOGICAL_CHANNEL); + } + } + } +} + +void decode_push_px2(decode_t* st, const int8_t* sbit, const unsigned int len, const unsigned int bc) +{ + if (bc % 2 == 0) + st->interleaver_px2.started = 1; + + if (st->interleaver_px2.started) + { + memcpy(st->interleaver_px2.buffer + len * (bc % 2), sbit, len * sizeof(int8_t)); + + if (bc % 2 == 1) + { + interleaver_iv(&st->interleaver_px2, st->viterbi_p4, len); + + if (st->interleaver_px2.ready) + { + nrsc5_conv_decode_p3_p4(st->viterbi_p4, st->scrambler_p4, len); + descramble(st->scrambler_p4, len); + frame_push(&st->input->frame, st->scrambler_p4, len, P4_LOGICAL_CHANNEL); + } + } + } +} + +void decode_push_pl_pu_s_t(decode_t* st, + const uint8_t* sym_pl, const uint8_t* sym_pu, const uint8_t* sym_s, + const uint8_t* sym_t, const unsigned int bc) +{ + memcpy(st->buffer_pl + (bc * BLKSZ * PARTITION_WIDTH_AM), sym_pl, BLKSZ * PARTITION_WIDTH_AM); + memcpy(st->buffer_pu + (bc * BLKSZ * PARTITION_WIDTH_AM), sym_pu, BLKSZ * PARTITION_WIDTH_AM); + memcpy(st->buffer_s + (bc * BLKSZ * PARTITION_WIDTH_AM), sym_s, BLKSZ * PARTITION_WIDTH_AM); + memcpy(st->buffer_t + (bc * BLKSZ * PARTITION_WIDTH_AM), sym_t, BLKSZ * PARTITION_WIDTH_AM); + + decode_process_p1_p3_am(st, bc); +} + +void decode_process_p1(decode_t *st) +{ + const int J = 20, B = 16, C = 36, M = 1; + interleaver_i(st->buffer_pm, st->viterbi_p1, + J, B, C, M, PM_V, PM_V_SIZE, P1_FRAME_LEN_ENCODED_FM); + + nrsc5_conv_decode_p1(st->viterbi_p1, st->scrambler_p1); + nrsc5_report_ber(st->input->radio, (float) bit_errors_2_5_fm(st->viterbi_p1, st->scrambler_p1, P1_FRAME_LEN_FM) / P1_FRAME_LEN_ENCODED_FM); + descramble(st->scrambler_p1, P1_FRAME_LEN_FM); + frame_push(&st->input->frame, st->scrambler_p1, P1_FRAME_LEN_FM, P1_LOGICAL_CHANNEL); +} + +void decode_process_pids(decode_t *st, const unsigned int bc) +{ + const int J = 20, B = 16, C = 36; + interleaver_ii(st->buffer_pm, st->viterbi_pids, (int)bc, J, B, C, PM_V, PM_V_SIZE, PIDS_FRAME_LEN_ENCODED_FM, + P1_FRAME_LEN_ENCODED_FM); + + nrsc5_conv_decode_pids(st->viterbi_pids, st->scrambler_pids); + descramble(st->scrambler_pids, PIDS_FRAME_LEN); + pids_frame_push(&st->pids, st->scrambler_pids); +} + +void decode_process_pids_am(decode_t *st, const uint8_t* sbit) +{ + uint8_t il[120], iu[120]; + + /* 1012s.pdf section 10.4 */ + for (int n = 0; n < 120; n++) { + int k, p, row; + + p = n % 4; + + k = (n + (n/60) + 11) % 30; + row = (11 * (k + (k/15)) + 3) % 32; + il[n] = (sbit[row*2] >> p) & 1; + + k = (n + (n/60)) % 30; + row = (11 * (k + (k/15)) + 3) % 32; + iu[n] = (sbit[row*2 + 1] >> p) & 1; + } + + /* 1012s.pdf figure 10-5 */ + const int pids1_disabled = (st->input->sync.psmi == 1) && st->input->sync.rdbi; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 12; j++) { + st->viterbi_pids[i*24 + pids_il_delay[j]] = pids1_disabled ? 0 : (il[i*12 + j] ? 1 : -1); + st->viterbi_pids[i*24 + pids_iu_delay[j]] = iu[i*12 + j] ? 1 : -1; + } + } + + nrsc5_conv_decode_e2_e3(st->viterbi_pids, st->scrambler_pids, PIDS_FRAME_LEN); + descramble(st->scrambler_pids, PIDS_FRAME_LEN); + pids_frame_push(&st->pids, st->scrambler_pids); +} + +void decode_process_p1_p3_am(decode_t *st, const unsigned int bc) +{ + if (bc == 0) + st->am_errors = 0; + + if (st->am_diversity_wait == 0) + { + nrsc5_conv_decode_e1(st->viterbi_p1_am + (bc * P1_FRAME_LEN_AM * 3), st->scrambler_p1_am, P1_FRAME_LEN_AM); + st->am_errors += bit_errors_e1(st->viterbi_p1_am + (bc * P1_FRAME_LEN_AM * 3), st->scrambler_p1_am, P1_FRAME_LEN_AM); + descramble(st->scrambler_p1_am, P1_FRAME_LEN_AM); + frame_push(&st->input->frame, st->scrambler_p1_am, P1_FRAME_LEN_AM, P1_LOGICAL_CHANNEL); + + if (bc == 7) + { + unsigned int total_frame_length = 8 * P1_FRAME_LEN_ENCODED_AM; + + if (!st->input->sync.rdbi) + { + if (st->input->sync.psmi != SERVICE_MODE_MA3) + { + total_frame_length += P3_FRAME_LEN_ENCODED_MA1; + nrsc5_conv_decode_e2_e3(st->viterbi_p3_am, st->scrambler_p3_am, P3_FRAME_LEN_MA1); + st->am_errors += bit_errors_e2(st->viterbi_p3_am, st->scrambler_p3_am, P3_FRAME_LEN_MA1); + descramble(st->scrambler_p3_am, P3_FRAME_LEN_MA1); + frame_push(&st->input->frame, st->scrambler_p3_am, P3_FRAME_LEN_MA1, P3_LOGICAL_CHANNEL); + } + else + { + total_frame_length += P3_FRAME_LEN_ENCODED_MA3; + nrsc5_conv_decode_e1(st->viterbi_p3_am, st->scrambler_p3_am, P3_FRAME_LEN_MA3); + st->am_errors += bit_errors_e1(st->viterbi_p3_am, st->scrambler_p3_am, P3_FRAME_LEN_MA3); + descramble(st->scrambler_p3_am, P3_FRAME_LEN_MA3); + frame_push(&st->input->frame, st->scrambler_p3_am, P3_FRAME_LEN_MA3, P3_LOGICAL_CHANNEL); + } + } + + nrsc5_report_ber(st->input->radio, (float) st->am_errors / (float) total_frame_length); + } + } + + if (bc == 7) + { + interleaver_ma1(st); + + if (st->am_diversity_wait > 0) + st->am_diversity_wait--; + } +} + +static void interleaver_iv_reset(interleaver_iv_t *interleaver) +{ + interleaver->i = 0; + memset(interleaver->pt, 0, sizeof(unsigned int) * 4); + interleaver->started = 0; + interleaver->ready = 0; +} + +void decode_reset(decode_t *st) +{ + st->idx_pm = 0; + st->started_pm = 0; + st->am_errors = 0; + st->am_diversity_wait = 4; + interleaver_iv_reset(&st->interleaver_px1); + interleaver_iv_reset(&st->interleaver_px2); + pids_init(&st->pids, st->input); +} + +void decode_init(decode_t *st, input_t *input) +{ + st->input = input; + decode_reset(st); +} diff --git a/third_party/nrsc5/upstream/src/decode.h b/third_party/nrsc5/upstream/src/decode.h new file mode 100644 index 000000000..317fd1dc8 --- /dev/null +++ b/third_party/nrsc5/upstream/src/decode.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include "defines.h" +#include "pids.h" + +#define DIVERSITY_DELAY_AM (18000 * 3) + +typedef struct +{ + int8_t buffer[144 * BLKSZ * 2]; + int8_t internal[P3_FRAME_LEN_MP3_MP11 * 32]; + unsigned int i; + unsigned int pt[4]; + int ready; + int started; +} interleaver_iv_t; + +typedef struct +{ + struct input_t *input; + int8_t buffer_pm[PM_BLOCK_SIZE * 16]; + unsigned int idx_pm; + int started_pm; + uint8_t buffer_pu[PARTITION_WIDTH_AM * BLKSZ * 8]; + uint8_t buffer_pl[PARTITION_WIDTH_AM * BLKSZ * 8]; + uint8_t buffer_s[PARTITION_WIDTH_AM * BLKSZ * 8]; + uint8_t buffer_t[PARTITION_WIDTH_AM * BLKSZ * 8]; + unsigned int am_errors; + unsigned int am_diversity_wait; + + uint8_t bl[18000]; + uint8_t bu[18000]; + uint8_t ml[18000 + DIVERSITY_DELAY_AM]; + uint8_t mu[18000 + DIVERSITY_DELAY_AM]; + uint8_t el[12000]; + uint8_t eu[24000]; + uint8_t ebl[18000]; + uint8_t ebu[18000]; + uint8_t eml[18000 + DIVERSITY_DELAY_AM]; + uint8_t emu[18000 + DIVERSITY_DELAY_AM]; + + int8_t viterbi_p1[P1_FRAME_LEN_FM * 3]; + uint8_t scrambler_p1[P1_FRAME_LEN_FM]; + int8_t viterbi_pids[PIDS_FRAME_LEN * 3]; + uint8_t scrambler_pids[PIDS_FRAME_LEN]; + interleaver_iv_t interleaver_px1; + interleaver_iv_t interleaver_px2; + int8_t viterbi_p3[P3_FRAME_LEN_MP3_MP11 * 3]; + int8_t viterbi_p4[P3_FRAME_LEN_MP3_MP11 * 3]; + uint8_t scrambler_p3[P3_FRAME_LEN_MP3_MP11]; + uint8_t scrambler_p4[P3_FRAME_LEN_MP3_MP11]; + + uint8_t p1_am[8 * P1_FRAME_LEN_ENCODED_AM]; + int8_t viterbi_p1_am[8 * P1_FRAME_LEN_AM * 3]; + uint8_t scrambler_p1_am[P1_FRAME_LEN_AM]; + uint8_t p3_am[P3_FRAME_LEN_ENCODED_MA3]; + int8_t viterbi_p3_am[P3_FRAME_LEN_MA3 * 3]; + uint8_t scrambler_p3_am[P3_FRAME_LEN_MA3]; + + pids_t pids; +} decode_t; + +void decode_process_p1(decode_t *st); +void decode_process_pids(decode_t *st, unsigned int bc); +void decode_process_p3_p4(const decode_t *st, interleaver_iv_t *interleaver, int8_t *viterbi, uint8_t *scrambler, logical_channel_t lc); +void decode_process_pids_am(decode_t *st, const uint8_t* sbit); +void decode_process_p1_p3_am(decode_t *st, unsigned int bc); + +void decode_push_pm(decode_t *st, const int8_t* sbit, unsigned int bc); +void decode_push_px1(decode_t *st, const int8_t* sbit, unsigned int len, unsigned int bc); +void decode_push_px2(decode_t *st, const int8_t* sbit, unsigned int len, unsigned int bc); + +void decode_push_pl_pu_s_t(decode_t *st, + const uint8_t* sym_pl, const uint8_t* sym_pu, const uint8_t* sym_s, const uint8_t* sym_t, + unsigned int bc); + +void decode_reset(decode_t *st); +void decode_init(decode_t *st, struct input_t *input); diff --git a/third_party/nrsc5/upstream/src/defines.h b/third_party/nrsc5/upstream/src/defines.h new file mode 100644 index 000000000..c4edce492 --- /dev/null +++ b/third_party/nrsc5/upstream/src/defines.h @@ -0,0 +1,138 @@ +#pragma once + +#include "config.h" + +#include +#include +#include +#include +#include + +// FFT length in samples +#define FFT_FM 2048 +#define FFT_AM 256 +// cyclic preflex length in samples +#define CP_FM 112 +#define CP_AM 14 +#define FFTCP_FM (FFT_FM + CP_FM) +#define FFTCP_AM (FFT_AM + CP_AM) +// OFDM symbols per L1 block +#define BLKSZ 32 +// symbols processed by each invocation of acquire_process +#define ACQUIRE_SYMBOLS (BLKSZ) +// index of first lower sideband subcarrier +#define LB_START ((FFT_FM / 2) - 546) +// index of last upper sideband subcarrier +#define UB_END ((FFT_FM / 2) + 546) +// index of AM carrier +#define CENTER_AM (FFT_AM / 2) +// indexes of AM subcarriers +#define REF_INDEX_AM 1 +#define PIDS_INNER_INDEX_AM 27 +#define PIDS_OUTER_INDEX_AM 53 +#define INNER_PARTITION_START_AM 2 +#define MIDDLE_PARTITION_START_AM 28 +#define OUTER_PARTITION_START_AM 57 +#define MAX_INDEX_AM 81 +// AM service modes +#define SERVICE_MODE_MA1 1 +#define SERVICE_MODE_MA3 2 +// bits per P1 frame +#define P1_FRAME_LEN_FM 146176 +#define P1_FRAME_LEN_AM 3750 +// bits per encoded P1 frame +#define P1_FRAME_LEN_ENCODED_FM (P1_FRAME_LEN_FM * 5 / 2) +#define P1_FRAME_LEN_ENCODED_AM (P1_FRAME_LEN_AM * 12 / 5) +// bits per PIDS frame +#define PIDS_FRAME_LEN 80 +// bits per encoded PIDS frame +#define PIDS_FRAME_LEN_ENCODED_FM (PIDS_FRAME_LEN * 5 / 2) +#define PIDS_FRAME_LEN_ENCODED_AM (PIDS_FRAME_LEN * 3) +// bits per P3 frame +#define P3_FRAME_LEN_MP2 2304 +#define P3_FRAME_LEN_MP3_MP11 4608 +#define P3_FRAME_LEN_MA1 24000 +#define P3_FRAME_LEN_MA3 30000 +// bits per encoded P3 frame +#define P3_FRAME_LEN_ENCODED_FM (P3_FRAME_LEN_FM * 2) +#define P3_FRAME_LEN_ENCODED_MA1 (P3_FRAME_LEN_MA1 * 3 / 2) +#define P3_FRAME_LEN_ENCODED_MA3 (P3_FRAME_LEN_MA3 * 12 / 5) +// bits per L2 PCI +#define PCI_LEN 24 +// bytes per L2 PDU (max) +#define MAX_PDU_LEN ((P1_FRAME_LEN_FM - PCI_LEN) / 8) +// bytes per L2 PDU in P1 frame (AM) +#define P1_PDU_LEN_AM 466 +// number of programs (max) +#define MAX_PROGRAMS 8 +// number of streams per program (max) +#define MAX_STREAMS 2 +// number of audio packets in the elastic buffer +#define ELASTIC_BUFFER_LEN 64 +// number of subcarriers per AM partition +#define PARTITION_WIDTH_AM 25 +// number of subcarriers per FM partition +#define PARTITION_WIDTH_FM 19 +// number of data carries per FM partition +#define PARTITION_DATA_CARRIERS 18 +// number of partitions in each Primary Main (PM) sideband +#define PM_PARTITIONS 10 +// size of one block in the PM interleaver matrix +#define PM_BLOCK_SIZE (2 * 2 * PM_PARTITIONS * PARTITION_DATA_CARRIERS * BLKSZ) + +#define log_debug(...) \ + do { if (LIBRARY_DEBUG_LEVEL <= 1) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } } while (0) +#define log_info(...) \ + do { if (LIBRARY_DEBUG_LEVEL <= 2) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } } while (0) +#define log_warn(...) \ + do { if (LIBRARY_DEBUG_LEVEL <= 3) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } } while (0) +#define log_error(...) \ + do { if (LIBRARY_DEBUG_LEVEL <= 4) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } } while (0) + +#define U8_F(x) ( (((float)(x)) - 127) / 128 ) +#define U8_Q15(x) ( ((int16_t)(x) - 127) * 64 ) + +typedef struct { + int16_t r, i; +} cint16_t; + +typedef enum { + P1_LOGICAL_CHANNEL, + P3_LOGICAL_CHANNEL, + P4_LOGICAL_CHANNEL, + NUM_LOGICAL_CHANNELS +} logical_channel_t; + +static inline float complex cq15_to_cf(cint16_t cq15) +{ + return CMPLXF((float)cq15.r / 32767.0f, (float)cq15.i / 32767.0f); +} + +static inline float complex cq15_to_cf_conj(cint16_t cq15) +{ + return CMPLXF((float)cq15.r / 32767.0f, (float)cq15.i / -32767.0f); +} + +static inline float normf(float complex v) +{ + float realf = crealf(v); + float imagf = cimagf(v); + return realf * realf + imagf * imagf; +} + +static inline void fftshift(float complex *x, unsigned int size) +{ + int i, h = size / 2; + for (i = 0; i < h; i += 4) + { + float complex t1 = x[i], t2 = x[i+1], t3 = x[i+2], t4 = x[i+3]; + x[i] = x[i + h]; + x[i+1] = x[i+1 + h]; + x[i+2] = x[i+2 + h]; + x[i+3] = x[i+3 + h]; + x[i + h] = t1; + x[i+1 + h] = t2; + x[i+2 + h] = t3; + x[i+3 + h] = t4; + } +} diff --git a/third_party/nrsc5/upstream/src/firdecim_cf32.c b/third_party/nrsc5/upstream/src/firdecim_cf32.c new file mode 100644 index 000000000..5d40ecf86 --- /dev/null +++ b/third_party/nrsc5/upstream/src/firdecim_cf32.c @@ -0,0 +1,98 @@ +#include "config.h" + +#include +#include + +#include "firdecim_cf32.h" + +#define WINDOW_SIZE 2048 + +struct firdecim_cf32 { + float * taps; + unsigned int ntaps; + float complex * window; + unsigned int idx; +}; + +firdecim_cf32 firdecim_cf32_create(const float * taps, unsigned int ntaps) +{ + firdecim_cf32 q; + + q = malloc(sizeof(*q)); + q->ntaps = (ntaps == 32) ? 32 : 15; + q->taps = malloc(sizeof(float) * ntaps); + q->window = calloc(WINDOW_SIZE, sizeof(float complex)); + firdecim_cf32_reset(q); + + // reverse order so we can push into the window + for (unsigned int i = 0; i < ntaps; ++i) + { + q->taps[i] = taps[ntaps - 1 - i]; + } + + return q; +} + +void firdecim_cf32_free(firdecim_cf32 q) +{ + free(q->taps); + free(q->window); + free(q); +} + +void firdecim_cf32_reset(firdecim_cf32 q) +{ + q->idx = q->ntaps - 1; +} + +static void push(firdecim_cf32 q, complex float x) +{ + if (q->idx == WINDOW_SIZE) + { + for (unsigned int i = 0; i < q->ntaps - 1; i++) + q->window[i] = q->window[q->idx - q->ntaps + 1 + i]; + q->idx = q->ntaps - 1; + } + q->window[q->idx++] = x; +} + +static float complex dotprod_32(const float complex *a, const float *b) +{ + float complex sum = { 0 }; + int i; + + for (i = 1; i < 16; i++) + { + sum += (a[i] + a[32-i]) * b[i]; + } + sum += a[i] * b[i]; + + return sum; +} + +static float complex dotprod_halfband_4(const float complex *a, const float *b) +{ + float complex sum = { 0 }; + int i; + + for (i = 0; i < 7; i += 2) + { + sum += (a[i] + a[14-i]) * b[i]; + } + sum += a[7]; + + return sum; +} + +void fir_cf32_execute(firdecim_cf32 q, const float complex *x, float complex *y) +{ + push(q, x[0]); + *y = dotprod_32(&q->window[q->idx - q->ntaps], q->taps); +} + +void halfband_cf32_execute(firdecim_cf32 q, const float complex *x, float complex *y) +{ + push(q, x[0]); + *y = dotprod_halfband_4(&q->window[q->idx - q->ntaps], q->taps); + push(q, x[1]); +} diff --git a/third_party/nrsc5/upstream/src/firdecim_cf32.h b/third_party/nrsc5/upstream/src/firdecim_cf32.h new file mode 100644 index 000000000..3df153e6d --- /dev/null +++ b/third_party/nrsc5/upstream/src/firdecim_cf32.h @@ -0,0 +1,11 @@ +#pragma once + +#include "defines.h" + +typedef struct firdecim_cf32 * firdecim_cf32; + +firdecim_cf32 firdecim_cf32_create(const float * taps, unsigned int ntaps); +void firdecim_cf32_free(firdecim_cf32); +void firdecim_cf32_reset(firdecim_cf32); +void fir_cf32_execute(firdecim_cf32 q, const float complex *x, float complex *y); +void halfband_cf32_execute(firdecim_cf32 q, const float complex *x, float complex *y); diff --git a/third_party/nrsc5/upstream/src/frame.c b/third_party/nrsc5/upstream/src/frame.c new file mode 100644 index 000000000..ab383a11b --- /dev/null +++ b/third_party/nrsc5/upstream/src/frame.c @@ -0,0 +1,795 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "defines.h" +#include "frame.h" +#include "input.h" +#include "rs_char.h" +#include "private.h" + +#define PCI_AUDIO 0x38D8D3 +#define PCI_AUDIO_OPP 0xCE3634 +#define PCI_AUDIO_FIXED 0xE3634C +#define PCI_AUDIO_FIXED_OPP 0x8D8D33 +#define PCI_FIXED 0x3634CE +#define PCI_RESERVED_CW5 0x8D338D +#define PCI_RESERVED_CW6 0xD8D338 +#define PCI_RESERVED_CW7 0x634CE3 +#define PCI_MAX_ERRORS 4 +#define PCI_COUNT 8 + +static const unsigned int PCI_POSSIBILITIES[PCI_COUNT] = { + PCI_AUDIO, + PCI_AUDIO_OPP, + PCI_AUDIO_FIXED, + PCI_AUDIO_FIXED_OPP, + PCI_FIXED, + PCI_RESERVED_CW5, + PCI_RESERVED_CW6, + PCI_RESERVED_CW7, +}; + +#define MAX_AUDIO_PACKETS 64 + +typedef struct +{ + unsigned int codec_mode; + unsigned int stream_id; + unsigned int pdu_seq; + unsigned int blend_control; + unsigned int per_stream_delay; + unsigned int common_delay; + unsigned int latency; + unsigned int pfirst; + unsigned int plast; + unsigned int seq; + unsigned int nop; + unsigned int hef; + unsigned int la_location; +} frame_header_t; + +typedef struct +{ + unsigned int class_ind; + unsigned int prog_num; + unsigned int pdu_len; + unsigned int prog_type; + unsigned int access; + unsigned int applied_services; + unsigned int pdu_marker; +} hef_t; + +static const uint8_t crc8_tab[] = { + 0, 0x31, 0x62, 0x53, 0xC4, 0xF5, 0xA6, 0x97, 0xB9, + 0x88, 0xDB, 0xEA, 0x7D, 0x4C, 0x1F, 0x2E, 0x43, 0x72, + 0x21, 0x10, 0x87, 0xB6, 0xE5, 0xD4, 0xFA, 0xCB, 0x98, + 0xA9, 0x3E, 0xF, 0x5C, 0x6D, 0x86, 0xB7, 0xE4, 0xD5, + 0x42, 0x73, 0x20, 0x11, 0x3F, 0xE, 0x5D, 0x6C, 0xFB, + 0xCA, 0x99, 0xA8, 0xC5, 0xF4, 0xA7, 0x96, 1, 0x30, + 0x63, 0x52, 0x7C, 0x4D, 0x1E, 0x2F, 0xB8, 0x89, 0xDA, + 0xEB, 0x3D, 0xC, 0x5F, 0x6E, 0xF9, 0xC8, 0x9B, 0xAA, + 0x84, 0xB5, 0xE6, 0xD7, 0x40, 0x71, 0x22, 0x13, 0x7E, + 0x4F, 0x1C, 0x2D, 0xBA, 0x8B, 0xD8, 0xE9, 0xC7, 0xF6, + 0xA5, 0x94, 3, 0x32, 0x61, 0x50, 0xBB, 0x8A, 0xD9, + 0xE8, 0x7F, 0x4E, 0x1D, 0x2C, 2, 0x33, 0x60, 0x51, + 0xC6, 0xF7, 0xA4, 0x95, 0xF8, 0xC9, 0x9A, 0xAB, 0x3C, + 0xD, 0x5E, 0x6F, 0x41, 0x70, 0x23, 0x12, 0x85, 0xB4, + 0xE7, 0xD6, 0x7A, 0x4B, 0x18, 0x29, 0xBE, 0x8F, 0xDC, + 0xED, 0xC3, 0xF2, 0xA1, 0x90, 7, 0x36, 0x65, 0x54, + 0x39, 8, 0x5B, 0x6A, 0xFD, 0xCC, 0x9F, 0xAE, 0x80, + 0xB1, 0xE2, 0xD3, 0x44, 0x75, 0x26, 0x17, 0xFC, 0xCD, + 0x9E, 0xAF, 0x38, 9, 0x5A, 0x6B, 0x45, 0x74, 0x27, + 0x16, 0x81, 0xB0, 0xE3, 0xD2, 0xBF, 0x8E, 0xDD, 0xEC, + 0x7B, 0x4A, 0x19, 0x28, 6, 0x37, 0x64, 0x55, 0xC2, + 0xF3, 0xA0, 0x91, 0x47, 0x76, 0x25, 0x14, 0x83, 0xB2, + 0xE1, 0xD0, 0xFE, 0xCF, 0x9C, 0xAD, 0x3A, 0xB, 0x58, + 0x69, 4, 0x35, 0x66, 0x57, 0xC0, 0xF1, 0xA2, 0x93, + 0xBD, 0x8C, 0xDF, 0xEE, 0x79, 0x48, 0x1B, 0x2A, 0xC1, + 0xF0, 0xA3, 0x92, 5, 0x34, 0x67, 0x56, 0x78, 0x49, + 0x1A, 0x2B, 0xBC, 0x8D, 0xDE, 0xEF, 0x82, 0xB3, 0xE0, + 0xD1, 0x46, 0x77, 0x24, 0x15, 0x3B, 0xA, 0x59, 0x68, + 0xFF, 0xCE, 0x9D, 0xAC +}; + +static const uint16_t fcs_tab[] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* Good final FCS value */ +#define VALIDFCS16 0xf0b8 + +static uint8_t crc8(const uint8_t *pkt, unsigned int cnt) +{ + unsigned int i, crc = 0xFF; + for (i = 0; i < cnt; ++i) + crc = crc8_tab[crc ^ pkt[i]]; + return crc; +} + +static uint16_t fcs16(const uint8_t *cp, int len) +{ + uint16_t crc = 0xFFFF; + while (len--) + crc = (crc >> 8) ^ fcs_tab[(crc ^ *cp++) & 0xFF]; + return (crc); +} + +static int has_audio(unsigned int pci) +{ + return pci == PCI_AUDIO + || pci == PCI_AUDIO_OPP + || pci == PCI_AUDIO_FIXED + || pci == PCI_AUDIO_FIXED_OPP; +} + +static int has_fixed(unsigned int pci) +{ + return pci == PCI_AUDIO_FIXED + || pci == PCI_AUDIO_FIXED_OPP + || pci == PCI_FIXED; +} + +static int fix_header(frame_t *st, uint8_t *buf) +{ + uint8_t hdr[RS_BLOCK_LEN]; + int i, corrections; + + memset(hdr, 0, RS_BLOCK_LEN-RS_CODEWORD_LEN); + for (i = 0; i < RS_CODEWORD_LEN; i++) + hdr[RS_BLOCK_LEN-i-1] = buf[i]; + + corrections = decode_rs_char(st->rs_dec, hdr, NULL, 0); + + if (corrections == -1) + return 0; + + for (i = 0; i < RS_BLOCK_LEN-RS_CODEWORD_LEN; i++) + if (hdr[i] != 0) + return 0; + + for (i = 0; i < RS_CODEWORD_LEN; i++) + buf[i] = hdr[RS_BLOCK_LEN-i-1]; + return 1; +} + +static void parse_header(uint8_t *buf, frame_header_t *hdr) +{ + hdr->codec_mode = buf[8] & 0xf; + hdr->stream_id = (buf[8] >> 4) & 0x3; + hdr->pdu_seq = (buf[8] >> 6) | ((buf[9] & 1) << 2); + hdr->blend_control = (buf[9] >> 1) & 0x3; + hdr->per_stream_delay = buf[9] >> 3; + hdr->common_delay = buf[10] & 0x3f; + hdr->latency = (buf[10] >> 6) | ((buf[11] & 1) << 2); + hdr->pfirst = (buf[11] >> 1) & 1; + hdr->plast = (buf[11] >> 2) & 1; + hdr->seq = (buf[11] >> 3) | ((buf[12] & 1) << 5); + hdr->nop = (buf[12] >> 1) & 0x3f; + hdr->hef = buf[12] >> 7; + hdr->la_location = buf[13]; +} + +static unsigned int parse_hef(uint8_t *buf, unsigned int length, hef_t *hef) +{ + uint8_t *byte = buf, *end = buf + length; + + do + { + if (byte >= end) return length; + + switch ((*byte >> 4) & 0x7) + { + case 0: + hef->class_ind = *byte & 0xf; + break; + case 1: + hef->prog_num = (*byte >> 1) & 0x7; + if (*byte & 0x1) + { + if (byte + 2 >= end) return length; + byte++; + hef->pdu_len = (*byte & 0x7f) << 7; + byte++; + hef->pdu_len |= (*byte & 0x7f); + } + break; + case 2: + if (byte + 1 >= end) return length; + hef->access = (*byte >> 3) & 0x1; + hef->prog_type = (*byte & 0x1) << 7; + byte++; + hef->prog_type |= (*byte & 0x7f); + break; + case 3: + if (*byte & 0x8) + { + if (byte + 4 >= end) return length; + byte += 4; + } + else + { + if (byte + 3 >= end) return length; + byte += 3; + } + break; + case 4: + if (*byte & 0x8) + { + if (byte + 3 >= end) return length; + hef->applied_services = (*byte & 0x7); + byte++; + hef->pdu_marker = (*byte & 0x7f) << 14; + byte++; + hef->pdu_marker |= (*byte & 0x7f) << 7; + byte++; + hef->pdu_marker |= (*byte & 0x7f); + } + else + { + if (byte + 1 >= end) return length; + byte++; + } + break; + default: + log_debug("unknown header expansion ID"); + } + } while (*(byte++) & 0x80); + + return byte - buf; +} + +static unsigned int calc_lc_bits(frame_header_t *hdr) +{ + switch(hdr->codec_mode) + { + case 0: + return 16; + case 1: + case 2: + case 3: + if (hdr->stream_id == 0) + return 12; + else + return 16; + case 10: + case 13: + return 12; + default: + log_warn("unknown codec field (%d)", hdr->codec_mode); + return 16; + } +} + +static unsigned int calc_avg_packets(frame_header_t *hdr) +{ + switch(hdr->codec_mode) + { + case 0: + return 32; + case 1: + case 2: + case 3: + if (hdr->stream_id == 0) + return 4; + else + return 32; + case 10: + if (hdr->stream_id == 0) + return 32; + else + return 4; + case 13: + return 4; + default: + log_warn("unknown codec field (%d)", hdr->codec_mode); + return 32; + } +} + +static unsigned int parse_location(uint8_t *buf, unsigned int lc_bits, unsigned int i) +{ + if (lc_bits == 16) + return (buf[2*i + 1] << 8) | buf[2*i]; + else + { + if (i % 2 == 0) + return ((buf[i/2*3 + 1] & 0xf) << 8) | buf[i/2*3]; + else + return (buf[i/2*3 + 2] << 4) | (buf[i/2*3 + 1] >> 4); + } +} + +static int unescape_hdlc(uint8_t *data, int length) +{ + uint8_t *p = data; + + for (int i = 0; i < length; i++) + { + if (data[i] == 0x7D) + *p++ = data[++i] | 0x20; + else + *p++ = data[i]; + } + + return p - data; +} + +static void aas_push(frame_t *st, uint8_t* psd, unsigned int length, logical_channel_t lc) +{ + (void)lc; // UNUSED + + length = unescape_hdlc(psd, length); + + if (length == 0) + { + // empty frames are used as padding + } + else if (fcs16(psd, length) != VALIDFCS16) + { + // occasional CRC errors are normal, because transmitters abandon their HDLC + // frame mid-stream when switching to new PSD data + } + else if (psd[0] != 0x21) + { + log_warn("unknown AAS protocol %x", psd[0]); + } + else + { + // remove protocol and fcs fields + output_aas_push(st->input->output, psd + 1, length - 3); + } +} + +static void parse_hdlc(frame_t *st, void (*process)(frame_t *, uint8_t *, unsigned int, logical_channel_t), uint8_t *buffer, int *bufidx, int bufsz, uint8_t *input, size_t inlen, logical_channel_t lc) +{ + for (size_t i = 0; i < inlen; i++) + { + uint8_t byte = input[i]; + if (byte == 0x7E) + { + if (*bufidx >= 0) + process(st, buffer, *bufidx, lc); + *bufidx = 0; + } + else if (*bufidx >= 0) + { + if (*bufidx == bufsz) + { + log_error("HDLC buffer overflow"); + *bufidx = -1; + continue; + } + buffer[(*bufidx)++] = byte; + } + } +} + +static void process_fixed_ccc(frame_t *st, uint8_t *buf, unsigned int buflen, logical_channel_t lc) +{ + ccc_data_t *ccc_data = &st->ccc_data[lc]; + buflen = unescape_hdlc(buf, buflen); + + // padding + if (buflen == 0) + return; + + // ignore new CCC packets (XXX they shouldn't change) + if (ccc_data->fixed_ready) + return; + + if (fcs16(buf, buflen) != VALIDFCS16) + { + log_info("bad CCC checksum"); + return; + } + + for (unsigned int i = 0; i < 4; i++) + { + fixed_subchannel_t *subch = &ccc_data->subchannel[i]; + subch->mode = 0; + subch->length = 0; + + if (5 + i * 4 <= buflen) + { + uint16_t mode = buf[1 + i * 4] | (buf[2 + i * 4] << 8); + uint16_t length = buf[3 + i * 4] | (buf[4 + i * 4] << 8); + + if (mode == 0) + { + subch->mode = mode; + subch->length = length; + subch->block_idx = 0; + subch->idx = -1; + } + else + { + log_warn("Subchannel mode %04X not supported", mode); + } + } + } + + ccc_data->fixed_ready = 1; +} + +/* FIXME: We only support mode=0 (no FEC, no interleaving) */ +static void process_fixed_block(frame_t *st, int i, logical_channel_t lc) +{ + ccc_data_t *ccc_data = &st->ccc_data[lc]; + fixed_subchannel_t *subch = &ccc_data->subchannel[i]; + parse_hdlc(st, aas_push, subch->data, &subch->idx, MAX_AAS_LEN, &subch->blocks[4], 255, lc); +} + +static unsigned int sync_width(uint8_t byte) +{ + if (byte == 0x00) + return 1; + else if ((byte >> 4) == (byte & 0xf)) + return (byte & 0xf) * 2; + else + return 0; // invalid +} + +static size_t process_fixed_data(frame_t *st, size_t length, logical_channel_t lc) +{ + ccc_data_t *ccc_data = &st->ccc_data[lc]; + + static const uint8_t bbm[] = { 0x7D, 0x3A, 0xE2, 0x42 }; + uint8_t *p = &st->buffer[length - 1]; + + if (ccc_data->sync_count < 2) + { + unsigned int width = sync_width(*p); + if ((width > 0) && (ccc_data->sync_width == width)) + ccc_data->sync_count++; + else + ccc_data->sync_count = 0; + ccc_data->sync_width = width; + + if (ccc_data->sync_count < 2) + return p - st->buffer; + } + + p -= ccc_data->sync_width; + parse_hdlc(st, process_fixed_ccc, ccc_data->ccc_buf, &ccc_data->ccc_idx, sizeof(ccc_data->ccc_buf), p, ccc_data->sync_width, lc); + + // wait until we have subchannel information + if (!ccc_data->fixed_ready) + return p - st->buffer; + + for (int i = 3; i >= 0; i--) + { + fixed_subchannel_t *subch = &ccc_data->subchannel[i]; + int length = subch->length; + + if (length == 0) + continue; + + p -= length; + for (int j = 0; j < length; j++) + { + subch->blocks[subch->block_idx++] = p[j]; + if (subch->block_idx == 4 && memcmp(subch->blocks, bbm, sizeof(bbm)) != 0) + { + // mis-aligned, skip a byte + memmove(subch->blocks, subch->blocks + 1, 3); + subch->block_idx--; + } + + if (subch->block_idx == 255 + 4) + { + // we have a complete block, deinterleave and process + process_fixed_block(st, i, lc); + subch->block_idx = 0; + } + } + } + + return p - st->buffer; +} + +void frame_process(frame_t *st, size_t length, logical_channel_t lc, unsigned int pci) +{ + unsigned int offset = 0; + unsigned int audio_end = length; + + if (has_fixed(pci)) + audio_end = process_fixed_data(st, length, lc); + + if (!has_audio(pci)) + return; + + while (offset < audio_end - RS_CODEWORD_LEN) + { + unsigned int start = offset; + unsigned int j, lc_bits, loc_bytes, prog, avg, seq, output_offset; + unsigned short locations[MAX_AUDIO_PACKETS]; + frame_header_t hdr = {0}; + hef_t hef = {0}; + + if (!fix_header(st, st->buffer + offset)) + return; + + parse_header(st->buffer + offset, &hdr); + offset += 14; + lc_bits = calc_lc_bits(&hdr); + loc_bytes = ((lc_bits * hdr.nop) + 4) / 8; + if (start + hdr.la_location + 1 < offset + loc_bytes || start + hdr.la_location >= audio_end) + return; + + for (j = 0; j < hdr.nop; j++) + { + locations[j] = parse_location(st->buffer + offset, lc_bits, j); + if (j == 0 && locations[j] <= hdr.la_location) return; + if (j > 0 && locations[j] <= locations[j-1]) return; + if (start + locations[j] >= audio_end) return; + } + offset += loc_bytes; + + if (hdr.stream_id >= MAX_STREAMS) + { + log_warn("invalid stream_id: %d", hdr.stream_id); + offset = start + locations[hdr.nop - 1] + 1; + continue; + } + + if (hdr.hef) + offset += parse_hef(st->buffer + offset, audio_end - offset, &hef); + prog = hef.prog_num; + audio_service_t *service = &st->services[prog]; + + if ((hdr.stream_id == 0) && ( + (service->access != (int) hef.access) || + (service->type != (int) hef.prog_type) || + (service->codec_mode != (int) hdr.codec_mode) || + (service->blend_control != (int) hdr.blend_control) || + (service->digital_audio_gain != (int) hdr.per_stream_delay) || + (service->common_delay != (int) hdr.common_delay) || + (service->latency != (int) hdr.latency) + )) + { + service->access = hef.access; + service->type = hef.prog_type; + service->codec_mode = hdr.codec_mode; + service->blend_control = hdr.blend_control; + service->digital_audio_gain = hdr.per_stream_delay; + service->common_delay = hdr.common_delay; + service->latency = hdr.latency; + + nrsc5_report_audio_service( + st->input->radio, + prog, + service->access, + service->type, + service->codec_mode, + service->blend_control, + (service->digital_audio_gain < 16) ? service->digital_audio_gain : (service->digital_audio_gain - 32), + service->common_delay * 4, + service->latency * 2 + ); + } + + avg = calc_avg_packets(&hdr); + seq = (ELASTIC_BUFFER_LEN + hdr.seq - hdr.pfirst) % ELASTIC_BUFFER_LEN; + + output_offset = (ELASTIC_BUFFER_LEN + (hdr.pdu_seq * avg) - (hdr.latency * 2)) % ELASTIC_BUFFER_LEN; + if (((ELASTIC_BUFFER_LEN + seq - output_offset) % ELASTIC_BUFFER_LEN) >= (ELASTIC_BUFFER_LEN / 2)) + output_offset = (output_offset + (ELASTIC_BUFFER_LEN / 2)) % ELASTIC_BUFFER_LEN; + + output_align(st->input->output, prog, hdr.stream_id, output_offset); + + parse_hdlc(st, aas_push, st->psd_buf[prog], &st->psd_idx[prog], MAX_AAS_LEN, st->buffer + offset, start + hdr.la_location + 1 - offset, lc); + offset = start + hdr.la_location + 1; + + for (j = 0; j < hdr.nop; ++j) + { + unsigned int cnt = start + locations[j] - offset; + uint8_t crc = crc8(st->buffer + offset, cnt + 1); + + packet_ref_t ref; + ref.program = prog; + ref.stream_id = hdr.stream_id; + ref.data = st->buffer + offset; + ref.size = cnt; + ref.seq = seq; + ref.flags = PACKET_FLAG_NONE; + + if (crc != 0) + ref.flags |= PACKET_FLAG_CRC_ERROR; + if (j == 0 && hdr.pfirst) + ref.shape = PACKET_HALF_BACK; + else if (j == hdr.nop - 1 && hdr.plast) + ref.shape = PACKET_HALF_FRONT; + else + ref.shape = PACKET_FULL; + + output_push(st->input->output, &ref); + + offset += cnt + 1; + seq = (seq + 1) % ELASTIC_BUFFER_LEN; + } + } + +} + +static unsigned int pop_count(unsigned int n) +{ + int c = 0; + for (; n; ++c) + n &= n - 1; + return c; +} + +static int fuzzy_pci(const unsigned int pci, const unsigned int pci_len, unsigned int* match) +{ + for (int i = 0; i < PCI_COUNT; i++) + { + const unsigned int score = pop_count((pci ^ PCI_POSSIBILITIES[i]) >> (24 - pci_len)); + if (score <= PCI_MAX_ERRORS) { + *match = PCI_POSSIBILITIES[i]; + return (int) score; + } + } + return -1; +} + +void frame_push(frame_t *st, uint8_t *bits, size_t length, logical_channel_t lc) +{ + unsigned int start, offset, pci_len; + unsigned int i, j = 0, h = 0, header = 0, val = 0; + uint8_t *ptr = st->buffer; + + switch (length) + { + case P1_FRAME_LEN_FM: + start = P1_FRAME_LEN_FM - 30000; + offset = 1248; + pci_len = 24; + break; + case P3_FRAME_LEN_MP3_MP11: + start = 120; + offset = 184; + pci_len = 24; + break; + case P3_FRAME_LEN_MP2: + start = 120; + offset = 88; + pci_len = 24; + break; + case P1_FRAME_LEN_AM: + start = 120; + offset = 160; + pci_len = 22; + break; + case P3_FRAME_LEN_MA1: + start = 120; + offset = 992; + pci_len = 24; + break; + case P3_FRAME_LEN_MA3: + start = 120; + offset = 1240; + pci_len = 24; + break; + default: + log_error("Unknown frame length: %u", (unsigned int)length); + return; + } + + for (i = 0; i < length; ++i) + { + // swap bit order + unsigned int byte_start = (i>>3)<<3; + unsigned int byte_len = (length - byte_start < 8) ? length - byte_start : 8; + uint8_t bit = bits[byte_start + byte_len - 1 - (i & 7)]; + + if (i >= start && ((i - start) % offset) == 0 && h < pci_len) + { + header |= bit << (23 - h); + ++h; + } + else + { + val |= bit << (7 - j); + if (++j == 8) + { + *ptr++ = val; + val = 0; + j = 0; + } + } + } + + unsigned int pci; + if (fuzzy_pci(header, pci_len, &pci) < 0) + { + if (lc == P1_LOGICAL_CHANNEL) + input_set_sync_state(st->input, SYNC_STATE_NONE); + return; + } + + frame_process(st, ptr - st->buffer, lc, pci); +} + +void frame_reset(frame_t *st) +{ + for (int prog = 0; prog < MAX_PROGRAMS; prog++) + { + st->services[prog].access = -1; + st->services[prog].type = -1; + st->services[prog].codec_mode = -1; + st->services[prog].blend_control = -1; + st->services[prog].digital_audio_gain = -1; + st->services[prog].common_delay = -1; + st->services[prog].latency = -1; + } + + for (int prog = 0; prog < MAX_PROGRAMS; prog++) + { + st->psd_idx[prog] = -1; + } + + for (int channel = 0; channel < NUM_LOGICAL_CHANNELS; channel++) + { + st->ccc_data[channel].fixed_ready = 0; + st->ccc_data[channel].sync_width = 0; + st->ccc_data[channel].sync_count = 0; + st->ccc_data[channel].ccc_idx = -1; + } +} + +void frame_init(frame_t *st, input_t *input) +{ + st->input = input; + st->rs_dec = init_rs_char(8, 0x11d, 1, 1, 8); + frame_reset(st); +} + +void frame_free(frame_t *st) +{ + free_rs_char(st->rs_dec); +} diff --git a/third_party/nrsc5/upstream/src/frame.h b/third_party/nrsc5/upstream/src/frame.h new file mode 100644 index 000000000..c32edc20e --- /dev/null +++ b/third_party/nrsc5/upstream/src/frame.h @@ -0,0 +1,56 @@ +#pragma once + +#include "defines.h" + +#define MAX_AAS_LEN 8212 +#define RS_BLOCK_LEN 255 +#define RS_CODEWORD_LEN 96 + +typedef struct +{ + int access; + int type; + int codec_mode; + int blend_control; + int digital_audio_gain; + int common_delay; + int latency; +} audio_service_t; + +typedef struct +{ + uint16_t mode; + uint16_t length; + unsigned int block_idx; + uint8_t blocks[255 + 4]; + int idx; + uint8_t data[MAX_AAS_LEN]; +} fixed_subchannel_t; + +typedef struct +{ + unsigned int sync_width; + unsigned int sync_count; + uint8_t ccc_buf[32]; + int ccc_idx; + fixed_subchannel_t subchannel[4]; + int fixed_ready; +} ccc_data_t; + +typedef struct +{ + struct input_t *input; + uint8_t buffer[MAX_PDU_LEN]; + audio_service_t services[MAX_PROGRAMS]; + unsigned int program; + uint8_t psd_buf[MAX_PROGRAMS][MAX_AAS_LEN]; + int psd_idx[MAX_PROGRAMS]; + ccc_data_t ccc_data[NUM_LOGICAL_CHANNELS]; + void *rs_dec; +} frame_t; + +void frame_push(frame_t *st, uint8_t *bits, size_t length, logical_channel_t lc); +void frame_reset(frame_t *st); +void frame_set_program(frame_t *st, unsigned int program); +void frame_init(frame_t *st, struct input_t *input); +void frame_free(frame_t *st); diff --git a/third_party/nrsc5/upstream/src/here_images.c b/third_party/nrsc5/upstream/src/here_images.c new file mode 100644 index 000000000..a0412ff2f --- /dev/null +++ b/third_party/nrsc5/upstream/src/here_images.c @@ -0,0 +1,146 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "here_images.h" +#include "private.h" + +static void process_packet(here_images_t *st) +{ + if (st->payload_len < 28) + { + log_warn("HERE Image frame too short"); + return; + } + + int image_type = (st->buffer[0] >> 4); + int seq = (st->buffer[0] & 0x0f); + + if ((image_type != NRSC5_HERE_IMAGE_TRAFFIC) && (image_type != NRSC5_HERE_IMAGE_WEATHER)) + { + log_warn("Unknown HERE Image type: %d", image_type); + return; + } + + int n1 = (st->buffer[2] << 8) | st->buffer[3]; + int n2 = (st->buffer[4] << 8) | st->buffer[5]; + unsigned int timestamp = ((unsigned int)st->buffer[9] << 24) | (st->buffer[10] << 16) + | (st->buffer[11] << 8) | st->buffer[12]; + + int lat1 = ((st->buffer[14] & 0x7f) << 18) | (st->buffer[15] << 10) | (st->buffer[16] << 2) | (st->buffer[17] >> 6); + if (st->buffer[14] & 0x80) + lat1 = -lat1; + + int lon1 = ((st->buffer[17] & 0x1f) << 20) | (st->buffer[18] << 12) | (st->buffer[19] << 4) | (st->buffer[20] >> 4); + if (st->buffer[17] & 0x20) + lon1 = -lon1; + + int lat2 = ((st->buffer[20] & 0x07) << 22) | (st->buffer[21] << 14) | (st->buffer[22] << 6) | (st->buffer[23] >> 2); + if (st->buffer[20] & 0x08) + lat2 = -lat2; + + int lon2 = ((st->buffer[23] & 0x01) << 24) | (st->buffer[24] << 16) | (st->buffer[25] << 8) | st->buffer[26]; + if (st->buffer[23] & 0x02) + lon2 = -lon2; + + int filename_len = st->buffer[27]; + + if (st->payload_len < 34 + filename_len) + { + log_warn("HERE Image frame too short"); + return; + } + + int file_len = (st->buffer[32 + filename_len] << 8) | st->buffer[33 + filename_len]; + + if (st->payload_len < 34 + filename_len + file_len) + { + log_warn("HERE Image frame too short"); + return; + } + + st->buffer[28 + filename_len] = '\0'; + + int timestamp_index = 0; + if (image_type == NRSC5_HERE_IMAGE_TRAFFIC) + { + if ((n1 >= 1) && (n1 <= HERE_TRAFFIC_TILES)) + { + timestamp_index = n1; + } + else + { + log_warn("Invalid traffic tile number: %d", n1); + return; + } + } + + if (st->last_timestamp[timestamp_index] != timestamp) + { + nrsc5_report_here_image(st->radio, image_type, seq, n1, n2, timestamp, + lat1 / 100000.f, lon1 / 100000.f, lat2 / 100000.f, lon2 / 100000.f, + (char *)&st->buffer[28], file_len, &st->buffer[34 + filename_len]); + st->last_timestamp[timestamp_index] = timestamp; + } +} + +void here_images_push(here_images_t *st, uint16_t seq, unsigned int len, uint8_t *buf) +{ + if (seq != st->expected_seq) + { + memset(st->buffer, 0, sizeof(st->buffer)); + st->payload_len = -1; + st->sync_state = 0; + } + + for (unsigned int offset = 0; offset < len; offset++) + { + st->sync_state <<= 8; + st->sync_state |= buf[offset]; + + if (st->payload_len == -1) // waiting for sync + { + if (((st->sync_state >> 16) & 0xffffffff) == 0xfff7fff7) + { + st->payload_len = st->sync_state & 0xffff; + st->buffer_idx = 0; + } + } + else + { + st->buffer[st->buffer_idx++] = buf[offset]; + if (st->buffer_idx == (st->payload_len + 2)) + { + process_packet(st); + st->payload_len = -1; + } + } + } + + st->expected_seq = (seq + 1) & 0xffff; +} + +void here_images_reset(here_images_t *st) +{ + st->expected_seq = -1; + memset(st->last_timestamp, 0, sizeof(st->last_timestamp)); +} + +void here_images_init(here_images_t *st, nrsc5_t *radio) +{ + st->radio = radio; +} diff --git a/third_party/nrsc5/upstream/src/here_images.h b/third_party/nrsc5/upstream/src/here_images.h new file mode 100644 index 000000000..98bff2587 --- /dev/null +++ b/third_party/nrsc5/upstream/src/here_images.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#define MAX_PAYLOAD_BYTES (65536 + 2) +#define HERE_TRAFFIC_TILES 9 + +typedef struct +{ + nrsc5_t *radio; + uint8_t buffer[MAX_PAYLOAD_BYTES]; + int buffer_idx; + int expected_seq; + int payload_len; + uint64_t sync_state; + unsigned int last_timestamp[1 + HERE_TRAFFIC_TILES]; +} here_images_t; + +void here_images_push(here_images_t *st, uint16_t seq, unsigned int len, uint8_t *buf); +void here_images_reset(here_images_t *st); +void here_images_init(here_images_t *st, nrsc5_t *radio); diff --git a/third_party/nrsc5/upstream/src/input.c b/third_party/nrsc5/upstream/src/input.c new file mode 100644 index 000000000..40d63c6b5 --- /dev/null +++ b/third_party/nrsc5/upstream/src/input.c @@ -0,0 +1,208 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "defines.h" +#include "input.h" +#include "private.h" + +/* + * GNU Radio Filter Design Tool + * FIR, Low Pass, Kaiser Window + * Sample rate: 1488375 + * End of pass band: 372094 + * Start of stop band: 530000 + * Stop band attenuation: 40 + */ +static float decim_taps[] = { + 0.6062333583831787, + 0, + -0.13481467962265015, + 0, + 0.032919470220804214, + 0, + -0.00410953676328063 +}; + +void input_push(input_t *st, const float complex* buf, const uint32_t length) +{ + unsigned int consumed = 0; + + while (consumed < length) + { + consumed += acquire_push(&st->acq, buf + consumed, length - consumed); + acquire_process(&st->acq); + } +} + +unsigned int decimate_samples(input_t *st, const uint8_t* in, const uint32_t len, float complex *out) +{ + unsigned int avail = 0; + + for (uint32_t i = 0; i < len; i += 4) + { + float complex x[2]; + + x[0] = CMPLXF(U8_F(in[i]), U8_F(in[i + 1])); + x[1] = CMPLXF(U8_F(in[i + 2]), U8_F(in[i + 3])); + + if (st->radio->mode == NRSC5_MODE_FM) + { + halfband_cf32_execute(st->decim[0], x, &out[avail++]); + } + else + { + halfband_cf32_execute(st->decim[0], x, &st->stages[0][st->offset & 1]); + if ((st->offset & 0x1) == 0x1) { + halfband_cf32_execute(st->decim[1], st->stages[0], &st->stages[1][(st->offset >> 1) & 1]); + } + if ((st->offset & 0x3) == 0x3) { + halfband_cf32_execute(st->decim[2], st->stages[1], &st->stages[2][(st->offset >> 2) & 1]); + } + if ((st->offset & 0x7) == 0x7) { + halfband_cf32_execute(st->decim[3], st->stages[2], &st->stages[3][(st->offset >> 3) & 1]); + } + if ((st->offset & 0xf) == 0xf) { + halfband_cf32_execute(st->decim[4], st->stages[3], &out[avail++]); + } + st->offset++; + } + } + + return avail; +} + +void input_push_cu8(input_t *st, const uint8_t *buf, const uint32_t len) +{ + float complex out[FFTCP_FM]; + uint32_t consumed = 0; + + nrsc5_report_iq(st->radio, buf, len); + + assert(len % 4 == 0); + + while (consumed < len) + { + const uint32_t left = len - consumed; + const uint32_t min = st->resample_input_size > left ? left : st->resample_input_size; + + assert(min % 4 == 0); + + const unsigned int avail = decimate_samples(st, buf + consumed, min, out); + input_push(st, out, avail); + + consumed += min; + } +} + +void input_push_cs16(input_t *st, const int16_t *buf, const uint32_t len) +{ + float complex out[FFTCP_FM]; + uint32_t consumed = 0; + + assert(len % 2 == 0); + + while (consumed < len) + { + const uint32_t left = len - consumed; + const uint32_t min = (FFTCP_FM * 2) > left ? left : (FFTCP_FM * 2); + const cint16_t* in = (cint16_t*) (buf + consumed); + + for (uint32_t i = 0; i < min / 2; ++i) + { + out[i] = cq15_to_cf(in[i]); + } + + input_push_cf32(st, (float*) out, min); + + consumed += min; + } +} + +void input_push_cf32(input_t *st, const float *buf, const uint32_t len) +{ + assert(len % 2 == 0); + + input_push(st, (float complex*) buf, len / 2); +} + +void input_reset(input_t *st) +{ + st->offset = 0; + st->resample_input_size = st->radio->mode == NRSC5_MODE_FM ? (FFTCP_FM * 2) : (FFTCP_AM * 32); + + input_set_sync_state(st, SYNC_STATE_NONE); + for (int i = 0; i < AM_DECIM_STAGES; i++) + firdecim_cf32_reset(st->decim[i]); + acquire_reset(&st->acq); + decode_reset(&st->decode); + frame_reset(&st->frame); + sync_reset(&st->sync); +} + +void input_init(input_t *st, nrsc5_t *radio, output_t *output) +{ + st->radio = radio; + st->output = output; + st->sync_state = SYNC_STATE_NONE; + + for (int i = 0; i < AM_DECIM_STAGES; i++) + st->decim[i] = firdecim_cf32_create(decim_taps, sizeof(decim_taps) / sizeof(decim_taps[0])); + + acquire_init(&st->acq, st); + decode_init(&st->decode, st); + frame_init(&st->frame, st); + sync_init(&st->sync, st); + + input_reset(st); +} + +void input_set_mode(input_t *st) +{ + acquire_set_mode(&st->acq, st->radio->mode); + input_reset(st); +} + +void input_free(input_t *st) +{ + acquire_free(&st->acq); + frame_free(&st->frame); + + for (int i = 0; i < AM_DECIM_STAGES; i++) + firdecim_cf32_free(st->decim[i]); +} + +void input_set_sync_state(input_t *st, unsigned int new_state) +{ + if (st->sync_state == new_state) + return; + + if (st->sync_state == SYNC_STATE_FINE) + nrsc5_report_lost_sync(st->radio); + if (new_state == SYNC_STATE_FINE) + { + float freq_offset = (st->acq.prev_angle - 2 * M_PI * st->acq.cfo) + * (st->radio->mode == NRSC5_MODE_FM ? NRSC5_SAMPLE_RATE_CS16_FM : NRSC5_SAMPLE_RATE_CS16_AM) + / (2 * M_PI * st->acq.fft); + nrsc5_report_sync(st->radio, freq_offset, st->sync.psmi, st->sync.pli, st->sync.hppi, st->sync.aabi, st->sync.rdbi); + } + + st->sync_state = new_state; +} diff --git a/third_party/nrsc5/upstream/src/input.h b/third_party/nrsc5/upstream/src/input.h new file mode 100644 index 000000000..3b80a41f8 --- /dev/null +++ b/third_party/nrsc5/upstream/src/input.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include + +#include "acquire.h" +#include "decode.h" +#include "defines.h" +#include "frame.h" +#include "firdecim_cf32.h" +#include "output.h" +#include "sync.h" + +#define AM_DECIM_STAGES 5 + +enum { SYNC_STATE_NONE, SYNC_STATE_COARSE, SYNC_STATE_FINE }; + +typedef struct input_t +{ + nrsc5_t *radio; + output_t *output; + + firdecim_cf32 decim[AM_DECIM_STAGES]; + float complex stages[AM_DECIM_STAGES][2]; + unsigned int resample_input_size; + unsigned int offset; + unsigned int sync_state; + + acquire_t acq; + decode_t decode; + frame_t frame; + sync_t sync; +} input_t; + +void input_init(input_t *st, nrsc5_t *radio, output_t *output); +void input_set_mode(input_t *st); +void input_reset(input_t *st); +void input_free(input_t *st); +void input_set_sync_state(input_t *st, unsigned int new_state); +void input_push_cu8(input_t *st, const uint8_t *buf, uint32_t len); +void input_push_cs16(input_t *st, const int16_t *buf, uint32_t len); +void input_push_cf32(input_t *st, const float *buf, uint32_t len); diff --git a/third_party/nrsc5/upstream/src/libnrsc5.map b/third_party/nrsc5/upstream/src/libnrsc5.map new file mode 100644 index 000000000..cf65c5ca4 --- /dev/null +++ b/third_party/nrsc5/upstream/src/libnrsc5.map @@ -0,0 +1,31 @@ +LIBNRSC5_1.0 { + global: + nrsc5_get_version; + nrsc5_service_data_type_name; + nrsc5_program_type_name; + nrsc5_alert_category_name; + nrsc5_open; + nrsc5_open_file; + nrsc5_open_pipe; + nrsc5_open_rtltcp; + nrsc5_close; + nrsc5_start; + nrsc5_stop; + nrsc5_set_mode; + nrsc5_set_bias_tee; + nrsc5_set_direct_sampling; + nrsc5_set_freq_correction; + nrsc5_get_frequency; + nrsc5_set_frequency; + nrsc5_get_gain; + nrsc5_set_gain; + nrsc5_set_auto_gain; + nrsc5_set_callback; + nrsc5_pipe_samples_cu8; + nrsc5_pipe_samples_cs16; + nrsc5_pipe_samples_cf32; + nrsc5_reset; + + local: + *; +}; diff --git a/third_party/nrsc5/upstream/src/libnrsc5.sym b/third_party/nrsc5/upstream/src/libnrsc5.sym new file mode 100644 index 000000000..2b04e2d9e --- /dev/null +++ b/third_party/nrsc5/upstream/src/libnrsc5.sym @@ -0,0 +1,25 @@ +_nrsc5_get_version +_nrsc5_service_data_type_name +_nrsc5_program_type_name +_nrsc5_alert_category_name +_nrsc5_open +_nrsc5_open_file +_nrsc5_open_pipe +_nrsc5_open_rtltcp +_nrsc5_close +_nrsc5_start +_nrsc5_stop +_nrsc5_set_mode +_nrsc5_set_bias_tee +_nrsc5_set_direct_sampling +_nrsc5_set_freq_correction +_nrsc5_get_frequency +_nrsc5_set_frequency +_nrsc5_get_gain +_nrsc5_set_gain +_nrsc5_set_auto_gain +_nrsc5_set_callback +_nrsc5_pipe_samples_cu8 +_nrsc5_pipe_samples_cs16 +_nrsc5_pipe_samples_cf32 +_nrsc5_reset diff --git a/third_party/nrsc5/upstream/src/log.c b/third_party/nrsc5/upstream/src/log.c new file mode 100644 index 000000000..493ffd189 --- /dev/null +++ b/third_party/nrsc5/upstream/src/log.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 rxi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "log.h" + +static struct { + void *udata; + log_LockFn lock; + FILE *fp; + int level; + int quiet; +} L; + + +static void lock(void) { + if (L.lock) { + L.lock(L.udata, 1); + } +} + + +static void unlock(void) { + if (L.lock) { + L.lock(L.udata, 0); + } +} + + +void log_set_udata(void *udata) { + L.udata = udata; +} + + +void log_set_lock(log_LockFn fn) { + L.lock = fn; +} + + +void log_set_level(int level) { + L.level = level; +} + + +void log_set_quiet(int enable) { + L.quiet = enable ? 1 : 0; +} + + +void log_log(int level, const char *fmt, ...) { + if (level < L.level) { + return; + } + + /* Acquire lock */ + lock(); + + /* Get current time */ + time_t t = time(NULL); + struct tm *lt = localtime(&t); + + /* Log to stderr */ + if (!L.quiet) { + va_list args; + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; + fprintf(stderr, "%s ", buf); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + /* XXX required for correct output on Windows */ + fflush(stderr); + } + + /* Release lock */ + unlock(); +} diff --git a/third_party/nrsc5/upstream/src/log.h b/third_party/nrsc5/upstream/src/log.h new file mode 100644 index 000000000..6e560140e --- /dev/null +++ b/third_party/nrsc5/upstream/src/log.h @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2017 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `log.c` for details. + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include + +#define LOG_VERSION "0.1.0" + +typedef void (*log_LockFn)(void *udata, int lock); + +enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; + +#define log_trace(...) log_log(LOG_TRACE, __VA_ARGS__) +#define log_debug(...) log_log(LOG_DEBUG, __VA_ARGS__) +#define log_info(...) log_log(LOG_INFO, __VA_ARGS__) +#define log_warn(...) log_log(LOG_WARN, __VA_ARGS__) +#define log_error(...) log_log(LOG_ERROR, __VA_ARGS__) +#define log_fatal(...) log_log(LOG_FATAL, __VA_ARGS__) + +void log_set_udata(void *udata); +void log_set_lock(log_LockFn fn); +void log_set_level(int level); +void log_set_quiet(int enable); + +void log_log(int level, const char *fmt, ...); + +#endif diff --git a/third_party/nrsc5/upstream/src/main.c b/third_party/nrsc5/upstream/src/main.c new file mode 100644 index 000000000..960697a97 --- /dev/null +++ b/third_party/nrsc5/upstream/src/main.c @@ -0,0 +1,1174 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef __MINGW32__ +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#include "bitwriter.h" +#include "log.h" + +#define AUDIO_BUFFERS 16 +#define AUDIO_DATA_LENGTH 8192 +#define FILE_BUFFER_LENGTH 32768 +#define STDIN_POLL_RATE_MS 100 + +typedef struct buffer_t { + struct buffer_t *next; + // The samples are signed 16-bit integers, but ao_play requires a char buffer. + char data[AUDIO_DATA_LENGTH]; +} audio_buffer_t; + +enum iq_format { + IQ_FORMAT_NONE, + IQ_FORMAT_CU8, + IQ_FORMAT_CS16, + IQ_FORMAT_CF32, +}; + +typedef struct { + float freq; + int mode; + float gain; + unsigned int device_index; + int bias_tee; + int direct_sampling; + int ppm_error; + char *input_name; + char *rtltcp_host; + ao_device *dev; + FILE *hdc_file; + FILE *iq_file; + char *aas_files_path; + enum iq_format iq_input_format; + + audio_buffer_t *head, *tail, *free; + pthread_mutex_t mutex; + pthread_cond_t cond; + + unsigned int program; + unsigned int audio_packets_valid; + unsigned int audio_packets; + unsigned int audio_bytes; + unsigned int audio_errors; + int done; +} state_t; + +static ao_sample_format sample_format = { + 16, + NRSC5_SAMPLE_RATE_AUDIO, + 2, + AO_FMT_NATIVE, + "L,R" +}; + +static ao_device *open_ao_live(void) +{ + return ao_open_live(ao_default_driver_id(), &sample_format, NULL); +} + +static ao_device *open_ao_file(const char *name, const char *type) +{ + return ao_open_file(ao_driver_id(type), name, 1, &sample_format, NULL); +} + +static void reset_audio_buffers(state_t *st) +{ + audio_buffer_t *b; + + // find the end of the head list + for (b = st->head; b && b->next; b = b->next) { } + + // if the head list is non-empty, prepend to free list + if (b != NULL) + { + b->next = st->free; + st->free = st->head; + } + + st->head = NULL; + st->tail = NULL; +} + +static void push_audio_buffer(state_t *st, unsigned int program, const int16_t *data, size_t count, unsigned int flags) +{ + audio_buffer_t *b; + + pthread_mutex_lock(&st->mutex); + if (program != st->program) + goto unlock; + + if (flags & NRSC5_AUDIO_FLAGS_DECODING_ERROR) + log_warn("Audio decoding error"); + + if (st->input_name) + { + while (st->free == NULL) + pthread_cond_wait(&st->cond, &st->mutex); + } + else + { + if (st->free == NULL) + { + log_warn("Audio output queue full, dropping samples"); + goto unlock; + } + } + + b = st->free; + st->free = b->next; + + assert(AUDIO_DATA_LENGTH == count * sizeof(data[0])); + memcpy(b->data, data, count * sizeof(data[0])); + + b->next = NULL; + if (st->tail) + st->tail->next = b; + else + st->head = b; + st->tail = b; + + pthread_cond_signal(&st->cond); + +unlock: + pthread_mutex_unlock(&st->mutex); +} + +static void init_audio_buffers(state_t *st) +{ + st->head = NULL; + st->tail = NULL; + st->free = NULL; + + for (int i = 0; i < AUDIO_BUFFERS; ++i) + { + audio_buffer_t *b = malloc(sizeof(audio_buffer_t)); + b->next = st->free; + st->free = b; + } + + pthread_cond_init(&st->cond, NULL); + pthread_mutex_init(&st->mutex, NULL); +} + +static void write_adts_header(FILE *fp, unsigned int len) +{ + uint8_t hdr[7]; + bitwriter_t bw; + + bw_init(&bw, hdr); + bw_addbits(&bw, 0xFFF, 12); // sync word + bw_addbits(&bw, 0, 1); // MPEG-4 + bw_addbits(&bw, 0, 2); // Layer + bw_addbits(&bw, 1, 1); // no CRC + bw_addbits(&bw, 1, 2); // AAC-LC + bw_addbits(&bw, 7, 4); // 22050 HZ + bw_addbits(&bw, 0, 1); // private bit + bw_addbits(&bw, 2, 3); // 2-channel configuration + bw_addbits(&bw, 0, 1); + bw_addbits(&bw, 0, 1); + bw_addbits(&bw, 0, 1); + bw_addbits(&bw, 0, 1); + bw_addbits(&bw, len + 7, 13); // frame length + bw_addbits(&bw, 0x7FF, 11); // buffer fullness (VBR) + bw_addbits(&bw, 0, 2); // 1 AAC frame per ADTS frame + + fwrite(hdr, 7, 1, fp); +} + +static void dump_hdc(FILE *fp, const uint8_t *pkt, unsigned int len) +{ + write_adts_header(fp, len); + fwrite(pkt, len, 1, fp); + fflush(fp); +} + +static void dump_aas_file(state_t *st, const nrsc5_event_t *evt) +{ +#if defined(WIN32) || defined(_WIN32) +#define PATH_SEPARATOR "\\" +#else +#define PATH_SEPARATOR "/" +#endif + + const char *name; + const uint8_t *data; + unsigned int size; + unsigned int number; + + switch (evt->event) + { + case NRSC5_EVENT_LOT: + name = evt->lot.name; + data = evt->lot.data; + size = evt->lot.size; + number = evt->lot.lot; + break; + case NRSC5_EVENT_HERE_IMAGE: + name = evt->here_image.name; + data = evt->here_image.data; + size = evt->here_image.size; +#if defined(WIN32) || defined(_WIN32) + number = _mkgmtime64(evt->here_image.time_utc); +#else + number = timegm(evt->here_image.time_utc); +#endif + break; + default: + log_error("invalid event type"); + return; + } + + char fullpath[strlen(st->aas_files_path) + strlen(name) + 16]; + FILE *fp; + + sprintf(fullpath, "%s" PATH_SEPARATOR "%u_%s", st->aas_files_path, number, name); + fp = fopen(fullpath, "wb"); + if (fp == NULL) + { + log_warn("Failed to open %s (%d)", fullpath, errno); + return; + } + fwrite(data, 1, size, fp); + fclose(fp); +} + +static void dump_ber(float cber) +{ + static float min = 1, max = 0, sum = 0, count = 0; + sum += cber; + count += 1; + if (cber < min) min = cber; + if (cber > max) max = cber; + log_info("BER: %f, avg: %f, min: %f, max: %f", cber, sum / count, min, max); +} + +static void done_signal(state_t *st) +{ + pthread_mutex_lock(&st->mutex); + st->done = 1; + pthread_cond_signal(&st->cond); + pthread_mutex_unlock(&st->mutex); +} + +static int is_done(state_t *st) +{ + int done; + + pthread_mutex_lock(&st->mutex); + done = st->done; + pthread_mutex_unlock(&st->mutex); + + return done; +} + +static void change_program(state_t *st, unsigned int program) +{ + pthread_mutex_lock(&st->mutex); + + // reset audio buffers + if (st->tail) + { + st->tail->next = st->free; + st->free = st->head; + st->head = st->tail = NULL; + } + // update current program + st->program = program; + + pthread_mutex_unlock(&st->mutex); +} + +static void callback(const nrsc5_event_t *evt, void *opaque) +{ + state_t *st = opaque; + nrsc5_sig_service_t *sig_service; + nrsc5_sig_component_t *sig_component; + nrsc5_id3_comment_t *comment; + const char *name; + char time_str[64]; + + switch (evt->event) + { + case NRSC5_EVENT_LOST_DEVICE: + done_signal(st); + break; + case NRSC5_EVENT_AGC: + if (evt->agc.is_final) + log_info("Best gain: %.1f dB, Peak amplitude: %.1f dBFS", evt->agc.gain_db, evt->agc.peak_dbfs); + else + log_debug("Gain: %.1f dB, Peak amplitude: %.1f dBFS", evt->agc.gain_db, evt->agc.peak_dbfs); + break; + case NRSC5_EVENT_BER: + dump_ber(evt->ber.cber); + break; + case NRSC5_EVENT_MER: + log_info("MER: %.1f dB (lower), %.1f dB (upper)", evt->mer.lower, evt->mer.upper); + break; + case NRSC5_EVENT_IQ: + if (st->iq_file) + fwrite(evt->iq.data, 1, evt->iq.count, st->iq_file); + break; + case NRSC5_EVENT_HDC: + if (evt->hdc.program == st->program) + { + if (st->hdc_file) + dump_hdc(st->hdc_file, evt->hdc.data, evt->hdc.count); + + st->audio_packets++; + st->audio_bytes += evt->hdc.count * sizeof(evt->hdc.data[0]); + if (evt->hdc.flags & NRSC5_PKT_FLAGS_CRC_ERROR) + st->audio_errors++; + else + st->audio_packets_valid++; + + if (st->audio_packets_valid >= 32) { + log_info("Audio bit rate: %.1f kbps", (float)st->audio_bytes * 8 * NRSC5_SAMPLE_RATE_AUDIO / NRSC5_AUDIO_FRAME_SAMPLES / st->audio_packets_valid / 1000); + st->audio_packets_valid = 0; + st->audio_bytes = 0; + } + if (st->audio_packets >= 32) + { + if (st->audio_errors > 0) + log_warn("Audio packet CRC mismatches: %d", st->audio_errors); + st->audio_packets = 0; + st->audio_errors = 0; + } + } + break; + case NRSC5_EVENT_AUDIO: + push_audio_buffer(st, evt->audio.program, evt->audio.data, evt->audio.count, evt->audio.flags); + break; + case NRSC5_EVENT_SYNC: + log_info("Synchronized"); + log_info("Frequency offset: %.0f Hz", evt->sync.freq_offset); + log_info("Primary service mode: %d", evt->sync.psmi); + if (evt->sync.pli != -1) + { + char am_flags[128] = ""; + strcat(am_flags, "Digital bandwidth: "); + strcat(am_flags, evt->sync.rdbi ? "reduced" : "full"); + if (!evt->sync.rdbi) + { + if (evt->sync.psmi != 2) + { + strcat(am_flags, ", analog bandwidth: "); + strcat(am_flags, evt->sync.aabi ? "8 kHz" : "5 kHz"); + strcat(am_flags, ", secondary/tertiary power: "); + strcat(am_flags, evt->sync.pli ? "high" : "low"); + } + strcat(am_flags, ", PIDS power: "); + strcat(am_flags, evt->sync.hppi ? "high" : "low"); + } + log_info(am_flags); + } + break; + case NRSC5_EVENT_LOST_SYNC: + log_info("Lost synchronization"); + break; + case NRSC5_EVENT_ID3: + if (evt->id3.program == st->program) + { + if (evt->id3.title) + log_info("Title: %s", evt->id3.title); + if (evt->id3.artist) + log_info("Artist: %s", evt->id3.artist); + if (evt->id3.album) + log_info("Album: %s", evt->id3.album); + if (evt->id3.genre) + log_info("Genre: %s", evt->id3.genre); + for (comment = evt->id3.comments; comment != NULL; comment = comment->next) + log_info("Comment: lang=%s %s %s", comment->lang, comment->short_content_desc, comment->full_text); + if (evt->id3.ufid.owner) + log_info("Unique file identifier: %s %s", evt->id3.ufid.owner, evt->id3.ufid.id); + if (evt->id3.xhdr.param >= 0) + log_info("XHDR: %d %08X %d", evt->id3.xhdr.param, evt->id3.xhdr.mime, evt->id3.xhdr.lot); + strftime(time_str, sizeof(time_str), "%Y-%m-%d", evt->id3.commercial.valid_until); + if (evt->id3.commercial.price) + log_info("Commercial: price=%s until=%s url=\"%s\" seller=\"%s\" desc=\"%s\" received_as=%d", + evt->id3.commercial.price, time_str, evt->id3.commercial.contact_url, evt->id3.commercial.seller, + evt->id3.commercial.description, evt->id3.commercial.received_as); + } + break; + case NRSC5_EVENT_SIG: + for (sig_service = evt->sig.services; sig_service != NULL; sig_service = sig_service->next) + { + log_info("SIG Service: type=%s number=%d name=%s", + sig_service->type == NRSC5_SIG_SERVICE_AUDIO ? "audio" : "data", + sig_service->number, sig_service->name); + + for (sig_component = sig_service->components; sig_component != NULL; sig_component = sig_component->next) + { + if (sig_component->type == NRSC5_SIG_SERVICE_AUDIO) + { + log_info(" Audio component: id=%d port=%04X type=%d mime=%08X", sig_component->id, + sig_component->audio.port, sig_component->audio.type, sig_component->audio.mime); + } + else if (sig_component->type == NRSC5_SIG_SERVICE_DATA) + { + log_info(" Data component: id=%d port=%04X service_data_type=%d type=%d mime=%08X", + sig_component->id, sig_component->data.port, sig_component->data.service_data_type, + sig_component->data.type, sig_component->data.mime); + } + } + } + break; + case NRSC5_EVENT_STREAM: + log_debug("Stream data: port=%04X seq=%04X mime=%08X size=%d", evt->stream.component->data.port, evt->stream.seq, evt->stream.component->data.mime, evt->stream.size); + break; + case NRSC5_EVENT_PACKET: + log_debug("Packet data: port=%04X seq=%04X mime=%08X size=%d", evt->packet.component->data.port, evt->packet.seq, evt->packet.component->data.mime, evt->packet.size); + break; + case NRSC5_EVENT_LOT: + if (st->aas_files_path) + dump_aas_file(st, evt); + strftime(time_str, sizeof(time_str), "%Y-%m-%dT%H:%M:%SZ", evt->lot.expiry_utc); + log_info("LOT file: port=%04X lot=%d name=%s size=%d mime=%08X expiry=%s", evt->lot.component->data.port, evt->lot.lot, evt->lot.name, evt->lot.size, evt->lot.mime, time_str); + break; + case NRSC5_EVENT_LOT_HEADER: + strftime(time_str, sizeof(time_str), "%Y-%m-%dT%H:%M:%SZ", evt->lot.expiry_utc); + log_debug("LOT header: port=%04X lot=%d name=%s size=%d mime=%08X expiry=%s", + evt->lot.component->data.port, evt->lot.lot, evt->lot.name, evt->lot.size, evt->lot.mime, time_str); + break; + case NRSC5_EVENT_LOT_FRAGMENT: + if (!evt->lot_fragment.is_duplicate) + log_debug("LOT fragment: port=%04X lot=%d seq=%d repeat=%d size=%d bytes_so_far=%d", + evt->lot_fragment.component->data.port, evt->lot_fragment.lot, evt->lot_fragment.seq, + evt->lot_fragment.repeat, evt->lot_fragment.size, evt->lot_fragment.bytes_so_far); + break; + case NRSC5_EVENT_STATION_ID: + log_info("Country: %s, FCC facility ID: %d", evt->station_id.country_code, evt->station_id.fcc_facility_id); + break; + case NRSC5_EVENT_STATION_NAME: + log_info("Station name: %s", evt->station_name.name); + break; + case NRSC5_EVENT_STATION_SLOGAN: + log_info("Slogan: %s", evt->station_slogan.slogan); + break; + case NRSC5_EVENT_STATION_MESSAGE: + log_info("Message: %s", evt->station_message.message); + break; + case NRSC5_EVENT_STATION_LOCATION: + log_info("Station location: %.4f, %.4f, %dm", evt->station_location.latitude, evt->station_location.longitude, evt->station_location.altitude); + break; + case NRSC5_EVENT_AUDIO_SERVICE_DESCRIPTOR: + nrsc5_program_type_name(evt->asd.type, &name); + log_info("Audio program %d: %s, type: %s, sound experience %d", + evt->asd.program, + evt->asd.access == NRSC5_ACCESS_PUBLIC ? "public" : "restricted", + name, evt->asd.sound_exp); + break; + case NRSC5_EVENT_DATA_SERVICE_DESCRIPTOR: + nrsc5_service_data_type_name(evt->dsd.type, &name); + log_info("Data service: %s, type: %s, MIME type %03x", + evt->dsd.access == NRSC5_ACCESS_PUBLIC ? "public" : "restricted", + name, evt->dsd.mime_type); + break; + case NRSC5_EVENT_EMERGENCY_ALERT: + if (evt->emergency_alert.message) + { + int i; + char alert_details[512] = ""; + const char *name = NULL; + + strcat(alert_details, "Category=["); + if (evt->emergency_alert.category1 >= 1) + { + nrsc5_alert_category_name(evt->emergency_alert.category1, &name); + strcat(alert_details, name); + } + if (evt->emergency_alert.category2 >= 1) + { + nrsc5_alert_category_name(evt->emergency_alert.category2, &name); + strcat(alert_details, ", "); + strcat(alert_details, name); + } + strcat(alert_details, "] "); + + switch (evt->emergency_alert.location_format) + { + case NRSC5_LOCATION_FORMAT_SAME: + strcat(alert_details, "SAME="); + break; + case NRSC5_LOCATION_FORMAT_FIPS: + strcat(alert_details, "FIPS="); + break; + case NRSC5_LOCATION_FORMAT_ZIP: + strcat(alert_details, "ZIP="); + break; + } + + strcat(alert_details, "["); + for (i = 0; i < evt->emergency_alert.num_locations; i++) + { + if (i > 0) + strcat(alert_details, ", "); + sprintf(alert_details + strlen(alert_details), "%d", evt->emergency_alert.locations[i]); + } + strcat(alert_details, "]"); + + log_info("Alert: %s %s", alert_details, evt->emergency_alert.message); + } + else + log_info("Alert ended"); + break; + case NRSC5_EVENT_AUDIO_SERVICE: + nrsc5_program_type_name(evt->audio_service.type, &name); + log_info("Audio service %d: %s, type: %s, codec: %d, blend: %d, gain: %d dB, delay: %d, latency: %d", + evt->audio_service.program, + evt->audio_service.access == NRSC5_ACCESS_PUBLIC ? "public" : "restricted", + name, + evt->audio_service.codec_mode, + evt->audio_service.blend_control, + evt->audio_service.digital_audio_gain, + evt->audio_service.common_delay, + evt->audio_service.latency); + break; + case NRSC5_EVENT_HERE_IMAGE: + if (st->aas_files_path) + dump_aas_file(st, evt); + strftime(time_str, sizeof(time_str), "%Y-%m-%dT%H:%M:%SZ", evt->here_image.time_utc); + log_info("HERE Image: type=%s, seq=%d, n1=%d, n2=%d, time=%s, lat1=%.5f, lon1=%.5f, lat2=%.5f, lon2=%.5f, name=%s, size=%d", + evt->here_image.image_type == NRSC5_HERE_IMAGE_TRAFFIC ? "TRAFFIC" : "WEATHER", + evt->here_image.seq, + evt->here_image.n1, + evt->here_image.n2, + time_str, + evt->here_image.latitude1, + evt->here_image.longitude1, + evt->here_image.latitude2, + evt->here_image.longitude2, + evt->here_image.name, + evt->here_image.size); + break; + case NRSC5_EVENT_EXCITER_INFO: + log_debug("Exciter manuf. \"%s\", core version %d.%d.%d.%d, core status %d, manuf. version %d.%d.%d.%d, manuf. status %d, importer connected? %s", + evt->exciter_info.manufacturer_id, + evt->exciter_info.core_version[0], evt->exciter_info.core_version[1], evt->exciter_info.core_version[2], + evt->exciter_info.core_version[3], evt->exciter_info.core_status, + evt->exciter_info.manufacturer_version[0], evt->exciter_info.manufacturer_version[1], evt->exciter_info.manufacturer_version[2], + evt->exciter_info.manufacturer_version[3], evt->exciter_info.manufacturer_status, + evt->exciter_info.importer_connected ? "yes" : "no"); + break; + case NRSC5_EVENT_IMPORTER_INFO: + log_debug("Importer manuf. \"%s\", core version %d.%d.%d.%d, core status %d, manuf. version %d.%d.%d.%d, manuf. status %d", + evt->importer_info.manufacturer_id, + evt->importer_info.core_version[0], evt->importer_info.core_version[1], evt->importer_info.core_version[2], + evt->importer_info.core_version[3], evt->importer_info.core_status, + evt->importer_info.manufacturer_version[0], evt->importer_info.manufacturer_version[1], evt->importer_info.manufacturer_version[2], + evt->importer_info.manufacturer_version[3], evt->importer_info.manufacturer_status); + break; + case NRSC5_EVENT_LEAP_SECOND_OFFSET: + log_debug("Leap second offset: pending=%d, current=%d, ALFN of pending adjustment=%d", + evt->leap_second_offset.pending_offset, + evt->leap_second_offset.current_offset, + evt->leap_second_offset.pending_alfn); + break; + case NRSC5_EVENT_LOCAL_TIME: + log_debug("Local time: UTC offset=%d minutes, DST schedule=%d, DST in effect regionally? %s, DST practiced locally? %s", + evt->local_time.utc_offset, + evt->local_time.dst_schedule, + evt->local_time.dst_regional ? "yes" : "no", + evt->local_time.dst_local ? "yes" : "no"); + break; + + } +} + +static int connect_tcp(char *host, const char *default_port) +{ + int err, s; + struct addrinfo hints, *res0; + char *p = strchr(host, ':'); + +#ifdef __MINGW32__ + WSADATA wsa_data; + if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) + return -1; +#endif + + if (p) + { + *p = 0; + default_port = p + 1; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo(host, default_port, &hints, &res0); + if (err) + return -1; + + for (struct addrinfo *res = res0; res != NULL; res = res->ai_next) + { + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s == -1) + continue; + + if (connect(s, res->ai_addr, res->ai_addrlen) == 0) + break; + + // failed, try next address + close(s); + s = -1; + } + + freeaddrinfo(res0); + return s; +} + +static void *audio_main(void *arg) +{ + state_t *st = arg; + + while (1) + { + audio_buffer_t *b; + + pthread_mutex_lock(&st->mutex); + while (!st->done && (st->head == NULL)) + pthread_cond_wait(&st->cond, &st->mutex); + + // exit once done and no more audio buffers + if (st->head == NULL) + { + pthread_mutex_unlock(&st->mutex); + break; + } + + // unlink from head list + b = st->head; + st->head = b->next; + if (st->head == NULL) + st->tail = NULL; + pthread_mutex_unlock(&st->mutex); + + ao_play(st->dev, b->data, sizeof(b->data)); + + pthread_mutex_lock(&st->mutex); + // add to free list + b->next = st->free; + st->free = b; + pthread_cond_signal(&st->cond); + pthread_mutex_unlock(&st->mutex); + } + + return NULL; +} + +static void on_key_press(state_t *st, char ch) +{ + switch (ch) + { + case 'q': + done_signal(st); + // user wants to immediately exit, so reset audio buffer + change_program(st, -1); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + change_program(st, ch - '0'); + break; + } +} + +static void *input_main(void *arg) +{ + state_t *st = arg; + + if (!isatty(STDIN_FILENO)) + return NULL; + +#ifdef __MINGW32__ + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + DWORD mode = 0; + GetConsoleMode(hStdin, &mode); + SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT) & (~ENABLE_LINE_INPUT)); +#else + struct termios prev_termios, t; + + // disable terminal canonical mode + tcgetattr(STDIN_FILENO, &prev_termios); + t = prev_termios; + t.c_lflag &= ~ICANON; + tcsetattr(STDIN_FILENO, TCSANOW, &t); + + struct pollfd pfd; + pfd.fd = STDIN_FILENO; + pfd.events = POLLIN; +#endif + + while (!is_done(st)) + { +#ifdef __MINGW32__ + INPUT_RECORD r; + DWORD read; + + switch (WaitForSingleObject(hStdin, STDIN_POLL_RATE_MS)) + { + case WAIT_TIMEOUT: + continue; + case WAIT_OBJECT_0: + if (!ReadConsoleInput(hStdin, &r, 1, &read)) + { + log_error("Stdin read failed: ReadConsoleInput error %d", GetLastError()); + break; + } + + const KEY_EVENT_RECORD key = r.Event.KeyEvent; + + if (r.EventType == KEY_EVENT && key.bKeyDown) + on_key_press(st, key.uChar.AsciiChar); + break; + case WAIT_ABANDONED: + log_error("Waiting for stdin failed: WAIT_ABANDONED"); + break; + case WAIT_FAILED: + log_error("Waiting for stdin failed: WAIT_FAILED"); + break; + default: + break; + } + +#else + int ret = poll(&pfd, 1, STDIN_POLL_RATE_MS); + char ch; + + if (ret > 0) + { + if (pfd.revents & POLLIN) + { + if (read(STDIN_FILENO, &ch, 1)) + on_key_press(st, ch); + } + } + else if (ret == 0) + continue; + else + { + log_error("Stdin read failed: poll error %d", errno); + break; + } +#endif + } + +#ifndef __MINGW32__ + // restore terminal settings + tcsetattr(STDIN_FILENO, TCSANOW, &prev_termios); +#endif + + return NULL; +} + +static void help(const char *progname) +{ + fprintf(stderr, "Usage: %s [-v] [-q] [--am] [-l log-level] [-d device-index] [-H rtltcp-host] [-p ppm-error] [-g gain] [-r iq-input] [--iq-input-format {cu8,cs16}] [-w iq-output] [-o audio-output] [-t audio-type] [-T] [-D direct-sampling-mode] [--dump-hdc hdc-output] [--dump-aas-files directory] frequency program\n", progname); +} + +static int ends_with(const char *str, const char *suffix) +{ + const size_t len = strlen(str); + const size_t suffix_len = strlen(suffix); + return (len >= suffix_len) && (strcmp(str + len - suffix_len, suffix) == 0); +} + +static int parse_args(state_t *st, int argc, char *argv[]) +{ + static const struct option long_opts[] = { + { "dump-aas-files", required_argument, NULL, 1 }, + { "dump-hdc", required_argument, NULL, 2 }, + { "am", no_argument, NULL, 3 }, + { "iq-input-format", required_argument, NULL, 4 }, + { 0 } + }; + const char *version = NULL; + char *output_name = NULL, *audio_name = NULL, *hdc_name = NULL; + char *audio_type = "wav"; + char *endptr; + int opt; + + st->mode = NRSC5_MODE_FM; + st->gain = -1; + st->bias_tee = 0; + st->direct_sampling = -1; + st->ppm_error = INT_MIN; + st->iq_input_format = IQ_FORMAT_NONE; + log_set_level(LOG_INFO); + + while ((opt = getopt_long(argc, argv, "r:w:o:t:d:p:g:ql:vH:TD:", long_opts, NULL)) != -1) + { + switch (opt) + { + case 1: + st->aas_files_path = strdup(optarg); + break; + case 2: + hdc_name = optarg; + break; + case 3: + st->mode = NRSC5_MODE_AM; + break; + case 4: + if (strcmp(optarg, "cu8") == 0) + { + st->iq_input_format = IQ_FORMAT_CU8; + } + else if (strcmp(optarg, "cs16") == 0) + { + st->iq_input_format = IQ_FORMAT_CS16; + } + else if (strcmp(optarg, "cf32") == 0) + { + st->iq_input_format = IQ_FORMAT_CF32; + } + else + { + log_fatal("I/Q input format must be either cu8, cs16 or cf32."); + return -1; + } + break; + case 'r': + st->input_name = strdup(optarg); + break; + case 'w': + output_name = optarg; + break; + case 'o': + audio_name = optarg; + break; + case 't': + if ((strcmp(optarg, "wav") != 0) && (strcmp(optarg, "raw") != 0)) + { + log_fatal("Audio type must be either wav or raw."); + return -1; + } + audio_type = optarg; + break; + case 'd': + st->device_index = strtoul(optarg, NULL, 10); + break; + case 'p': + st->ppm_error = strtol(optarg, NULL, 10); + break; + case 'g': + st->gain = strtof(optarg, &endptr); + if (*endptr != 0) + { + log_fatal("Invalid gain."); + return -1; + } + break; + case 'q': + log_set_quiet(1); + break; + case 'l': + log_set_level(atoi(optarg)); + break; + case 'v': + nrsc5_get_version(&version); + printf("nrsc5 revision %s\n", version); + return 1; + case 'H': + st->rtltcp_host = strdup(optarg); + break; + case 'T': + st->bias_tee = 1; + break; + case 'D': + st->direct_sampling = atoi(optarg); + break; + default: + help(argv[0]); + return 1; + } + } + + if (optind + (!st->input_name + 1) != argc) + { + help(argv[0]); + return 1; + } + + if (!st->input_name) + { + st->freq = strtof(argv[optind++], &endptr); + if (*endptr != 0) + { + log_fatal("Invalid frequency."); + return -1; + } + + // compatibility with previous versions + if (st->freq < 10000.0f) + st->freq *= 1e6f; + } + + if (st->input_name && (st->iq_input_format == IQ_FORMAT_NONE)) + { + if (ends_with(st->input_name, ".cs16")) + st->iq_input_format = IQ_FORMAT_CS16; + else if (ends_with(st->input_name, ".cf32")) + st->iq_input_format = IQ_FORMAT_CF32; + else + st->iq_input_format = IQ_FORMAT_CU8; + } + + st->program = strtoul(argv[optind++], &endptr, 0); + if (*endptr != 0) + { + log_fatal("Invalid program."); + return -1; + } + + if (audio_name) + st->dev = open_ao_file(audio_name, audio_type); + else + st->dev = open_ao_live(); + + if (st->dev == NULL) + { + log_fatal("Unable to open audio device."); + return 1; + } + + if (output_name) + { + if (strcmp(output_name, "-") == 0) + st->iq_file = stdout; + else + st->iq_file = fopen(output_name, "wb"); + if (st->iq_file == NULL) + { + log_fatal("Unable to open IQ output."); + return 1; + } + } + + if (hdc_name) + { + if (strcmp(hdc_name, "-") == 0) + st->hdc_file = stdout; + else + st->hdc_file = fopen(hdc_name, "wb"); + if (st->hdc_file == NULL) + { + log_fatal("Unable to open HDC output."); + return 1; + } + } + + return 0; +} + +static void log_lock(void *udata, int lock) +{ + pthread_mutex_t *mutex = udata; + if (lock) + pthread_mutex_lock(mutex); + else + pthread_mutex_unlock(mutex); +} + +static void cleanup(state_t *st) +{ + reset_audio_buffers(st); + while (st->free) + { + audio_buffer_t *b = st->free; + st->free = b->next; + free(b); + } + + if (st->hdc_file) + fclose(st->hdc_file); + if (st->iq_file) + fclose(st->iq_file); + + free(st->input_name); + free(st->aas_files_path); + + if (st->dev) + ao_close(st->dev); +} + +int main(int argc, char *argv[]) +{ + pthread_mutex_t log_mutex; + pthread_t audio_thread; + pthread_t input_thread; + nrsc5_t *radio = NULL; + state_t *st = calloc(1, sizeof(state_t)); + FILE *fp = NULL; + + pthread_mutex_init(&log_mutex, NULL); + log_set_lock(log_lock); + log_set_udata(&log_mutex); + + ao_initialize(); + init_audio_buffers(st); + if (parse_args(st, argc, argv) != 0) + return 0; + +#ifdef __MINGW32__ + SetConsoleOutputCP(CP_UTF8); + setmode(fileno(stdin), O_BINARY); + setmode(fileno(stdout), O_BINARY); +#endif + + if (st->input_name) + { + fp = strcmp(st->input_name, "-") == 0 ? stdin : fopen(st->input_name, "rb"); + if (fp == NULL) + { + log_fatal("Open IQ file failed: %s", strerror(errno)); + return 1; + } + if (nrsc5_open_pipe(&radio) != 0) + { + log_fatal("Open IQ failed."); + return 1; + } + } + else if (st->rtltcp_host) + { + int s = connect_tcp(st->rtltcp_host, "1234"); + if (s == -1) + { + log_fatal("Connection failed."); + return 1; + } + if (nrsc5_open_rtltcp(&radio, s) != 0) + { + log_fatal("Open remote device failed."); + return 1; + } + } + else + { + if (nrsc5_open(&radio, st->device_index) != 0) + { + log_fatal("Open device failed."); + return 1; + } + } + if (nrsc5_set_bias_tee(radio, st->bias_tee) != 0) + { + log_fatal("Set bias-T failed."); + return 1; + } + if (st->direct_sampling != -1) + { + if (nrsc5_set_direct_sampling(radio, st->direct_sampling) != 0) + { + log_fatal("Set direct sampling failed."); + return 1; + } + } + if (st->ppm_error != INT_MIN && nrsc5_set_freq_correction(radio, st->ppm_error) != 0) + { + log_fatal("Set frequency correction failed."); + return 1; + } + if (nrsc5_set_frequency(radio, st->freq) != 0) + { + log_fatal("Set frequency failed."); + return 1; + } + nrsc5_set_mode(radio, st->mode); + if (st->gain >= 0.0f) + nrsc5_set_gain(radio, st->gain); + nrsc5_set_callback(radio, callback, st); + nrsc5_start(radio); + + pthread_create(&audio_thread, NULL, audio_main, st); + pthread_create(&input_thread, NULL, input_main, st); + + if (st->input_name) + { + uint8_t buffer[FILE_BUFFER_LENGTH]; + + while (!is_done(st)) + { + size_t samples_read = 0; + + if (st->iq_input_format == IQ_FORMAT_CU8) { + samples_read = fread(buffer, 2, sizeof(buffer) / 2, fp); + } else if (st->iq_input_format == IQ_FORMAT_CS16) { + samples_read = fread(buffer, 4, sizeof(buffer) / 4, fp); + } else if (st->iq_input_format == IQ_FORMAT_CF32) { + samples_read = fread(buffer, 8, sizeof(buffer) / 8, fp); + } + + if (samples_read == 0) + { + done_signal(st); + break; + } + + if (st->iq_input_format == IQ_FORMAT_CU8) { + nrsc5_pipe_samples_cu8(radio, buffer, samples_read * 2); + } else if (st->iq_input_format == IQ_FORMAT_CS16) { + nrsc5_pipe_samples_cs16(radio, (int16_t *)buffer, samples_read * 2); + } else if (st->iq_input_format == IQ_FORMAT_CF32) { + nrsc5_pipe_samples_cf32(radio, (float *)buffer, samples_read * 2); + } + } + } + + pthread_join(audio_thread, NULL); + pthread_join(input_thread, NULL); + + nrsc5_stop(radio); + nrsc5_set_bias_tee(radio, 0); + nrsc5_close(radio); + + if (st->input_name) + { + fclose(fp); + } + + cleanup(st); + free(st); + ao_shutdown(); + return 0; +} diff --git a/third_party/nrsc5/upstream/src/nrsc5.c b/third_party/nrsc5/upstream/src/nrsc5.c new file mode 100644 index 000000000..b75247b91 --- /dev/null +++ b/third_party/nrsc5/upstream/src/nrsc5.c @@ -0,0 +1,1206 @@ +#include +#include + +#include "private.h" + +pthread_mutex_t fftw_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int get_tuner_gains(nrsc5_t *st, int *gains) +{ + if (st->dev) + return rtlsdr_get_tuner_gains(st->dev, gains); + assert(st->rtltcp); + return rtltcp_get_tuner_gains(st->rtltcp, gains); +} + +static int set_tuner_gain(nrsc5_t *st, int gain) +{ + if (st->dev) + return rtlsdr_set_tuner_gain(st->dev, gain); + assert(st->rtltcp); + return rtltcp_set_tuner_gain(st->rtltcp, gain); +} + +static int do_auto_gain(nrsc5_t *st) +{ + int gain_count, best_gain = 0, ret = 1; + int *gain_list = NULL; + float best_amplitude_db = 0.0f; + + gain_count = get_tuner_gains(st, NULL); + if (gain_count < 0) + goto error; + + gain_list = malloc(gain_count * sizeof(*gain_list)); + if (!gain_list) + goto error; + + gain_count = get_tuner_gains(st, gain_list); + if (gain_count < 0) + goto error; + + int low = 0; + int high = gain_count - 1; + + while (low <= high) + { + int mid = (low + high) / 2; + int gain = gain_list[mid]; + + if (set_tuner_gain(st, gain) != 0) + continue; + + if (st->rtltcp) + { + // there is no good way to wait for samples after the new gain was applied + // dump 250ms of samples and hope for the best + rtltcp_reset_buffer(st->rtltcp, (NRSC5_SAMPLE_RATE_CU8 / 4) * 2); + } + + int len = sizeof(st->samples_buf); + + if (st->dev) + { + if (rtlsdr_read_sync(st->dev, st->samples_buf, len, &len) != 0) + goto error; + } + else + { + assert(st->rtltcp); + if (rtltcp_read(st->rtltcp, st->samples_buf, len) != len) + goto error; + } + + uint8_t max_sample = 0; + uint8_t min_sample = 255; + for (int j = len/4; j < len; j++) + { + if (st->samples_buf[j] > max_sample) + max_sample = st->samples_buf[j]; + if (st->samples_buf[j] < min_sample) + min_sample = st->samples_buf[j]; + } + + float amplitude_db = 20 * log10f((max_sample - min_sample + 1) / 256.0f); + nrsc5_report_agc(st, gain / 10.0f, amplitude_db, 0); + + if (amplitude_db < -6.0f) + { + best_gain = gain; + best_amplitude_db = amplitude_db; + low = mid + 1; + } + else + { + high = mid - 1; + } + + if (high == -1) + { + best_gain = gain; + best_amplitude_db = amplitude_db; + } + } + + nrsc5_report_agc(st, best_gain / 10.0f, best_amplitude_db, 1); + st->gain = best_gain; + set_tuner_gain(st, best_gain); + ret = 0; + +error: + free(gain_list); + return ret; +} + +static int using_worker(nrsc5_t *st) +{ + return st->dev || st->rtltcp || st->iq_file; +} + +static void worker_cb(uint8_t *buf, uint32_t len, void *arg) +{ + nrsc5_t *st = arg; + + if (st->stopped && st->dev) + rtlsdr_cancel_async(st->dev); + else + input_push_cu8(&st->input, buf, len); +} + +static void *worker_thread(void *arg) +{ + nrsc5_t *st = arg; + + pthread_mutex_lock(&st->worker_mutex); + while (!st->closed) + { + if (st->stopped && !st->worker_stopped) + { + st->worker_stopped = 1; + pthread_cond_broadcast(&st->worker_cond); + } + else if (!st->stopped && st->worker_stopped) + { + st->worker_stopped = 0; + pthread_cond_broadcast(&st->worker_cond); + + if (st->dev && rtlsdr_reset_buffer(st->dev) != 0) + log_error("rtlsdr_reset_buffer failed"); + + if (st->dev || st->rtltcp) + { + if (st->auto_gain && st->gain < 0 && do_auto_gain(st) != 0) + { + st->stopped = 1; + continue; + } + } + } + + if (st->stopped) + { + // wait for a signal + pthread_cond_wait(&st->worker_cond, &st->worker_mutex); + } + else + { + int err = 0; + + pthread_mutex_unlock(&st->worker_mutex); + + if (st->dev) + { + err = rtlsdr_read_async(st->dev, worker_cb, st, 120, 32768); + } + else if (st->rtltcp) + { + err = rtltcp_read(st->rtltcp, st->samples_buf, sizeof(st->samples_buf)); + if (err >= 0) + { + // a short read is possible when EOF and may not be aligned + input_push_cu8(&st->input, st->samples_buf, err & ~3); + if (err == sizeof(st->samples_buf)) + err = 0; + } + } + else if (st->iq_file) + { + int count = fread(st->samples_buf, 4, sizeof(st->samples_buf) / 4, st->iq_file); + if (count > 0) + input_push_cu8(&st->input, st->samples_buf, count * 4); + if (feof(st->iq_file) || ferror(st->iq_file)) + err = 1; + } + + pthread_mutex_lock(&st->worker_mutex); + + if (err) + { + st->stopped = 1; + nrsc5_report_lost_device(st); + } + } + } + + pthread_mutex_unlock(&st->worker_mutex); + return NULL; +} + +static void nrsc5_init(nrsc5_t *st) +{ + st->closed = 0; + st->stopped = 1; + st->worker_stopped = 1; + st->auto_gain = 1; + st->gain = -1; + st->freq = NRSC5_SCAN_BEGIN; + st->mode = NRSC5_MODE_FM; + st->callback = NULL; + + output_init(&st->output, st); + input_init(&st->input, st, &st->output); + + if (using_worker(st)) + { + // Create worker thread + pthread_mutex_init(&st->worker_mutex, NULL); + pthread_cond_init(&st->worker_cond, NULL); + pthread_create(&st->worker, NULL, worker_thread, st); + } +} + +void nrsc5_get_version(const char **version) +{ + *version = GIT_COMMIT_HASH; +} + +void nrsc5_service_data_type_name(unsigned int type, const char **name) +{ + switch (type) + { + case NRSC5_SERVICE_DATA_TYPE_NON_SPECIFIC: *name = "Non-specific"; break; + case NRSC5_SERVICE_DATA_TYPE_NEWS: *name = "News"; break; + case NRSC5_SERVICE_DATA_TYPE_SPORTS: *name = "Sports"; break; + case NRSC5_SERVICE_DATA_TYPE_WEATHER: *name = "Weather"; break; + case NRSC5_SERVICE_DATA_TYPE_EMERGENCY: *name = "Emergency"; break; + case NRSC5_SERVICE_DATA_TYPE_TRAFFIC: *name = "Traffic"; break; + case NRSC5_SERVICE_DATA_TYPE_IMAGE_MAPS: *name = "Image Maps"; break; + case NRSC5_SERVICE_DATA_TYPE_TEXT: *name = "Text"; break; + case NRSC5_SERVICE_DATA_TYPE_ADVERTISING: *name = "Advertising"; break; + case NRSC5_SERVICE_DATA_TYPE_FINANCIAL: *name = "Financial"; break; + case NRSC5_SERVICE_DATA_TYPE_STOCK_TICKER: *name = "Stock Ticker"; break; + case NRSC5_SERVICE_DATA_TYPE_NAVIGATION: *name = "Navigation"; break; + case NRSC5_SERVICE_DATA_TYPE_ELECTRONIC_PROGRAM_GUIDE: *name = "Electronic Program Guide"; break; + case NRSC5_SERVICE_DATA_TYPE_AUDIO: *name = "Audio"; break; + case NRSC5_SERVICE_DATA_TYPE_PRIVATE_DATA_NETWORK: *name = "Private Data Network"; break; + case NRSC5_SERVICE_DATA_TYPE_SERVICE_MAINTENANCE: *name = "Service Maintenance"; break; + case NRSC5_SERVICE_DATA_TYPE_HD_RADIO_SYSTEM_SERVICES: *name = "HD Radio System Services"; break; + case NRSC5_SERVICE_DATA_TYPE_AUDIO_RELATED_DATA: *name = "Audio-Related Objects"; break; + case NRSC5_SERVICE_DATA_TYPE_RESERVED_FOR_SPECIAL_TESTS: *name = "Reserved for Special Tests"; break; + default: *name = "Unknown"; break; + } +} + +void nrsc5_program_type_name(unsigned int type, const char **name) +{ + switch (type) + { + case NRSC5_PROGRAM_TYPE_UNDEFINED: *name = "None"; break; + case NRSC5_PROGRAM_TYPE_NEWS: *name = "News"; break; + case NRSC5_PROGRAM_TYPE_INFORMATION: *name = "Information"; break; + case NRSC5_PROGRAM_TYPE_SPORTS: *name = "Sports"; break; + case NRSC5_PROGRAM_TYPE_TALK: *name = "Talk"; break; + case NRSC5_PROGRAM_TYPE_ROCK: *name = "Rock"; break; + case NRSC5_PROGRAM_TYPE_CLASSIC_ROCK: *name = "Classic Rock"; break; + case NRSC5_PROGRAM_TYPE_ADULT_HITS: *name = "Adult Hits"; break; + case NRSC5_PROGRAM_TYPE_SOFT_ROCK: *name = "Soft Rock"; break; + case NRSC5_PROGRAM_TYPE_TOP_40: *name = "Top 40"; break; + case NRSC5_PROGRAM_TYPE_COUNTRY: *name = "Country"; break; + case NRSC5_PROGRAM_TYPE_OLDIES: *name = "Oldies"; break; + case NRSC5_PROGRAM_TYPE_SOFT: *name = "Soft"; break; + case NRSC5_PROGRAM_TYPE_NOSTALGIA: *name = "Nostalgia"; break; + case NRSC5_PROGRAM_TYPE_JAZZ: *name = "Jazz"; break; + case NRSC5_PROGRAM_TYPE_CLASSICAL: *name = "Classical"; break; + case NRSC5_PROGRAM_TYPE_RHYTHM_AND_BLUES: *name = "Rhythm and Blues"; break; + case NRSC5_PROGRAM_TYPE_SOFT_RHYTHM_AND_BLUES: *name = "Soft Rhythm and Blues"; break; + case NRSC5_PROGRAM_TYPE_FOREIGN_LANGUAGE: *name = "Foreign Language"; break; + case NRSC5_PROGRAM_TYPE_RELIGIOUS_MUSIC: *name = "Religious Music"; break; + case NRSC5_PROGRAM_TYPE_RELIGIOUS_TALK: *name = "Religious Talk"; break; + case NRSC5_PROGRAM_TYPE_PERSONALITY: *name = "Personality"; break; + case NRSC5_PROGRAM_TYPE_PUBLIC: *name = "Public"; break; + case NRSC5_PROGRAM_TYPE_COLLEGE: *name = "College"; break; + case NRSC5_PROGRAM_TYPE_SPANISH_TALK: *name = "Spanish Talk"; break; + case NRSC5_PROGRAM_TYPE_SPANISH_MUSIC: *name = "Spanish Music"; break; + case NRSC5_PROGRAM_TYPE_HIP_HOP: *name = "Hip-Hop"; break; + case NRSC5_PROGRAM_TYPE_WEATHER: *name = "Weather"; break; + case NRSC5_PROGRAM_TYPE_EMERGENCY_TEST: *name = "Emergency Test"; break; + case NRSC5_PROGRAM_TYPE_EMERGENCY: *name = "Emergency"; break; + case NRSC5_PROGRAM_TYPE_TRAFFIC: *name = "Traffic"; break; + case NRSC5_PROGRAM_TYPE_SPECIAL_READING_SERVICES: *name = "Special Reading Services"; break; + default: *name = "Unknown"; break; + } +} + +void nrsc5_alert_category_name(unsigned int type, const char **name) +{ + switch (type) + { + case NRSC5_ALERT_CATEGORY_NON_SPECIFIC: *name = "Non-specific"; break; + case NRSC5_ALERT_CATEGORY_GEOPHYSICAL: *name = "Geophysical"; break; + case NRSC5_ALERT_CATEGORY_WEATHER: *name = "Weather"; break; + case NRSC5_ALERT_CATEGORY_SAFETY: *name = "Safety"; break; + case NRSC5_ALERT_CATEGORY_SECURITY: *name = "Security"; break; + case NRSC5_ALERT_CATEGORY_RESCUE: *name = "Rescue"; break; + case NRSC5_ALERT_CATEGORY_FIRE: *name = "Fire"; break; + case NRSC5_ALERT_CATEGORY_HEALTH: *name = "Health"; break; + case NRSC5_ALERT_CATEGORY_ENVIRONMENTAL: *name = "Environmental"; break; + case NRSC5_ALERT_CATEGORY_TRANSPORTATION: *name = "Transportation"; break; + case NRSC5_ALERT_CATEGORY_UTILITIES: *name = "Utilities"; break; + case NRSC5_ALERT_CATEGORY_HAZMAT: *name = "Hazmat"; break; + case NRSC5_ALERT_CATEGORY_TEST: *name = "Test"; break; + default: *name = "Unknown"; break; + } +} + +static nrsc5_t *nrsc5_alloc(void) +{ + nrsc5_t *st = calloc(1, sizeof(*st)); + return st; +} + +int nrsc5_open(nrsc5_t **result, int device_index) +{ + int err; + nrsc5_t *st = nrsc5_alloc(); + + if (rtlsdr_open(&st->dev, device_index) != 0) + goto error_init; + + err = rtlsdr_set_sample_rate(st->dev, NRSC5_SAMPLE_RATE_CU8); + if (err) goto error; + err = rtlsdr_set_tuner_gain_mode(st->dev, 1); + if (err) goto error; + err = rtlsdr_set_offset_tuning(st->dev, 1); + if (err && err != -2) goto error; + + nrsc5_init(st); + + *result = st; + return 0; + +error: + log_error("nrsc5_open error: %d", err); + rtlsdr_close(st->dev); +error_init: + free(st); + *result = NULL; + return 1; +} + +int nrsc5_open_file(nrsc5_t **result, FILE *fp) +{ + nrsc5_t *st = nrsc5_alloc(); + st->iq_file = fp; + nrsc5_init(st); + + *result = st; + return 0; +} + +int nrsc5_open_pipe(nrsc5_t **result) +{ + nrsc5_t *st = nrsc5_alloc(); + nrsc5_init(st); + + *result = st; + return 0; +} + +int nrsc5_open_rtltcp(nrsc5_t **result, int socket) +{ + int err; + nrsc5_t *st = nrsc5_alloc(); + + st->rtltcp = rtltcp_open(socket); + if (st->rtltcp == NULL) + goto error; + + err = rtltcp_set_sample_rate(st->rtltcp, NRSC5_SAMPLE_RATE_CU8); + if (err) goto error; + err = rtltcp_set_tuner_gain_mode(st->rtltcp, 1); + if (err) goto error; + err = rtltcp_set_offset_tuning(st->rtltcp, 1); + if (err) goto error; + + nrsc5_init(st); + + *result = st; + return 0; +error: + free(st); + *result = NULL; + return 1; +} + +void nrsc5_close(nrsc5_t *st) +{ + if (!st) + return; + + if (using_worker(st)) + { + // signal the worker to exit + pthread_mutex_lock(&st->worker_mutex); + st->closed = 1; + pthread_cond_broadcast(&st->worker_cond); + pthread_mutex_unlock(&st->worker_mutex); + + // wait for worker to finish + pthread_join(st->worker, NULL); + } + + if (st->dev) + rtlsdr_close(st->dev); + if (st->iq_file) + fclose(st->iq_file); + if (st->rtltcp) + rtltcp_close(st->rtltcp); + + input_free(&st->input); + output_free(&st->output); + free(st); +} + +void nrsc5_start(nrsc5_t *st) +{ + if (using_worker(st)) + { + // signal the worker to start + pthread_mutex_lock(&st->worker_mutex); + st->stopped = 0; + pthread_cond_broadcast(&st->worker_cond); + pthread_mutex_unlock(&st->worker_mutex); + } +} + +void nrsc5_stop(nrsc5_t *st) +{ + if (using_worker(st)) + { + // signal the worker to stop + pthread_mutex_lock(&st->worker_mutex); + st->stopped = 1; + pthread_cond_broadcast(&st->worker_cond); + pthread_mutex_unlock(&st->worker_mutex); + + // wait for worker to stop + pthread_mutex_lock(&st->worker_mutex); + while (st->stopped != st->worker_stopped) + pthread_cond_wait(&st->worker_cond, &st->worker_mutex); + pthread_mutex_unlock(&st->worker_mutex); + } +} + +int nrsc5_set_mode(nrsc5_t *st, int mode) +{ + if (mode == NRSC5_MODE_FM || mode == NRSC5_MODE_AM) + { + st->mode = mode; + input_set_mode(&st->input); + return 0; + } + return 1; +} + +int nrsc5_set_bias_tee(nrsc5_t *st, int on) +{ + if (st->dev) + { + int err = rtlsdr_set_bias_tee(st->dev, on); + if (err) + return 1; + } + else if (st->rtltcp) + { + int err = rtltcp_set_bias_tee(st->rtltcp, on); + if (err) + return 1; + } + return 0; +} + +int nrsc5_set_direct_sampling(nrsc5_t *st, int on) +{ + if (st->dev) + { + int err = rtlsdr_set_direct_sampling(st->dev, on); + if (err) + return 1; + } + else if (st->rtltcp) + { + int err = rtltcp_set_direct_sampling(st->rtltcp, on); + if (err) + return 1; + } + return 0; +} + +int nrsc5_set_freq_correction(nrsc5_t *st, int ppm_error) +{ + if (st->dev) + { + int err = rtlsdr_set_freq_correction(st->dev, ppm_error); + if (err && err != -2) + return 1; + } + else if (st->rtltcp) + { + int err = rtltcp_set_freq_correction(st->rtltcp, ppm_error); + if (err) + return 1; + } + return 0; +} + +void nrsc5_get_frequency(nrsc5_t *st, float *freq) +{ + if (st->dev) + *freq = rtlsdr_get_center_freq(st->dev); + else + *freq = st->freq; +} + +int nrsc5_set_frequency(nrsc5_t *st, float freq) +{ + if (st->freq == freq) + return 0; + if (!st->stopped) + return 1; + + if (st->dev && rtlsdr_set_center_freq(st->dev, freq) != 0) + return 1; + if (st->rtltcp && rtltcp_set_center_freq(st->rtltcp, freq) != 0) + return 1; + + nrsc5_reset(st); + + st->freq = freq; + return 0; +} + +NRSC5_API int nrsc5_reset(nrsc5_t *st) { + if (!st->stopped) + return 1; + if (st->auto_gain) + st->gain = -1; + + input_reset(&st->input); + output_reset(&st->output); + return 0; +} + +void nrsc5_get_gain(nrsc5_t *st, float *gain) +{ + if (st->dev) + *gain = rtlsdr_get_tuner_gain(st->dev) / 10.0f; + else + *gain = st->gain; +} + +int nrsc5_set_gain(nrsc5_t *st, float gain) +{ + if (st->gain == gain) + return 0; + if (!st->stopped) + return 1; + + if (st->dev && rtlsdr_set_tuner_gain(st->dev, gain * 10) != 0) + return 1; + if (st->rtltcp && rtltcp_set_tuner_gain(st->rtltcp, gain * 10) != 0) + return 1; + + st->gain = gain; + return 0; +} + +void nrsc5_set_auto_gain(nrsc5_t *st, int enabled) +{ + st->auto_gain = enabled; + st->gain = -1; +} + +void nrsc5_set_callback(nrsc5_t *st, nrsc5_callback_t callback, void *opaque) +{ + if (using_worker(st)) + pthread_mutex_lock(&st->worker_mutex); + st->callback = callback; + st->callback_opaque = opaque; + if (using_worker(st)) + pthread_mutex_unlock(&st->worker_mutex); +} + +int nrsc5_pipe_samples_cu8(nrsc5_t *st, const uint8_t *samples, unsigned int length) +{ + unsigned int sample_groups; + + while (st->leftover_u8_num > 0 && length > 0) + { + st->leftover_u8[st->leftover_u8_num++] = samples[0]; + samples++; + length--; + + if (st->leftover_u8_num == 4) { + input_push_cu8(&st->input, st->leftover_u8, 4); + st->leftover_u8_num = 0; + break; + } + } + + sample_groups = length / 4; + input_push_cu8(&st->input, samples, sample_groups * 4); + samples += (sample_groups * 4); + length -= (sample_groups * 4); + + while (length > 0) + { + st->leftover_u8[st->leftover_u8_num++] = samples[0]; + samples++; + length--; + } + + return 0; +} + +int nrsc5_pipe_samples_cs16(nrsc5_t *st, const int16_t *samples, unsigned int length) +{ + unsigned int sample_groups; + + if (st->leftover_s16_num == 1 && length > 0) + { + st->leftover_s16[st->leftover_s16_num++] = samples[0]; + samples++; + length--; + + input_push_cs16(&st->input, st->leftover_s16, 2); + st->leftover_s16_num = 0; + } + + sample_groups = length / 2; + input_push_cs16(&st->input, samples, sample_groups * 2); + samples += (sample_groups * 2); + length -= (sample_groups * 2); + + if (length == 1) + st->leftover_s16[st->leftover_s16_num++] = samples[0]; + + return 0; +} + +int nrsc5_pipe_samples_cf32(nrsc5_t *st, const float *samples, unsigned int length) +{ + if (length % 2 != 0) + { + return -1; + } + + input_push_cf32(&st->input, samples, length); + return 0; +} + +void nrsc5_report(nrsc5_t *st, const nrsc5_event_t *evt) +{ + if (st->callback) + st->callback(evt, st->callback_opaque); +} + +void nrsc5_report_lost_device(nrsc5_t *st) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_LOST_DEVICE; + nrsc5_report(st, &evt); +} + +void nrsc5_report_agc(nrsc5_t *st, float gain_db, float peak_dbfs, int is_final) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_AGC; + evt.agc.gain_db = gain_db; + evt.agc.peak_dbfs = peak_dbfs; + evt.agc.is_final = is_final; + nrsc5_report(st, &evt); +} + +void nrsc5_report_iq(nrsc5_t *st, const void *data, size_t count) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_IQ; + evt.iq.data = data; + evt.iq.count = count; + nrsc5_report(st, &evt); +} + +void nrsc5_report_sync(nrsc5_t *st, float freq_offset, int psmi, int pli, int hppi, int aabi, int rdbi) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_SYNC; + evt.sync.freq_offset = freq_offset; + evt.sync.psmi = psmi; + evt.sync.pli = pli; + evt.sync.hppi = hppi; + evt.sync.aabi = aabi; + evt.sync.rdbi = rdbi; + nrsc5_report(st, &evt); +} + +void nrsc5_report_lost_sync(nrsc5_t *st) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_LOST_SYNC; + nrsc5_report(st, &evt); +} + +void nrsc5_report_hdc(nrsc5_t *st, unsigned int program, const packet_t* pkt) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_HDC; + evt.hdc.program = program; + evt.hdc.data = NULL; + evt.hdc.count = 0; + evt.hdc.flags = NRSC5_PKT_FLAGS_NONE; + + if (pkt->shape == PACKET_FULL) + { + evt.hdc.data = pkt->data; + evt.hdc.count = pkt->size; + } + if (pkt->flags & PACKET_FLAG_CRC_ERROR) + evt.hdc.flags |= NRSC5_PKT_FLAGS_CRC_ERROR; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_audio(nrsc5_t *st, unsigned int program, const int16_t *data, size_t count, unsigned int flags) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_AUDIO; + evt.audio.program = program; + evt.audio.data = data; + evt.audio.count = count; + evt.audio.flags = flags; + nrsc5_report(st, &evt); +} + +void nrsc5_report_mer(nrsc5_t *st, float lower, float upper) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_MER; + evt.mer.lower = lower; + evt.mer.upper = upper; + nrsc5_report(st, &evt); +} + +void nrsc5_report_ber(nrsc5_t *st, float cber) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_BER; + evt.ber.cber = cber; + nrsc5_report(st, &evt); +} + +void nrsc5_report_stream(nrsc5_t *st, uint16_t seq, unsigned int size, const uint8_t *data, + nrsc5_sig_service_t *service, nrsc5_sig_component_t *component) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_STREAM; + evt.stream.port = component->data.port; + evt.stream.seq = seq; + evt.stream.size = size; + evt.stream.mime = component->data.mime; + evt.stream.data = data; + evt.stream.service = service; + evt.stream.component = component; + nrsc5_report(st, &evt); +} + +void nrsc5_report_packet(nrsc5_t *st, uint16_t seq, unsigned int size, const uint8_t *data, + nrsc5_sig_service_t *service, nrsc5_sig_component_t *component) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_PACKET; + evt.packet.port = component->data.port; + evt.packet.seq = seq; + evt.packet.size = size; + evt.packet.mime = component->data.mime; + evt.packet.data = data; + evt.packet.service = service; + evt.packet.component = component; + nrsc5_report(st, &evt); +} + +void nrsc5_report_lot(nrsc5_t *st, int event, unsigned int lot, unsigned int size, uint32_t mime, + const char *name, const uint8_t *data, struct tm *expiry_utc, + nrsc5_sig_service_t *service, nrsc5_sig_component_t *component) +{ + nrsc5_event_t evt; + + evt.event = event; + evt.lot.port = component->data.port; + evt.lot.lot = lot; + evt.lot.size = size; + evt.lot.mime = mime; + evt.lot.name = name; + evt.lot.data = data; + evt.lot.expiry_utc = expiry_utc; + evt.lot.service = service; + evt.lot.component = component; + nrsc5_report(st, &evt); +} + +void nrsc5_report_lot_fragment(nrsc5_t *st, unsigned int lot, unsigned int seq, unsigned int repeat, int is_duplicate, + unsigned int size, unsigned int bytes_so_far, const uint8_t *data, + nrsc5_sig_service_t *service, nrsc5_sig_component_t *component) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_LOT_FRAGMENT; + evt.lot_fragment.lot = lot; + evt.lot_fragment.seq = seq; + evt.lot_fragment.repeat = repeat; + evt.lot_fragment.is_duplicate = is_duplicate; + evt.lot_fragment.size = size; + evt.lot_fragment.bytes_so_far = bytes_so_far; + evt.lot_fragment.data = data; + evt.lot_fragment.service = service; + evt.lot_fragment.component = component; + nrsc5_report(st, &evt); +} + +static uint8_t convert_sig_component_type(uint8_t type) +{ + switch (type) + { + case SIG_COMPONENT_AUDIO: return NRSC5_SIG_COMPONENT_AUDIO; + case SIG_COMPONENT_DATA: return NRSC5_SIG_COMPONENT_DATA; + default: + assert(0 && "Invalid component type"); + return 0; + } +} + +static uint8_t convert_sig_service_type(uint8_t type) +{ + switch (type) + { + case SIG_SERVICE_AUDIO: return NRSC5_SIG_SERVICE_AUDIO; + case SIG_SERVICE_DATA: return NRSC5_SIG_SERVICE_DATA; + default: + assert(0 && "Invalid service type"); + return 0; + } +} + +void nrsc5_report_audio_service(nrsc5_t *st, unsigned int program, unsigned int access, unsigned int type, + unsigned int codec_mode, unsigned int blend_control, int digital_audio_gain, + unsigned int common_delay, unsigned int latency) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_AUDIO_SERVICE; + evt.audio_service.access = access; + evt.audio_service.program = program; + evt.audio_service.type = type; + evt.audio_service.codec_mode = codec_mode; + evt.audio_service.blend_control = blend_control; + evt.audio_service.digital_audio_gain = digital_audio_gain; + evt.audio_service.common_delay = common_delay; + evt.audio_service.latency = latency; + nrsc5_report(st, &evt); +} + +void nrsc5_report_sig(nrsc5_t *st, sig_service_t *services) +{ + nrsc5_sig_service_t *service = NULL; + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_SIG; + + // convert internal structures to public structures + for (unsigned int i = 0; i < MAX_SIG_SERVICES; i++) + { + nrsc5_sig_component_t *component = NULL; + nrsc5_sig_service_t *prev = service; + + if (services[i].type == SIG_SERVICE_NONE) + break; + + service = calloc(1, sizeof(nrsc5_sig_service_t)); + service->type = convert_sig_service_type(services[i].type); + service->number = services[i].number; + service->name = services[i].name; + + if (prev == NULL) + evt.sig.services = service; + else + prev->next = service; + + for (unsigned int j = 0; j < MAX_SIG_COMPONENTS; j++) + { + nrsc5_sig_component_t *prevc = component; + sig_component_t *internal = &services[i].component[j]; + + if (internal->type == SIG_COMPONENT_NONE) + continue; + + component = calloc(1, sizeof(nrsc5_sig_component_t)); + component->type = convert_sig_component_type(internal->type); + component->id = internal->id; + + if (internal->type == SIG_COMPONENT_AUDIO) + { + component->audio.port = internal->audio.port; + component->audio.type = internal->audio.type; + component->audio.mime = internal->audio.mime; + service->audio_component = component; + } + else if (internal->type == SIG_COMPONENT_DATA) + { + component->data.port = internal->data.port; + component->data.service_data_type = internal->data.service_data_type; + component->data.type = internal->data.type; + component->data.mime = internal->data.mime; + } + + // cache the service & component records for use in API events + internal->service_ext = service; + internal->component_ext = component; + + if (prevc == NULL) + service->components = component; + else + prevc->next = component; + } + } + + nrsc5_report(st, &evt); + st->sig_table = evt.sig.services; +} + +void nrsc5_clear_sig(nrsc5_t *st) +{ + nrsc5_sig_service_t *service = NULL; + + // free the data structures + for (service = st->sig_table; service != NULL; ) + { + void *p; + nrsc5_sig_component_t *component; + + for (component = service->components; component != NULL; ) + { + p = component; + component = component->next; + free(p); + } + + p = service; + service = service->next; + free(p); + } + st->sig_table = NULL; +} + +void nrsc5_report_sis(nrsc5_t *st, const char *country_code, int fcc_facility_id, const char *name, + const char *slogan, const char *message, const char *alert, const uint8_t *cnt, int cnt_length, + int category1, int category2, int location_format, int num_locations, const int *locations, + float latitude, float longitude, int altitude, nrsc5_sis_asd_t *audio_services, + nrsc5_sis_dsd_t *data_services) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_SIS; + evt.sis.country_code = country_code; + evt.sis.fcc_facility_id = fcc_facility_id; + evt.sis.name = name; + evt.sis.slogan = slogan; + evt.sis.message = message; + evt.sis.alert = alert; + evt.sis.alert_cnt = cnt; + evt.sis.alert_cnt_length = cnt_length; + evt.sis.alert_category1 = category1; + evt.sis.alert_category2 = category2; + evt.sis.alert_location_format = location_format; + evt.sis.alert_num_locations = num_locations; + evt.sis.alert_locations = locations; + evt.sis.latitude = latitude; + evt.sis.longitude = longitude; + evt.sis.altitude = altitude; + evt.sis.audio_services = audio_services; + evt.sis.data_services = data_services; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_station_id(nrsc5_t *st, const char *country_code, int fcc_facility_id) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_STATION_ID; + evt.station_id.country_code = country_code; + evt.station_id.fcc_facility_id = fcc_facility_id; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_station_name(nrsc5_t *st, const char *name) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_STATION_NAME; + evt.station_name.name = name; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_station_slogan(nrsc5_t *st, const char *slogan) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_STATION_SLOGAN; + evt.station_slogan.slogan = slogan; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_station_message(nrsc5_t *st, const char *message) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_STATION_MESSAGE; + evt.station_message.message = message; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_station_location(nrsc5_t *st, float latitude, float longitude, int altitude) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_STATION_LOCATION; + evt.station_location.latitude = latitude; + evt.station_location.longitude = longitude; + evt.station_location.altitude = altitude; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_asd(nrsc5_t *st, unsigned int program, unsigned int access, unsigned int type, unsigned int sound_exp) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_AUDIO_SERVICE_DESCRIPTOR; + evt.asd.program = program; + evt.asd.access = access; + evt.asd.type = type; + evt.asd.sound_exp = sound_exp; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_dsd(nrsc5_t *st, unsigned int access, unsigned int type, uint32_t mime_type) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_DATA_SERVICE_DESCRIPTOR; + evt.dsd.access = access; + evt.dsd.type = type; + evt.dsd.mime_type = mime_type; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_emergency_alert(nrsc5_t *st, const char *message, const uint8_t *control_data, + int control_data_length, int category1, int category2, int location_format, + int num_locations, const int *locations) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_EMERGENCY_ALERT; + evt.emergency_alert.message = message; + evt.emergency_alert.control_data = control_data; + evt.emergency_alert.control_data_length = control_data_length; + evt.emergency_alert.category1 = category1; + evt.emergency_alert.category2 = category2; + evt.emergency_alert.location_format = location_format; + evt.emergency_alert.num_locations = num_locations; + evt.emergency_alert.locations = locations; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_exciter_info(nrsc5_t *st, const char* manufacturer_id, + const int core_version[NRSC5_DEVICE_VERSION_LENGTH], + const int manufacturer_version[NRSC5_DEVICE_VERSION_LENGTH], + const int core_status, const int manufacturer_status, + const int importer_connected) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_EXCITER_INFO; + + memcpy(evt.exciter_info.core_version, core_version, sizeof(int) * NRSC5_DEVICE_VERSION_LENGTH); + memcpy(evt.exciter_info.manufacturer_version, manufacturer_version, sizeof(int) * NRSC5_DEVICE_VERSION_LENGTH); + + evt.exciter_info.manufacturer_id = manufacturer_id; + evt.exciter_info.core_status = core_status; + evt.exciter_info.manufacturer_status = manufacturer_status; + evt.exciter_info.importer_connected = importer_connected; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_importer_info(nrsc5_t *st, const char* manufacturer_id, + const int core_version[NRSC5_DEVICE_VERSION_LENGTH], + const int manufacturer_version[NRSC5_DEVICE_VERSION_LENGTH], + const int core_status, const int manufacturer_status) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_IMPORTER_INFO; + + memcpy(evt.importer_info.core_version, core_version, sizeof(int) * NRSC5_DEVICE_VERSION_LENGTH); + memcpy(evt.importer_info.manufacturer_version, manufacturer_version, sizeof(int) * NRSC5_DEVICE_VERSION_LENGTH); + + evt.importer_info.manufacturer_id = manufacturer_id; + evt.importer_info.core_status = core_status; + evt.importer_info.manufacturer_status = manufacturer_status; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_leap_second_offset(nrsc5_t *st, const int pending_offset, const int current_offset, + const unsigned int pending_alfn) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_LEAP_SECOND_OFFSET; + evt.leap_second_offset.pending_offset = pending_offset; + evt.leap_second_offset.current_offset = current_offset; + evt.leap_second_offset.pending_alfn = pending_alfn; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_local_time(nrsc5_t *st, const int tzo, const int dst_regional, + const int dst_local, const int dst_schedule) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_LOCAL_TIME; + evt.local_time.utc_offset = tzo; + evt.local_time.dst_regional = dst_regional; + evt.local_time.dst_local = dst_local; + evt.local_time.dst_schedule = dst_schedule; + + nrsc5_report(st, &evt); +} + +void nrsc5_report_here_image(nrsc5_t *st, int image_type, int seq, int n1, int n2, unsigned int timestamp, + float latitude1, float longitude1, float latitude2, float longitude2, + const char *name, unsigned int size, const uint8_t *data) +{ + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_HERE_IMAGE; + evt.here_image.image_type = image_type; + evt.here_image.seq = seq; + evt.here_image.n1 = n1; + evt.here_image.n2 = n2; +#if defined(WIN32) || defined(_WIN32) + __time64_t ts = (__time64_t) timestamp; + evt.here_image.time_utc = _gmtime64(&ts); +#else + time_t ts = (time_t) timestamp; + evt.here_image.time_utc = gmtime(&ts); +#endif + evt.here_image.latitude1 = latitude1; + evt.here_image.longitude1 = longitude1; + evt.here_image.latitude2 = latitude2; + evt.here_image.longitude2 = longitude2; + evt.here_image.name = name; + evt.here_image.size = size; + evt.here_image.data = data; + + nrsc5_report(st, &evt); +} diff --git a/third_party/nrsc5/upstream/src/output.c b/third_party/nrsc5/upstream/src/output.c new file mode 100644 index 000000000..563c02f0d --- /dev/null +++ b/third_party/nrsc5/upstream/src/output.c @@ -0,0 +1,983 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "defines.h" +#include "output.h" +#include "private.h" +#include "unicode.h" +#include "here_images.h" + +void output_align(output_t *st, unsigned int program, unsigned int stream_id, unsigned int offset) +{ + elastic_buffer_t *elastic = &st->elastic[program][stream_id]; + elastic->audio_offset = offset; +} + +static int is_complete_pkt(const packet_t* pkt) +{ + return pkt->shape == PACKET_FULL; +} + +static int is_crc_ok(const packet_t* pkt) +{ + return !(pkt->flags & PACKET_FLAG_CRC_ERROR); +} + +void output_push(output_t *st, const packet_ref_t* ref) +{ + elastic_buffer_t *elastic = &st->elastic[ref->program][ref->stream_id]; + packet_t* pkt = &elastic->packets[ref->seq]; + + if (ref->stream_id != 0) + return; // TODO: Process enhanced stream + + if (pkt->shape == PACKET_FULL) + log_warn("Packet %d already exists in elastic buffer for program %d, stream %d. Overwriting.", ref->seq, ref->program, ref->stream_id); + + if (ref->shape == PACKET_HALF_BACK && pkt->shape == PACKET_HALF_FRONT) + { + pkt->flags |= ref->flags; + pkt->shape = PACKET_FULL; + + if (is_crc_ok(pkt)) + { + memcpy(pkt->data + pkt->size, ref->data, ref->size); + pkt->size += ref->size; + } + else + { + pkt->size = 0; + } + } + else + { + if (ref->shape == PACKET_HALF_BACK) + return; + + pkt->flags = ref->flags; + pkt->shape = ref->shape; + + if (is_crc_ok(pkt)) + { + memcpy(pkt->data, ref->data, ref->size); + pkt->size = ref->size; + } + else + { + pkt->size = 0; + } + } +} + +static void pkt_reset(packet_t* pkt) +{ + pkt->size = 0; + pkt->flags = PACKET_FLAG_NONE; + pkt->shape = PACKET_NONE; +} + +void output_advance(output_t *st) +{ + unsigned int program, frame; + unsigned int audio_frames = (st->radio->mode == NRSC5_MODE_FM ? 2 : 4); + + for (program = 0; program < MAX_PROGRAMS; program++) + { + elastic_buffer_t *elastic = &st->elastic[program][0]; // TODO: Process enhanced stream + + if (elastic->audio_offset == -1) + continue; + + for (frame = 0; frame < audio_frames; frame++) + { + packet_t* pkt = &elastic->packets[elastic->audio_offset]; +#ifdef USE_FAAD2 + int produced_audio = 0; + unsigned int flags = NRSC5_AUDIO_FLAGS_NONE; +#endif + + if (is_complete_pkt(pkt)) + { + nrsc5_report_hdc(st->radio, program, pkt); + } + + if (is_complete_pkt(pkt) && is_crc_ok(pkt)) + { +#ifdef USE_FAAD2 + void *buffer; + NeAACDecFrameInfo info; + + if (!st->aacdec[program]) + { + NeAACDecInitHDC(&st->aacdec[program]); + } + + buffer = NeAACDecDecode(st->aacdec[program], &info, pkt->data, pkt->size); + if (info.error > 0) + { + log_warn("Decode error: %s", NeAACDecGetErrorMessage(info.error)); + flags |= NRSC5_AUDIO_FLAGS_DECODING_ERROR; + } + + if (info.error == 0 && info.samples > 0) + { + nrsc5_report_audio(st->radio, program, buffer, info.samples, flags); + produced_audio = 1; + } +#endif + } + else + { +#ifdef USE_FAAD2 + // Reset decoder. Missing packets. + if (st->aacdec[program]) + { + NeAACDecClose(st->aacdec[program]); + st->aacdec[program] = NULL; + } +#endif + } + + pkt_reset(pkt); + +#ifdef USE_FAAD2 + if (!produced_audio) + { + flags |= NRSC5_AUDIO_FLAGS_UNAVAILABLE; + nrsc5_report_audio(st->radio, program, st->silence, NRSC5_AUDIO_FRAME_SAMPLES * 2, flags); + } +#endif + + elastic->audio_offset = (elastic->audio_offset + 1) % ELASTIC_BUFFER_LEN; + } + } +} + +static void aas_free_lot(aas_file_t *file) +{ + free(file->name); + if (file->fragments) + { + for (int i = 0; i < MAX_LOT_FRAGMENTS; i++) + free(file->fragments[i]); + free(file->fragments); + } + memset(file, 0, sizeof(*file)); +} + +static void aas_reset(output_t *st) +{ + free(st->sig_bytes); + st->sig_bytes = NULL; + st->sig_len = 0; + + for (int i = 0; i < MAX_SIG_SERVICES; i++) + { + sig_service_t *service = &st->services[i]; + free(service->name); + + for (int j = 0; j < MAX_SIG_COMPONENTS; j++) + { + sig_component_t *component = &service->component[j]; + + if (component->type == SIG_COMPONENT_DATA) + for (int k = 0; k < MAX_LOT_FILES; k++) + aas_free_lot(&component->data.lot_files[k]); + } + } + st->lot_lru_counter = 1; + + memset(st->services, 0, sizeof(st->services)); + nrsc5_clear_sig(st->radio); +} + +void output_reset(output_t *st) +{ + aas_reset(st); + + for (int i = 0; i < MAX_PROGRAMS; i++) + { + for (int j = 0; j < MAX_STREAMS; j++) + { + for (int k = 0; k < ELASTIC_BUFFER_LEN; k++) + { + pkt_reset(&st->elastic[i][j].packets[k]); + } + st->elastic[i][j].audio_offset = -1; + } +#ifdef USE_FAAD2 + if (st->aacdec[i]) + NeAACDecClose(st->aacdec[i]); + st->aacdec[i] = NULL; +#endif + } + + here_images_reset(&st->here_images); +} + +void output_init(output_t *st, nrsc5_t *radio) +{ + st->radio = radio; +#ifdef USE_FAAD2 + for (int i = 0; i < MAX_PROGRAMS; i++) + st->aacdec[i] = NULL; + memset(st->silence, 0, sizeof(st->silence)); +#endif + + memset(st->services, 0, sizeof(st->services)); + here_images_init(&st->here_images, radio); + + output_reset(st); +} + +void output_free(output_t *st) +{ + output_reset(st); +} + +static unsigned int id3_length(uint8_t *buf) +{ + return ((buf[0] & 0x7f) << 21) | ((buf[1] & 0x7f) << 14) | ((buf[2] & 0x7f) << 7) | (buf[3] & 0x7f); +} + +static char* id3_encode_utf8(uint8_t enc, uint8_t *buf, unsigned int len) +{ + char *text; + + if (enc == 0) + return iso_8859_1_to_utf_8(buf, len); + else if (enc == 1) + return ucs_2_to_utf_8(buf, len); + else + log_warn("Invalid encoding: %d", enc); + + text = malloc(1); + text[0] = 0; + return text; +} + +static char *id3_text(uint8_t *buf, unsigned int frame_len) +{ + if (frame_len > 0) + return id3_encode_utf8(buf[0], buf + 1, frame_len - 1); + else + return id3_encode_utf8(0, NULL, 0); +} + +static uint8_t* memchr_enc(const int enc, uint8_t *buf, const unsigned int len) +{ + if (enc == 0) + { + return memchr(buf, 0, len); + } + else if (enc == 1) + { + for (unsigned int i = 0; i < len - 1; i += 2) + { + if (buf[i] == 0 && buf[i + 1] == 0) + { + return buf + i; + } + } + return NULL; + } + else + log_warn("Invalid encoding: %d", enc); + return NULL; +} + +static int parse_digits(const char *p, size_t n) +{ + int value = 0; + + for (size_t i = 0; i < n; i++) + { + if (p[i] < '0' || p[i] > '9') + return -1; + + value = value * 10 + (p[i] - '0'); + } + + return value; +} + +static void output_id3(output_t *st, unsigned int program, uint8_t *buf, unsigned int len) +{ + char *title = NULL, *artist = NULL, *album = NULL, *genre = NULL, *ufid_owner = NULL, *ufid_id = NULL; + uint32_t xhdr_mime = 0; + int xhdr_param = -1, xhdr_lot = -1; + nrsc5_id3_comment_t *comm = NULL; + char *price = NULL, *url = NULL, *seller = NULL, *desc = NULL; + struct tm until = { 0 }; + uint8_t received_as = 0; + + unsigned int off = 0, id3_len; + nrsc5_event_t evt; + + evt.event = NRSC5_EVENT_ID3; + evt.id3.comments = NULL; + + if (len < 10 || memcmp(buf, "ID3\x03\x00", 5) || buf[5]) return; + id3_len = id3_length(buf + 6) + 10; + if (id3_len > len) return; + off += 10; + + while (off + 10 <= id3_len) + { + uint8_t *tag = buf + off; + uint8_t *data = tag + 10; + unsigned int frame_len = ((unsigned int)tag[4] << 24) | (tag[5] << 16) | (tag[6] << 8) | tag[7]; + if (off + 10 + frame_len > id3_len) + break; + + if (memcmp(tag, "TIT2", 4) == 0) + { + free(title); + title = id3_text(data, frame_len); + } + else if (memcmp(tag, "TPE1", 4) == 0) + { + free(artist); + artist = id3_text(data, frame_len); + } + else if (memcmp(tag, "TALB", 4) == 0) + { + free(album); + album = id3_text(data, frame_len); + } + else if (memcmp(tag, "TCON", 4) == 0) + { + free(genre); + genre = id3_text(data, frame_len); + } + else if (memcmp(tag, "UFID", 4) == 0) + { + uint8_t *delim = memchr(data, 0, frame_len); + uint8_t *end = data + frame_len; + + if (delim) + { + free(ufid_owner); + ufid_owner = strdup((char *)data); + + free(ufid_id); + ufid_id = strndup((char *)delim + 1, end - delim - 1); + } + } + else if (memcmp(tag, "COMR", 4) == 0) + { + uint8_t *pos = data; + uint8_t *end = data + frame_len; + if (pos + 1 > end) + { + log_warn("bad COMR tag (frame_len %d)", frame_len); + break; + } + uint8_t enc = data[0]; + pos += 1; + + uint8_t *delim[4]; + uint8_t *delim_end[4]; + int year, mon, mday; + int i; + + for (i = 0; i < 4; i++) + { + int delim_enc = (i >= 2) ? enc : 0; + int delim_len = (delim_enc == 1) ? 2 : 1; + delim[i] = memchr_enc(delim_enc, pos, end - pos); + if (delim[i] == NULL) + { + log_warn("bad COMR tag (frame_len %d)", frame_len); + break; + } + delim_end[i] = delim[i] + delim_len; + pos = delim_end[i]; + + if (i == 0) + { + if (pos + 8 > end) + { + log_warn("bad COMR tag (frame_len %d)", frame_len); + break; + } + + year = parse_digits((char *) delim[0] + 1, 4) - 1900; + mon = parse_digits((char *) delim[0] + 5, 2) - 1; + mday = parse_digits((char *) delim[0] + 7, 2); + + if (year < 0 || mon < 0 || mday < 0) + { + log_warn("Failed to parse valid_until on COMR tag."); + break; + } + + pos += 8; + } + else if (i == 1) + { + if (pos + 1 > end) + { + log_warn("bad COMR tag (frame_len %d)", frame_len); + break; + } + + pos += 1; + } + } + + if (i == 4) + { + price = (char *) data + 1; + until.tm_year = year; + until.tm_mon = mon; + until.tm_mday = mday; + url = (char *) delim_end[0] + 8; + received_as = *(delim_end[1]); + seller = id3_encode_utf8(enc, delim_end[1] + 1, delim[2] - (delim_end[1] + 1)); + desc = id3_encode_utf8(enc, delim_end[2], delim[3] - delim_end[2]); + } + } + else if (memcmp(tag, "COMM", 4) == 0) + { + if (frame_len < 5) + { + log_warn("bad COMM tag (frame_len %d)", frame_len); + } + else + { + uint8_t enc = data[0]; + uint8_t *delim = memchr_enc(enc, data + 4, frame_len - 4); + uint8_t *end = data + frame_len; + + if (delim) + { + nrsc5_id3_comment_t* prev = comm; + uint8_t* end_text = delim + (enc == 1 ? 2 : 1); + + comm = calloc(1, sizeof(nrsc5_id3_comment_t)); + comm->lang = strndup((char*) data + 1, 3); + comm->short_content_desc = id3_encode_utf8(enc, data + 4, delim - (data + 4)); + comm->full_text = id3_encode_utf8(enc, end_text, end - end_text); + + if (prev == NULL) + evt.id3.comments = comm; + else + prev->next = comm; + } + } + } + else if (memcmp(tag, "XHDR", 4) == 0) + { + uint8_t extlen; + + if (frame_len < 6) + { + log_warn("bad XHDR tag (frame_len %d)", frame_len); + } + else + { + xhdr_mime = data[0] | (data[1] << 8) | (data[2] << 16) | ((uint32_t)data[3] << 24); + xhdr_param = data[4]; + extlen = data[5]; + if (6u + extlen != frame_len) + log_warn("bad XHDR tag (frame_len %d, extlen %d)", frame_len, extlen); + else if (xhdr_param == 0 && extlen == 2) + xhdr_lot = data[6] | (data[7] << 8); + else if (xhdr_param == 1 && extlen == 0) + xhdr_lot = -1; + else + log_warn("unhandled XHDR param (frame_len %d, param %d, extlen %d)", frame_len, xhdr_param, extlen); + } + } + else + { + unsigned int i; + char *hex = malloc(3 * frame_len + 1); + for (i = 0; i < frame_len; i++) + sprintf(hex + (3 * i), "%02X ", buf[off + 10 + i]); + hex[3 * i - 1] = 0; + log_debug("%c%c%c%c tag: %s", buf[off], buf[off+1], buf[off+2], buf[off+3], hex); + free(hex); + } + + off += 10 + frame_len; + } + + evt.id3.program = program; + evt.id3.title = title; + evt.id3.artist = artist; + evt.id3.album = album; + evt.id3.genre = genre; + evt.id3.ufid.owner = ufid_owner; + evt.id3.ufid.id = ufid_id; + evt.id3.xhdr.mime = xhdr_mime; + evt.id3.xhdr.param = xhdr_param; + evt.id3.xhdr.lot = xhdr_lot; + evt.id3.commercial.price = price; + evt.id3.commercial.contact_url = url; + evt.id3.commercial.seller = seller; + evt.id3.commercial.description = desc; + evt.id3.commercial.received_as = received_as; + evt.id3.commercial.valid_until = &until; + + nrsc5_report(st->radio, &evt); + + free(title); + free(artist); + free(album); + free(genre); + free(ufid_owner); + free(ufid_id); + free(seller); + free(desc); + + for (comm = evt.id3.comments; comm != NULL; ) + { + void *p = comm; + + free(comm->lang); + free(comm->short_content_desc); + free(comm->full_text); + + comm = comm->next; + free(p); + } +} + +static int find_component(sig_service_t *service, uint8_t component_id) +{ + int component_idx; + + for (component_idx = 0; component_idx < MAX_SIG_COMPONENTS; component_idx++) + { + if (service->component[component_idx].type == SIG_COMPONENT_NONE) + break; // reached a free slot in the component list + + if (service->component[component_idx].id == component_id) + { + log_warn("duplicate SIG component: service %d, component %d", service->number, component_id); + break; + } + } + + return component_idx; +} + +static void parse_sig(output_t *st, uint8_t *buf, unsigned int len) +{ + uint8_t *p = buf; + sig_service_t *service = NULL; + + if (st->sig_bytes) + { + if ((len == st->sig_len) && (memcmp(buf, st->sig_bytes, len) == 0)) + { + // previously parsed SIG table has not changed + return; + } + else + { + aas_reset(st); + } + } + + st->sig_bytes = (uint8_t *) malloc(len); + memcpy(st->sig_bytes, buf, len); + st->sig_len = len; + + while (p < buf + len) + { + uint8_t type = *p++; + switch (type & 0xF0) + { + case 0x40: + { + uint16_t service_number = p[0] | (p[1] << 8); + int service_idx; + + for (service_idx = 0; service_idx < MAX_SIG_SERVICES; service_idx++) + { + if (st->services[service_idx].type == SIG_SERVICE_NONE) + break; // reached a free slot in the service list + + if (st->services[service_idx].number == service_number) + { + log_warn("duplicate SIG service: %d", service_number); + free(st->services[service_idx].name); + memset(&st->services[service_idx], 0, sizeof(st->services[service_idx])); + break; + } + } + + if (service_idx == MAX_SIG_SERVICES) + { + log_warn("Too many SIG services"); + goto done; + } + + service = &st->services[service_idx]; + service->type = type == 0x40 ? SIG_SERVICE_AUDIO : SIG_SERVICE_DATA; + service->number = service_number; + + p += 3; + break; + } + case 0x60: + { + // length (1-byte) value (length - 1) + uint8_t l = *p++; + if (service == NULL) + { + log_warn("Invalid SIG data (%02X)", type); + goto done; + } + else if (type == 0x69) + { + service->name = iso_8859_1_to_utf_8(p + 1, l - 2); + } + else if (type == 0x67) + { + sig_component_t *comp; + uint8_t component_id = p[0]; + int component_idx = find_component(service, component_id); + + if (component_idx == MAX_SIG_COMPONENTS) + { + log_warn("Too many SIG components"); + goto done; + } + + comp = &service->component[component_idx]; + comp->type = SIG_COMPONENT_DATA; + comp->id = component_id; + comp->data.port = p[1] | (p[2] << 8); + comp->data.service_data_type = p[3] | (p[4] << 8); + comp->data.type = p[5]; + comp->data.mime = p[8] | (p[9] << 8) | (p[10] << 16) | ((uint32_t)p[11] << 24); + } + else if (type == 0x66) + { + sig_component_t *comp; + uint8_t component_id = p[0]; + int component_idx = find_component(service, component_id); + + if (component_idx == MAX_SIG_COMPONENTS) + { + log_warn("Too many SIG components"); + goto done; + } + + comp = &service->component[component_idx]; + comp->type = SIG_COMPONENT_AUDIO; + comp->id = component_id; + comp->audio.port = p[1]; + comp->audio.type = p[2]; + comp->audio.mime = p[7] | (p[8] << 8) | (p[9] << 16) | ((uint32_t)p[10] << 24); + } + p += l - 1; + break; + } + default: + log_warn("unexpected byte %02X", *p); + goto done; + } + } + +done: + nrsc5_report_sig(st->radio, st->services); +} + +static sig_component_t *find_port(output_t *st, uint16_t port_id) +{ + unsigned int i, j; + for (i = 0; i < MAX_SIG_SERVICES; i++) + { + sig_service_t *service = &st->services[i]; + if (service->type == SIG_SERVICE_NONE) + break; + + for (j = 0; j < MAX_SIG_COMPONENTS; j++) + { + sig_component_t *component = &service->component[j]; + if (component->type == SIG_COMPONENT_NONE) + break; + + if ((component->type == SIG_COMPONENT_DATA) && (component->data.port == port_id)) + return component; + } + } + return NULL; +} + +static aas_file_t *find_lot(sig_component_t *component, unsigned int lot) +{ + for (int i = 0; i < MAX_LOT_FILES; i++) + { + if (component->data.lot_files[i].timestamp == 0) + continue; + if (component->data.lot_files[i].lot == lot) + return &component->data.lot_files[i]; + } + return NULL; +} + +static aas_file_t *find_free_lot(sig_component_t *component) +{ + unsigned int min_timestamp = UINT_MAX; + unsigned int min_idx = 0; + aas_file_t *file; + + for (int i = 0; i < MAX_LOT_FILES; i++) + { + unsigned int timestamp = component->data.lot_files[i].timestamp; + if (timestamp == 0) + return &component->data.lot_files[i]; + if (timestamp < min_timestamp) + { + min_timestamp = timestamp; + min_idx = i; + } + } + + file = &component->data.lot_files[min_idx]; + aas_free_lot(file); + return file; +} + +static void process_port(output_t *st, uint16_t port_id, uint16_t seq, uint8_t *buf, unsigned int len) +{ + sig_component_t *component; + + if (st->services[0].type == SIG_SERVICE_NONE) + { + // Wait until we receive SIG data. + return; + } + + component = find_port(st, port_id); + if (component == NULL) + { + log_debug("port %04X not defined in SIG table", port_id); + return; + } + + switch (component->data.type) + { + case NRSC5_AAS_TYPE_STREAM: + { + nrsc5_report_stream(st->radio, seq, len, buf, component->service_ext, component->component_ext); + if (component->data.mime == NRSC5_MIME_HERE_IMAGE) + here_images_push(&st->here_images, seq, len, buf); + break; + } + case NRSC5_AAS_TYPE_PACKET: + { + nrsc5_report_packet(st->radio, seq, len, buf, component->service_ext, component->component_ext); + break; + } + case NRSC5_AAS_TYPE_LOT: + { + if (len < 8) + { + log_warn("bad fragment (port %04X, len %d)", port_id, len); + return; + } + uint8_t hdrlen = buf[0]; + uint8_t repeat = buf[1]; + uint16_t lot = buf[2] | (buf[3] << 8); + uint32_t seq = buf[4] | (buf[5] << 8) | (buf[6] << 16) | ((uint32_t)buf[7] << 24); + if (hdrlen < 8 || hdrlen > len) + { + log_warn("wrong header len (port %04X, len %d, hdrlen %d)", port_id, len, hdrlen); + return; + } + buf += 8; + len -= 8; + hdrlen -= 8; + + if (seq >= MAX_LOT_FRAGMENTS) + { + log_warn("sequence too large (%d)", seq); + return; + } + + aas_file_t *file = find_lot(component, lot); + if (file == NULL) + { + file = find_free_lot(component); + file->lot = lot; + file->fragments = calloc(MAX_LOT_FRAGMENTS, sizeof(uint8_t*)); + } + file->timestamp = st->lot_lru_counter++; + + int new_data = 0; + + if (hdrlen > 0) + { + if (hdrlen < 16) + { + log_warn("header is too short (port %04X, len %d, hdrlen %d)", port_id, len, hdrlen); + return; + } + + uint32_t version = buf[0] | (buf[1] << 8) | (buf[2] << 16) | ((uint32_t)buf[3] << 24); + if (version != 1) + log_warn("unknown LOT version: %d", version); + + int year = ((buf[7] << 4) | (buf[6] >> 4)) - 1900; + int mon = (buf[6] & 0xf) - 1; + int mday = (buf[5] >> 3); + int hour = ((buf[5] & 0x7) << 2) | (buf[4] >> 6); + int min = (buf[4] & 0x3f); + + uint32_t size = buf[8] | (buf[9] << 8) | (buf[10] << 16) | ((uint32_t)buf[11] << 24); + uint32_t mime = buf[12] | (buf[13] << 8) | (buf[14] << 16) | ((uint32_t)buf[15] << 24); + buf += 16; + len -= 16; + hdrlen -= 16; + // Everything after the fixed header is the filename. + + if (file->name) + { + if ((hdrlen != strlen(file->name)) + || (memcmp(buf, file->name, hdrlen) != 0) + || (size != file->size) + || (mime != file->mime) + || (year != file->expiry_utc.tm_year) + || (mon != file->expiry_utc.tm_mon) + || (mday != file->expiry_utc.tm_mday) + || (hour != file->expiry_utc.tm_hour) + || (min != file->expiry_utc.tm_min)) + { + // Reset, since metadata has changed + aas_free_lot(file); + file->lot = lot; + file->fragments = calloc(MAX_LOT_FRAGMENTS, sizeof(uint8_t*)); + file->timestamp = st->lot_lru_counter; + new_data = 1; + } + } + else + { + // Metadata received for the first time + new_data = 1; + } + + free(file->name); + file->name = strndup((const char *)buf, hdrlen); + file->size = size; + file->mime = mime; + file->expiry_utc.tm_year = year; + file->expiry_utc.tm_mon = mon; + file->expiry_utc.tm_mday = mday; + file->expiry_utc.tm_hour = hour; + file->expiry_utc.tm_min = min; + + buf += hdrlen; + len -= hdrlen; + hdrlen = 0; + + if (new_data) + { + nrsc5_report_lot(st->radio, NRSC5_EVENT_LOT_HEADER, file->lot, + file->size, file->mime, file->name, NULL, &file->expiry_utc, + component->service_ext, component->component_ext); + } + } + + int is_duplicate = 1; + if (!file->fragments[seq]) + { + new_data = 1; + is_duplicate = 0; + uint8_t *fragment = calloc(LOT_FRAGMENT_SIZE, 1); + if (len > LOT_FRAGMENT_SIZE) + { + log_warn("fragment too large (%d)", len); + break; + } + memcpy(fragment, buf, len); + file->fragments[seq] = fragment; + file->bytes_so_far += len; + } + nrsc5_report_lot_fragment(st->radio, file->lot, seq, repeat, is_duplicate, len, file->bytes_so_far, buf, + component->service_ext, component->component_ext); + + if (new_data && file->size) + { + int complete = 1; + int num_fragments = (file->size + LOT_FRAGMENT_SIZE - 1) / LOT_FRAGMENT_SIZE; + for (int i = 0; i < num_fragments; i++) + { + if (file->fragments[i] == NULL) + { + complete = 0; + break; + } + } + if (complete) + { + uint8_t *data = malloc(num_fragments * LOT_FRAGMENT_SIZE); + for (int i = 0; i < num_fragments; i++) + memcpy(data + i * LOT_FRAGMENT_SIZE, file->fragments[i], LOT_FRAGMENT_SIZE); + nrsc5_report_lot(st->radio, NRSC5_EVENT_LOT, file->lot, file->size, file->mime, + file->name, data, &file->expiry_utc, + component->service_ext, component->component_ext); + free(data); + } + } + break; + } + default: + log_info("unknown port type %d", component->data.type); + break; + } +} + +void output_aas_push(output_t *st, uint8_t *buf, unsigned int len) +{ + uint16_t port = buf[0] | (buf[1] << 8); + uint16_t seq = buf[2] | (buf[3] << 8); + if (port == 0x5100 || (port >= 0x5201 && port <= 0x5207)) + { + // PSD ports + output_id3(st, port & 0x7, buf + 4, len - 4); + } + else if (port == 0x20) + { + // Station Information Guide + parse_sig(st, buf + 4, len - 4); + } + else if (port >= 0x401 && port <= 0x50FF) + { + process_port(st, port, seq, buf + 4, len - 4); + } + else + { + log_warn("unknown AAS port %04X, seq %04X, length %d", port, seq, len); + } +} diff --git a/third_party/nrsc5/upstream/src/output.h b/third_party/nrsc5/upstream/src/output.h new file mode 100644 index 000000000..70fb16402 --- /dev/null +++ b/third_party/nrsc5/upstream/src/output.h @@ -0,0 +1,140 @@ +#pragma once + +#include "config.h" +#include "here_images.h" + +#include + +#ifdef HAVE_FAAD2 +#include +#endif + +#define AUDIO_FRAME_BYTES 8192 +#define MAX_SIG_SERVICES 16 +#define MAX_SIG_COMPONENTS 8 +#define MAX_LOT_FILES 12 +#define LOT_FRAGMENT_SIZE 256 +#define MAX_FILE_BYTES 65536 +#define MAX_LOT_FRAGMENTS (MAX_FILE_BYTES / LOT_FRAGMENT_SIZE) + +enum +{ + PACKET_FLAG_NONE = 0, + PACKET_FLAG_CRC_ERROR = 1 << 0, +}; + +enum +{ + PACKET_NONE = 0, + PACKET_FULL, + PACKET_HALF_FRONT, + PACKET_HALF_BACK, +}; + +enum +{ + SIG_COMPONENT_NONE, + SIG_COMPONENT_DATA, + SIG_COMPONENT_AUDIO +}; + +enum +{ + SIG_SERVICE_NONE, + SIG_SERVICE_DATA, + SIG_SERVICE_AUDIO +}; + +typedef struct +{ + unsigned int timestamp; + char *name; + uint32_t mime; + struct tm expiry_utc; + uint16_t lot; + uint32_t size; + uint32_t bytes_so_far; + uint8_t **fragments; +} aas_file_t; + +typedef struct +{ + uint8_t type; + uint8_t id; + + nrsc5_sig_service_t *service_ext; + nrsc5_sig_component_t *component_ext; + + union + { + struct { + uint16_t port; + uint16_t service_data_type; + uint8_t type; + uint32_t mime; + aas_file_t lot_files[MAX_LOT_FILES]; + } data; + struct { + uint8_t port; + uint8_t type; + uint32_t mime; + } audio; + }; +} sig_component_t; + +typedef struct +{ + uint8_t type; + uint16_t number; + char *name; + + sig_component_t component[MAX_SIG_COMPONENTS]; +} sig_service_t; + +typedef struct +{ + uint8_t *data; + unsigned int size; + unsigned int program; + unsigned int stream_id; + unsigned int seq; + unsigned int flags; + unsigned int shape; +} packet_ref_t; + +typedef struct +{ + unsigned int size; + uint8_t data[MAX_PDU_LEN]; + unsigned int flags; + unsigned int shape; +} packet_t; + +typedef struct +{ + packet_t packets[ELASTIC_BUFFER_LEN]; + int audio_offset; +} elastic_buffer_t; + +typedef struct +{ + nrsc5_t *radio; + elastic_buffer_t elastic[MAX_PROGRAMS][MAX_STREAMS]; +#ifdef HAVE_FAAD2 + NeAACDecHandle aacdec[MAX_PROGRAMS]; + int16_t silence[NRSC5_AUDIO_FRAME_SAMPLES * 2]; +#endif + uint8_t *sig_bytes; + unsigned int sig_len; + sig_service_t services[MAX_SIG_SERVICES]; + unsigned int lot_lru_counter; + here_images_t here_images; +} output_t; + +void output_align(output_t *st, unsigned int program, unsigned int stream_id, unsigned int offset); +void output_push(output_t *st, const packet_ref_t* ref); +void output_advance(output_t *st); +void output_reset(output_t *st); +void output_init(output_t *st, nrsc5_t *); +void output_free(output_t *st); +void output_aas_push(output_t *st, uint8_t *psd, unsigned int len); diff --git a/third_party/nrsc5/upstream/src/pids.c b/third_party/nrsc5/upstream/src/pids.c new file mode 100644 index 000000000..8dbf51cc2 --- /dev/null +++ b/third_party/nrsc5/upstream/src/pids.c @@ -0,0 +1,1102 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "defines.h" +#include "pids.h" +#include "private.h" +#include "unicode.h" + +#define ALERT_TIMEOUT_LIMIT 16 +#define PIDS_TYPE_SIS 0 +#define PIDS_TYPE_LLDS 1 + +#define SIS_MSG_ID_STATION_ID 0 +#define SIS_MSG_ID_STATION_NAME_SHORT 1 +#define SIS_MSG_ID_STATION_NAME_LONG 2 +#define SIS_MSG_ID_STATION_LOCATION 4 +#define SIS_MSG_ID_STATION_MESSAGE 5 +#define SIS_MSG_ID_SERVICE_INFORMATION 6 +#define SIS_MSG_ID_PARAMETER_MESSAGE 7 +#define SIS_MSG_ID_UNIVERSAL_SHORT_STATION_NAME 8 +#define SIS_MSG_ID_EMERGENCY_ALERTS_MESSAGE 9 +#define SIS_MSG_ID_ADV_SERVICE_INFORMATION 10 + +#define SIS_SERVICE_CATEGORY_AUDIO 0 +#define SIS_SERVICE_CATEGORY_DATA 1 + +#define SIS_EA_LOCATION_FORMAT_SAME 0 +#define SIS_EA_LOCATION_FORMAT_FIPS 1 +#define SIS_EA_LOCATION_FORMAT_ZIP 2 + + +static char *chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ?-*$ "; +static int payload_sizes[] = { + 32, 22, 58, 32, 27, 58, 27, 22, + 58, 58, 27, -1, -1, -1, -1, -1 +}; + +static uint16_t crc12(uint8_t *bits) +{ + uint16_t poly = 0xD010; + uint16_t reg = 0x0000; + int i, lowbit; + + for (i = 67; i >= 0; i--) + { + lowbit = reg & 1; + reg >>= 1; + reg ^= ((uint16_t)bits[i] << 15); + if (lowbit) reg ^= poly; + } + for (i = 0; i < 16; i++) + { + lowbit = reg & 1; + reg >>= 1; + if (lowbit) reg ^= poly; + } + reg ^= 0x955; + return reg & 0xfff; +} + +static int check_crc12(uint8_t *bits) +{ + uint16_t expected_crc = 0; + int i; + + for (i = 68; i < 80; i++) + { + expected_crc <<= 1; + expected_crc |= bits[i]; + } + return expected_crc == crc12(bits); +} + +static int crc7(const char *alert, int len) +{ + const unsigned char poly = 0x09; + unsigned char reg = 0x42; + int byte_index, bit_index; + + for (byte_index = len - 1; byte_index >= 0; byte_index--) + { + for (bit_index = 6; bit_index >= 0; bit_index--) + { + unsigned char bit = ((unsigned char)alert[byte_index] >> bit_index) & 1; + if ((bit_index == 0) && (byte_index > 0)) + bit ^= ((unsigned char)alert[byte_index - 1] >> 7); + + reg <<= 1; + reg ^= bit; + if (reg & 0x80) + reg ^= (0x80 | poly); + } + } + + for (bit_index = 6; bit_index >= 0; bit_index--) + { + reg <<= 1; + if (reg & 0x80) + reg ^= (0x80 | poly); + } + + return reg; +} + +static int control_data_crc(const char *control_data, int len) +{ + unsigned short poly = 0xD010; + unsigned short reg = 0x7E1B; + int lowbit; + + int byte_index, bit_index; + + for (byte_index = len - 1; byte_index >= 1; byte_index--) + { + for (bit_index = 0; bit_index < 8; bit_index++) + { + unsigned short bit = + ((unsigned char)control_data[byte_index] >> bit_index) & 1; + if ((byte_index == 1) || (byte_index == 2 && bit_index < 4)) + bit = 0; // Skip CRC bits + + lowbit = reg & 1; + reg >>= 1; + reg ^= (bit << 15); + if (lowbit) + reg ^= poly; + } + } + + for (bit_index = 0; bit_index < 16; bit_index++) + { + lowbit = reg & 1; + reg >>= 1; + if (lowbit) + reg ^= poly; + } + + return reg & 0x0fff; +} + +static unsigned int decode_int(const uint8_t *bits, int *off, unsigned int length) +{ + unsigned int i, result = 0; + for (i = 0; i < length; i++) + { + result <<= 1; + result |= bits[(*off)++]; + } + return result; +} + +static unsigned int decode_int_reverse(const uint8_t *bits, int *off, unsigned int length) +{ + unsigned int i, result = 0; + for (i = 0; i < length; i++) + result |= (bits[(*off)++] << i); + return result; +} + +static int decode_signed_int(const uint8_t *bits, int *off, const unsigned int length) +{ + int result = (int) decode_int(bits, off, length); + return (result & (1 << (length - 1))) ? result - (1 << length) : result; +} + +static char decode_char5(const uint8_t *bits, int *off) +{ + return chars[decode_int(bits, off, 5)]; +} + +static char decode_char7(const uint8_t *bits, int *off) +{ + return (char) decode_int(bits, off, 7); +} + +static int decode_locations(const uint8_t *bits, const int len, int locations[MAX_ALERT_LOCATIONS], const int location_format, const int num_locations) +{ + int off = 0; + int previous_location = 0; + int full_len, compressed_len; + + switch (location_format) + { + case SIS_EA_LOCATION_FORMAT_SAME: + full_len = 20; + compressed_len = 14; + break; + case SIS_EA_LOCATION_FORMAT_FIPS: + case SIS_EA_LOCATION_FORMAT_ZIP: + full_len = 17; + compressed_len = 10; + break; + default: + log_warn("Invalid location format: %d", location_format); + return -1; + } + + for (int i = 0; i < num_locations; i++) + { + if (off + 1 > len) + { + log_warn("Invalid location data"); + return -1; + } + + if ((i == 0) || bits[off++]) + { + // Full-length location + if (off + full_len > len) + { + log_warn("Invalid location data"); + return -1; + } + locations[i] = (int)decode_int_reverse(bits, &off, full_len); + } + else + { + // Compressed location + if (off + compressed_len > len) + { + log_warn("Invalid location data"); + return -1; + } + int new_digits = (int)decode_int_reverse(bits, &off, compressed_len); + int old_digits = (previous_location % 100000) - (previous_location % 1000); + locations[i] = ((new_digits / 1000) * 100000) + (new_digits % 1000) + old_digits; + } + + previous_location = locations[i]; + } + + return 0; +} + +static void decode_control_data(const char *control_data, const int len, int *category1, int *category2, int locations[MAX_ALERT_LOCATIONS], int *location_format, int *num_locations) +{ + uint8_t bits[MAX_ALERT_CNT_LEN * 8]; + int off = 0; + + for (int i = 0; i < len; i++) + for (int j = 0; j < 8; j++) + bits[i*8 + j] = ((unsigned char)control_data[i] >> j) & 1; + + off += 8; // unknown + off += 12; // CNT CRC + off += 8; // unknown + *category1 = (int)decode_int_reverse(bits, &off, 5); + *category2 = (int)decode_int_reverse(bits, &off, 5); + off += 9; // unknown + *location_format = (int)decode_int_reverse(bits, &off, 3); + *num_locations = (int)decode_int_reverse(bits, &off, 5); + off += 1; // unknown + + if (decode_locations(bits + off, len*8 - off, locations, *location_format, *num_locations) < 0) + *num_locations = 0; +} + +static char *utf8_encode(const encoding_t encoding, const char *buf, const int len) +{ + if (encoding == ENCODING_ISO_8859_1) + return iso_8859_1_to_utf_8((uint8_t *) buf, len); + else if (encoding == ENCODING_UCS_2) + return ucs_2_to_utf_8((uint8_t *) buf, len); + else + log_warn("Invalid encoding: %d", encoding); + + return NULL; +} + +static void report(pids_t *st) +{ + int i; + const char *country_code = NULL; + char *name = NULL; + char *slogan = NULL; + char *message = NULL; + char *alert = NULL; + float latitude = NAN; + float longitude = NAN; + int altitude = 0; + nrsc5_sis_asd_t *audio_services = NULL; + nrsc5_sis_dsd_t *data_services = NULL; + int category1 = -1; + int category2 = -1; + int location_format = -1; + int num_locations = -1; + int locations[MAX_ALERT_LOCATIONS]; + + if (st->country_code[0] != 0) + country_code = st->country_code; + + if (st->universal_short_name_displayed) + name = utf8_encode(st->universal_short_name_encoding, + st->universal_short_name_final, + strlen(st->universal_short_name_final)); + else if (st->short_name[0] != 0) + name = strdup(st->short_name); + + if (st->slogan_displayed) + slogan = utf8_encode(st->slogan_encoding, st->slogan, st->slogan_len); + else if (st->long_name_displayed) + slogan = utf8_encode(ENCODING_ISO_8859_1, st->long_name, strlen(st->long_name)); + + if (st->message_displayed) + message = utf8_encode(st->message_encoding, st->message, st->message_len); + + if (st->alert_displayed) + { + alert = utf8_encode(st->alert_encoding, st->alert + st->alert_cnt_len, st->alert_len - st->alert_cnt_len); + decode_control_data(st->alert, st->alert_cnt_len, &category1, &category2, locations, &location_format, &num_locations); + } + + if (!isnan(st->latitude) && !isnan(st->longitude)) + { + latitude = st->latitude; + longitude = st->longitude; + altitude = st->altitude; + } + + for (i = MAX_AUDIO_SERVICES - 1; i >= 0; i--) + { + if (st->audio_services[i].type != -1) + { + nrsc5_sis_asd_t *asd = malloc(sizeof(nrsc5_sis_asd_t)); + asd->next = audio_services; + asd->program = i; + asd->access = st->audio_services[i].access; + asd->type = st->audio_services[i].type; + asd->sound_exp = st->audio_services[i].sound_exp; + audio_services = asd; + } + } + + for (i = MAX_DATA_SERVICES - 1; i >= 0; i--) + { + if (st->data_services[i].type != -1) + { + nrsc5_sis_dsd_t *dsd = malloc(sizeof(nrsc5_sis_dsd_t)); + dsd->next = data_services; + dsd->access = st->data_services[i].access; + dsd->type = st->data_services[i].type; + dsd->mime_type = st->data_services[i].mime_type; + data_services = dsd; + } + } + + nrsc5_report_sis(st->input->radio, country_code, st->fcc_facility_id, name, slogan, message, alert, + (uint8_t *)st->alert, st->alert_cnt_len, category1, category2, location_format, num_locations, locations, + latitude, longitude, altitude, audio_services, data_services); + + free(name); + free(slogan); + free(message); + free(alert); + + while (audio_services) + { + nrsc5_sis_asd_t *asd = audio_services; + audio_services = asd->next; + free(asd); + } + + while (data_services) + { + nrsc5_sis_dsd_t *dsd = data_services; + data_services = dsd->next; + free(dsd); + } +} + +static void reset_alert(pids_t *st) +{ + memset(st->alert, 0, sizeof(st->alert)); + memset(st->alert_have_frame, 0, sizeof(st->alert_have_frame)); + st->alert_seq = -1; + st->alert_displayed = 0; + st->alert_timeout = 0; +} + +static int sis_decode_station_id(pids_t *st, const uint8_t* bits) +{ + int updated = 0; + char country_code[3] = {0}; + int fcc_facility_id; + int j, off = 0; + + for (j = 0; j < 2; j++) + { + country_code[j] = decode_char5(bits, &off); + } + off += 3; // reserved + fcc_facility_id = (int)decode_int(bits, &off, 19); + + if ((strcmp(country_code, st->country_code) != 0) || (fcc_facility_id != st->fcc_facility_id)) + { + strcpy(st->country_code, country_code); + st->fcc_facility_id = fcc_facility_id; + nrsc5_report_station_id(st->input->radio, st->country_code, st->fcc_facility_id); + updated = 1; + } + + return updated; +} + +static int sis_decode_station_name_short(pids_t *st, const uint8_t* bits) +{ + char short_name[8] = {0}; + int off = 0, updated = 0, j; + + for (j = 0; j < 4; j++) + { + short_name[j] = decode_char5(bits, &off); + } + if (bits[off] == 0 && bits[off+1] == 1) + strcat(short_name, "-FM"); + off += 2; + + if (strcmp(short_name, st->short_name) != 0) + { + strcpy(st->short_name, short_name); + nrsc5_report_station_name(st->input->radio, st->short_name); + updated = 1; + } + + return updated; +} + +static int sis_decode_station_long_name(pids_t *st, const uint8_t* bits) +{ + unsigned int j; + int updated = 0, off = 0; + int tmp = 55; + + const unsigned last_frame = decode_int(bits, &off, 3); + const unsigned current_frame = decode_int(bits, &off, 3); + const int seq = (int)decode_int(bits, &tmp, 3); + + if ((current_frame == 0) && (seq != st->long_name_seq)) + { + memset(st->long_name, 0, sizeof(st->long_name)); + memset(st->long_name_have_frame, 0, sizeof(st->long_name_have_frame)); + st->long_name_seq = seq; + st->long_name_displayed = 0; + } + + for (j = 0; j < 7; j++) + st->long_name[current_frame * 7 + j] = decode_char7(bits, &off); + st->long_name_have_frame[current_frame] = 1; + + if ((st->long_name_seq >= 0) && !st->long_name_displayed) + { + int complete = 1; + for (j = 0; j < last_frame + 1; j++) + complete &= st->long_name_have_frame[j]; + + if (complete) + { + st->long_name_displayed = 1; + + if (!st->slogan_displayed) + { + char *slogan = utf8_encode(ENCODING_ISO_8859_1, st->long_name, strlen(st->long_name)); + nrsc5_report_station_slogan(st->input->radio, slogan); + free(slogan); + } + + updated = 1; + } + } + return updated; +} + +static int sis_decode_station_location(pids_t *st, const uint8_t* bits) +{ + int updated = 0, off = 0; + + if (bits[off++]) + { + const float latitude = (float)decode_signed_int(bits, &off, 22) / 8192.0f; + const unsigned int altitude_high = decode_int(bits, &off, 4) << 8; + if ((latitude != st->latitude) || (altitude_high != (st->altitude & 0xf00))) + { + st->latitude = latitude; + st->altitude = (st->altitude & 0x0f0) | altitude_high; + if (!isnan(st->longitude)) + { + nrsc5_report_station_location(st->input->radio, st->latitude, st->longitude, st->altitude); + updated = 1; + } + } + } + else + { + const float longitude = (float)decode_signed_int(bits, &off, 22) / 8192.0f; + const unsigned altitude_low = decode_int(bits, &off, 4) << 4; + if ((longitude != st->longitude) || (altitude_low != (st->altitude & 0x0f0))) + { + st->longitude = longitude; + st->altitude = (st->altitude & 0xf00) | altitude_low; + if (!isnan(st->latitude)) + { + nrsc5_report_station_location(st->input->radio, st->latitude, st->longitude, st->altitude); + updated = 1; + } + } + } + + return updated; +} + +static int sis_decode_station_message(pids_t *st, const uint8_t* bits) +{ + int off = 0, updated = 0, j; + const int current_frame = (int)decode_int(bits, &off, 5); + const int seq = (int)decode_int(bits, &off, 2); + + if (current_frame == 0) + { + if (seq != st->message_seq) + { + memset(st->message, 0, sizeof(st->message)); + memset(st->message_have_frame, 0, sizeof(st->message_have_frame)); + st->message_seq = seq; + st->message_displayed = 0; + } + st->message_priority = bits[off++]; + st->message_encoding = decode_int(bits, &off, 3); + st->message_len = (int)decode_int(bits, &off, 8); + st->message_checksum = decode_int(bits, &off, 7); + for (j = 0; j < 4; j++) + st->message[j] = (char)decode_int(bits, &off, 8); + } + else + { + off += 3; // reserved + for (j = 0; j < 6; j++) + st->message[current_frame * 6 - 2 + j] = (char)decode_int(bits, &off, 8); + } + st->message_have_frame[current_frame] = 1; + + if ((st->message_seq >= 0) && !st->message_displayed) + { + int complete = 1; + for (j = 0; j < (st->message_len + 7) / 6; j++) + complete &= st->message_have_frame[j]; + + if (complete) + { + unsigned int checksum = 0; + for (j = 0; j < st->message_len; j++) + checksum += (unsigned char) st->message[j]; + checksum = (((checksum >> 8) & 0x7f) + (checksum & 0xff)) & 0x7f; + + if (checksum == st->message_checksum) + { + st->message_displayed = 1; + + char *message = utf8_encode(st->message_encoding, st->message, st->message_len); + nrsc5_report_station_message(st->input->radio, message); + free(message); + + updated = 1; + } + else + { + log_warn("Invalid message checksum: %d != %d", st->message_checksum, checksum); + } + } + } + + return updated; +} + +static int sis_decode_service_information(pids_t *st, const uint8_t* bits) +{ + int updated = 0, off = 0, j; + int prog_num; + asd_t audio_service; + dsd_t data_service; + + const unsigned int category = decode_int(bits, &off, 2); + + switch (category) + { + case SIS_SERVICE_CATEGORY_AUDIO: + audio_service.access = (int)decode_int(bits, &off, 1); + prog_num = (int)decode_int(bits, &off, 6); + audio_service.type = (int)decode_int(bits, &off, 8); + off += 5; // reserved + audio_service.sound_exp = (int)decode_int(bits, &off, 5); + + if (prog_num >= MAX_AUDIO_SERVICES) + { + log_warn("Invalid program number: %d", prog_num); + break; + } + + if (st->audio_services[prog_num].access != audio_service.access + || st->audio_services[prog_num].type != audio_service.type + || st->audio_services[prog_num].sound_exp != audio_service.sound_exp) + { + st->audio_services[prog_num] = audio_service; + nrsc5_report_asd(st->input->radio, prog_num, audio_service.access, audio_service.type, audio_service.sound_exp); + updated = 1; + } + break; + case SIS_SERVICE_CATEGORY_DATA: + data_service.access = (int)decode_int(bits, &off, 1); + data_service.type = (int)decode_int(bits, &off, 9); + off += 3; // reserved + data_service.mime_type = (int)decode_int(bits, &off, 12); + + for (j = 0; j < MAX_DATA_SERVICES; j++) + { + if (st->data_services[j].access == data_service.access + && st->data_services[j].type == data_service.type + && st->data_services[j].mime_type == data_service.mime_type) + { + break; + } + else if (st->data_services[j].type == -1) + { + st->data_services[j] = data_service; + nrsc5_report_dsd(st->input->radio, data_service.access, data_service.type, data_service.mime_type); + updated = 1; + break; + } + } + break; + default: + log_warn("Unknown service category identifier: %d", category); + } + + return updated; +} + +static void sis_decode_parameter(pids_t *st, const uint8_t* bits) +{ + int off = 0; + + const unsigned int index = decode_int(bits, &off, 6); + const int parameter = (int)decode_int(bits, &off, 16); + + if (index >= NUM_PARAMETERS) + { + log_warn("Invalid parameter index: %d", index); + return; + } + + if (st->parameters[index] != parameter) + { + st->parameters[index] = parameter; + switch (index) + { + case 0: + case 1: + case 2: + if (st->parameters[0] >= 0 && st->parameters[1] >= 0 && st->parameters[2] >= 0) + { + const int pending_offset = st->parameters[0] >> 8; + const int current_offset = st->parameters[0] & 0xff; + const unsigned int pending_alfn = (unsigned int)st->parameters[2] << 16 | st->parameters[1]; + + nrsc5_report_leap_second_offset(st->input->radio, pending_offset, current_offset, pending_alfn); + } + break; + case 3: + { + const int dst_regional = st->parameters[3] & 0x1; + const int dst_local = (st->parameters[3] >> 1) & 0x1; + const int dst_schedule = (st->parameters[3] >> 2) & 0x7; + int tzo = (st->parameters[3] >> 5) & 0x7ff; + + if (tzo >= 1024) + tzo -= 2048; + + nrsc5_report_local_time(st->input->radio, tzo, dst_regional, dst_local, dst_schedule); + } + break; + case 4: + case 5: + case 6: + case 7: + if (st->parameters[4] >= 0 && st->parameters[5] >= 0 && st->parameters[6] >= 0 && st->parameters[7] >= 0) + { + const char manufacturer_id[3] = { + (char)((st->parameters[4] >> 8) & 0x7f), (char)(st->parameters[4] & 0x7f), + '\0' + }; + const int core_version[NRSC5_DEVICE_VERSION_LENGTH] = { + (st->parameters[5] >> 11) & 0x1f, (st->parameters[5] >> 6) & 0x1f, (st->parameters[5] >> 1) & 0x1f, + (st->parameters[7] >> 11) & 0x1f + }; + const int manufacturer_version[NRSC5_DEVICE_VERSION_LENGTH] = { + (st->parameters[6] >> 11) & 0x1f, (st->parameters[6] >> 6) & 0x1f, (st->parameters[6] >> 1) & 0x1f, + (st->parameters[7] >> 6) & 0x1f, + }; + + const int core_status = (st->parameters[7] >> 3) & 0x7; + const int manufacturer_status = st->parameters[7] & 0x7; + const int importer_connected = (st->parameters[4] >> 7) & 0x1; + + nrsc5_report_exciter_info(st->input->radio, manufacturer_id, core_version, manufacturer_version, + core_status, manufacturer_status, importer_connected); + } + break; + case 8: + case 9: + case 10: + case 11: + if (st->parameters[8] >= 0 && st->parameters[9] >= 0 && st->parameters[10] >= 0 && st->parameters[11] >= 0) + { + const char manufacturer_id[3] = { + (char)((st->parameters[8] >> 8) & 0x7f), (char)(st->parameters[8] & 0x7f), + '\0' + }; + const int core_version[NRSC5_DEVICE_VERSION_LENGTH] = { + (st->parameters[9] >> 11) & 0x1f, (st->parameters[9] >> 6) & 0x1f, (st->parameters[9] >> 1) & 0x1f, + (st->parameters[11] >> 11) & 0x1f, + }; + const int manufacturer_version[NRSC5_DEVICE_VERSION_LENGTH] = { + (st->parameters[10] >> 11) & 0x1f, (st->parameters[10] >> 6) & 0x1f, (st->parameters[10] >> 1) & 0x1f, + (st->parameters[11] >> 6) & 0x1f + }; + const int core_status = (st->parameters[11] >> 3) & 0x7; + const int manufacturer_status = st->parameters[11] & 0x7; + + nrsc5_report_importer_info(st->input->radio, manufacturer_id, core_version, manufacturer_version, + core_status, manufacturer_status); + } + break; + case 12: + log_debug("Importer configuration number: %d", parameter); + break; + default: + log_warn("Unknown SIS parameter index: %d", index); + break; + } + } +} + +static int sis_decode_universal_short_station_name(pids_t *st, const uint8_t *bits) +{ + int j, off = 0; + int updated = 0; + + const unsigned int current_frame = decode_int(bits, &off, 4); + if (bits[off++] == 0) + { + if (current_frame >= MAX_UNIVERSAL_SHORT_NAME_FRAMES) + { + log_error("unexpected frame number in Universal Short Station Name: %d", current_frame); + off += 53; + return 0; + } + + if (current_frame == 0) + { + st->universal_short_name_encoding = decode_int(bits, &off, 3); + st->universal_short_name_append = bits[off++]; + st->universal_short_name_len = bits[off++] + 1; + for (j = 0; j < 6; j++) + st->universal_short_name[j] = (char)decode_int(bits, &off, 8); + } + else + { + off += 5; // reserved + for (j = 0; j < 6; j++) + st->universal_short_name[current_frame * 6 + j] = (char)decode_int(bits, &off, 8); + } + st->universal_short_name_have_frame[current_frame] = 1; + + if (st->universal_short_name_len >= 0 && !st->universal_short_name_displayed) + { + int complete = 1; + for (j = 0; j < st->universal_short_name_len; j++) + complete &= st->universal_short_name_have_frame[j]; + + if (complete) + { + strcpy(st->universal_short_name_final, st->universal_short_name); + if (st->universal_short_name_append) + strcat(st->universal_short_name_final, "-FM"); + st->universal_short_name_displayed = 1; + + char *name = utf8_encode(st->universal_short_name_encoding, + st->universal_short_name_final, + strlen(st->universal_short_name_final)); + nrsc5_report_station_name(st->input->radio, name); + free(name); + + updated = 1; + } + } + } + else + { + if (current_frame == 0) + { + st->slogan_encoding = decode_int(bits, &off, 3); + off += 3; // reserved + st->slogan_len = (int)decode_int(bits, &off, 7); + for (j = 0; j < 5; j++) + st->slogan[j] = (char)decode_int(bits, &off, 8); + } + else + { + off += 5; // reserved + for (j = 0; j < 6; j++) + st->slogan[current_frame * 6 - 1 + j] = (char)decode_int(bits, &off, 8); + } + st->slogan_have_frame[current_frame] = 1; + + if (st->slogan_len >= 0 && !st->slogan_displayed) + { + int complete = 1; + for (j = 0; j < (st->slogan_len + 6) / 6; j++) + complete &= st->slogan_have_frame[j]; + + if (complete) + { + st->slogan_displayed = 1; + + if (!st->long_name_displayed) + { + char *slogan = utf8_encode(st->slogan_encoding, st->slogan, st->slogan_len); + nrsc5_report_station_slogan(st->input->radio, slogan); + free(slogan); + } + + updated = 1; + } + } + } + + return updated; +} + +static int sis_decode_emergency_alerts(pids_t *st, const uint8_t *bits) +{ + int off = 0, updated = 0, j; + + const unsigned int current_frame = decode_int(bits, &off, 6); + const int seq = (int)decode_int(bits, &off, 2); + off += 2; // reserved + + st->alert_timeout = 0; + + if (current_frame == 0) + { + if (seq != st->alert_seq) + { + memset(st->alert, 0, sizeof(st->alert)); + memset(st->alert_have_frame, 0, sizeof(st->alert_have_frame)); + st->alert_seq = seq; + st->alert_displayed = 0; + } + st->alert_encoding = decode_int(bits, &off, 3); + st->alert_len = (int)decode_int(bits, &off, 9); + st->alert_crc = (int)decode_int(bits, &off, 7); + st->alert_cnt_len = 1 + (2 * (int)decode_int(bits, &off, 5)); + for (j = 0; j < 3; j++) + st->alert[j] = (char)decode_int(bits, &off, 8); + } + else + { + for (j = 0; j < 6; j++) + st->alert[current_frame * 6 - 3 + j] = (char)decode_int(bits, &off, 8); + } + st->alert_have_frame[current_frame] = 1; + + if (st->alert_len >= 0 && !st->alert_displayed) + { + int complete = 1; + for (j = 0; j < (st->alert_len + 8) / 6; j++) + complete &= st->alert_have_frame[j]; + + if (complete) + { + const int expected_alert_crc = crc7(st->alert, st->alert_len); + if (st->alert_crc != expected_alert_crc) + { + log_warn("Invalid alert CRC: 0x%02x != 0x%02x", st->alert_crc, expected_alert_crc); + return 0; + } + + if ((st->alert_cnt_len < 7) || (st->alert_len < st->alert_cnt_len)) + { + log_warn("Invalid alert CNT length"); + return 0; + } + + const int actual_cnt_crc = (((unsigned char)st->alert[2] & 0x0f) << 8) | (unsigned char)st->alert[1]; + const int expected_cnt_crc = control_data_crc(st->alert, st->alert_cnt_len); + if (actual_cnt_crc == expected_cnt_crc) + { + st->alert_displayed = 1; + + int category1 = -1; + int category2 = -1; + int location_format = -1; + int num_locations = -1; + int locations[MAX_ALERT_LOCATIONS]; + char *message = utf8_encode(st->alert_encoding, st->alert + st->alert_cnt_len, st->alert_len - st->alert_cnt_len); + decode_control_data(st->alert, st->alert_cnt_len, &category1, &category2, locations, &location_format, &num_locations); + nrsc5_report_emergency_alert(st->input->radio, message, (uint8_t *)st->alert, st->alert_cnt_len, category1, category2, location_format, num_locations, locations); + free(message); + + updated = 1; + } + else + { + log_warn("Invalid CNT CRC: 0x%03x != 0x%03x", actual_cnt_crc, expected_cnt_crc); + } + } + } + + return updated; +} + +static void sis_decode(pids_t *st, const uint8_t *bits) +{ + int off = 0; + int updated = 0; + + const int payloads = bits[0] + 1; + off += 1; + + if (st->alert_displayed) + st->alert_timeout++; + + for (int i = 0; i < payloads; i++) + { + if (off > 59) break; + const unsigned int msg_id = decode_int(bits, &off, 4); + const int payload_size = payload_sizes[msg_id]; + + if (payload_size == -1) + { + log_error("unexpected msg_id: %d", msg_id); + break; + } + + if (off > 63 - payload_size) + { + log_error("not enough room for SIS payload, msg_id: %d", msg_id); + break; + } + + switch (msg_id) + { + case SIS_MSG_ID_STATION_ID: + if (sis_decode_station_id(st, bits + off)) + updated = 1; + off += 32; + break; + case SIS_MSG_ID_STATION_NAME_SHORT: + if (sis_decode_station_name_short(st, bits + off)) + updated = 1; + off += 22; + break; + case SIS_MSG_ID_STATION_NAME_LONG: + if (sis_decode_station_long_name(st, bits + off)) + updated = 1; + off += 58; + break; + case 3: + // reserved + off += 32; + break; + case SIS_MSG_ID_STATION_LOCATION: + if (sis_decode_station_location(st, bits + off)) + updated = 1; + off += 27; + break; + case SIS_MSG_ID_STATION_MESSAGE: + if (sis_decode_station_message(st, bits + off)) + updated = 1; + off += 58; + break; + case SIS_MSG_ID_SERVICE_INFORMATION: + case SIS_MSG_ID_ADV_SERVICE_INFORMATION: + if (sis_decode_service_information(st, bits + off)) + updated = 1; + off += 27; + break; + case SIS_MSG_ID_PARAMETER_MESSAGE: + sis_decode_parameter(st, bits + off); + off += 22; + break; + case SIS_MSG_ID_UNIVERSAL_SHORT_STATION_NAME: + if (sis_decode_universal_short_station_name(st, bits + off)) + updated = 1; + off += 58; + break; + case SIS_MSG_ID_EMERGENCY_ALERTS_MESSAGE: + if (sis_decode_emergency_alerts(st, bits + off)) + updated = 1; + off += 58; + break; + default: + log_warn("Unknown SIS MSG ID: %d", msg_id); + break; + } + } + + if (st->alert_displayed && (st->alert_timeout >= ALERT_TIMEOUT_LIMIT)) + { + reset_alert(st); + nrsc5_report_emergency_alert(st->input->radio, NULL, NULL, -1, -1, -1, -1, -1, NULL); + updated = 1; + } + + if (updated) + report(st); +} + +void pids_frame_push(pids_t *st, const uint8_t *bits) +{ + uint8_t pids[PIDS_FRAME_LEN]; + + for (int i = 0; i < PIDS_FRAME_LEN; i++) + { + pids[i] = bits[((i>>3)<<3) + 7 - (i & 7)]; + } + + if (check_crc12(pids)) + { + const int type = pids[0]; + + if (type == PIDS_TYPE_SIS) + sis_decode(st, pids + 1); + else if (type == PIDS_TYPE_LLDS) + log_debug("Ignoring LLDS frame"); + } +} + +void pids_init(pids_t *st, input_t *input) +{ + int i; + + memset(st->country_code, 0, sizeof(st->country_code)); + st->fcc_facility_id = -1; + + memset(st->short_name, 0, sizeof(st->short_name)); + + st->long_name_seq = -1; + st->long_name_displayed = 0; + + st->latitude = NAN; + st->longitude = NAN; + st->altitude = 0; + + st->message_seq = -1; + st->message_displayed = 0; + + for (i = 0; i < MAX_AUDIO_SERVICES; i++) + { + st->audio_services[i].access = -1; + st->audio_services[i].type = -1; + st->audio_services[i].sound_exp = -1; + } + for (i = 0; i < MAX_DATA_SERVICES; i++) + { + st->data_services[i].access = -1; + st->data_services[i].type = -1; + st->data_services[i].mime_type = -1; + } + + for (i = 0; i < NUM_PARAMETERS; i++) + st->parameters[i] = -1; + + memset(st->universal_short_name, 0, sizeof(st->universal_short_name)); + memset(st->universal_short_name_final, 0, sizeof(st->universal_short_name_final)); + memset(st->universal_short_name_have_frame, 0, sizeof(st->universal_short_name_have_frame)); + st->universal_short_name_append = -1; + st->universal_short_name_len = -1; + st->universal_short_name_displayed = 0; + + memset(st->slogan, 0, sizeof(st->slogan)); + memset(st->slogan_have_frame, 0, sizeof(st->slogan_have_frame)); + st->slogan_len = -1; + st->slogan_displayed = 0; + + reset_alert(st); + + st->input = input; +} diff --git a/third_party/nrsc5/upstream/src/pids.h b/third_party/nrsc5/upstream/src/pids.h new file mode 100644 index 000000000..85b8fb594 --- /dev/null +++ b/third_party/nrsc5/upstream/src/pids.h @@ -0,0 +1,99 @@ +#pragma once + +#include + +#define MAX_LONG_NAME_LEN 56 +#define MAX_LONG_NAME_FRAMES 8 +#define MAX_MESSAGE_LEN 190 +#define MAX_MESSAGE_FRAMES 32 +#define MAX_AUDIO_SERVICES 8 +#define MAX_DATA_SERVICES 16 +#define NUM_PARAMETERS 13 +#define MAX_UNIVERSAL_SHORT_NAME_LEN 12 +#define MAX_UNIVERSAL_SHORT_NAME_FRAMES 2 +#define MAX_SLOGAN_LEN 95 +#define MAX_SLOGAN_FRAMES 16 +#define MAX_ALERT_LEN 381 +#define MAX_ALERT_FRAMES 64 +#define MAX_ALERT_CNT_LEN 63 +#define MAX_ALERT_LOCATIONS 31 + +typedef struct +{ + int access; + int type; + int sound_exp; +} asd_t; + +typedef struct +{ + int access; + int type; + int mime_type; +} dsd_t; + +typedef enum +{ + ENCODING_ISO_8859_1 = 0, + ENCODING_UCS_2 = 4 +} encoding_t; + +typedef struct +{ + struct input_t *input; + + char country_code[3]; + int fcc_facility_id; + + char short_name[8]; + + char long_name[MAX_LONG_NAME_LEN + 1]; + uint8_t long_name_have_frame[MAX_LONG_NAME_FRAMES]; + int long_name_seq; + int long_name_displayed; + + float latitude; + float longitude; + int altitude; + + char message[MAX_MESSAGE_LEN + 1]; + uint8_t message_have_frame[MAX_MESSAGE_FRAMES]; + int message_seq; + int message_priority; + encoding_t message_encoding; + int message_len; + unsigned int message_checksum; + int message_displayed; + + asd_t audio_services[MAX_AUDIO_SERVICES]; + dsd_t data_services[MAX_DATA_SERVICES]; + + int parameters[NUM_PARAMETERS]; + + char universal_short_name[MAX_UNIVERSAL_SHORT_NAME_LEN + 1]; + char universal_short_name_final[MAX_UNIVERSAL_SHORT_NAME_LEN + 4]; + uint8_t universal_short_name_have_frame[MAX_UNIVERSAL_SHORT_NAME_FRAMES]; + encoding_t universal_short_name_encoding; + int universal_short_name_append; + int universal_short_name_len; + int universal_short_name_displayed; + + char slogan[MAX_SLOGAN_LEN + 1]; + uint8_t slogan_have_frame[MAX_SLOGAN_FRAMES]; + encoding_t slogan_encoding; + int slogan_len; + int slogan_displayed; + + char alert[MAX_ALERT_LEN + 1]; + uint8_t alert_have_frame[MAX_ALERT_FRAMES]; + int alert_seq; + encoding_t alert_encoding; + int alert_len; + int alert_crc; + int alert_cnt_len; + int alert_displayed; + int alert_timeout; +} pids_t; + +void pids_frame_push(pids_t *st, const uint8_t *bits); +void pids_init(pids_t *st, struct input_t *input); diff --git a/third_party/nrsc5/upstream/src/private.h b/third_party/nrsc5/upstream/src/private.h new file mode 100644 index 000000000..7c993f1e8 --- /dev/null +++ b/third_party/nrsc5/upstream/src/private.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include +#include + +#include + +#include "config.h" +#include "defines.h" +#include "input.h" +#include "output.h" +#include "rtltcp.h" + +extern pthread_mutex_t fftw_mutex; + +struct nrsc5_t +{ + rtlsdr_dev_t *dev; + FILE *iq_file; + rtltcp_t *rtltcp; + uint8_t samples_buf[128 * 256]; + float freq; + int mode; + int gain; + int auto_gain; + int stopped; + int worker_stopped; + int closed; + nrsc5_callback_t callback; + void *callback_opaque; + nrsc5_sig_service_t *sig_table; + + uint8_t leftover_u8[4]; + unsigned int leftover_u8_num; + int16_t leftover_s16[2]; + unsigned int leftover_s16_num; + + pthread_t worker; + pthread_mutex_t worker_mutex; + pthread_cond_t worker_cond; + + input_t input; + output_t output; +}; + +void nrsc5_report(nrsc5_t *, const nrsc5_event_t *evt); +void nrsc5_report_lost_device(nrsc5_t *st); +void nrsc5_report_agc(nrsc5_t *st, float gain_db, float peak_dbfs, int is_final); +void nrsc5_report_iq(nrsc5_t *, const void *data, size_t count); +void nrsc5_report_sync(nrsc5_t *, float freq_offset, int psmi, int pli, int hppi, int aabi, int rdbi); +void nrsc5_report_lost_sync(nrsc5_t *); +void nrsc5_report_mer(nrsc5_t *, float lower, float upper); +void nrsc5_report_ber(nrsc5_t *, float cber); +void nrsc5_report_hdc(nrsc5_t *, unsigned int program, const packet_t* pkt); +void nrsc5_report_audio(nrsc5_t *, unsigned int program, const int16_t *data, size_t count, unsigned int flags); +void nrsc5_report_stream(nrsc5_t *, uint16_t seq, unsigned int size, const uint8_t *data, + nrsc5_sig_service_t *service, nrsc5_sig_component_t *component); +void nrsc5_report_packet(nrsc5_t *, uint16_t seq, unsigned int size, const uint8_t *data, + nrsc5_sig_service_t *service, nrsc5_sig_component_t *component); +void nrsc5_report_lot(nrsc5_t *, int event, unsigned int lot, unsigned int size, uint32_t mime, + const char *name, const uint8_t *data, struct tm *expiry_utc, + nrsc5_sig_service_t *service, nrsc5_sig_component_t *component); +void nrsc5_report_lot_fragment(nrsc5_t *, unsigned int lot, unsigned int seq, unsigned int repeat, int is_duplicate, + unsigned int size, unsigned int bytes_so_far, const uint8_t *data, + nrsc5_sig_service_t *service, nrsc5_sig_component_t *component); +void nrsc5_report_audio_service(nrsc5_t *, unsigned int program, unsigned int access, unsigned int type, + unsigned int codec_mode, unsigned int blend_control, int digital_audio_gain, + unsigned int common_delay, unsigned int latency); +void nrsc5_report_sig(nrsc5_t *, sig_service_t *services); +void nrsc5_clear_sig(nrsc5_t *); +void nrsc5_report_sis(nrsc5_t *, const char *country_code, int fcc_facility_id, const char *name, + const char *slogan, const char *message, const char *alert, const uint8_t *cnt, int cnt_length, + int category1, int category2, int location_format, int num_locations, const int *locations, + float latitude, float longitude, int altitude, nrsc5_sis_asd_t *audio_services, + nrsc5_sis_dsd_t *data_services); +void nrsc5_report_station_id(nrsc5_t *st, const char *country_code, int fcc_facility_id); +void nrsc5_report_station_name(nrsc5_t *st, const char *name); +void nrsc5_report_station_slogan(nrsc5_t *st, const char *slogan); +void nrsc5_report_station_message(nrsc5_t *st, const char *message); +void nrsc5_report_station_location(nrsc5_t *st, float latitude, float longitude, int altitude); +void nrsc5_report_asd(nrsc5_t *st, unsigned int program, unsigned int access, unsigned int type, unsigned int sound_exp); +void nrsc5_report_dsd(nrsc5_t *st, unsigned int access, unsigned int type, uint32_t mime_type); +void nrsc5_report_emergency_alert(nrsc5_t *st, const char *message, const uint8_t *control_data, + int control_data_length, int category1, int category2, + int location_format, int num_locations, const int *locations); +void nrsc5_report_here_image(nrsc5_t *st, int image_type, int seq, int n1, int n2, unsigned int timestamp, + float latitude1, float longitude1, float latitude2, float longitude2, + const char *name, unsigned int size, const uint8_t *data); +void nrsc5_report_exciter_info(nrsc5_t *st, const char* manufacturer_id, const int core_version[4], const int manufacturer_version[4], + int core_status, int manufacturer_status, int importer_connected); +void nrsc5_report_importer_info(nrsc5_t *st, const char* manufacturer_id, const int core_version[4], const int manufacturer_version[4], + int core_status, int manufacturer_status); +void nrsc5_report_leap_second_offset(nrsc5_t *st, int pending_offset, int current_offset, + unsigned int pending_alfn); +void nrsc5_report_local_time(nrsc5_t *st, int utc_offset, int dst_regional, int dst_local, + int dst_schedule); diff --git a/third_party/nrsc5/upstream/src/rs_char.h b/third_party/nrsc5/upstream/src/rs_char.h new file mode 100644 index 000000000..563dc017a --- /dev/null +++ b/third_party/nrsc5/upstream/src/rs_char.h @@ -0,0 +1,52 @@ +/* Include file to configure the RS codec for character symbols + * + * Copyright 2002, Phil Karn, KA9Q + * May be used under the terms of the GNU General Public License (GPL) + */ + +#define DTYPE unsigned char + +/* Reed-Solomon codec control block */ +struct rs { + unsigned int mm; /* Bits per symbol */ + unsigned int nn; /* Symbols per block (= (1<= rs->nn) { + x -= rs->nn; + x = (x >> rs->mm) + (x & rs->nn); + } + return x; +} +#define MODNN(x) modnn(rs,x) + +#define MM (rs->mm) +#define NN (rs->nn) +#define ALPHA_TO (rs->alpha_to) +#define INDEX_OF (rs->index_of) +#define GENPOLY (rs->genpoly) +#define NROOTS (rs->nroots) +#define FCR (rs->fcr) +#define PRIM (rs->prim) +#define IPRIM (rs->iprim) +#define A0 (NN) + +#define ENCODE_RS encode_rs_char +#define DECODE_RS decode_rs_char +#define INIT_RS init_rs_char +#define FREE_RS free_rs_char + +void ENCODE_RS(void *p,DTYPE *data,DTYPE *parity); +int DECODE_RS(void *p,DTYPE *data,int *eras_pos,int no_eras); +void *INIT_RS(unsigned int symsize,unsigned int gfpoly,unsigned int fcr, + unsigned int prim,unsigned int nroots); +void FREE_RS(void *p); diff --git a/third_party/nrsc5/upstream/src/rs_decode.c b/third_party/nrsc5/upstream/src/rs_decode.c new file mode 100644 index 000000000..becedc447 --- /dev/null +++ b/third_party/nrsc5/upstream/src/rs_decode.c @@ -0,0 +1,210 @@ +/* Reed-Solomon decoder + * Copyright 2002 Phil Karn, KA9Q + * May be used under the terms of the GNU General Public License (GPL) + */ + +#include + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +#include "rs_char.h" + +int DECODE_RS( +void *p, +DTYPE *data, int *eras_pos, int no_eras){ + + struct rs *rs = (struct rs *)p; + int deg_lambda, el, deg_omega; + int i, j, r, k; + DTYPE u,q,tmp,num1,num2,den,discr_r; + DTYPE lambda[NROOTS+1], s[NROOTS]; /* Err+Eras Locator poly + * and syndrome poly */ + DTYPE b[NROOTS+1], t[NROOTS+1], omega[NROOTS+1]; + DTYPE root[NROOTS], reg[NROOTS+1], loc[NROOTS]; + int syn_error, count; + + /* form the syndromes; i.e., evaluate data(x) at roots of g(x) */ + for(i=0;(unsigned int)i 0) { + /* Init lambda to be the erasure locator polynomial */ + lambda[1] = ALPHA_TO[MODNN(PRIM*(NN-1-eras_pos[0]))]; + for (i = 1; i < no_eras; i++) { + u = MODNN(PRIM*(NN-1-eras_pos[i])); + for (j = i+1; j > 0; j--) { + tmp = INDEX_OF[lambda[j - 1]]; + if(tmp != A0) + lambda[j] ^= ALPHA_TO[MODNN(u + tmp)]; + } + } + + } + for(i=0;(unsigned int)i 0; j--){ + if (reg[j] != A0) { + reg[j] = MODNN(reg[j] + j); + q ^= ALPHA_TO[reg[j]]; + } + } + if (q != 0) + continue; /* Not a root */ + /* store root (index-form) and error location number */ + root[count] = i; + loc[count] = k; + /* If we've already found max possible roots, + * abort the search to save time + */ + if(++count == deg_lambda) + break; + } + if (deg_lambda != count) { + /* + * deg(lambda) unequal to number of roots => uncorrectable + * error detected + */ + count = -1; + goto finish; + } + /* + * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo + * x**NROOTS). in index form. Also find deg(omega). + */ + deg_omega = 0; + for (i = 0; (unsigned int)i < NROOTS;i++){ + tmp = 0; + j = (deg_lambda < i) ? deg_lambda : i; + for(;j >= 0; j--){ + if ((s[i - j] != A0) && (lambda[j] != A0)) + tmp ^= ALPHA_TO[MODNN(s[i - j] + lambda[j])]; + } + if(tmp != 0) + deg_omega = i; + omega[i] = INDEX_OF[tmp]; + } + omega[NROOTS] = A0; + + /* + * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + * inv(X(l))**(FCR-1) and den = lambda_pr(inv(X(l))) all in poly-form + */ + for (j = count-1; j >=0; j--) { + num1 = 0; + for (i = deg_omega; i >= 0; i--) { + if (omega[i] != A0) + num1 ^= ALPHA_TO[MODNN(omega[i] + i * root[j])]; + } + num2 = ALPHA_TO[MODNN(root[j] * (FCR - 1) + NN)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ + for (i = (int)min((unsigned int)deg_lambda,NROOTS-1) & ~1; i >= 0; i -=2) { + if(lambda[i+1] != A0) + den ^= ALPHA_TO[MODNN(lambda[i+1] + i * root[j])]; + } + if (den == 0) { + count = -1; + goto finish; + } + /* Apply error to data */ + if (num1 != 0) { + data[loc[j]] ^= ALPHA_TO[MODNN(INDEX_OF[num1] + INDEX_OF[num2] + NN - INDEX_OF[den])]; + } + } + finish: + if(eras_pos != NULL){ + for(i=0;i + +#include "rs_char.h" + +#ifndef NULL +#define NULL ((void *)0) +#endif + +void FREE_RS(void *p){ + struct rs *rs = (struct rs *)p; + + free(rs->alpha_to); + free(rs->index_of); + free(rs->genpoly); + free(rs->modnn_table); + free(rs); +} + +/* Initialize a Reed-Solomon codec + * symsize = symbol size, bits (1-8) + * gfpoly = Field generator polynomial coefficients + * fcr = first root of RS code generator polynomial, index form + * prim = primitive element to generate polynomial roots + * nroots = RS code generator polynomial degree (number of roots) + */ +void *INIT_RS(unsigned int symsize,unsigned int gfpoly,unsigned fcr,unsigned prim, + unsigned int nroots){ + struct rs *rs; + int sr,root,iprim; + unsigned int i, j; + + if(symsize > 8*sizeof(DTYPE)) + return NULL; /* Need version with ints rather than chars */ + + if(fcr >= (1u<= (1u<= (1u<mm = symsize; + rs->nn = (1<alpha_to = (DTYPE *)malloc(sizeof(DTYPE)*(rs->nn+1)); + if(rs->alpha_to == NULL){ + free(rs); + return NULL; + } + rs->index_of = (DTYPE *)malloc(sizeof(DTYPE)*(rs->nn+1)); + if(rs->index_of == NULL){ + free(rs->alpha_to); + free(rs); + return NULL; + } + + /* Generate Galois field lookup tables */ + rs->index_of[0] = A0; /* log(zero) = -inf */ + rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */ + sr = 1; + for(i=0;inn;i++){ + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr <<= 1; + if(sr & (1<nn; + } + if(sr != 1){ + /* field generator polynomial is not primitive! */ + free(rs->alpha_to); + free(rs->index_of); + free(rs); + return NULL; + } + + /* Form RS code generator polynomial from its roots */ + rs->genpoly = (DTYPE *)malloc(sizeof(DTYPE)*(nroots+1)); + if(rs->genpoly == NULL){ + free(rs->alpha_to); + free(rs->index_of); + free(rs); + return NULL; + } + rs->fcr = fcr; + rs->prim = prim; + rs->nroots = nroots; + + /* Find prim-th root of 1, used in decoding */ + for(iprim=1;(iprim % prim) != 0;iprim += rs->nn) + ; + rs->iprim = iprim / prim; + + rs->genpoly[0] = 1; + for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) { + rs->genpoly[i+1] = 1; + + /* Multiply rs->genpoly[] by @**(root + x) */ + for (j = i; j > 0; j--){ + if (rs->genpoly[j] != 0) + rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)]; + else + rs->genpoly[j] = rs->genpoly[j-1]; + } + /* rs->genpoly[0] can never be zero */ + rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)]; + } + /* convert rs->genpoly[] to index form for quicker encoding */ + for (i = 0; i <= nroots; i++) + rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; + + /* Form modnn lookup table */ + rs->modnn_table = (int *)malloc(sizeof(int)*(2<<((sizeof(unsigned char))*8))); + if(rs->modnn_table == NULL){ + free(rs->genpoly); + free(rs->alpha_to); + free(rs->index_of); + free(rs); + return NULL; + } + for(i = 0; i < (2<<((sizeof(unsigned char))*8)); i++){ + j = i; + rs->modnn_table[i] = modnn(rs,j); + } + + return rs; +} diff --git a/third_party/nrsc5/upstream/src/rtltcp.c b/third_party/nrsc5/upstream/src/rtltcp.c new file mode 100644 index 000000000..b4efedee8 --- /dev/null +++ b/third_party/nrsc5/upstream/src/rtltcp.c @@ -0,0 +1,197 @@ +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#endif + +#include +#include "defines.h" +#include "rtltcp.h" + +struct rtltcp_t +{ + int socket; + uint32_t tuner_type; + uint32_t gain_count; +}; + +typedef struct { + unsigned char cmd; + uint32_t param; +} __attribute__((packed)) command_t; + +typedef struct { + char magic[4]; + uint32_t tuner_type; + uint32_t tuner_gain_count; +} dongle_info_t; + +#define RTLTCP_DEFINE(name, opc) \ + int rtltcp_##name(rtltcp_t *st, unsigned int param) \ + { \ + command_t cmd = { .cmd = opc, .param = htonl(param) }; \ + return send(st->socket, (char *)&cmd, sizeof(cmd), 0) == -1 ? errno : 0; \ + } + +RTLTCP_DEFINE(set_center_freq, 0x01) +RTLTCP_DEFINE(set_sample_rate, 0x02) +RTLTCP_DEFINE(set_tuner_gain_mode, 0x03) +RTLTCP_DEFINE(set_tuner_gain, 0x04) +RTLTCP_DEFINE(set_freq_correction, 0x05) +RTLTCP_DEFINE(set_direct_sampling, 0x09) +RTLTCP_DEFINE(set_offset_tuning, 0x0a) +RTLTCP_DEFINE(set_bias_tee, 0x0e) + +#undef RTLTCP_DEFINE + +rtltcp_t *rtltcp_open(int socket) +{ + dongle_info_t dongle_info; + rtltcp_t *st = calloc(1, sizeof(*st)); + st->socket = socket; + + if (rtltcp_read(st, (void *)&dongle_info, sizeof(dongle_info)) != sizeof(dongle_info)) + goto error; + + if (memcmp(dongle_info.magic, "RTL0", 4) != 0) + goto error; + + st->tuner_type = htonl(dongle_info.tuner_type); + st->gain_count = htonl(dongle_info.tuner_gain_count); + return st; +error: + free(st); + return NULL; +} + +void rtltcp_close(rtltcp_t *st) +{ +#ifdef _WIN32 + closesocket(st->socket); +#else + close(st->socket); +#endif + free(st); +} + +int rtltcp_read(rtltcp_t *st, uint8_t *buf, size_t cnt) +{ + int offset = 0; + while (cnt > 0) + { + int err = recv(st->socket, (char *)buf + offset, cnt, 0); + if (err < 0) + return err; + else if (err == 0) + break; + + cnt -= err; + offset += err; + } + return offset; +} + +// from https://github.com/steve-m/librtlsdr/blob/master/src/librtlsdr.c +int rtltcp_get_tuner_gains(rtltcp_t *st, int *gains) +{ + /* all gain values are expressed in tenths of a dB */ + const int e4k_gains[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215, + 240, 290, 340, 420 }; + const int fc0012_gains[] = { -99, -40, 71, 179, 192 }; + const int fc0013_gains[] = { -99, -73, -65, -63, -60, -58, -54, 58, 61, + 63, 65, 67, 68, 70, 71, 179, 181, 182, + 184, 186, 188, 191, 197 }; + const int fc2580_gains[] = { 0 /* no gain values */ }; + const int r82xx_gains[] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157, + 166, 197, 207, 229, 254, 280, 297, 328, + 338, 364, 372, 386, 402, 421, 434, 439, + 445, 480, 496 }; + const int unknown_gains[] = { 0 /* no gain values */ }; + + const int *ptr = NULL; + int len = 0; + + switch (st->tuner_type) { + case RTLSDR_TUNER_E4000: + ptr = e4k_gains; len = sizeof(e4k_gains); + break; + case RTLSDR_TUNER_FC0012: + ptr = fc0012_gains; len = sizeof(fc0012_gains); + break; + case RTLSDR_TUNER_FC0013: + ptr = fc0013_gains; len = sizeof(fc0013_gains); + break; + case RTLSDR_TUNER_FC2580: + ptr = fc2580_gains; len = sizeof(fc2580_gains); + break; + case RTLSDR_TUNER_R820T: + case RTLSDR_TUNER_R828D: + ptr = r82xx_gains; len = sizeof(r82xx_gains); + break; + default: + log_error("Unknown tuner type: %d", st->tuner_type); + ptr = unknown_gains; len = sizeof(unknown_gains); + break; + } + + if (!gains) + { + /* no buffer provided, just return the count */ + return len / sizeof(int); + } + else + { + if (len) + memcpy(gains, ptr, len); + + return len / sizeof(int); + } +} + +int rtltcp_reset_buffer(rtltcp_t *st, size_t cnt) +{ + char buf[1024]; + unsigned int recvd = 0; + int flags = 0; +#ifdef _WIN32 + unsigned long mode = 1; + ioctlsocket(st->socket, FIONBIO, &mode); +#else + flags |= MSG_DONTWAIT; +#endif + // first, clear pending data + while (1) + { + int err = recv(st->socket, buf, sizeof(buf), flags); + if (err <= 0) + break; + recvd += err; + } +#ifdef _WIN32 + mode = 0; + ioctlsocket(st->socket, FIONBIO, &mode); +#endif + // if we read an odd-number of bytes, read one more byte + if (recvd & 1) + recv(st->socket, buf, 1, 0); + + // then, read cnt bytes + while (cnt > 0) + { + size_t to_read = sizeof(buf); + if (cnt < to_read) + to_read = cnt; + + to_read = recv(st->socket, buf, to_read, 0); + if (to_read <= 0) + return 1; + cnt -= to_read; + } + return 0; +} diff --git a/third_party/nrsc5/upstream/src/rtltcp.h b/third_party/nrsc5/upstream/src/rtltcp.h new file mode 100644 index 000000000..7472c3a62 --- /dev/null +++ b/third_party/nrsc5/upstream/src/rtltcp.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +typedef struct rtltcp_t rtltcp_t; + +#define RTLTCP_DEFINE(name, opc) \ + int rtltcp_##name(rtltcp_t *, unsigned int param); +RTLTCP_DEFINE(set_center_freq, 0x01) +RTLTCP_DEFINE(set_sample_rate, 0x02) +RTLTCP_DEFINE(set_tuner_gain_mode, 0x03) +RTLTCP_DEFINE(set_tuner_gain, 0x04) +RTLTCP_DEFINE(set_freq_correction, 0x05) +RTLTCP_DEFINE(set_direct_sampling, 0x09) +RTLTCP_DEFINE(set_offset_tuning, 0x0a) +RTLTCP_DEFINE(set_bias_tee, 0x0e) +#undef RTLTCP_DEFINE + +rtltcp_t *rtltcp_open(int socket); +void rtltcp_close(rtltcp_t *); +int rtltcp_read(rtltcp_t *, uint8_t *buf, size_t cnt); +int rtltcp_get_tuner_gains(rtltcp_t *, int *gains); +int rtltcp_reset_buffer(rtltcp_t *, size_t cnt); diff --git a/third_party/nrsc5/upstream/src/strndup.c b/third_party/nrsc5/upstream/src/strndup.c new file mode 100644 index 000000000..e6dbfbb0d --- /dev/null +++ b/third_party/nrsc5/upstream/src/strndup.c @@ -0,0 +1,32 @@ +/* A replacement function, for systems that lack strndup. + Copyright (C) 1996-1998, 2001-2003, 2005-2007, 2009-2017 Free Software + Foundation, Inc. + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include "config.h" + +#include +#include + +#ifndef HAVE_STRNDUP +char *strndup (char const *s, size_t n) +{ + size_t len = strnlen (s, n); + char *new = malloc (len + 1); + + if (new == NULL) + return NULL; + + new[len] = '\0'; + return memcpy (new, s, len); +} +#endif diff --git a/third_party/nrsc5/upstream/src/sync.c b/third_party/nrsc5/upstream/src/sync.c new file mode 100644 index 000000000..76e7ef654 --- /dev/null +++ b/third_party/nrsc5/upstream/src/sync.c @@ -0,0 +1,841 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "defines.h" +#include "input.h" +#include "private.h" +#include "sync.h" + +#define MAX_PARTITIONS 14 +#define MIDDLE_REF_SC 30 // midpoint of Table 11-3 in 1011s.pdf + +// Table 6-4 in 1011s.pdf +static const int compatibility_mode[64] = { + 0, 1, 2, 3, 1, 5, 6, 5, 6, 1, 2, 11, 1, 5, 6, 5, + 6, 1, 2, 3, 1, 5, 6, 5, 6, 1, 2, 11, 1, 5, 6, 5, + 6, 1, 2, 3, 1, 5, 6, 5, 6, 1, 2, 11, 1, 5, 6, 5, + 6, 1, 2, 3, 1, 5, 6, 5, 6, 1, 2, 11, 1, 5, 6, 5 +}; + +static uint8_t gray4(float f) +{ + if (f < -1) + return 0; + else if (f < 0) + return 2; + else if (f < 1) + return 3; + else + return 1; +} + +static uint8_t gray8(float f) +{ + if (f < -3) + return 0; + else if (f < -2) + return 4; + else if (f < -1) + return 6; + else if (f < 0) + return 2; + else if (f < 1) + return 3; + else if (f < 2) + return 7; + else if (f < 3) + return 5; + else + return 1; +} + +static inline int8_t demod(float x, float mult) +{ + float clamped = fmaxf(fminf(x, 1), -1); + return lroundf(clamped * mult); +} + +static uint8_t qpsk(complex float cf) +{ + return (crealf(cf) < 0 ? 0 : 1) | (cimagf(cf) < 0 ? 0 : 2); +} + +static uint8_t qam16(complex float cf) +{ + return gray4(crealf(cf)) | (gray4(cimagf(cf)) << 2); +} + +static uint8_t qam64(complex float cf) +{ + return gray8(crealf(cf)) | (gray8(cimagf(cf)) << 3); +} + +static void adjust_ref(sync_t *st, unsigned int ref, int cfo) +{ + unsigned int n; + float cfo_freq = 2 * M_PI * cfo * CP_FM / FFT_FM; + + // differentially-encoded sync & parity bits + static const signed char sync[] = { + -1, 1, -1, -1, -1, 1, 1, 0, 1, -1, 0, 0, 0, -1, -1, 0, + 0, 0, 0, 0, -1, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 + }; + + for (n = 0; n < BLKSZ; n++) + { + float error = cargf(st->buffer[ref][n] * st->buffer[ref][n] * cexpf(-I * 2 * st->costas_phase[ref])) * 0.5; + + st->phases[ref][n] = st->costas_phase[ref]; + st->buffer[ref][n] *= cexpf(-I * st->costas_phase[ref]); + + st->costas_freq[ref] += st->beta * error; + if (st->costas_freq[ref] > 0.5) st->costas_freq[ref] = 0.5; + if (st->costas_freq[ref] < -0.5) st->costas_freq[ref] = -0.5; + st->costas_phase[ref] += st->costas_freq[ref] + cfo_freq + (st->alpha * error); + if (st->costas_phase[ref] > M_PI) st->costas_phase[ref] -= 2 * M_PI; + if (st->costas_phase[ref] < -M_PI) st->costas_phase[ref] += 2 * M_PI; + } + + // compare to sync & parity bits + float x = 0; + for (n = 0; n < BLKSZ; n++) + x += crealf(st->buffer[ref][n]) * sync[n]; + if (x < 0) + { + // adjust phase by pi to compensate + for (n = 0; n < BLKSZ; n++) + { + st->phases[ref][n] += M_PI; + st->buffer[ref][n] *= -1; + } + st->costas_phase[ref] += M_PI; + } +} + +static void reset_ref(sync_t *st, unsigned int ref) +{ + for (unsigned int n = 0; n < BLKSZ; n++) + st->buffer[ref][n] *= cexpf(I * st->phases[ref][n]); +} + +static void decode_dbpsk(const float complex *buf, unsigned char *data, int size) +{ + unsigned char prev = 0; + + for (int n = 0; n < size; n++) + { + unsigned char bit = crealf(buf[n]) <= 0 ? 0 : 1; + data[n] = bit ^ prev; + prev = bit; + } +} + +static int fuzzy_match(const signed char *needle, unsigned int needle_size, const unsigned char *data, int size) +{ + for (int n = 0; n < size; n++) + { + unsigned int i; + for (i = 0; i < needle_size; i++) + { + // ignore don't care bits + if (needle[i] < 0) continue; + // test if bit is correct + if (needle[i] != data[(n + i) % size]) + break; + } + if (i == needle_size) + return n; + } + return -1; +} + +static int decode_ref_fm(sync_t *st, unsigned int ref, unsigned int rsid, unsigned int *bc, unsigned int *psmi) +{ + signed char needle[] = { + 0, 1, 0, 0, 0, 1, 1, -1, 1, 0, rsid >> 1, (rsid >> 1) ^ (rsid & 1), -1, 0, 0, -1, + -1, -1, -1, -1, 0, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0 + }; + unsigned char data[BLKSZ]; + + for (int n = 0; n < BLKSZ; n++) + if (needle[n] >= 0) + if (needle[n] != (crealf(st->buffer[ref][n]) > 0)) + return -1; + + decode_dbpsk(st->buffer[ref], data, BLKSZ); + *bc = (data[16] << 3) | (data[17] << 2) | (data[18] << 1) | data[19]; + *psmi = (data[25] << 5) | (data[26] << 4) | (data[27] << 3) | (data[28] << 2) | (data[29] << 1) | data[30]; + return 0; +} + +static int find_ref_fm(sync_t *st, unsigned int ref, unsigned int rsid) +{ + signed char needle[] = { + 0, 1, 0, 0, 0, 1, 1, -1, 1, 0, rsid >> 1, (rsid >> 1) ^ (rsid & 1), -1, 0, 0, -1, + -1, -1, -1, -1, 0, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0 + }; + unsigned char data[BLKSZ]; + + for (int n = 0; n < BLKSZ; n++) + data[n] = crealf(st->buffer[ref][n]) <= 0 ? 0 : 1; + + int match = fuzzy_match(needle, sizeof(needle), data, BLKSZ); + if (match >= 0) + return match; + + for (int n = 0; n < BLKSZ; n++) + data[n] ^= 1; + + return fuzzy_match(needle, sizeof(needle), data, BLKSZ); +} + +static int find_block_am(sync_t *st, unsigned int ref) +{ + signed char needle[] = { + 0, 1, 1, 0, 0, 1, 0, -1, -1, 1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + unsigned char data[BLKSZ]; + int bc; + + for (int n = 0; n < BLKSZ; n++) + { + data[n] = cimagf(st->buffer[ref][n]) <= 0 ? 0 : 1; + if ((needle[n] >= 0) && (data[n] != needle[n])) return -1; + } + + // parity checks + if (data[7] ^ data[8]) return -1; + if (data[10] ^ data[11] ^ data[12] ^ data[13]) return -1; + if (data[15] ^ data[16] ^ data[17] ^ data[18] ^ data[19] ^ data[20]) return -1; + if (data[23] ^ data[24] ^ data[25] ^ data[26] ^ data[27] ^ data[28] ^ data[29] ^ data[30] ^ data[31]) return -1; + + bc = (data[17] << 2) | (data[18] << 1) | data[19]; + if (bc == 0) + { + st->psmi = (data[26] << 4) | (data[27] << 3) | (data[28] << 2) | (data[29] << 1) | data[30]; + st->pli = data[7]; + st->hppi = data[11]; + st->aabi = data[12]; + st->rdbi = data[15]; + } + return bc; +} + +static int find_ref_am(sync_t *st, unsigned int ref) +{ + signed char needle[] = { + 0, 1, 1, 0, 0, 1, 0, -1, -1, 1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, 1, 1 + }; + unsigned char data[BLKSZ]; + + for (int n = 0; n < BLKSZ; n++) + data[n] = cimagf(st->buffer[ref][n]) <= 0 ? 0 : 1; + + return fuzzy_match(needle, sizeof(needle), data, BLKSZ); +} + +static float calc_smag(sync_t *st, unsigned int ref) +{ + float sum = 0; + // phase was already corrected, so imaginary component is zero + for (int n = 0; n < BLKSZ; n++) + sum += fabsf(crealf(st->buffer[ref][n])); + return sum / BLKSZ; +} + +static void adjust_data(sync_t *st, unsigned int lower, unsigned int upper) +{ + float smag0, smag19; + smag0 = calc_smag(st, lower); + smag19 = calc_smag(st, upper); + + for (int n = 0; n < BLKSZ; n++) + { + float complex upper_phase = cexpf(st->phases[upper][n] * I); + float complex lower_phase = cexpf(st->phases[lower][n] * I); + + for (int k = 1; k < PARTITION_WIDTH_FM; k++) + { + // average phase difference + float complex C = CMPLXF(PARTITION_WIDTH_FM, PARTITION_WIDTH_FM) / (k * smag19 * upper_phase + (PARTITION_WIDTH_FM - k) * smag0 * lower_phase); + // adjust sample + st->buffer[lower + k][n] *= C; + } + } +} + +float phase_diff(float a, float b) +{ + float diff = a - b; + while (diff > M_PI / 2) diff -= M_PI; + while (diff < -M_PI / 2) diff += M_PI; + return diff; +} + +void detect_cfo(sync_t *st) +{ + for (int cfo = -2 * PARTITION_WIDTH_FM; cfo < 2 * PARTITION_WIDTH_FM; cfo++) + { + int offset; + int best_offset = -1; + unsigned int best_count = 0; + unsigned int offset_count[BLKSZ]; + + memset(offset_count, 0, BLKSZ * sizeof(unsigned int)); + + for (int i = 0; i <= PM_PARTITIONS; i++) + { + adjust_ref(st, cfo + LB_START + i * PARTITION_WIDTH_FM, cfo); + offset = find_ref_fm(st, cfo + LB_START + i * PARTITION_WIDTH_FM, (MIDDLE_REF_SC-i) & 0x3); + reset_ref(st, cfo + LB_START + i * PARTITION_WIDTH_FM); + if (offset >= 0) + offset_count[offset]++; + + adjust_ref(st, cfo + UB_END - i * PARTITION_WIDTH_FM, cfo); + offset = find_ref_fm(st, cfo + UB_END - i * PARTITION_WIDTH_FM, (MIDDLE_REF_SC-i) & 0x3); + reset_ref(st, cfo + UB_END - i * PARTITION_WIDTH_FM); + if (offset >= 0) + offset_count[offset]++; + } + + for (offset = 0; offset < BLKSZ; offset++) + { + if (offset_count[offset] > best_count) { + best_offset = offset; + best_count = offset_count[offset]; + } + } + + if (best_offset >= 0 && best_count >= 3) + { + // At least three offsets matched, so this is likely the correct CFO. + acquire_keep_extra(&st->input->acq, ((BLKSZ - best_offset) % BLKSZ) * FFTCP_FM); + acquire_cfo_adjust(&st->input->acq, cfo); + + // Wait until the buffers have cleared before measuring again. + st->cfo_wait = 8; + break; + } + } +} + +void sync_process_fm(sync_t *st) +{ + int i, partitions_per_band; + + switch (compatibility_mode[st->psmi]) { + case 2: + partitions_per_band = 11; + break; + case 3: + partitions_per_band = 12; + break; + case 5: + case 6: + case 11: + partitions_per_band = 14; + break; + default: + partitions_per_band = 10; + } + + for (i = 0; i < partitions_per_band * PARTITION_WIDTH_FM + 1; i += PARTITION_WIDTH_FM) + { + adjust_ref(st, LB_START + i, 0); + adjust_ref(st, UB_END - i, 0); + } + + // check if we now have synchronization + if (st->input->sync_state == SYNC_STATE_COARSE) + { + unsigned int good_refs = 0; + unsigned int seen_bc[16] = {0}; + unsigned int seen_psmi[64] = {0}; + for (i = 0; i <= partitions_per_band; i++) + { + unsigned int bc, psmi; + if (decode_ref_fm(st, LB_START + i * PARTITION_WIDTH_FM, (MIDDLE_REF_SC-i) & 0x3, &bc, &psmi) == 0) + { + good_refs++; + seen_bc[bc]++; + seen_psmi[psmi]++; + } + if (decode_ref_fm(st, UB_END - i * PARTITION_WIDTH_FM, (MIDDLE_REF_SC-i) & 0x3, &bc, &psmi) == 0) + { + good_refs++; + seen_bc[bc]++; + seen_psmi[psmi]++; + } + } + + if (good_refs >= 4) + { + int majority_bc = -1; + for (unsigned int bc = 0; bc < 16; bc++) + if (seen_bc[bc] > good_refs / 2) + majority_bc = bc; + + int majority_psmi = -1; + for (unsigned int psmi = 0; psmi < 16; psmi++) + if (seen_psmi[psmi] > good_refs / 2) + majority_psmi = psmi; + + if ((majority_bc >= 0) && (majority_psmi >= 0)) + { + st->bc = majority_bc; + st->psmi = majority_psmi; + + input_set_sync_state(st->input, SYNC_STATE_FINE); + + decode_reset(&st->input->decode); + + frame_reset(&st->input->frame); + } + } + else if (st->cfo_wait == 0) + { + detect_cfo(st); + } + else + { + // Decrease wait counter. + st->cfo_wait--; + } + } + + // if we are still synchronized + if (st->input->sync_state == SYNC_STATE_FINE) + { + float samperr = 0, angle = 0; + float sum_xy = 0, sum_x2 = 0; + for (i = 0; i < partitions_per_band * PARTITION_WIDTH_FM; i += PARTITION_WIDTH_FM) + { + adjust_data(st, LB_START + i, LB_START + i + PARTITION_WIDTH_FM); + adjust_data(st, UB_END - i - PARTITION_WIDTH_FM, UB_END - i); + + samperr += phase_diff(st->phases[LB_START + i][0], st->phases[LB_START + i + PARTITION_WIDTH_FM][0]); + samperr += phase_diff(st->phases[UB_END - i - PARTITION_WIDTH_FM][0], st->phases[UB_END - i][0]); + } + samperr = samperr / (partitions_per_band * 2) * FFT_FM / PARTITION_WIDTH_FM / (2 * M_PI); + + for (i = 0; i < partitions_per_band * PARTITION_WIDTH_FM + 1; i += PARTITION_WIDTH_FM) + { + float x, y; + + x = LB_START + i - (FFT_FM / 2); + y = st->costas_freq[LB_START + i]; + angle += y; + sum_xy += x * y; + sum_x2 += x * x; + + x = UB_END - i - (FFT_FM / 2); + y = st->costas_freq[UB_END - i]; + angle += y; + sum_xy += x * y; + sum_x2 += x * x; + } + samperr -= (sum_xy / sum_x2) * FFT_FM / (2 * M_PI) * ACQUIRE_SYMBOLS; + st->samperr = roundf(samperr); + + angle /= (partitions_per_band + 1) * 2; + st->angle = angle; + for (i = 0; i < partitions_per_band * PARTITION_WIDTH_FM + 1; i += PARTITION_WIDTH_FM) + { + st->costas_freq[LB_START + i] -= angle; + st->costas_freq[UB_END - i] -= angle; + } + + // Calculate modulation error + float error_lb = 0, error_ub = 0; + for (int n = 0; n < BLKSZ; n++) + { + float complex c, ideal; + for (i = 0; i < partitions_per_band * PARTITION_WIDTH_FM; i += PARTITION_WIDTH_FM) + { + unsigned int j; + for (j = 1; j < PARTITION_WIDTH_FM; j++) + { + c = st->buffer[LB_START + i + j][n]; + ideal = CMPLXF(crealf(c) >= 0 ? 1 : -1, cimagf(c) >= 0 ? 1 : -1); + error_lb += normf(ideal - c); + + c = st->buffer[UB_END - i - PARTITION_WIDTH_FM + j][n]; + ideal = CMPLXF(crealf(c) >= 0 ? 1 : -1, cimagf(c) >= 0 ? 1 : -1); + error_ub += normf(ideal - c); + } + } + } + + st->error_lb += error_lb; + st->error_ub += error_ub; + + // Display average MER for each sideband + if (++st->mer_cnt == 16) + { + float signal = 2 * BLKSZ * (partitions_per_band * PARTITION_DATA_CARRIERS) * st->mer_cnt; + float mer_db_lb = 10 * log10f(signal / st->error_lb); + float mer_db_ub = 10 * log10f(signal / st->error_ub); + + nrsc5_report_mer(st->input->radio, mer_db_lb, mer_db_ub); + + st->mer_cnt = 0; + st->error_lb = 0; + st->error_ub = 0; + } + + // Soft demod based on MER for each sideband + const float mer_lb = 2.0f * BLKSZ * (float)(partitions_per_band * PARTITION_DATA_CARRIERS) / error_lb; + const float mer_ub = 2.0f * BLKSZ * (float)(partitions_per_band * PARTITION_DATA_CARRIERS) / error_ub; + const float mult_lb = fmaxf(fminf(mer_lb * 10, 127), 1); + const float mult_ub = fmaxf(fminf(mer_ub * 10, 127), 1); + + int8_t buffer_pm[PM_BLOCK_SIZE]; + int8_t buffer_px1[P3_FRAME_LEN_MP3_MP11]; + int8_t buffer_px2[P3_FRAME_LEN_MP3_MP11]; + int out_pm = 0, out_px1 = 0, out_px2 = 0; + + for (int n = 0; n < BLKSZ; n++) + { + float complex c; + for (i = LB_START; i < LB_START + (PM_PARTITIONS * PARTITION_WIDTH_FM); i += PARTITION_WIDTH_FM) + { + unsigned int j; + for (j = 1; j < PARTITION_WIDTH_FM; j++) + { + c = st->buffer[i + j][n]; + buffer_pm[out_pm++] = demod(crealf(c), mult_lb); + buffer_pm[out_pm++] = demod(cimagf(c), mult_lb); + } + } + for (i = UB_END - (PM_PARTITIONS * PARTITION_WIDTH_FM); i < UB_END; i += PARTITION_WIDTH_FM) + { + unsigned int j; + for (j = 1; j < PARTITION_WIDTH_FM; j++) + { + c = st->buffer[i + j][n]; + buffer_pm[out_pm++] = demod(crealf(c), mult_ub); + buffer_pm[out_pm++] = demod(cimagf(c), mult_ub); + } + } + if (compatibility_mode[st->psmi] == 2) { + unsigned int j; + for (j = 1; j < PARTITION_WIDTH_FM; j++) + { + c = st->buffer[LB_START + (PM_PARTITIONS * PARTITION_WIDTH_FM) + j][n]; + buffer_px1[out_px1++] = demod(crealf(c), mult_lb); + buffer_px1[out_px1++] = demod(cimagf(c), mult_lb); + } + for (j = 1; j < PARTITION_WIDTH_FM; j++) + { + c = st->buffer[UB_END - (PM_PARTITIONS + 1) * PARTITION_WIDTH_FM + j][n]; + buffer_px1[out_px1++] = demod(crealf(c), mult_ub); + buffer_px1[out_px1++] = demod(cimagf(c), mult_ub); + } + } + if ((compatibility_mode[st->psmi] == 3) || (compatibility_mode[st->psmi] == 11)) { + for (i = LB_START + (PM_PARTITIONS * PARTITION_WIDTH_FM); i < LB_START + (PM_PARTITIONS + 2) * PARTITION_WIDTH_FM; i += PARTITION_WIDTH_FM) + { + unsigned int j; + for (j = 1; j < PARTITION_WIDTH_FM; j++) + { + c = st->buffer[i + j][n]; + buffer_px1[out_px1++] = demod(crealf(c), mult_lb); + buffer_px1[out_px1++] = demod(cimagf(c), mult_lb); + } + } + for (i = UB_END - (PM_PARTITIONS + 2) * PARTITION_WIDTH_FM; i < UB_END - (PM_PARTITIONS * PARTITION_WIDTH_FM); i += PARTITION_WIDTH_FM) + { + unsigned int j; + for (j = 1; j < PARTITION_WIDTH_FM; j++) + { + c = st->buffer[i + j][n]; + buffer_px1[out_px1++] = demod(crealf(c), mult_ub); + buffer_px1[out_px1++] = demod(cimagf(c), mult_ub); + } + } + } + if (compatibility_mode[st->psmi] == 11) { + for (i = LB_START + (PM_PARTITIONS + 2) * PARTITION_WIDTH_FM; i < LB_START + (PM_PARTITIONS + 4) * PARTITION_WIDTH_FM; i += PARTITION_WIDTH_FM) + { + unsigned int j; + for (j = 1; j < PARTITION_WIDTH_FM; j++) + { + c = st->buffer[i + j][n]; + buffer_px2[out_px2++] = demod(crealf(c), mult_lb); + buffer_px2[out_px2++] = demod(cimagf(c), mult_lb); + } + } + for (i = UB_END - (PM_PARTITIONS + 4) * PARTITION_WIDTH_FM; i < UB_END - (PM_PARTITIONS + 2) * PARTITION_WIDTH_FM; i += PARTITION_WIDTH_FM) + { + unsigned int j; + for (j = 1; j < PARTITION_WIDTH_FM; j++) + { + c = st->buffer[i + j][n]; + buffer_px2[out_px2++] = demod(crealf(c), mult_lb); + buffer_px2[out_px2++] = demod(cimagf(c), mult_lb); + } + } + } + } + + decode_push_pm(&st->input->decode, buffer_pm, st->bc); + if (out_px1 > 0) + { + decode_push_px1(&st->input->decode, buffer_px1, out_px1, st->bc); + } + if (out_px2 > 0) + { + decode_push_px2(&st->input->decode, buffer_px2, out_px2, st->bc); + } + + st->bc = (st->bc + 1) % 16; + } +} + +void sync_process_am(sync_t *st) +{ + int offset; + + for (int i = REF_INDEX_AM; i <= MAX_INDEX_AM; i++) + { + for (int n = 0; n < BLKSZ; n++) + { + st->buffer[CENTER_AM - i][n] = -conjf(st->buffer[CENTER_AM - i][n]); + } + } + + if (st->psmi != SERVICE_MODE_MA3) + { + for (int i = REF_INDEX_AM; i <= PIDS_OUTER_INDEX_AM; i++) + { + for (int n = 0; n < BLKSZ; n++) + { + st->buffer[CENTER_AM + i][n] += st->buffer[CENTER_AM - i][n]; + } + } + } + + if (st->input->sync_state == SYNC_STATE_COARSE && st->cfo_wait == 0) + { + offset = find_ref_am(st, CENTER_AM + REF_INDEX_AM); + if (offset > 0) + { + acquire_keep_extra(&st->input->acq, ((BLKSZ - offset) % BLKSZ) * FFTCP_AM); + st->cfo_wait = 8; + } + } + else + { + st->cfo_wait--; + } + + if (st->input->sync_state == SYNC_STATE_COARSE) + { + int bc = find_block_am(st, CENTER_AM + REF_INDEX_AM); + + if (bc == -1) + st->offset_history = 0; + else + st->offset_history = (st->offset_history << 4) | bc; + + if ((st->offset_history & 0xffff) == 0x5670) + { + st->bc = 0; + input_set_sync_state(st->input, SYNC_STATE_FINE); + decode_reset(&st->input->decode); + frame_reset(&st->input->frame); + st->offset_history = 0; + } + } + + if (st->input->sync_state == SYNC_STATE_FINE) + { + const int pids1_index = (st->psmi != SERVICE_MODE_MA3) ? PIDS_INNER_INDEX_AM : -PIDS_INNER_INDEX_AM; + const int pids2_index = (st->psmi != SERVICE_MODE_MA3) ? PIDS_OUTER_INDEX_AM : PIDS_INNER_INDEX_AM; + + const float complex pids1_mult = 2 * CMPLXF(1.5, -0.5) / (st->buffer[CENTER_AM + pids1_index][8] + st->buffer[CENTER_AM + pids1_index][24]); + const float complex pids2_mult = 2 * CMPLXF(1.5, -0.5) / (st->buffer[CENTER_AM + pids2_index][8] + st->buffer[CENTER_AM + pids2_index][24]); + uint8_t pids[2 * BLKSZ]; + int pids_out = 0; + + for (int n = 0; n < BLKSZ; n++) + { + st->buffer[CENTER_AM + pids1_index][n] *= pids1_mult; + pids[pids_out++] = qam16(st->buffer[CENTER_AM + pids1_index][n]); + + st->buffer[CENTER_AM + pids2_index][n] *= pids2_mult; + pids[pids_out++] = qam16(st->buffer[CENTER_AM + pids2_index][n]); + } + + decode_process_pids_am(&st->input->decode, pids); + + float complex pl_mult[PARTITION_WIDTH_AM]; + float complex pu_mult[PARTITION_WIDTH_AM]; + float complex s_mult[PARTITION_WIDTH_AM]; + float complex t_mult[PARTITION_WIDTH_AM]; + + const int primary_index = (st->psmi != SERVICE_MODE_MA3) ? OUTER_PARTITION_START_AM : INNER_PARTITION_START_AM; + const int secondary_index = MIDDLE_PARTITION_START_AM; + const int tertiary_index = (st->psmi != SERVICE_MODE_MA3) ? INNER_PARTITION_START_AM : MIDDLE_PARTITION_START_AM; + + float samperr = 0; + for (int col = 0; col < PARTITION_WIDTH_AM; col++) + { + int train1 = (5 + 11*col) % 32; + int train2 = (21 + 11*col) % 32; + + pl_mult[col] = 2 * CMPLXF(2.5, -2.5) / (st->buffer[CENTER_AM - primary_index - col][train1] + st->buffer[CENTER_AM - primary_index - col][train2]); + pu_mult[col] = 2 * CMPLXF(2.5, -2.5) / (st->buffer[CENTER_AM + primary_index + col][train1] + st->buffer[CENTER_AM + primary_index + col][train2]); + if (st->psmi != SERVICE_MODE_MA3) + { + s_mult[col] = 2 * CMPLXF(1.5, -0.5) / (st->buffer[CENTER_AM + secondary_index + col][train1] + st->buffer[CENTER_AM + secondary_index + col][train2]); + t_mult[col] = 2 * CMPLXF(-0.5, 0.5) / (st->buffer[CENTER_AM + tertiary_index + col][train1] + st->buffer[CENTER_AM + tertiary_index + col][train2]); + } + else + { + s_mult[col] = 2 * CMPLXF(2.5, -2.5) / (st->buffer[CENTER_AM + secondary_index + col][train1] + st->buffer[CENTER_AM + secondary_index + col][train2]); + t_mult[col] = 2 * CMPLXF(2.5, -2.5) / (st->buffer[CENTER_AM - tertiary_index - col][train1] + st->buffer[CENTER_AM - tertiary_index - col][train2]); + } + + if (col > 0) + { + samperr += phase_diff(cargf(pl_mult[col]), cargf(pl_mult[col-1])); + samperr += phase_diff(cargf(pu_mult[col]), cargf(pu_mult[col-1])); + } + } + samperr = samperr / (2 * (PARTITION_WIDTH_AM-1)) * FFT_AM / (2 * M_PI); + st->samperr = roundf(samperr); + + uint8_t pl[BLKSZ * PARTITION_WIDTH_AM]; + uint8_t pu[BLKSZ * PARTITION_WIDTH_AM]; + uint8_t s[BLKSZ * PARTITION_WIDTH_AM]; + uint8_t t[BLKSZ * PARTITION_WIDTH_AM]; + + for (int n = 0; n < BLKSZ; n++) + { + for (int col = 0; col < PARTITION_WIDTH_AM; col++) + { + st->buffer[CENTER_AM - primary_index - col][n] *= pl_mult[col]; + st->buffer[CENTER_AM + primary_index + col][n] *= pu_mult[col]; + st->buffer[CENTER_AM + secondary_index + col][n] *= s_mult[col]; + if (st->psmi != SERVICE_MODE_MA3) + st->buffer[CENTER_AM + tertiary_index + col][n] *= t_mult[col]; + else + st->buffer[CENTER_AM - tertiary_index - col][n] *= t_mult[col]; + + if (st->psmi != SERVICE_MODE_MA3) + { + pl[n * PARTITION_WIDTH_AM + col] = qam64(st->buffer[CENTER_AM - primary_index - col][n]); + pu[n * PARTITION_WIDTH_AM + col] = qam64(st->buffer[CENTER_AM + primary_index + col][n]); + s[n * PARTITION_WIDTH_AM + col] = qam16(st->buffer[CENTER_AM + secondary_index + col][n]); + t[n * PARTITION_WIDTH_AM + col] = qpsk(st->buffer[CENTER_AM + tertiary_index + col][n]); + } + else + { + pl[n * PARTITION_WIDTH_AM + col] = qam64(st->buffer[CENTER_AM - primary_index - col][n]); + pu[n * PARTITION_WIDTH_AM + col] = qam64(st->buffer[CENTER_AM + primary_index + col][n]); + s[n * PARTITION_WIDTH_AM + col] = qam64(st->buffer[CENTER_AM + secondary_index + col][n]); + t[n * PARTITION_WIDTH_AM + col] = qam64(st->buffer[CENTER_AM - tertiary_index - col][n]); + } + } + } + + decode_push_pl_pu_s_t( + &st->input->decode, + pl, pu, s, t, st->bc + ); + + st->bc = (st->bc + 1) % 8; + } +} + +void sync_adjust(sync_t *st, int sample_adj) +{ + int i; + for (i = 0; i < MAX_PARTITIONS * PARTITION_WIDTH_FM + 1; i++) + { + st->costas_phase[LB_START + i] -= sample_adj * (LB_START + i - (FFT_FM / 2)) * 2 * M_PI / FFT_FM; + st->costas_phase[UB_END - i] -= sample_adj * (UB_END - i - (FFT_FM / 2)) * 2 * M_PI / FFT_FM; + } +} + +void sync_push(sync_t *st, float complex *fftout) +{ + unsigned int i; + + if (st->input->radio->mode == NRSC5_MODE_FM) + { + for (i = 0; i < MAX_PARTITIONS * PARTITION_WIDTH_FM + 1; i++) + { + st->buffer[LB_START + i][st->idx] = fftout[LB_START + i]; + st->buffer[UB_END - i][st->idx] = fftout[UB_END - i]; + } + } + else + { + for (i = CENTER_AM - MAX_INDEX_AM; i <= CENTER_AM + MAX_INDEX_AM; i++) + { + st->buffer[i][st->idx] = fftout[i]; + } + } + + if (++st->idx == BLKSZ) + { + st->idx = 0; + + if (st->input->radio->mode == NRSC5_MODE_FM) + sync_process_fm(st); + else + sync_process_am(st); + } +} + +void sync_reset(sync_t *st) +{ + unsigned int i; + for (i = 0; i < FFT_FM; i++) + { + st->costas_freq[i] = 0; + st->costas_phase[i] = 0; + } + + st->idx = 0; + st->psmi = 1; + st->pli = -1; + st->hppi = -1; + st->aabi = -1; + st->rdbi = -1; + st->cfo_wait = 0; + st->offset_history = 0; + st->mer_cnt = 0; + st->error_lb = 0; + st->error_ub = 0; +} + +void sync_init(sync_t *st, input_t *input) +{ + float loop_bw = 0.05, damping = 0.70710678; + float denom = 1 + (2 * damping * loop_bw) + (loop_bw * loop_bw); + st->alpha = (4 * damping * loop_bw) / denom; + st->beta = (4 * loop_bw * loop_bw) / denom; + + st->input = input; + sync_reset(st); +} diff --git a/third_party/nrsc5/upstream/src/sync.h b/third_party/nrsc5/upstream/src/sync.h new file mode 100644 index 000000000..ff40b750d --- /dev/null +++ b/third_party/nrsc5/upstream/src/sync.h @@ -0,0 +1,37 @@ +#pragma once + +#include "config.h" + +#include + +typedef struct +{ + struct input_t *input; + float complex buffer[FFT_FM][BLKSZ]; + float phases[FFT_FM][BLKSZ]; + unsigned int idx; + int psmi; + int pli; + int hppi; + int aabi; + int rdbi; + int cfo_wait; + unsigned int bc; + unsigned int offset_history; + int samperr; + float angle; + + float alpha; + float beta; + float costas_freq[FFT_FM]; + float costas_phase[FFT_FM]; + + int mer_cnt; + float error_lb; + float error_ub; +} sync_t; + +void sync_adjust(sync_t *st, int sample_adj); +void sync_push(sync_t *st, float complex *fft); +void sync_reset(sync_t *st); +void sync_init(sync_t *st, struct input_t *input); diff --git a/third_party/nrsc5/upstream/src/unicode.c b/third_party/nrsc5/upstream/src/unicode.c new file mode 100644 index 000000000..5ce8dd5b4 --- /dev/null +++ b/third_party/nrsc5/upstream/src/unicode.c @@ -0,0 +1,78 @@ +#include + +#include "unicode.h" + +char *iso_8859_1_to_utf_8(uint8_t *buf, unsigned int len) +{ + unsigned int i, j; + char *out = malloc(len * 2 + 1); + + j = 0; + for (i = 0; i < len; i++) + { + uint8_t ch = buf[i]; + + if (ch < 0x80) + { + out[j++] = ch; + } + else + { + out[j++] = 0xc0 | (ch >> 6); + out[j++] = 0x80 | (ch & 0x3f); + } + } + + out[j] = 0; + return out; +} + +char *ucs_2_to_utf_8(uint8_t *buf, unsigned int len) +{ + unsigned int i = 0, j = 0; + unsigned int big_endian = 0; + char *out = malloc((len / 2) * 3 + 1); + + if (len >= 2) + { + if ((buf[0] == 0xfe) && (buf[1] == 0xff)) + { + big_endian = 1; + i += 2; + } + else if ((buf[0] == 0xff) && (buf[1] == 0xfe)) + { + big_endian = 0; + i += 2; + } + } + + for (; i < len - 1; i += 2) + { + uint16_t ch; + + if (big_endian) + ch = (buf[i] << 8) | buf[i+1]; + else + ch = buf[i] | (buf[i+1] << 8); + + if (ch < 0x80) + { + out[j++] = ch; + } + else if (ch < 0x800) + { + out[j++] = 0xc0 | (ch >> 6); + out[j++] = 0x80 | (ch & 0x3f); + } + else + { + out[j++] = 0xe0 | (ch >> 12); + out[j++] = 0x80 | ((ch >> 6) & 0x3f); + out[j++] = 0x80 | (ch & 0x3f); + } + } + + out[j] = 0; + return out; +} diff --git a/third_party/nrsc5/upstream/src/unicode.h b/third_party/nrsc5/upstream/src/unicode.h new file mode 100644 index 000000000..f0194eeb5 --- /dev/null +++ b/third_party/nrsc5/upstream/src/unicode.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +char *iso_8859_1_to_utf_8(uint8_t *buf, unsigned int len); +char *ucs_2_to_utf_8(uint8_t *buf, unsigned int len); diff --git a/third_party/nrsc5/upstream/support/faad2-hdc-support.patch b/third_party/nrsc5/upstream/support/faad2-hdc-support.patch new file mode 100644 index 000000000..e6a6744cc --- /dev/null +++ b/third_party/nrsc5/upstream/support/faad2-hdc-support.patch @@ -0,0 +1,958 @@ +From 813725c185276045641e7df90329dbeab5029434 Mon Sep 17 00:00:00 2001 +From: Clayton Smith +Date: Mon, 11 Aug 2025 14:42:46 -0700 +Subject: [PATCH] Support for HDC variant + +--- + CMakeLists.txt | 10 +- + frontend/main.c | 3 +- + include/neaacdec.h | 6 +- + libfaad/bits.c | 2 +- + libfaad/bits.h | 2 +- + libfaad/common.c | 5 + + libfaad/common.h | 6 + + libfaad/decoder.c | 54 ++++++++- + libfaad/drm_dec.c | 108 +++++++++++++----- + libfaad/drm_dec.h | 5 +- + libfaad/sbr_dec.c | 10 +- + libfaad/sbr_dec.h | 4 + + libfaad/sbr_syntax.c | 24 +++- + libfaad/syntax.c | 257 ++++++++++++++++++++++++++++++++++++++++++- + libfaad/syntax.h | 6 + + 15 files changed, 458 insertions(+), 44 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 97b61cc..5956418 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -146,7 +146,7 @@ endif() # GCC + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/include/neaacdec.h + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include) + +-foreach(LIB faad faad_drm faad_fixed faad_drm_fixed) ++foreach(LIB faad faad_drm faad_hdc faad_fixed faad_drm_fixed) + add_library(${LIB} ${LIBFAAD_SOURCES}) + if(MATH_LIBRARY) + target_link_libraries(${LIB} PUBLIC ${MATH_LIBRARY}) +@@ -171,6 +171,12 @@ target_compile_definitions(${LIB} PRIVATE + ) + endforeach() + ++foreach(LIB faad_hdc) ++ target_compile_definitions(${LIB} PRIVATE ++ HDC_SUPPORT ++ ) ++endforeach() ++ + foreach(LIB faad_fixed faad_drm_fixed) + target_compile_definitions(${LIB} PRIVATE + FIXED_POINT +@@ -271,7 +277,7 @@ if(NOT FAAD_BUNDLED_MODE) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/faad2.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + +- set(INSTALL_TARGETS faad faad_drm) ++ set(INSTALL_TARGETS faad faad_drm faad_hdc) + if (FAAD_BUILD_CLI) + list(APPEND INSTALL_TARGETS faad_cli) + endif() +diff --git a/frontend/main.c b/frontend/main.c +index 895ed18..18aefb7 100644 +--- a/frontend/main.c ++++ b/frontend/main.c +@@ -1164,7 +1164,8 @@ static int faad_main(int argc, char *argv[]) + if ((val != LC) && + (val != MAIN) && + (val != LTP) && +- (val != LD)) ++ (val != LD) && ++ (val != HDC_LC)) + { + showHelp = 1; + } +diff --git a/include/neaacdec.h b/include/neaacdec.h +index f7d5f67..dbe1354 100644 +--- a/include/neaacdec.h ++++ b/include/neaacdec.h +@@ -60,7 +60,7 @@ extern "C" { + + #ifdef _WIN32 + #pragma pack(push, 8) +- #define NEAACDECAPI __declspec(dllexport) ++ #define NEAACDECAPI + #elif defined(__GNUC__) && __GNUC__ >= 4 + #define NEAACDECAPI __attribute__((visibility("default"))) + #else +@@ -81,6 +81,7 @@ extern "C" { + #define ER_LTP 19 + #define LD 23 + #define DRM_ER_LC 27 /* special object type for DRM */ ++#define HDC_LC 127 /* non standard type for HDC */ + + /* header types */ + #define RAW 0 +@@ -227,6 +228,9 @@ NEAACDECAPI char NeAACDecInit2(NeAACDecHandle hDecoder, + NEAACDECAPI char NeAACDecInitDRM(NeAACDecHandle *hDecoder, unsigned long samplerate, + unsigned char channels); + ++/* Init the library for HDC */ ++NEAACDECAPI char NeAACDecInitHDC(NeAACDecHandle *hDecoder); ++ + NEAACDECAPI void NeAACDecPostSeekReset(NeAACDecHandle hDecoder, long frame); + + NEAACDECAPI void NeAACDecClose(NeAACDecHandle hDecoder); +diff --git a/libfaad/bits.c b/libfaad/bits.c +index 110c9dc..ee37aff 100644 +--- a/libfaad/bits.c ++++ b/libfaad/bits.c +@@ -244,7 +244,7 @@ uint8_t *faad_getbitbuffer(bitfile *ld, uint32_t bits + return buffer; + } + +-#ifdef DRM ++#if defined(DRM) || defined(HDC) + /* return the original data buffer */ + void *faad_origbitbuffer(bitfile *ld) + { +diff --git a/libfaad/bits.h b/libfaad/bits.h +index c01c54e..44ea3fa 100644 +--- a/libfaad/bits.h ++++ b/libfaad/bits.h +@@ -87,7 +87,7 @@ void faad_rewindbits(bitfile *ld); + void faad_resetbits(bitfile *ld, uint32_t bits); + uint8_t *faad_getbitbuffer(bitfile *ld, uint32_t bits + DEBUGDEC); +-#ifdef DRM ++#if defined(DRM) || defined(HDC) + void *faad_origbitbuffer(bitfile *ld); + uint32_t faad_origbitbuffer_size(bitfile *ld); + #endif +diff --git a/libfaad/common.c b/libfaad/common.c +index 1f4a7d1..70d83a4 100644 +--- a/libfaad/common.c ++++ b/libfaad/common.c +@@ -165,6 +165,11 @@ int8_t can_decode_ot(const uint8_t object_type) + #else + return -1; + #endif ++#endif ++ ++#ifdef HDC ++ case HDC_LC: ++ return 0; + #endif + } + +diff --git a/libfaad/common.h b/libfaad/common.h +index aba8e90..d6a5c59 100644 +--- a/libfaad/common.h ++++ b/libfaad/common.h +@@ -95,6 +95,12 @@ extern "C" { + #define DRM + #define DRM_PS + #endif ++/* Allow decoding of HDC */ ++#ifdef HDC_SUPPORT ++#define DRM ++#define DRM_PS ++#define HDC ++#endif + + /* LD can't do without LTP */ + #ifdef LD_DEC +diff --git a/libfaad/decoder.c b/libfaad/decoder.c +index de3b169..731ebe8 100644 +--- a/libfaad/decoder.c ++++ b/libfaad/decoder.c +@@ -350,6 +350,13 @@ long NeAACDecInit(NeAACDecHandle hpDecoder, + if (!*samplerate) + return -1; + ++#ifdef HDC ++ if (hDecoder->config.defObjectType == HDC_LC) ++ { ++ hDecoder->object_type = hDecoder->config.defObjectType; ++ } ++#endif ++ + #if (defined(PS_DEC) || defined(DRM_PS)) + /* check if we have a mono file */ + if (*channels == 1) +@@ -529,6 +536,38 @@ char NeAACDecInitDRM(NeAACDecHandle *hpDecoder, + } + #endif + ++#ifdef HDC ++char NeAACDecInitHDC(NeAACDecHandle *hpDecoder) ++{ ++ NeAACDecStruct** hDecoder = (NeAACDecStruct**)hpDecoder; ++ if (hDecoder == NULL) ++ return 1; /* error */ ++ ++ NeAACDecClose(*hDecoder); ++ ++ *hDecoder = NeAACDecOpen(); ++ ++ /* Special object type defined for HDC */ ++ (*hDecoder)->config.defObjectType = HDC_LC; ++ ++ (*hDecoder)->config.defSampleRate = 22050; ++#ifdef ERROR_RESILIENCE ++ (*hDecoder)->aacSectionDataResilienceFlag = 0; ++ (*hDecoder)->aacScalefactorDataResilienceFlag = 0; ++ (*hDecoder)->aacSpectralDataResilienceFlag = 0; ++#endif ++ (*hDecoder)->frameLength = 1024; ++ (*hDecoder)->sf_index = get_sr_index((*hDecoder)->config.defSampleRate); ++ (*hDecoder)->object_type = (*hDecoder)->config.defObjectType; ++ ++ (*hDecoder)->channelConfiguration = 2; ++ (*hDecoder)->forceUpSampling = 1; ++ ++ (*hDecoder)->fb = filter_bank_init((*hDecoder)->frameLength); ++ return 0; ++} ++#endif ++ + void NeAACDecClose(NeAACDecHandle hpDecoder) + { + uint8_t i; +@@ -987,7 +1026,20 @@ static void* aac_frame_decode(NeAACDecStruct *hDecoder, + DRM_aac_scalable_main_element(hDecoder, hInfo, &ld, &hDecoder->pce, hDecoder->drc); + } else { + #endif +- raw_data_block(hDecoder, hInfo, &ld, &hDecoder->pce, hDecoder->drc); ++ ++#ifdef HDC ++ if (hDecoder->object_type == HDC_LC) ++ { ++ hdc_data_block(hDecoder, hInfo, &ld, &hDecoder->pce, hDecoder->drc); ++ } else { ++#endif ++ ++ raw_data_block(hDecoder, hInfo, &ld, &hDecoder->pce, hDecoder->drc); ++ ++#ifdef HDC ++ } ++#endif ++ + #ifdef DRM + } + #endif +diff --git a/libfaad/drm_dec.c b/libfaad/drm_dec.c +index 1a24aca..5cea492 100644 +--- a/libfaad/drm_dec.c ++++ b/libfaad/drm_dec.c +@@ -44,6 +44,9 @@ + #define DECAY_CUTOFF 3 + #define DECAY_SLOPE 0.05f + ++/* macros */ ++#define IS_HDC(num_subsamples) ((num_subsamples) == 32) ++ + /* type definitaions */ + typedef const int8_t (*drm_ps_huff_tab)[2]; + +@@ -225,6 +228,30 @@ static const real_t pan_pow_2_30_neg[8][5] = { + { COEF_CONST(0.969764715), COEF_CONST(0.947691892), COEF_CONST(0.922571949), COEF_CONST(0.898117847), COEF_CONST(0.874311936) } + }; + ++/* 2^(pan_quant[x][y]/32) */ ++static const real_t pan_pow_2_32_pos[8][5] = { ++ { COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1) }, ++ { COEF_CONST(1.003604347), COEF_CONST(1.003604347), COEF_CONST(1.007221686), COEF_CONST(1.007221686), COEF_CONST(1.007221686) }, ++ { COEF_CONST(1.007221686), COEF_CONST(1.007221686), COEF_CONST(1.014495524), COEF_CONST(1.018152118), COEF_CONST(1.018152118) }, ++ { COEF_CONST(1.010852062), COEF_CONST(1.014495524), COEF_CONST(1.021821892), COEF_CONST(1.032910767), COEF_CONST(1.036633736) }, ++ { COEF_CONST(1.014495524), COEF_CONST(1.021821892), COEF_CONST(1.032910767), COEF_CONST(1.04788335), COEF_CONST(1.055448548) }, ++ { COEF_CONST(1.018152118), COEF_CONST(1.029201168), COEF_CONST(1.04788335), COEF_CONST(1.066902341), COEF_CONST(1.078480432) }, ++ { COEF_CONST(1.021821892), COEF_CONST(1.040370124), COEF_CONST(1.063070665), COEF_CONST(1.086268878), COEF_CONST(1.105986959) }, ++ { COEF_CONST(1.029201168), COEF_CONST(1.051658007), COEF_CONST(1.078480432), COEF_CONST(1.105986959), COEF_CONST(1.134195038) } ++}; ++ ++/* 2^(-pan_quant[x][y]/32) */ ++static const real_t pan_pow_2_32_neg[8][5] = { ++ { COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1), COEF_CONST(1) }, ++ { COEF_CONST(0.996408597), COEF_CONST(0.996408597), COEF_CONST(0.992830093), COEF_CONST(0.992830093), COEF_CONST(0.992830093) }, ++ { COEF_CONST(0.992830093), COEF_CONST(0.992830093), COEF_CONST(0.985711594), COEF_CONST(0.982171507), COEF_CONST(0.982171507) }, ++ { COEF_CONST(0.989264441), COEF_CONST(0.985711594), COEF_CONST(0.978644134), COEF_CONST(0.968137841), COEF_CONST(0.964660869) }, ++ { COEF_CONST(0.985711594), COEF_CONST(0.978644134), COEF_CONST(0.968137841), COEF_CONST(0.954304695), COEF_CONST(0.947464471) }, ++ { COEF_CONST(0.982171507), COEF_CONST(0.971627346), COEF_CONST(0.954304695), COEF_CONST(0.93729291), COEF_CONST(0.927230546) }, ++ { COEF_CONST(0.978644134), COEF_CONST(0.961196383), COEF_CONST(0.940671239), COEF_CONST(0.92058239), COEF_CONST(0.904169793) }, ++ { COEF_CONST(0.971627346), COEF_CONST(0.950879462), COEF_CONST(0.927230546), COEF_CONST(0.904169793), COEF_CONST(0.881682573) } ++}; ++ + static const real_t g_decayslope[MAX_SA_BAND] = { + FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(1), FRAC_CONST(0.95),FRAC_CONST(0.9), FRAC_CONST(0.85), FRAC_CONST(0.8), + FRAC_CONST(0.75),FRAC_CONST(0.7), FRAC_CONST(0.65),FRAC_CONST(0.6), FRAC_CONST(0.55),FRAC_CONST(0.5), FRAC_CONST(0.45), +@@ -635,7 +662,7 @@ static void drm_ps_delta_decode(drm_ps_info *ps) + } + } + +-static void drm_calc_sa_side_signal(drm_ps_info *ps, qmf_t X[38][64]) ++static void drm_calc_sa_side_signal(drm_ps_info *ps, qmf_t X[38][64], int num_subsamples) + { + uint8_t s, b, k; + complex_t qfrac, tmp0, tmp, in, R0; +@@ -659,7 +686,7 @@ static void drm_calc_sa_side_signal(drm_ps_info *ps, qmf_t X[38][64]) + RE(Phi_Fract) = RE(Phi_Fract_Qmf[b]); + IM(Phi_Fract) = IM(Phi_Fract_Qmf[b]); + +- for (s = 0; s < NUM_OF_SUBSAMPLES; s++) ++ for (s = 0; s < num_subsamples; s++) + { + const real_t gamma = REAL_CONST(1.5); + const real_t sigma = REAL_CONST(1.5625); +@@ -750,19 +777,21 @@ static void drm_calc_sa_side_signal(drm_ps_info *ps, qmf_t X[38][64]) + ps->delay_buf_index_ser[k] = temp_delay_ser[k]; + } + +-static void drm_add_ambiance(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64]) ++static void drm_add_ambiance(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64], int num_subsamples) + { +- uint8_t s, b, ifreq, qclass; ++ uint8_t s, b, ifreq, qclass, is_hdc; + real_t sa_map[MAX_SA_BAND], sa_dir_map[MAX_SA_BAND], k_sa_map[MAX_SA_BAND], k_sa_dir_map[MAX_SA_BAND]; + real_t new_dir_map, new_sa_map; + ++ is_hdc = IS_HDC(num_subsamples); ++ + if (ps->bs_enable_sa) + { + /* Instead of dequantization and mapping, we use an inverse mapping + to look up all the values we need */ + for (b = 0; b < sa_freq_scale[DRM_NUM_SA_BANDS]; b++) + { +- const real_t inv_f_num_of_subsamples = FRAC_CONST(0.03333333333); ++ const real_t inv_f_num_of_subsamples = is_hdc ? FRAC_CONST(0.03125) : FRAC_CONST(0.03333333333); + + ifreq = sa_inv_freq[b]; + qclass = (b != 0); +@@ -779,7 +808,7 @@ static void drm_add_ambiance(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_righ + + } + +- for (s = 0; s < NUM_OF_SUBSAMPLES; s++) ++ for (s = 0; s < num_subsamples; s++) + { + for (b = 0; b < sa_freq_scale[DRM_NUM_SA_BANDS]; b++) + { +@@ -799,7 +828,7 @@ static void drm_add_ambiance(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_righ + } + } + else { +- for (s = 0; s < NUM_OF_SUBSAMPLES; s++) ++ for (s = 0; s < num_subsamples; s++) + { + for (b = 0; b < NUM_OF_QMF_CHANNELS; b++) + { +@@ -810,14 +839,16 @@ static void drm_add_ambiance(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_righ + } + } + +-static void drm_add_pan(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64]) ++static void drm_add_pan(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38][64], int num_subsamples) + { +- uint8_t s, b, qclass, ifreq; ++ uint8_t s, b, qclass, ifreq, is_hdc; + real_t tmp, coeff1, coeff2; + real_t pan_base[MAX_PAN_BAND]; + real_t pan_delta[MAX_PAN_BAND]; + qmf_t temp_l, temp_r; + ++ is_hdc = IS_HDC(num_subsamples); ++ + if (ps->bs_enable_pan) + { + for (b = 0; b < NUM_OF_QMF_CHANNELS; b++) +@@ -834,31 +865,56 @@ static void drm_add_pan(drm_ps_info *ps, qmf_t X_left[38][64], qmf_t X_right[38] + pan_base[b] = pan_pow_2_neg[-ps->g_prev_pan_index[ifreq]][qclass]; + } + +- /* 2^((a-b)/30) = 2^(a/30) * 1/(2^(b/30)) */ ++ /* 2^((a-b)/32) = 2^(a/32) * 1/(2^(b/32)) if HDC */ ++ /* 2^((a-b)/30) = 2^(a/30) * 1/(2^(b/30)) if regular DRM */ + /* a en b can be negative so we may need to inverse parts */ + if (ps->g_pan_index[ifreq] >= 0) + { + if (ps->g_prev_pan_index[ifreq] >= 0) + { +- pan_delta[b] = MUL_C(pan_pow_2_30_pos[ps->g_pan_index[ifreq]][qclass], +- pan_pow_2_30_neg[ps->g_prev_pan_index[ifreq]][qclass]); ++ if (is_hdc) ++ { ++ pan_delta[b] = MUL_C(pan_pow_2_32_pos[ps->g_pan_index[ifreq]][qclass], ++ pan_pow_2_32_neg[ps->g_prev_pan_index[ifreq]][qclass]); ++ } else { ++ pan_delta[b] = MUL_C(pan_pow_2_30_pos[ps->g_pan_index[ifreq]][qclass], ++ pan_pow_2_30_neg[ps->g_prev_pan_index[ifreq]][qclass]); ++ } + } else { +- pan_delta[b] = MUL_C(pan_pow_2_30_pos[ps->g_pan_index[ifreq]][qclass], +- pan_pow_2_30_pos[-ps->g_prev_pan_index[ifreq]][qclass]); ++ if (is_hdc) ++ { ++ pan_delta[b] = MUL_C(pan_pow_2_32_pos[ps->g_pan_index[ifreq]][qclass], ++ pan_pow_2_32_pos[-ps->g_prev_pan_index[ifreq]][qclass]); ++ } else { ++ pan_delta[b] = MUL_C(pan_pow_2_30_pos[ps->g_pan_index[ifreq]][qclass], ++ pan_pow_2_30_pos[-ps->g_prev_pan_index[ifreq]][qclass]); ++ } + } + } else { + if (ps->g_prev_pan_index[ifreq] >= 0) + { +- pan_delta[b] = MUL_C(pan_pow_2_30_neg[-ps->g_pan_index[ifreq]][qclass], +- pan_pow_2_30_neg[ps->g_prev_pan_index[ifreq]][qclass]); ++ if (is_hdc) ++ { ++ pan_delta[b] = MUL_C(pan_pow_2_32_neg[-ps->g_pan_index[ifreq]][qclass], ++ pan_pow_2_32_neg[ps->g_prev_pan_index[ifreq]][qclass]); ++ } else { ++ pan_delta[b] = MUL_C(pan_pow_2_30_neg[-ps->g_pan_index[ifreq]][qclass], ++ pan_pow_2_30_neg[ps->g_prev_pan_index[ifreq]][qclass]); ++ } + } else { +- pan_delta[b] = MUL_C(pan_pow_2_30_neg[-ps->g_pan_index[ifreq]][qclass], +- pan_pow_2_30_pos[-ps->g_prev_pan_index[ifreq]][qclass]); ++ if (is_hdc) ++ { ++ pan_delta[b] = MUL_C(pan_pow_2_32_neg[-ps->g_pan_index[ifreq]][qclass], ++ pan_pow_2_32_pos[-ps->g_prev_pan_index[ifreq]][qclass]); ++ } else { ++ pan_delta[b] = MUL_C(pan_pow_2_30_neg[-ps->g_pan_index[ifreq]][qclass], ++ pan_pow_2_30_pos[-ps->g_prev_pan_index[ifreq]][qclass]); ++ } + } + } + } + +- for (s = 0; s < NUM_OF_SUBSAMPLES; s++) ++ for (s = 0; s < num_subsamples; s++) + { + /* PAN always uses all 64 channels */ + for (b = 0; b < NUM_OF_QMF_CHANNELS; b++) +@@ -901,17 +957,19 @@ void drm_ps_free(drm_ps_info *ps) + } + + /* main DRM PS decoding function */ +-uint8_t drm_ps_decode(drm_ps_info *ps, uint8_t guess, qmf_t X_left[38][64], qmf_t X_right[38][64]) ++uint8_t drm_ps_decode(drm_ps_info *ps, uint8_t guess, qmf_t X_left[38][64], qmf_t X_right[38][64], uint8_t hdc_sbr) + { ++ int num_subsamples = hdc_sbr ? NUM_OF_HDC_SUBSAMPLES : NUM_OF_SUBSAMPLES; ++ + if (ps == NULL) + { +- memcpy(X_right, X_left, sizeof(qmf_t)*30*64); ++ memcpy(X_right, X_left, sizeof(qmf_t)*num_subsamples*64); + return 0; + } + + if (!ps->drm_ps_data_available && !guess) + { +- memcpy(X_right, X_left, sizeof(qmf_t)*30*64); ++ memcpy(X_right, X_left, sizeof(qmf_t)*num_subsamples*64); + memset(ps->g_prev_sa_index, 0, sizeof(ps->g_prev_sa_index)); + memset(ps->g_prev_pan_index, 0, sizeof(ps->g_prev_pan_index)); + return 0; +@@ -933,8 +991,8 @@ uint8_t drm_ps_decode(drm_ps_info *ps, uint8_t guess, qmf_t X_left[38][64], qmf_ + + ps->drm_ps_data_available = 0; + +- drm_calc_sa_side_signal(ps, X_left); +- drm_add_ambiance(ps, X_left, X_right); ++ drm_calc_sa_side_signal(ps, X_left, num_subsamples); ++ drm_add_ambiance(ps, X_left, X_right, num_subsamples); + + if (ps->bs_enable_sa) + { +@@ -948,7 +1006,7 @@ uint8_t drm_ps_decode(drm_ps_info *ps, uint8_t guess, qmf_t X_left[38][64], qmf_ + + if (ps->bs_enable_pan) + { +- drm_add_pan(ps, X_left, X_right); ++ drm_add_pan(ps, X_left, X_right, num_subsamples); + + ps->g_last_had_pan = 1; + +diff --git a/libfaad/drm_dec.h b/libfaad/drm_dec.h +index 62276a9..56a4b3d 100644 +--- a/libfaad/drm_dec.h ++++ b/libfaad/drm_dec.h +@@ -43,6 +43,7 @@ extern "C" { + #define NUM_OF_LINKS 3 + #define NUM_OF_QMF_CHANNELS 64 + #define NUM_OF_SUBSAMPLES 30 ++#define NUM_OF_HDC_SUBSAMPLES 32 + #define MAX_SA_BAND 46 + #define MAX_PAN_BAND 64 + #define MAX_DELAY 5 +@@ -75,7 +76,7 @@ typedef struct + + uint8_t delay_buf_index_ser[NUM_OF_LINKS]; + +- qmf_t SA[NUM_OF_SUBSAMPLES][MAX_SA_BAND]; ++ qmf_t SA[NUM_OF_HDC_SUBSAMPLES][MAX_SA_BAND]; + + complex_t d_buff[2][MAX_SA_BAND]; + complex_t d2_buff[NUM_OF_LINKS][MAX_DELAY][MAX_SA_BAND]; +@@ -91,7 +92,7 @@ uint16_t drm_ps_data(drm_ps_info *ps, bitfile *ld); + drm_ps_info *drm_ps_init(void); + void drm_ps_free(drm_ps_info *ps); + +-uint8_t drm_ps_decode(drm_ps_info *ps, uint8_t guess, qmf_t X_left[38][64], qmf_t X_right[38][64]); ++uint8_t drm_ps_decode(drm_ps_info *ps, uint8_t guess, qmf_t X_left[38][64], qmf_t X_right[38][64], uint8_t hdc_sdr); + + #ifdef __cplusplus + } +diff --git a/libfaad/sbr_dec.c b/libfaad/sbr_dec.c +index d301e66..99463aa 100644 +--- a/libfaad/sbr_dec.c ++++ b/libfaad/sbr_dec.c +@@ -656,9 +656,17 @@ uint8_t sbrDecodeSingleFramePS(sbr_info *sbr, real_t *left_channel, real_t *righ + + /* perform parametric stereo */ + #ifdef DRM_PS ++#ifdef HDC ++ if (sbr->Is_DRM_SBR || sbr->Is_HDC_SBR) ++#else + if (sbr->Is_DRM_SBR) ++#endif + { +- drm_ps_decode(sbr->drm_ps, (sbr->ret > 0), X_left, X_right); ++#ifdef HDC ++ drm_ps_decode(sbr->drm_ps, (sbr->ret > 0), X_left, X_right, sbr->Is_HDC_SBR); ++#else ++ drm_ps_decode(sbr->drm_ps, (sbr->ret > 0), X_left, X_right, 0); ++#endif + } else { + #endif + #ifdef PS_DEC +diff --git a/libfaad/sbr_dec.h b/libfaad/sbr_dec.h +index 40e53ae..ce85feb 100644 +--- a/libfaad/sbr_dec.h ++++ b/libfaad/sbr_dec.h +@@ -176,6 +176,10 @@ typedef struct + ps_info *ps; + #endif + ++#ifdef HDC ++ uint8_t Is_HDC_SBR; ++#endif ++ + uint8_t numTimeSlotsRate; + uint8_t numTimeSlots; + uint8_t tHFGen; +diff --git a/libfaad/sbr_syntax.c b/libfaad/sbr_syntax.c +index e27980f..4c2b91c 100644 +--- a/libfaad/sbr_syntax.c ++++ b/libfaad/sbr_syntax.c +@@ -153,7 +153,11 @@ uint8_t sbr_extension_data(bitfile *ld, sbr_info *sbr, uint16_t cnt, + #endif + + #ifdef DRM ++#ifdef HDC ++ if (!sbr->Is_DRM_SBR && !sbr->Is_HDC_SBR) ++#else + if (!sbr->Is_DRM_SBR) ++#endif + #endif + { + uint8_t bs_extension_type = (uint8_t)faad_getbits(ld, 4 +@@ -246,7 +250,11 @@ uint8_t sbr_extension_data(bitfile *ld, sbr_info *sbr, uint16_t cnt, + } + + #ifdef DRM ++#ifdef HDC ++ if (!sbr->Is_DRM_SBR && !sbr->Is_HDC_SBR) ++#else + if (!sbr->Is_DRM_SBR) ++#endif + #endif + { + /* -4 does not apply, bs_extension_type is re-read in this function */ +@@ -391,6 +399,12 @@ static uint8_t sbr_single_channel_element(bitfile *ld, sbr_info *sbr) + faad_get1bit(ld); + } + #endif ++#ifdef HDC ++ if (sbr->Is_HDC_SBR) ++ { ++ faad_get1bit(ld); ++ } ++#endif + + if ((result = sbr_grid(ld, sbr, 0)) > 0) + return result; +@@ -888,7 +902,11 @@ static uint16_t sbr_extension(bitfile *ld, sbr_info *sbr, + #ifdef DRM_PS + case DRM_PARAMETRIC_STEREO: + /* If not expected then only decode but do not expose. */ ++#ifdef HDC ++ if (sbr->Is_DRM_SBR || sbr->Is_HDC_SBR) ++#else + if (sbr->Is_DRM_SBR) ++#endif + { + sbr->ps_used = 1; + } +@@ -899,9 +917,9 @@ static uint16_t sbr_extension(bitfile *ld, sbr_info *sbr, + return drm_ps_data(sbr->drm_ps, ld); + #endif + default: +- sbr->bs_extension_data = (uint8_t)faad_getbits(ld, 6 +- DEBUGVAR(1,279,"sbr_single_channel_element(): bs_extension_data")); +- return 6; ++ sbr->bs_extension_data = (uint8_t)faad_getbits(ld, num_bits_left ++ DEBUGVAR(1,279,"sbr_single_channel_element(): bs_extension_data")); ++ return num_bits_left; + } + } + +diff --git a/libfaad/syntax.c b/libfaad/syntax.c +index 56ae310..2c7ae05 100644 +--- a/libfaad/syntax.c ++++ b/libfaad/syntax.c +@@ -86,7 +86,7 @@ static void gain_control_data(bitfile *ld, ic_stream *ics); + static uint8_t spectral_data(NeAACDecStruct *hDecoder, ic_stream *ics, bitfile *ld, + int16_t *spectral_data); + static uint8_t pulse_data(ic_stream *ics, pulse_info *pul, bitfile *ld); +-static void tns_data(ic_stream *ics, tns_info *tns, bitfile *ld); ++static void tns_data(NeAACDecStruct *hDecoder, ic_stream *ics, tns_info *tns, bitfile *ld); + #ifdef LTP_DEC + static uint8_t ltp_data(NeAACDecStruct *hDecoder, ic_stream *ics, ltp_info *ltp, bitfile *ld); + #endif +@@ -446,6 +446,238 @@ static void decode_cpe(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, bitfi + hDecoder->fr_ch_ele++; + } + ++#ifdef HDC ++ ++static int hdc_data_frame(NeAACDecStruct *hDecoder, ic_stream *ics1, ic_stream *ics2, bitfile *ld, int layer_stereo) ++{ ++ uint8_t retval = 0; ++ uint8_t ics_reserved_bit; ++ ++ ics_reserved_bit = faad_get1bit(ld ++ DEBUGVAR(1,43,"hdc_data_frame(): ics_reserved_bit")); ++ if (ics_reserved_bit != 0) ++ return 32; ++ ics1->window_shape = faad_get1bit(ld ++ DEBUGVAR(1,45,"hdc_data_frame(): window_shape")); ++ ics1->window_sequence = (uint8_t)faad_getbits(ld, 2 ++ DEBUGVAR(1,44,"hdc_data_frame(): window_sequence")); ++ ++ if (ics1->window_sequence == EIGHT_SHORT_SEQUENCE) ++ { ++ ics1->max_sfb = (uint8_t)faad_getbits(ld, 4 ++ DEBUGVAR(1,46,"hdc_data_frame(): max_sfb (short)")); ++ ics1->scale_factor_grouping = (uint8_t)faad_getbits(ld, 7 ++ DEBUGVAR(1,47,"hdc_data_frame(): scale_factor_grouping")); ++ } else { ++ ics1->max_sfb = (uint8_t)faad_getbits(ld, 6 ++ DEBUGVAR(1,48,"hdc_data_frame(): max_sfb (long)")); ++ } ++ ++ /* get the grouping information */ ++ if ((retval = window_grouping_info(hDecoder, ics1)) > 0) ++ return retval; ++ ++ /* should be an error */ ++ /* check the range of max_sfb */ ++ if (ics1->max_sfb > ics1->num_swb) ++ return 16; ++ ++ if (layer_stereo) ++ { ++ ics1->ms_mask_present = (uint8_t)faad_getbits(ld, 2 ++ DEBUGVAR(1,306,"hdc_data_frame(): ms_mask_present")); ++ if (ics1->ms_mask_present == 3) ++ { ++ /* bitstream error */ ++ return 32; ++ } ++ if (ics1->ms_mask_present == 1) ++ { ++ uint8_t g, sfb; ++ for (g = 0; g < ics1->num_window_groups; g++) ++ { ++ for (sfb = 0; sfb < ics1->max_sfb; sfb++) ++ { ++ ics1->ms_used[g][sfb] = faad_get1bit(ld ++ DEBUGVAR(1,307,"hdc_data_frame(): faad_get1bit")); ++ } ++ } ++ } ++ memcpy(ics2, ics1, sizeof(ic_stream)); ++ } ++ else ++ { ++ ics1->ms_mask_present = 0; ++ } ++ ++ return 0; ++} ++ ++static uint8_t hdc_sbr_data_block(NeAACDecStruct *hDecoder, bitfile *ld) ++{ ++ uint16_t count; ++ uint32_t bitsconsumed = faad_get_processed_bits(ld); ++ uint32_t buffer_size = faad_origbitbuffer_size(ld); ++ ++ if (bitsconsumed > buffer_size*8) ++ { ++ return 14; ++ } ++ ++ if (!hDecoder->sbr[0]) ++ { ++ hDecoder->sbr[0] = sbrDecodeInit(hDecoder->frameLength, ++ hDecoder->element_id[0], 2*get_sample_rate(hDecoder->sf_index), ++ hDecoder->downSampledSBR ++#ifdef DRM ++ , 0 ++#endif ++ ++ ); ++ } ++ hDecoder->sbr[0]->Is_HDC_SBR = 1; ++ ++ count = (uint16_t)bit2byte(buffer_size*8 - bitsconsumed); ++ ++ hDecoder->sbr[0]->ret = sbr_extension_data(ld, hDecoder->sbr[0], count, hDecoder->postSeekResetFlag); ++#if (defined(PS_DEC) || defined(DRM_PS)) ++ if (hDecoder->sbr[0]->ps_used) ++ { ++ hDecoder->ps_used[0] = 1; ++ hDecoder->ps_used_global = 1; ++ } ++#endif ++ return 0; ++} ++ ++static int hdc_is_stereo_layer(int block_type) ++{ ++ switch (block_type) ++ { ++ case 0: ++ return 0; ++ case 1: ++ return 0; ++ case 2: ++ return 1; ++ case 5: ++ case 6: ++ return 0; ++ case 7: ++ return 1; ++ default: ++ { ++ fprintf(stderr, "Unknown HDC block type: %d\n", block_type); ++ return 0; ++ } ++ } ++} ++ ++void hdc_data_block(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, ++ bitfile *ld, program_config *pce, drc_info *drc) ++{ ++ uint8_t channels = hDecoder->fr_channels = 0; ++ uint8_t block_type; ++ uint8_t layer_stereo = 0; ++ element cpe = {0}; ++ ic_stream *ics1 = &(cpe.ics1); ++ ic_stream *ics2 = &(cpe.ics2); ++ ALIGN int16_t spec_data1[1024] = {0}; ++ ALIGN int16_t spec_data2[1024] = {0}; ++ ++ hDecoder->fr_ch_ele = 0; ++ hDecoder->first_syn_ele = 25; ++ hDecoder->has_lfe = 0; ++ ++ block_type = (uint8_t)faad_getbits(ld, LEN_SE_ID); ++ layer_stereo = hdc_is_stereo_layer(block_type); ++ ++ if (layer_stereo) ++ { ++ hDecoder->element_id[hDecoder->fr_ch_ele] = ID_CPE; ++ if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 0) ++ hDecoder->element_output_channels[hDecoder->fr_ch_ele] = 2; ++ } ++ else ++ { ++ hDecoder->element_id[hDecoder->fr_ch_ele] = ID_SCE; ++ } ++ ++ hInfo->error = hdc_data_frame(hDecoder, ics1, ics2, ld, layer_stereo); ++ if (hInfo->error > 0) ++ return; ++ ++ cpe.common_window = 1; ++ ++ if (layer_stereo) ++ { ++ cpe.channel = channels; ++ cpe.paired_channel = channels+1; ++ } ++ ++ ics1->tns_data_present = faad_get1bit(ld); ++ if (ics1->tns_data_present) ++ tns_data(hDecoder, ics1, &(ics1->tns), ld); ++ if (layer_stereo) ++ { ++ /* get tns data */ ++ if ((ics2->tns_data_present = faad_get1bit(ld)) & 1) ++ tns_data(hDecoder, ics2, &(ics2->tns), ld); ++ } ++ ++ hInfo->error = side_info(hDecoder, &cpe, ld, ics1, 1); ++ if (hInfo->error > 0) ++ return; ++ ++ /* decode the spectral data */ ++ if ((hInfo->error = spectral_data(hDecoder, ics1, ld, spec_data1)) > 0) ++ return; ++ if (layer_stereo) ++ { ++ hInfo->error = side_info(hDecoder, &cpe, ld, ics2, 1); ++ if (hInfo->error > 0) ++ return; ++ ++ if ((hInfo->error = spectral_data(hDecoder, ics2, ld, spec_data2)) > 0) ++ return; ++ } ++ ++ /* check if next bitstream element is a fill element */ ++ /* if so, read it now so SBR decoding can be done in case of a file with SBR */ ++ if (faad_showbits(ld, LEN_SE_ID) == ID_FIL) ++ { ++ faad_flushbits(ld, LEN_SE_ID); ++ ++ if (faad_getbits(ld, 1) && (hInfo->error = hdc_sbr_data_block(hDecoder, ld)) > 0) ++ return; ++ } ++ ++ if (layer_stereo) ++ { ++ hInfo->error = reconstruct_channel_pair(hDecoder, ics1, ics2, &cpe, spec_data1, spec_data2); ++ if (hInfo->error > 0) ++ return; ++ } else { ++ hInfo->error = reconstruct_single_channel(hDecoder, ics1, &cpe, spec_data1); ++ if (hInfo->error > 0) ++ return; ++ } ++ ++ /* map output channels position to internal data channels */ ++ if (hDecoder->element_output_channels[hDecoder->fr_ch_ele] == 2) ++ { ++ /* this might be faulty when pce_set is true */ ++ hDecoder->internal_channel[channels] = channels; ++ hDecoder->internal_channel[channels+1] = channels+1; ++ } else { ++ hDecoder->internal_channel[channels] = channels; ++ } ++ ++ hDecoder->fr_channels += hDecoder->element_output_channels[hDecoder->fr_ch_ele]; ++ hDecoder->fr_ch_ele++; ++} ++#endif ++ + void raw_data_block(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, + bitfile *ld, program_config *pce, drc_info *drc) + { +@@ -1172,6 +1404,14 @@ static uint8_t fill_element(NeAACDecStruct *hDecoder, bitfile *ld, drc_info *drc + hDecoder->ps_used_global = 1; + } + #endif ++ } else if (bs_extension_type == 0) { ++ // padding ++ int i; ++ for (i = 0; i < count; i++) ++ { ++ if ((uint8_t)faad_getbits(ld, 8) != 0) ++ fprintf(stderr, "sbr_extension_data: zero fil data not zero\n"); ++ } + } else { + #endif + #ifndef DRM +@@ -1362,12 +1602,12 @@ void DRM_aac_scalable_main_element(NeAACDecStruct *hDecoder, NeAACDecFrameInfo * + } + /* Stereo4 / Mono2 */ + if (ics1->tns_data_present) +- tns_data(ics1, &(ics1->tns), ld); ++ tns_data(hDecoder, ics1, &(ics1->tns), ld); + if (this_layer_stereo) + { + /* Stereo5 */ + if (ics2->tns_data_present) +- tns_data(ics2, &(ics2->tns), ld); ++ tns_data(hDecoder, ics2, &(ics2->tns), ld); + } + + #ifdef DRM +@@ -1617,7 +1857,7 @@ static uint8_t side_info(NeAACDecStruct *hDecoder, element *ele, + #ifdef ERROR_RESILIENCE + if (hDecoder->object_type < ER_OBJECT_START) + #endif +- tns_data(ics, &(ics->tns), ld); ++ tns_data(hDecoder, ics, &(ics->tns), ld); + } + + /* get gain control data */ +@@ -1681,7 +1921,7 @@ static uint8_t individual_channel_stream(NeAACDecStruct *hDecoder, element *ele, + if (hDecoder->object_type >= ER_OBJECT_START) + { + if (ics->tns_data_present) +- tns_data(ics, &(ics->tns), ld); ++ tns_data(hDecoder, ics, &(ics->tns), ld); + } + + #ifdef DRM +@@ -2016,7 +2256,7 @@ static uint8_t scale_factor_data(NeAACDecStruct *hDecoder, ic_stream *ics, bitfi + } + + /* Table 4.4.27 */ +-static void tns_data(ic_stream *ics, tns_info *tns, bitfile *ld) ++static void tns_data(NeAACDecStruct *hDecoder, ic_stream *ics, tns_info *tns, bitfile *ld) + { + uint8_t w, filt, i, coef_bits; + uint8_t n_filt_bits = 2; +@@ -2033,6 +2273,11 @@ static void tns_data(ic_stream *ics, tns_info *tns, bitfile *ld) + for (w = 0; w < ics->num_windows; w++) + { + uint8_t start_coef_bits = 3; ++#ifdef HDC ++ if (hDecoder->object_type == HDC_LC && ics->window_sequence != EIGHT_SHORT_SEQUENCE) ++ tns->n_filt[w] = 1; ++ else ++#endif + tns->n_filt[w] = (uint8_t)faad_getbits(ld, n_filt_bits + DEBUGVAR(1,74,"tns_data(): n_filt")); + #if 0 +diff --git a/libfaad/syntax.h b/libfaad/syntax.h +index b6311ed..ca19292 100644 +--- a/libfaad/syntax.h ++++ b/libfaad/syntax.h +@@ -46,6 +46,7 @@ extern "C" { + #define ER_LC 17 + #define ER_LTP 19 + #define DRM_ER_LC 27 /* special object type for DRM */ ++#define HDC_LC 127 /* special object type for HDC */ + + /* header types */ + #define RAW 0 +@@ -126,6 +127,11 @@ void DRM_aac_scalable_main_element(NeAACDecStruct *hDecoder, NeAACDecFrameInfo * + uint32_t faad_latm_frame(latm_header *latm, bitfile *ld); + #endif + ++#ifdef HDC ++void hdc_data_block(NeAACDecStruct *hDecoder, NeAACDecFrameInfo *hInfo, ++ bitfile *ld, program_config *pce, drc_info *drc); ++#endif ++ + #ifdef __cplusplus + } + #endif +-- +2.43.0 + diff --git a/tools/hd_fm_recording_probe.cpp b/tools/hd_fm_recording_probe.cpp new file mode 100644 index 000000000..50a09aa3e --- /dev/null +++ b/tools/hd_fm_recording_probe.cpp @@ -0,0 +1,327 @@ +// Explicit offline diagnostic, never a default CTest or a radio/USB client. +// Usage: hd_fm_recording_probe RAW_CU8 [--program 0..7] +// [--compressed-source SOURCE.xz] +// The input format is the pinned nrsc5 sample format: interleaved unsigned +// I/Q bytes at 1488375 complex samples/s. Decompress before invoking this tool. +#include "core/backends/rtl/HdFmIqAdapter.h" +#include "core/backends/rtl/Nrsc5FmDecoder.h" +#include "core/HdFmReception.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +// The pinned C API header does not declare C++ linkage guards. +extern "C" { +#include +} +#include +#include +#include +#include +#include +#include +#include + +#ifndef AETHER_ENABLE_NRSC5 +#error "The recording probe requires the opt-in native HD decoder target." +#endif + +namespace { +using Adapter = AetherSDR::rtl::HdFmIqAdapter; +using Decoder = AetherSDR::rtl::Nrsc5FmDecoder; +using Reception = AetherSDR::rtl::HdFmRawReception; +using AudioIdentity = AetherSDR::rtl::HdFmAudioIdentity; +namespace Policy = AetherSDR::SharedCapturePolicy; +constexpr qint64 kMaximumInputBytes = 256 * 1024 * 1024; +constexpr qint64 kMaximumWallMs = 120000; +constexpr std::uint64_t kSession = 1, kRevision = 1; +static_assert(NRSC5_SAMPLE_RATE_CU8 == 1488375); +static_assert(NRSC5_SAMPLE_RATE_AUDIO == 44100); +static_assert(Adapter::kOutputRate == NRSC5_SAMPLE_RATE_NATIVE_FM); +static_assert(sizeof(float) == 4 && std::numeric_limits::is_iec559); + +QJsonValue number(std::uint64_t value) { return QJsonValue(static_cast(value)); } +QJsonValue optionalNumber(std::optional value) +{ return value && std::isfinite(*value) ? QJsonValue(*value) : QJsonValue(QJsonValue::Null); } +std::uint64_t monotonicMs() +{ + return std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); +} +template QString text(const std::array& bytes, int limit) +{ + const auto end = std::find(bytes.begin(), bytes.end(), '\0'); + return AetherSDR::boundedBroadcastText(QString::fromUtf8(bytes.data(), end - bytes.begin()), limit); +} +QJsonObject receptionJson(const Reception& value) +{ + QJsonArray services; + for (const auto& service : value.services) { + if (service.program >= 0) { + services.append(QJsonObject{{"program", service.program}, {"name", text(service.name, 64)}, + {"audioAvailable", service.audioAvailable}}); + } + } + return {{"valid", value.valid}, {"synced", value.synced}, {"audioValid", value.audioValid}, + {"selectedProgram", value.selectedProgram}, {"services", services}, + {"stationName", text(value.stationName, 64)}, {"title", text(value.title, 256)}, + {"artist", text(value.artist, 128)}, {"merLowerDb", optionalNumber(value.merLowerDb)}, + {"merUpperDb", optionalNumber(value.merUpperDb)}, {"cber", optionalNumber(value.cber)}, + {"frequencyOffsetHz", optionalNumber(value.frequencyOffsetHz)}, + {"syncLossCount", number(value.syncLossCount)}, {"reacquisitionCount", number(value.reacquisitionCount)}, + {"syncDurationMs", number(value.syncDurationMs)}, {"observationSequence", number(value.observationSequence)}, + {"audioSequence", number(value.audioSequence)}, {"audioEpoch", number(value.audioEpoch)}, + {"observationMonotonicMs", number(value.observationMonotonicMs)}, + {"audioMonotonicMs", number(value.audioMonotonicMs)}}; +} +void printJson(const QJsonObject& value) +{ + const QByteArray bytes = QJsonDocument(value).toJson(QJsonDocument::Indented); + std::fwrite(bytes.constData(), 1, static_cast(bytes.size()), stdout); +} +int fail(const QString& message) +{ + printJson({{"schema", 1}, {"completed", false}, {"passed", false}, {"error", message}}); + return 2; +} +std::optional fileIdentity(const QString& path, qint64 maximum) +{ + const QFileInfo info(path); + QFile file(path); + if (!info.isFile() || info.size() <= 0 || info.size() > maximum || !file.open(QIODevice::ReadOnly) + || file.isSequential()) { return {}; } + QCryptographicHash hash(QCryptographicHash::Sha256); + std::array bytes{}; + qint64 total = 0; + while (total < info.size()) { + const qint64 count = file.read(bytes.data(), std::min(bytes.size(), info.size() - total)); + if (count <= 0) { return {}; } + hash.addData(QByteArrayView(bytes.data(), count)); total += count; + } + if (!file.atEnd() || file.size() != info.size()) { return {}; } + return QJsonObject{{"path", info.canonicalFilePath()}, {"bytes", total}, + {"sha256", QString::fromLatin1(hash.result().toHex())}}; +} + +struct AudioStats final : Decoder::Sink { + std::uint64_t callbacks = 0, frames = 0, differingFrames = 0, nonzeroFrames = 0, faults = 0; + std::uint64_t retirements = 0, epoch = 0, next = 0, lastAudioEpoch = 0; + std::uint64_t inputFeedEnd = 0, firstAudioInput = 0, lastAudioInput = 0; + std::uint64_t maxInputGap = 0, maxSameEpochInputGap = 0, lastAudioWall = 0, maxWallGapMs = 0; + std::uint64_t largestCallbackFrames = 0; + double leftEnergy = 0, rightEnergy = 0, differenceEnergy = 0, leftPeak = 0, rightPeak = 0; + QCryptographicHash leftHash{QCryptographicHash::Sha256}, rightHash{QCryptographicHash::Sha256}; + bool decoded(const AudioIdentity& identity, std::uint64_t first, + std::span left, std::span right) noexcept override + { + const auto now = monotonicMs(); + if (identity.sessionId != kSession || identity.revision != kRevision || !identity.audioEpoch + || !identity.producedMonotonicMs || identity.producedMonotonicMs > now + || left.empty() || left.size() != right.size() || left.size() > 2048 + || identity.audioEpoch < epoch || (identity.audioEpoch == epoch ? first != next : first != 0)) { + ++faults; return false; + } + std::array leftBytes{}, rightBytes{}; + for (std::size_t i = 0; i < left.size(); ++i) { + if (!std::isfinite(left[i]) || !std::isfinite(right[i]) + || std::abs(left[i]) > 1.0f || std::abs(right[i]) > 1.0f) { ++faults; return false; } + const double difference = double(left[i]) - right[i]; + leftEnergy += double(left[i]) * left[i]; rightEnergy += double(right[i]) * right[i]; + differenceEnergy += difference * difference; + leftPeak = std::max(leftPeak, std::abs(double(left[i]))); + rightPeak = std::max(rightPeak, std::abs(double(right[i]))); + differingFrames += left[i] != right[i]; nonzeroFrames += left[i] != 0 || right[i] != 0; + const auto l = std::bit_cast(left[i]), r = std::bit_cast(right[i]); + for (unsigned byte = 0; byte < 4; ++byte) { + leftBytes[4 * i + byte] = static_cast((l >> (8 * byte)) & 255); + rightBytes[4 * i + byte] = static_cast((r >> (8 * byte)) & 255); + } + } + if (!callbacks) { firstAudioInput = inputFeedEnd; } + else { + maxInputGap = std::max(maxInputGap, inputFeedEnd - lastAudioInput); + if (identity.audioEpoch == lastAudioEpoch) { + maxSameEpochInputGap = std::max(maxSameEpochInputGap, inputFeedEnd - lastAudioInput); + } + maxWallGapMs = std::max(maxWallGapMs, now - lastAudioWall); + } + lastAudioInput = inputFeedEnd; lastAudioWall = now; lastAudioEpoch = identity.audioEpoch; + epoch = identity.audioEpoch; next = first + left.size(); + ++callbacks; frames += left.size(); largestCallbackFrames = std::max(largestCallbackFrames, left.size()); + leftHash.addData(QByteArrayView(leftBytes.data(), left.size_bytes())); + rightHash.addData(QByteArrayView(rightBytes.data(), right.size_bytes())); + return true; + } + void audioRetired(std::uint64_t value) noexcept override + { + if (!value || value <= epoch) { ++faults; } + ++retirements; epoch = value; next = 0; + } +}; + +struct Feed final : Adapter::Sink { + AudioStats& audio; + Decoder& decoder; + std::uint64_t iqFrames = 0, calls = 0, maxFeedFrames = 0, maxCallbacks = 0, maxPcmFrames = 0; + unsigned discoveredPrograms = 0; + bool observedSync = false; + Reception lastSynced, lastAudio; + Feed(AudioStats& output, Decoder& native) : audio(output), decoder(native) {} + bool hdIq(std::span input) noexcept override + { + audio.inputFeedEnd = iqFrames + input.size() / 2; + const auto beforeCallbacks = audio.callbacks, beforeFrames = audio.frames; + const bool accepted = decoder.feed(input, kSession, kRevision); + iqFrames += input.size() / 2; ++calls; + maxFeedFrames = std::max(maxFeedFrames, input.size() / 2); + maxCallbacks = std::max(maxCallbacks, audio.callbacks - beforeCallbacks); + maxPcmFrames = std::max(maxPcmFrames, audio.frames - beforeFrames); + const auto& value = decoder.reception(); + if (value.synced) { observedSync = true; lastSynced = value; } + if (audio.callbacks != beforeCallbacks) { lastAudio = value; } + for (const auto& service : value.services) { + if (service.program >= 0 && service.program < 8) { discoveredPrograms |= 1u << service.program; } + } + return accepted; + } +}; +} // namespace + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + const QStringList arguments = app.arguments(); + if (arguments.size() < 2 || arguments.size() > 6) { + return fail(QStringLiteral("Usage: hd_fm_recording_probe RAW_CU8 [--program 0..7] [--compressed-source SOURCE.xz]")); + } + const QString inputPath = arguments[1]; + QString compressedPath; + int program = 0; + bool programSeen = false; + for (qsizetype i = 2; i < arguments.size(); i += 2) { + if (i + 1 == arguments.size()) { return fail(QStringLiteral("Missing option value")); } + if (arguments[i] == QLatin1String("--program") && !programSeen) { + bool ok = false; program = arguments[i + 1].toInt(&ok); programSeen = true; + if (!ok || program < 0 || program > 7) { return fail(QStringLiteral("Program must be 0..7")); } + } else if (arguments[i] == QLatin1String("--compressed-source") && compressedPath.isEmpty()) { + compressedPath = arguments[i + 1]; + } else { return fail(QStringLiteral("Unknown or repeated option")); } + } + const QFileInfo inputInfo(inputPath); + QFile input(inputPath); + if (!inputInfo.isFile() || inputInfo.size() <= 0 || inputInfo.size() > kMaximumInputBytes + || inputInfo.size() % 2 || !input.open(QIODevice::ReadOnly) || input.isSequential()) { + return fail(QStringLiteral("Input must be a nonempty regular cu8 file, even byte count, at most 256 MiB")); + } + if (input.peek(6) == QByteArray::fromHex("fd377a585a00")) { + return fail(QStringLiteral("Input is XZ compressed; supply the decompressed cu8 recording")); + } + QJsonValue compressed(QJsonValue::Null); + if (!compressedPath.isEmpty()) { + const auto identity = fileIdentity(compressedPath, kMaximumInputBytes); + if (!identity) { return fail(QStringLiteral("Cannot hash bounded compressed-source file")); } + compressed = *identity; + } + const auto executable = fileIdentity(QCoreApplication::applicationFilePath(), 1024LL * 1024 * 1024); + if (!executable) { return fail(QStringLiteral("Cannot hash the running probe executable")); } + const char* libraryVersion = nullptr; + nrsc5_get_version(&libraryVersion); + const auto utcStart = QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs); + QElapsedTimer timer; timer.start(); + // Zero frequency offset, with a synthetic positive reference solely to + // satisfy shared RF geometry. This does not assert the recording's RF. + constexpr double geometryReferenceHz = 1000000; + const Policy::CaptureDescriptor capture{1, 1, geometryReferenceHz, NRSC5_SAMPLE_RATE_CU8, + NRSC5_SAMPLE_RATE_CU8 * 0.45, NRSC5_SAMPLE_RATE_CU8 * 0.45}; + const Policy::SliceDescriptor footprint{0, geometryReferenceHz, -225000, 225000, 0, 3000, 3000}; + Adapter adapter(capture, footprint); + AudioStats audio; + Reception initial; initial.receiverEpoch = 1; initial.selectedProgram = program; + Decoder decoder(initial, audio); // Production NativePipe, never an injected Pipe. + if (!adapter.valid() || !decoder.valid()) { return fail(QStringLiteral("Native adapter or decoder initialization failed")); } + Feed feed(audio, decoder); + QCryptographicHash rawHash(QCryptographicHash::Sha256); + std::array bytes{}; + std::array, Adapter::kMaxInput> samples{}; + std::uint64_t inputBytes = 0, inputFrames = 0, maxBlockCallbacks = 0, maxBlockPcmFrames = 0; + QString error; + while (inputBytes < static_cast(inputInfo.size())) { + if (timer.elapsed() > kMaximumWallMs) { error = QStringLiteral("Probe wall-time budget exceeded"); break; } + const qint64 count = input.read(bytes.data(), std::min(bytes.size(), inputInfo.size() - inputBytes)); + if (count <= 0 || count % 2) { error = QStringLiteral("Read error or truncated IQ pair"); break; } + rawHash.addData(QByteArrayView(bytes.data(), count)); + const std::size_t frames = static_cast(count / 2); + for (std::size_t i = 0; i < frames; ++i) { + // Match pinned upstream U8_F exactly, including its center of 127. + samples[i] = {(static_cast(bytes[2 * i]) - 127.0f) / 128.0f, + (static_cast(bytes[2 * i + 1]) - 127.0f) / 128.0f}; + } + const auto beforeCallbacks = audio.callbacks, beforeFrames = audio.frames; + const bool accepted = adapter.process(inputFrames, std::span(samples).first(frames), feed); + inputBytes += count; inputFrames += frames; + maxBlockCallbacks = std::max(maxBlockCallbacks, audio.callbacks - beforeCallbacks); + maxBlockPcmFrames = std::max(maxBlockPcmFrames, audio.frames - beforeFrames); + if (!accepted) { error = QStringLiteral("Production adapter or decoder refused input"); break; } + } + const bool completed = error.isEmpty() && inputBytes == static_cast(inputInfo.size()) + && input.atEnd() && input.size() == inputInfo.size(); + const bool passed = completed && adapter.valid() && decoder.valid() && feed.observedSync + && audio.frames && audio.nonzeroFrames && !audio.faults; + if (!completed && error.isEmpty()) { error = QStringLiteral("Recording changed while reading"); } + QJsonArray programs; + for (int i = 0; i < 8; ++i) { if (feed.discoveredPrograms & (1u << i)) { programs.append(i); } } + const double denominator = audio.frames ? double(audio.frames) : 1.0; + QJsonObject result{{"schema", 1}, {"completed", completed}, {"passed", passed}, {"error", error}, + {"utcStart", utcStart}, {"utcEnd", QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs)}, + {"wallSeconds", timer.nsecsElapsed() / 1e9}, {"maximumWallSeconds", kMaximumWallMs / 1000}, + {"nativeNrsc5Version", QString::fromUtf8(libraryVersion ? libraryVersion : "unknown")}, + {"executable", *executable}, {"compressedSource", compressed}, + {"compressedSourceRelation", "caller-supplied archive identity; decompression is external"}, + {"rawInput", QJsonObject{{"path", inputInfo.canonicalFilePath()}, {"declaredBytes", inputInfo.size()}, + {"readBytes", number(inputBytes)}, {"readSha256", QString::fromLatin1(rawHash.result().toHex())}, + {"format", "cu8"}, {"complexRateHz", NRSC5_SAMPLE_RATE_CU8}, {"readComplexFrames", number(inputFrames)}, + {"durationSeconds", double(inputFrames) / NRSC5_SAMPLE_RATE_CU8}, {"rfFrequencyHz", QJsonValue(QJsonValue::Null)}, + {"geometryReferenceHz", geometryReferenceHz}, {"translationHz", 0}, {"unsignedConversion", "(byte - 127) / 128"}}}, + {"decoderInput", QJsonObject{{"complexRateHz", Adapter::kOutputRate}, {"submittedComplexFrames", number(feed.iqFrames)}, + {"adapterOutputFrames", number(adapter.outputFrames())}, {"nativeFeedCount", number(feed.calls)}, + {"maximumNativeFeedComplexFrames", number(feed.maxFeedFrames)}, {"maximumInputBlockComplexFrames", qint64(Adapter::kMaxInput)}, + {"eofPolicy", "no invented RF padding or decoder flush; converter staging may retain a bounded tail"}}}, + {"observedSync", feed.observedSync}, {"discoveredPrograms", programs}, + {"lastReception", receptionJson(decoder.reception())}, + {"lastSyncedReception", feed.observedSync ? QJsonValue(receptionJson(feed.lastSynced)) : QJsonValue(QJsonValue::Null)}, + {"lastAudioReception", audio.callbacks ? QJsonValue(receptionJson(feed.lastAudio)) : QJsonValue(QJsonValue::Null)}, + {"audio", QJsonObject{{"rateHz", NRSC5_SAMPLE_RATE_AUDIO}, {"channelCount", 2}, {"program", program}, + {"frames", number(audio.frames)}, {"seconds", double(audio.frames) / NRSC5_SAMPLE_RATE_AUDIO}, + {"callbacks", number(audio.callbacks)}, {"maximumCallbackFrames", number(audio.largestCallbackFrames)}, + {"nonzeroFrames", number(audio.nonzeroFrames)}, {"differingLeftRightFrames", number(audio.differingFrames)}, + {"leftRms", std::sqrt(audio.leftEnergy / denominator)}, {"rightRms", std::sqrt(audio.rightEnergy / denominator)}, + {"leftRightDifferenceRms", std::sqrt(audio.differenceEnergy / denominator)}, + {"leftPeak", audio.leftPeak}, {"rightPeak", audio.rightPeak}, + {"leftSha256", QString::fromLatin1(audio.leftHash.result().toHex())}, + {"rightSha256", QString::fromLatin1(audio.rightHash.result().toHex())}, {"hashFormat", "float32 little endian per channel"}, + {"sourceSession", number(kSession)}, {"sourceRevision", number(kRevision)}, {"lastAudioEpoch", number(audio.lastAudioEpoch)}, + {"retirements", number(audio.retirements)}, {"identityOrSampleFaults", number(audio.faults)}}}, + {"burstAndGap", QJsonObject{{"maximumPcmCallbacksPerInputBlock", number(maxBlockCallbacks)}, + {"maximumPcmFramesPerInputBlock", number(maxBlockPcmFrames)}, {"maximumPcmCallbacksPerNativeFeed", number(feed.maxCallbacks)}, + {"maximumPcmFramesPerNativeFeed", number(feed.maxPcmFrames)}, {"maximumDecoderIqFramesBetweenAudioCallbacks", number(audio.maxInputGap)}, + {"maximumSameEpochDecoderIqFramesBetweenAudioCallbacks", number(audio.maxSameEpochInputGap)}, + {"maximumSourceSecondsBetweenAudioCallbacks", double(audio.maxInputGap) / Adapter::kOutputRate}, + {"maximumSameEpochSourceSecondsBetweenAudioCallbacks", double(audio.maxSameEpochInputGap) / Adapter::kOutputRate}, + {"firstAudioAtSourceSeconds", audio.callbacks ? QJsonValue(double(audio.firstAudioInput) / Adapter::kOutputRate) : QJsonValue(QJsonValue::Null)}, + {"trailingSourceSecondsWithoutAudio", audio.callbacks ? QJsonValue(double(feed.iqFrames - audio.lastAudioInput) / Adapter::kOutputRate) : QJsonValue(QJsonValue::Null)}, + {"maximumOfflineWallMsBetweenAudioCallbacks", number(audio.maxWallGapMs)}, + {"sourceTimeBasis", "actual converted IQ count at native-feed end; resolution is maximumNativeFeedComplexFrames"}}}, + {"scope", "offline native decoder/adapter; no worker queue, speaker playout, listening, GUI, USB or live RF proof"}}; +#ifdef AETHER_GIT_SHA + result.insert("aetherBuildRevision", QStringLiteral(AETHER_GIT_SHA)); +#else + result.insert("aetherBuildRevision", QJsonValue(QJsonValue::Null)); +#endif + printJson(result); + return passed ? 0 : 1; +} diff --git a/tools/hd_fm_worker_recording_probe.cpp b/tools/hd_fm_worker_recording_probe.cpp new file mode 100644 index 000000000..7a8bb4fc3 --- /dev/null +++ b/tools/hd_fm_worker_recording_probe.cpp @@ -0,0 +1,410 @@ +// Explicit offline, source-clock-paced diagnostic. No USB, socket, audio device, +// PCM export, settings or injected decoder. Not a default build or CTest. +// Usage: hd_fm_worker_recording_probe RAW_CU8 [--program 0..7] +// [--compressed-source SOURCE.xz] +#include "core/backends/rtl/RtlReceivePipeline.h" +#include "core/HdFmReception.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef Q_OS_WIN +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif +// The pinned C API header does not declare C++ linkage guards. +extern "C" { +#include +} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef AETHER_ENABLE_NRSC5 +#error "This diagnostic requires the opt-in native HD decoder." +#endif +namespace { +using Pipeline = AetherSDR::rtl::RtlReceivePipeline; +using Transaction = Pipeline::Transaction; +using Clock = std::chrono::steady_clock; +using namespace std::chrono_literals; +constexpr qint64 kMaximumBytes = 256 * 1024 * 1024; +constexpr auto kMaximumWall = 120s; +constexpr std::size_t kInputFrames = 8192; +constexpr double kRate = NRSC5_SAMPLE_RATE_CU8; +constexpr Transaction::Token kToken{1, 1}; +static_assert(NRSC5_SAMPLE_RATE_CU8 == 1488375 && NRSC5_SAMPLE_RATE_AUDIO == 44100); +std::uint64_t nowMs() +{ return std::chrono::duration_cast(Clock::now().time_since_epoch()).count(); } +QJsonValue number(std::uint64_t value) { return QJsonValue(static_cast(value)); } +double seconds(Clock::duration value) { return std::chrono::duration(value).count(); } +std::optional processCpuSeconds() +{ +#ifdef Q_OS_WIN + // The Microsoft CRT's clock() measures elapsed wall time, not CPU time. + FILETIME created{}, exited{}, kernel{}, user{}; + if (!GetProcessTimes(GetCurrentProcess(), &created, &exited, &kernel, &user)) { return {}; } + const auto ticks = [](const FILETIME& value) { + return (static_cast(value.dwHighDateTime) << 32) | value.dwLowDateTime; + }; + return (static_cast(ticks(kernel)) + static_cast(ticks(user))) / 10000000.0; +#else + const std::clock_t value = std::clock(); + if (value == std::clock_t(-1)) { return {}; } + return double(value) / CLOCKS_PER_SEC; +#endif +} +void output(const QJsonObject& result) +{ + const auto bytes = QJsonDocument(result).toJson(QJsonDocument::Indented); + std::fwrite(bytes.constData(), 1, bytes.size(), stdout); std::fflush(stdout); +} +int fail(const QString& message) +{ output({{"schema", 1}, {"completed", false}, {"passed", false}, {"error", message}}); return 2; } +// Bounds even an upstream feed/planner/join that fails to return. This owns no +// hardware or user state; exit 124 is a failed, incomplete diagnostic. +class Deadline final { +public: + Deadline() : m_thread([this] { + std::unique_lock lock(m_mutex); + if (!m_condition.wait_for(lock, kMaximumWall, [this] { return m_done; })) { + std::fputs("HD worker recording probe exceeded its 120 s hard process budget\n", stderr); + std::fflush(stderr); std::_Exit(124); + } + }) {} + ~Deadline() + { + { const std::lock_guard lock(m_mutex); m_done = true; } + m_condition.notify_all(); m_thread.join(); + } +private: + std::mutex m_mutex; + std::condition_variable m_condition; + bool m_done = false; + std::thread m_thread; +}; +std::optional fileIdentity(const QString& path, qint64 maximum) +{ + QFileInfo info(path); QFile file(path); + if (!info.isFile() || info.size() <= 0 || info.size() > maximum || !file.open(QIODevice::ReadOnly) + || file.isSequential()) { return {}; } + QCryptographicHash hash(QCryptographicHash::Sha256); + std::array bytes{}; qint64 total = 0; + while (total < info.size()) { + const auto count = file.read(bytes.data(), std::min(bytes.size(), info.size() - total)); + if (count <= 0) { return {}; } + hash.addData(QByteArrayView(bytes.data(), count)); total += count; + } + if (!file.atEnd() || file.size() != info.size()) { return {}; } + return QJsonObject{{"path", info.canonicalFilePath()}, {"bytes", total}, + {"sha256", QString::fromLatin1(hash.result().toHex())}}; +} +QJsonObject diagnostics(const Pipeline::Diagnostics& value) +{ + return {{"observed", value.observed}, {"droppedPackets", number(value.droppedPackets)}, + {"mixerLateFrames", number(value.mixerLateFrames)}, {"mixerRejectedBlocks", number(value.mixerRejectedBlocks)}, + {"mixerConfigurationFailures", number(value.mixerConfigurationFailures)}, {"droppedTraceEvents", number(value.droppedTraceEvents)}}; +} +struct Audio { + std::uint64_t packets = 0, frames = 0, nonzero = 0, differing = 0, invalid = 0; + std::uint64_t acceptedPackets = 0, acceptedFrames = 0, acceptedNonzero = 0, rejectedPackets = 0; + std::uint64_t rejectedProducedPackets = 0, rejectedProducedFrames = 0; + std::uint64_t epoch = 0, next = 0, sequenceGaps = 0, discontinuities = 0, retirements = 0; + std::uint64_t maxSourceAgeMs = 0, expiredPackets = 0, intentionalZeroFrames = 0, unexpectedNonzeroSilence = 0; + double leftEnergy = 0, rightEnergy = 0, differenceEnergy = 0, peak = 0; + void add(const Pipeline::Packet& packet, bool accepted, std::uint64_t now) + { + ++packets; frames += packet.frames; discontinuities += packet.discontinuity; + if (epoch && packet.audioEpoch != epoch) { ++retirements; } + if (epoch == packet.audioEpoch && packet.firstSample != next) { ++sequenceGaps; } + epoch = packet.audioEpoch; next = packet.firstSample + packet.frames; + if (packet.producedMonotonicMs) { + if (packet.producedMonotonicMs > now) { ++invalid; } + else { + maxSourceAgeMs = std::max(maxSourceAgeMs, now - packet.producedMonotonicMs); + expiredPackets += now - packet.producedMonotonicMs > 500; + } + } else { intentionalZeroFrames += packet.frames; } + if (accepted) { ++acceptedPackets; acceptedFrames += packet.frames; } else { + ++rejectedPackets; + if (packet.producedMonotonicMs) { ++rejectedProducedPackets; rejectedProducedFrames += packet.frames; } + } + for (std::size_t i = 0; i < packet.frames; ++i) { + const double l = packet.samples[2 * i], r = packet.samples[2 * i + 1]; + if (!std::isfinite(l) || !std::isfinite(r) || std::abs(l) > 1.0 || std::abs(r) > 1.0) { ++invalid; continue; } + const bool audibleSample = l != 0 || r != 0; + nonzero += audibleSample; differing += l != r; acceptedNonzero += accepted && audibleSample; + unexpectedNonzeroSilence += !packet.producedMonotonicMs && audibleSample; + leftEnergy += l * l; rightEnergy += r * r; differenceEnergy += (l - r) * (l - r); + peak = std::max({peak, std::abs(l), std::abs(r)}); + } + } + QJsonObject json(int rate) const + { + const double divisor = frames ? double(frames) : 1.0; + return {{"rateHz", rate}, {"channelCount", 2}, {"packets", number(packets)}, {"frames", number(frames)}, + {"nonzeroFrames", number(nonzero)}, {"differingLeftRightFrames", number(differing)}, {"invalidSamplesOrTime", number(invalid)}, + {"acceptedPackets", number(acceptedPackets)}, {"acceptedFrames", number(acceptedFrames)}, + {"acceptedNonzeroFrames", number(acceptedNonzero)}, {"rejectedByOwnerFencePackets", number(rejectedPackets)}, + {"rejectedProducedPackets", number(rejectedProducedPackets)}, {"rejectedProducedFrames", number(rejectedProducedFrames)}, + {"sameEpochSequenceGaps", number(sequenceGaps)}, {"epochTransitions", number(retirements)}, + {"discontinuities", number(discontinuities)}, {"maximumSourceAgeMs", number(maxSourceAgeMs)}, + {"expiredPackets", number(expiredPackets)}, {"intentionalZeroFrames", number(intentionalZeroFrames)}, + {"unexpectedNonzeroSilenceFrames", number(unexpectedNonzeroSilence)}, + {"leftRms", std::sqrt(leftEnergy / divisor)}, {"rightRms", std::sqrt(rightEnergy / divisor)}, + {"leftRightDifferenceRms", std::sqrt(differenceEnergy / divisor)}, {"peak", peak}}; + } +}; +struct Observations { + explicit Observations(int program) : expectedProgram(program) {} + const int expectedProgram; + Pipeline::HdFmObservation last; + Audio tap, speaker; + QJsonArray transitions, traces; + std::uint64_t observationCount = 0, invalid = 0, expiredObservations = 0, counterRegressions = 0; + std::uint64_t iqDrops = 0, pcmDrops = 0, underruns = 0, publication = 0; + std::uint64_t transitionOverflow = 0, traceCount = 0, traceOverflow = 0; + double firstReady = -1, readyStart = 0, longestReady = 0; + bool ready = false, hadObservation = false; + unsigned discovered = 0; + bool fresh(std::uint64_t stamp, std::uint64_t now) const { return stamp && stamp <= now && now - stamp <= 500; } + bool audioReady(std::uint64_t now) const + { + const auto& raw = last.reception; + return hadObservation && raw.valid && raw.synced && raw.audioValid + && fresh(raw.observationMonotonicMs, now) && fresh(raw.audioMonotonicMs, now); + } + void drain(Pipeline& pipeline, std::uint64_t sourceFrames) + { + const auto now = nowMs(); const double sourceSeconds = sourceFrames / kRate; + Pipeline::HdFmObservation observation; + while (pipeline.takeHdObservation(observation)) { + ++observationCount; const auto& raw = observation.reception; + if (observation.token != kToken || observation.slot != 0 || !observation.instance + || !observation.receiverEpoch || !observation.captureEpoch || !observation.audioEpoch + || raw.sessionId != kToken.session || raw.revision != kToken.revision + || raw.receiverEpoch != observation.receiverEpoch || raw.audioEpoch != observation.audioEpoch + || raw.selectedProgram != expectedProgram || raw.frequencyHz != 1000000 + || raw.publicationSequence <= publication) { ++invalid; continue; } + publication = raw.publicationSequence; + counterRegressions += raw.iqDrops < iqDrops || raw.pcmDrops < pcmDrops || raw.playoutUnderruns < underruns; + iqDrops = std::max(iqDrops, raw.iqDrops); pcmDrops = std::max(pcmDrops, raw.pcmDrops); + underruns = std::max(underruns, raw.playoutUnderruns); + for (const auto& service : raw.services) { + if (service.program >= 0 && service.program < 8) { discovered |= 1u << service.program; } + } + if (raw.valid && !fresh(raw.observationMonotonicMs, now)) { ++expiredObservations; continue; } + last = observation; hadObservation = true; + } + const bool currentReady = audioReady(now); + if (currentReady != ready) { + if (currentReady) { readyStart = sourceSeconds; if (firstReady < 0) { firstReady = sourceSeconds; } } + else { longestReady = std::max(longestReady, sourceSeconds - readyStart); } + if (transitions.size() < 512) { + transitions.append(QJsonObject{{"sourceSeconds", sourceSeconds}, {"monotonicMs", number(now)}, + {"ready", currentReady}, {"audioEpoch", number(last.audioEpoch)}, + {"publicationSequence", number(last.reception.publicationSequence)}, + {"audioSequence", number(last.reception.audioSequence)}, + {"synced", last.reception.synced}, {"valid", last.reception.valid}}); + } else { ++transitionOverflow; } + ready = currentReady; + } + Pipeline::Packet packet; + while (pipeline.takePacket(packet)) { + if (packet.token != kToken || packet.frames == 0 || packet.frames > 1024 || packet.channelCount != 2 + || (packet.slot != 0 && packet.slot != -1) || (packet.slot == 0 ? packet.sampleRateHz != 44100 : packet.sampleRateHz != 48000)) { + ++invalid; continue; + } + const bool identity = hadObservation && packet.audioEpoch == last.audioEpoch && packet.captureEpoch == last.captureEpoch; + bool accepted = identity; + if (packet.slot == 0) { + accepted &= currentReady && fresh(packet.producedMonotonicMs, now) + && packet.instance == last.instance && packet.receiverEpoch == last.receiverEpoch; + tap.add(packet, accepted, now); + } else { + accepted &= last.reception.valid && fresh(last.reception.observationMonotonicMs, now) + && (packet.producedMonotonicMs ? currentReady && fresh(packet.producedMonotonicMs, now) : !currentReady); + speaker.add(packet, accepted, now); + } + } + Pipeline::TraceEvent trace; + while (pipeline.takeTraceEvent(trace)) { + ++traceCount; + if (traces.size() < 128) { + traces.append(QJsonObject{{"kind", trace.kind == Pipeline::TraceEvent::Kind::ReceiverFailure ? "receiver_failure" : "mixer_missing"}, + {"captureFirst", number(trace.captureFirst)}, {"captureFrames", number(trace.captureFrames)}, + {"quantumFirst", number(trace.quantumFirst)}, {"receiverEpoch", number(trace.receiverEpoch)}, + {"missingLow", QString::number(trace.missingMask[0], 16)}, {"missingHigh", QString::number(trace.missingMask[1], 16)}}); + } else { ++traceOverflow; } + } + } +}; +double percentile(std::vector values, double fraction) +{ + if (values.empty()) { return 0; } + std::sort(values.begin(), values.end()); + return values[std::min(values.size() - 1, static_cast(std::ceil(fraction * values.size())) - 1)]; +} +} // namespace + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); Deadline hardDeadline; + const auto start = Clock::now(); const auto arguments = app.arguments(); + if (arguments.size() < 2 || arguments.size() > 6) { return fail(QStringLiteral("Usage: hd_fm_worker_recording_probe RAW_CU8 [--program 0..7] [--compressed-source SOURCE.xz]")); } + int program = 0; bool programSeen = false; QString compressedPath; + for (qsizetype i = 2; i < arguments.size(); i += 2) { + if (i + 1 >= arguments.size()) { return fail(QStringLiteral("Missing option value")); } + if (arguments[i] == QLatin1String("--program") && !programSeen) { + bool ok = false; program = arguments[i + 1].toInt(&ok); programSeen = true; + if (!ok || program < 0 || program > 7) { return fail(QStringLiteral("Program must be 0..7")); } + } else if (arguments[i] == QLatin1String("--compressed-source") && compressedPath.isEmpty()) { compressedPath = arguments[i + 1]; } + else { return fail(QStringLiteral("Unknown or repeated option")); } + } + QFileInfo inputInfo(arguments[1]); QFile input(arguments[1]); + if (!inputInfo.isFile() || inputInfo.size() <= 0 || inputInfo.size() > kMaximumBytes || inputInfo.size() % 2 + || !input.open(QIODevice::ReadOnly) || input.isSequential()) { return fail(QStringLiteral("Input must be regular even-sized decompressed CU8, at most 256 MiB")); } + if (input.peek(6) == QByteArray::fromHex("fd377a585a00")) { return fail(QStringLiteral("Decompress the recording before running")); } + const auto executable = fileIdentity(QCoreApplication::applicationFilePath(), 1024LL * 1024 * 1024); + if (!executable) { return fail(QStringLiteral("Cannot hash running executable")); } + QJsonValue compressed(QJsonValue::Null); + if (!compressedPath.isEmpty()) { + const auto value = fileIdentity(compressedPath, kMaximumBytes); + if (!value) { return fail(QStringLiteral("Cannot hash bounded compressed source")); } + compressed = *value; + } + Transaction::State state; state.token = kToken; + state.hardware.centerHz = 1000000; state.hardware.sampleRateHz = NRSC5_SAMPLE_RATE_CU8; + state.capture = {1, 1, 1000000, kRate, 0.45 * kRate, 0.45 * kRate}; + Transaction::Receiver receiver; receiver.passband = {0, 1000000, -100000, 100000, 0, 3000, 3000}; + receiver.mode = Transaction::Mode::Wfm; receiver.wfmHdStereo = true; receiver.hdProgram = program; + state.receivers = {receiver}; state.receivingIds = {0}; + auto pipeline = std::make_unique(1, true); + const auto prepareStart = Clock::now(); + const auto preparationFailure = [&](const QString& message) { + pipeline->stop(); pipeline.reset(); + const bool released = QThreadPool::globalInstance()->waitForDone(9000); + const int code = fail(message + (released ? QString() : QStringLiteral("; teardown also timed out"))); + if (!released) { std::_Exit(code); } + return code; + }; + if (!pipeline->prepare(state, true)) { return preparationFailure(QStringLiteral("Production pipeline refused HD preparation")); } + bool prepared = false; + while (Clock::now() - prepareStart < 20s) { + const auto result = pipeline->service(); + if (result == Pipeline::Preparation::Failed) { break; } + if (result == Pipeline::Preparation::Ready) { prepared = pipeline->adopt(); break; } + std::this_thread::sleep_for(1ms); + } + if (!prepared) { return preparationFailure(QStringLiteral("Native HD preparation/adoption failed or exceeded 20 s")); } + const double preparationSeconds = seconds(Clock::now() - prepareStart); + const auto initialDiagnostics = pipeline->diagnostics(); + QCryptographicHash rawHash(QCryptographicHash::Sha256); + std::array bytes{}; + std::array, kInputFrames> samples{}; + std::vector callbackLatenessMs, processUs; callbackLatenessMs.reserve(16384); processUs.reserve(16384); + Observations observations(program); QString error; + std::uint64_t inputBytes = 0, inputFrames = 0, calls = 0, catchup = 0, maxCatchup = 0; + const auto playbackStart = Clock::now(); const auto cpuStart = processCpuSeconds(); + const auto utcStart = QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs); + while (inputBytes < static_cast(inputInfo.size())) { + if (Clock::now() - start > 110s) { error = QStringLiteral("Playback reached soft wall budget; reserving teardown time"); break; } + const auto count = input.read(bytes.data(), std::min(bytes.size(), inputInfo.size() - static_cast(inputBytes))); + if (count <= 0 || count % 2) { error = QStringLiteral("Read error or truncated IQ pair"); break; } + rawHash.addData(QByteArrayView(bytes.data(), count)); const std::size_t frames = count / 2; + for (std::size_t i = 0; i < frames; ++i) { + samples[i] = {(static_cast(bytes[2 * i]) - 127.0f) / 128.0f, + (static_cast(bytes[2 * i + 1]) - 127.0f) / 128.0f}; + } + const auto target = playbackStart + std::chrono::duration_cast(std::chrono::duration((inputFrames + frames) / kRate)); + if (Clock::now() < target) { std::this_thread::sleep_until(target); catchup = 0; } + else { maxCatchup = std::max(maxCatchup, ++catchup); } + const auto entered = Clock::now(); callbackLatenessMs.push_back(1000 * seconds(entered - target)); + const bool accepted = pipeline->process(inputFrames, std::span(samples).first(frames)); + processUs.push_back(1e6 * seconds(Clock::now() - entered)); + inputBytes += count; inputFrames += frames; ++calls; + observations.drain(*pipeline, inputFrames); + if (!accepted || pipeline->needsRepair()) { error = QStringLiteral("Production receiver/pipeline withdrew; no repair or retry is hidden"); break; } + } + const auto cpuEnd = processCpuSeconds(); const double playbackWallSeconds = seconds(Clock::now() - playbackStart); + observations.drain(*pipeline, inputFrames); + if (observations.ready) { observations.longestReady = std::max(observations.longestReady, inputFrames / kRate - observations.readyStart); } + const auto finalDiagnostics = pipeline->diagnostics(); const bool repair = pipeline->needsRepair(); + const auto teardownStart = Clock::now(); pipeline->stop(); pipeline.reset(); + const bool teardownComplete = QThreadPool::globalInstance()->waitForDone(9000); + const double teardownSeconds = seconds(Clock::now() - teardownStart); + if (!teardownComplete && error.isEmpty()) { error = QStringLiteral("Registry/decoder teardown did not complete within 9 s"); } + const bool completed = error.isEmpty() && inputBytes == static_cast(inputInfo.size()) + && input.atEnd() && input.size() == inputInfo.size() && teardownComplete; + if (!completed && error.isEmpty()) { error = QStringLiteral("Input changed or ended early"); } + const bool zeroPipelineFaults = !finalDiagnostics.droppedPackets && !finalDiagnostics.mixerLateFrames + && !finalDiagnostics.mixerRejectedBlocks && !finalDiagnostics.mixerConfigurationFailures && !finalDiagnostics.droppedTraceEvents; + const bool passed = completed && !repair && zeroPipelineFaults && !observations.invalid && !observations.counterRegressions && !observations.expiredObservations + && !observations.iqDrops && !observations.pcmDrops && !observations.underruns + && !observations.tap.invalid && !observations.speaker.invalid && !observations.speaker.unexpectedNonzeroSilence + && !observations.tap.sequenceGaps && !observations.speaker.sequenceGaps + && !observations.tap.expiredPackets && !observations.speaker.expiredPackets + && !observations.tap.rejectedProducedPackets && !observations.speaker.rejectedProducedPackets + && !observations.tap.intentionalZeroFrames + && observations.tap.acceptedFrames && observations.speaker.acceptedNonzero; + QJsonArray programs; for (int i = 0; i < 8; ++i) { if (observations.discovered & (1u << i)) { programs.append(i); } } + const auto& raw = observations.last.reception; + QJsonObject result{{"schema", 1}, {"completed", completed}, {"passed", passed}, {"error", error}, + {"utcStart", utcStart}, {"utcEnd", QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs)}, + {"maximumWallSeconds", 120}, {"totalWallSeconds", seconds(Clock::now() - start)}, + {"preparationSeconds", preparationSeconds}, {"playbackWallSeconds", playbackWallSeconds}, + {"teardownSeconds", teardownSeconds}, {"teardownComplete", teardownComplete}, {"executable", *executable}, + {"compressedSource", compressed}, {"compressedSourceRelation", "caller-supplied archive identity; decompression external"}, + {"rawInput", QJsonObject{{"path", inputInfo.canonicalFilePath()}, {"declaredBytes", inputInfo.size()}, {"readBytes", number(inputBytes)}, + {"readSha256", QString::fromLatin1(rawHash.result().toHex())}, {"format", "cu8"}, {"complexRateHz", kRate}, + {"readComplexFrames", number(inputFrames)}, {"durationSeconds", inputFrames / kRate}, {"program", program}, + {"rfFrequencyHz", QJsonValue(QJsonValue::Null)}, {"geometryReferenceHz", 1000000}, {"unsignedConversion", "(byte - 127) / 128"}}}, + {"pacing", QJsonObject{{"rateMultiplier", 1}, {"blockFramesMaximum", qint64(kInputFrames)}, {"callbacks", number(calls)}, + {"latenessP95Ms", percentile(callbackLatenessMs, 0.95)}, {"latenessMaximumMs", percentile(callbackLatenessMs, 1)}, + {"processP95Us", percentile(processUs, 0.95)}, {"processMaximumUs", percentile(processUs, 1)}, + {"maximumConsecutiveCatchupCallbacks", number(maxCatchup)}, {"policy", "absolute source-block-end deadlines; lateness is retained, no rebasing"}}}, + {"nativeTap", observations.tap.json(44100)}, {"speaker", observations.speaker.json(48000)}, + {"initialDiagnostics", diagnostics(initialDiagnostics)}, {"finalDiagnostics", diagnostics(finalDiagnostics)}, {"needsRepair", repair}, + {"hd", QJsonObject{{"observations", number(observations.observationCount)}, {"invalidIdentity", number(observations.invalid)}, + {"expiredObservations", number(observations.expiredObservations)}, {"counterRegressions", number(observations.counterRegressions)}, + {"iqDrops", number(observations.iqDrops)}, {"pcmDrops", number(observations.pcmDrops)}, {"playoutUnderruns", number(observations.underruns)}, + {"firstReadyAtSourceSeconds", observations.firstReady < 0 ? QJsonValue(QJsonValue::Null) : QJsonValue(observations.firstReady)}, + {"longestReadySourceSeconds", observations.longestReady}, {"finalRawSynced", raw.synced}, {"finalRawAudioValid", raw.audioValid}, + {"finalAudioEpoch", number(raw.audioEpoch)}, {"syncLossCount", number(raw.syncLossCount)}, {"reacquisitionCount", number(raw.reacquisitionCount)}, + {"discoveredPrograms", programs}, {"transitions", observations.transitions}, {"omittedTransitions", number(observations.transitionOverflow)}}}, + {"traceCount", number(observations.traceCount)}, {"traces", observations.traces}, {"omittedTraces", number(observations.traceOverflow)}, + {"scope", "real pipeline, registry, native worker and shared mixer; owner-like identity/age analysis, not full backend normalization/high-water acceptance or GUI/audio-device/USB/live RF proof"}, + {"queueOccupancyMeasured", false}, + {"eofPolicy", "no RF padding or flush; stop discards pending worker/converter/playout tail; totals describe delivered prefix only"}}; + if (cpuStart && cpuEnd && *cpuEnd >= *cpuStart) { + result.insert("playbackProcessCpuSeconds", *cpuEnd - *cpuStart); + } +#ifdef AETHER_GIT_SHA + result.insert("aetherBuildRevision", QStringLiteral(AETHER_GIT_SHA)); +#else + result.insert("aetherBuildRevision", QJsonValue(QJsonValue::Null)); +#endif + output(result); + // A failed join cannot fall into an unbounded Qt global-pool destructor. + // This standalone diagnostic owns no device or external mutable state. + if (!teardownComplete) { std::_Exit(2); } + return passed ? 0 : 1; +} From 5c689a24bdbfa77d1a37c3da4de017118e17bd6d Mon Sep 17 00:00:00 2001 From: Ozy311 Date: Mon, 5 Oct 2026 19:04:58 -0700 Subject: [PATCH 8/9] Document the optional embedded Digital FM build. Principle VIII. --- BUILD-OPTIONS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BUILD-OPTIONS.md b/BUILD-OPTIONS.md index e787e4efe..9c67c7462 100644 --- a/BUILD-OPTIONS.md +++ b/BUILD-OPTIONS.md @@ -60,6 +60,7 @@ alone. MSVC flags and `Release` builds are unaffected. |---|---|---| | `ENABLE_DEEPFIST_EXPERIMENT` | OFF | Build the experimental DeepFist CW receive decoder. Requires ONNX Runtime at configure time (configuration fails without it) and a separate verified model bundle at runtime; see below. | | `ENABLE_RTL` | ON | Build the experimental receive-only RTL-SDR USB backend when both `librtlsdr` and single-precision FFTW (`fftw3f`) are found. Missing either disables the backend unless `REQUIRE_RTL=ON`. | +| `ENABLE_HD_FM` | OFF | Build experimental embedded Digital FM using the pinned nrsc5 and FAAD HDC sources. Requires the RTL backend, librtlsdr and float FFTW; qualified build paths are Linux/GNU, macOS/AppleClang and Windows/MSVC x64 (with LLVM compiler-rt builtins). Official release workflows explicitly keep it OFF. See [the experimental guide](docs/rtl-hd-fm-experimental.md). | | `AETHER_HL2_TX_TXA` | ON | Select WDSP's TXA chain for the Hermes-Lite 2 SSB transmit modulator. OFF builds the in-tree phasing modulator. This choice has no runtime toggle. | **DeepFist:** enabling `-DENABLE_DEEPFIST_EXPERIMENT=ON` alone does not supply its From a8112373126246df213ee944dc07804f73a8263e Mon Sep 17 00:00:00 2001 From: Ozy311 Date: Fri, 9 Oct 2026 15:49:42 -0700 Subject: [PATCH 9/9] Own Digital FM release defaults in the draft. Principle V. Keep the optional decoder OFF in release and local packaging entry points. Document the opt-in toolchain and actual Digital controls in the user guide. Preserve decoder source pins, original upstream notices, internal identifiers and enabled-build behavior. --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos-dmg.yml | 2 +- .github/workflows/windows-installer.yml | 2 +- docs/user/docs/building-from-source.md | 3 ++ docs/user/docs/rtl-sdr.md | 10 +++++++ .../src/components/LogAnalyzer/rules.json | 28 +++++++++---------- packaging/macos/build-installer.sh | 2 +- scripts/build-windows-local.ps1 | 2 +- third_party/faad_hdc/README.aethersdr.md | 4 --- third_party/nrsc5/README.aethersdr.md | 1 - 10 files changed, 32 insertions(+), 24 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 7a7cd6e7d..73453fc69 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -270,7 +270,7 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ "-DCMAKE_C_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ "-DCMAKE_CXX_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ - -DENABLE_RTL=ON -DREQUIRE_RTL=ON \ + -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON \ -DCMAKE_INSTALL_PREFIX=/usr \ -DREQUIRE_SERIALPORT=ON \ -DREQUIRE_KEYCHAIN=ON \ diff --git a/.github/workflows/macos-dmg.yml b/.github/workflows/macos-dmg.yml index 8dbdbfd2b..51c35c7c4 100644 --- a/.github/workflows/macos-dmg.yml +++ b/.github/workflows/macos-dmg.yml @@ -371,7 +371,7 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ "-DCMAKE_C_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ "-DCMAKE_CXX_FLAGS_RELEASE=-O3 -DNDEBUG -g" \ - -DENABLE_RTL=ON -DREQUIRE_RTL=ON \ + -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_OSX_DEPLOYMENT_TARGET="${{ matrix.deploy_target }}" \ diff --git a/.github/workflows/windows-installer.yml b/.github/workflows/windows-installer.yml index 9ea4c596d..03654a683 100644 --- a/.github/workflows/windows-installer.yml +++ b/.github/workflows/windows-installer.yml @@ -290,7 +290,7 @@ jobs: # native command), so a configure failure still fails this step — # the reason the bash legs need `set -o pipefail` and this one does # not. - cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log + cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log # AETHER_GPU_SPECTRUM=ON is a request, not a guarantee: CMake flips it # back OFF without failing when Qt6GuiPrivate can't be located, and diff --git a/docs/user/docs/building-from-source.md b/docs/user/docs/building-from-source.md index 61c3aa3d1..fcc360b96 100644 --- a/docs/user/docs/building-from-source.md +++ b/docs/user/docs/building-from-source.md @@ -93,12 +93,15 @@ Start with [AGENTS.md](https://github.com/aethersdr/AetherSDR/blob/main/AGENTS.m | `AETHER_GPU_SPECTRUM` | ON | QRhi GPU spectrum and waterfall. Needs Qt's private GUI headers; without them the CPU renderer is built. | | `ENABLE_RTL` | ON | Experimental receive-only RTL-SDR backend, when `librtlsdr` and single-precision FFTW are found | | `REQUIRE_RTL` | OFF | Fail configure if the RTL backend cannot be built. Official release packages set this and `ENABLE_RTL` to ON. | +| `ENABLE_HD_FM` | OFF | Experimental embedded Digital FM. Requires RTL, float FFTW and a supported evaluation toolchain; official release builds leave it OFF. | | `ENABLE_ASR` | ON | Copy Assist on-device speech-to-text | | `ENABLE_NVIDIA_AFX` | ON | BNR NVIDIA GPU noise removal (x86-64 Linux and Windows) | | `AETHER_FETCH_QT` | OFF | Run the Qt setup script during configure if the pinned Qt is missing | | `USE_SYSTEM_*` | OFF | For distribution packagers: use system zlib, libmspack, libmosquitto, RtMidi, libwhisper or SQLite instead of the bundled copies | | `LOWER_CASE_BINARY_NAME` | OFF | Lower-case executable name on Linux | +Digital FM evaluation supports Linux/GNU C, macOS/AppleClang and Windows x64/MSVC with installed `clang-cl` and matching LLVM compiler-rt builtins. Other toolchains fail configure when the option is ON. Dependencies are pinned in the source tree; configure does not download a decoder or toolchain. See the [experimental build guide](https://github.com/aethersdr/AetherSDR/blob/main/docs/rtl-hd-fm-experimental.md). + Builds honour `SOURCE_DATE_EPOCH` for reproducible output. ## Troubleshooting diff --git a/docs/user/docs/rtl-sdr.md b/docs/user/docs/rtl-sdr.md index 7ac1521de..2eec9cf37 100644 --- a/docs/user/docs/rtl-sdr.md +++ b/docs/user/docs/rtl-sdr.md @@ -44,6 +44,16 @@ Normal launches admit **one receiver**, even though the capture is wide. Select **WFM** to use the **Broadcast FM** applet. It shows the selected slice letter and confirmed frequency. **Mono / Auto Stereo**, **De-emphasis** (50 or 75 µs) and **Bandwidth** belong to that receiver. **Auto Stereo** falls back to mono without a detected stereo pilot. The observed pilot status is separate from your selected audio mode; parking clears observations while retaining the controls. +### Optional Digital FM evaluation + +Digital FM is an experimental, opt-in build feature under [RFC #6079](https://github.com/aethersdr/AetherSDR/issues/6079). Official release builds leave it off. It requires `ENABLE_HD_FM=ON` and the existing RTL dependencies; see [Building from Source](./building-from-source.md). + +In an enabled build, the **Broadcast FM** audio-mode button cycles **Mono → Auto Stereo → Digital**. **Digital program** lists discovered audio services as **P1** through **P8**; it does not invent program names. **Digital acquiring**, **Digital synced · awaiting audio** and **Digital audio valid** describe separate observed states. Selection alone does not establish decoded audio. There is no automatic analog/Digital blending. + +The applet displays current Digital station and now-playing text. **SpotHub → Display → WFM RDS** toggles the local panadapter metadata overlay without stopping the decoder or applet text. This overlay shows Digital metadata; analog RDS/RBDS decoding is not provided. Stale metadata clears on loss, retune, parking or disconnect, and nothing is published to the DX cluster. + +Digital reception admits one active wide receiver and does not raise the multi-receiver evaluation limit. Its full RF footprint must fit the capture. Native decoded audio is stereo at 44.1 kHz and is converted for speaker, DAX and TCI output; TCI still negotiates only 8, 12, 24 or 48 kHz. A successful build or startup does not qualify sustained reception on a platform. + ### Receive meters The RTL receive meter reports relative RF peak-bin level in **dBFS**, not calibrated antenna dBm or audio level. **Settings → Radio Setup... → RTL Receiver → Enable receive meters** turns these updates on or off. Turning them off clears the readout; reception, squelch and audio continue. diff --git a/docs/user/src/components/LogAnalyzer/rules.json b/docs/user/src/components/LogAnalyzer/rules.json index 146eb8cac..e32e128f3 100644 --- a/docs/user/src/components/LogAnalyzer/rules.json +++ b/docs/user/src/components/LogAnalyzer/rules.json @@ -9,7 +9,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7770, + "line": 7773, "text": "else if (key == \"model\")" } ] @@ -21,7 +21,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7779, + "line": 7782, "text": "else if (key == \"software_ver\")" } ] @@ -94,7 +94,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7502, + "line": 7505, "text": "\"RadioModel: terminal GUI client registration failure\"" } ] @@ -111,12 +111,12 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7432, + "line": 7435, "text": "RadioModel: this GUI client was force-disconnected by another client" }, { "file": "src/models/RadioModel.cpp", - "line": 7476, + "line": 7479, "text": "RadioModel: radio disconnected this session because another GUI client used the same ID" } ] @@ -133,7 +133,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7342, + "line": 7345, "text": "RadioModel: multiFLEX disabled, other clients present" } ] @@ -167,7 +167,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 8473, + "line": 8476, "text": "consecutive pings unanswered — forcing disconnect" } ] @@ -193,7 +193,7 @@ }, { "file": "src/models/RadioModel.cpp", - "line": 7779, + "line": 7782, "text": "else if (key == \"software_ver\")" } ] @@ -210,7 +210,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7667, + "line": 7670, "text": "RadioModel: no VITA-49 UDP data received after 10s" } ] @@ -227,7 +227,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 7645, + "line": 7648, "text": "RadioModel: UDP stream setup failed" }, { @@ -237,7 +237,7 @@ }, { "file": "src/models/RadioModel.cpp", - "line": 7720, + "line": 7723, "text": "RadioModel: client udpport collision" } ] @@ -474,7 +474,7 @@ "source": [ { "file": "src/models/RadioModel.cpp", - "line": 12733, + "line": 12771, "text": "RadioModel: DAX TX create failed" } ] @@ -547,7 +547,7 @@ "source": [ { "file": "src/gui/SpectrumWidget.cpp", - "line": 13049, + "line": 13058, "text": "\"SpectrumWidget: QRhi failure:\"" }, { @@ -574,7 +574,7 @@ "source": [ { "file": "src/gui/SpectrumWidget.cpp", - "line": 1995, + "line": 1996, "text": "SpectrumWidget: AETHER_NO_GPU/QT_OPENGL=software" } ] diff --git a/packaging/macos/build-installer.sh b/packaging/macos/build-installer.sh index 3c65d6af6..0f51183d7 100755 --- a/packaging/macos/build-installer.sh +++ b/packaging/macos/build-installer.sh @@ -15,7 +15,7 @@ echo "=== Building AetherSDR macOS installer v${VERSION} ===" export MACOS_DEPLOYMENT_TARGET="${MACOS_DEPLOYMENT_TARGET:-14.0}" bash scripts/setup/setup-macos-deps.sh export PKG_CONFIG_PATH="$PWD/third_party/macos-deps/lib/pkgconfig:${PKG_CONFIG_PATH:-}" -cmake -B "${BUILD_DIR}" -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOS_DEPLOYMENT_TARGET" -DCMAKE_PREFIX_PATH="$PWD/third_party/macos-deps;${CMAKE_PREFIX_PATH:-}" +cmake -B "${BUILD_DIR}" -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOS_DEPLOYMENT_TARGET" -DCMAKE_PREFIX_PATH="$PWD/third_party/macos-deps;${CMAKE_PREFIX_PATH:-}" cmake --build "${BUILD_DIR}" -j$(sysctl -n hw.ncpu) # 1b. Build HAL plugin (separate build because libASPL FetchContent conflicts with main Ninja build) diff --git a/scripts/build-windows-local.ps1 b/scripts/build-windows-local.ps1 index 8c8d9dc89..7dfec50c4 100644 --- a/scripts/build-windows-local.ps1 +++ b/scripts/build-windows-local.ps1 @@ -113,7 +113,7 @@ Write-Host " VULKAN_SDK = $env:VULKAN_SDK" -ForegroundColor Green Write-Host "== Configure ==" -ForegroundColor Cyan cmake -B build -G "Ninja" ` -DCMAKE_BUILD_TYPE=RelWithDebInfo ` - -DENABLE_RTL=ON -DREQUIRE_RTL=ON ` + -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON ` -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON ` -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON ` -DCMAKE_PREFIX_PATH="$QtDir;$env:VULKAN_SDK" ` diff --git a/third_party/faad_hdc/README.aethersdr.md b/third_party/faad_hdc/README.aethersdr.md index 7a1faea36..b4f335a1c 100644 --- a/third_party/faad_hdc/README.aethersdr.md +++ b/third_party/faad_hdc/README.aethersdr.md @@ -25,10 +25,6 @@ copyright notice. The required acknowledgment is: **Code from FAAD2 is copyright (c) Nero AG, www.nero.com** -The original notices discuss separate commercial licensing and possible patent -royalties. Vendoring does not provide patent clearance. No proprietary binary, -decoder decompilation, or dynamically downloaded runtime is included. - ## Build and reproduction `cmake/AetherHdFm.cmake` builds only static `aether_faad_hdc`, with diff --git a/third_party/nrsc5/README.aethersdr.md b/third_party/nrsc5/README.aethersdr.md index d6e71a000..09455d941 100644 --- a/third_party/nrsc5/README.aethersdr.md +++ b/third_party/nrsc5/README.aethersdr.md @@ -13,7 +13,6 @@ in `COPYING.GPL-3.0`, with its source/hash in `LICENSE-SOURCE.json`. Preserve per-file notices, including Ettus Research's GPLv3 convolutional decoder, Phil Karn's GPL Reed-Solomon code, and rxi's MIT notice in `src/log.c` (not built). The HDC patch has its own author/commit header and is preserved byte-for-byte. -These source licenses are not a patent clearance or a product certification. ## Build and target use