From 7920d9f25100f44a12be4c0d222b2002f0f38acb Mon Sep 17 00:00:00 2001 From: Attila Uygun Date: Fri, 18 Sep 2020 00:03:21 +0200 Subject: [PATCH] Update. - Implement PersistentData. - Update jsoncpp (1.9.3). - Update logging implementation. - New audio resampler (SincResampler). - Avoid busy-looping in audio thread (AudioAlsa). - Add BindWeak. - AdMob support. - Support for record/replay user input. - Custom shader support for ImageQuad. - Implement SetKeepScreenOn for Android. - Move animation updates to Engine. - Code refactoring for renderer. - Various fixes and code cleanup. --- .travis.yml | 20 - README.md | 4 +- assets/engine/pass_through.glsl_fragment | 4 +- assets/engine/pass_through.glsl_vertex | 4 +- assets/engine/quad.mesh | 10 - assets/engine/solid.glsl_vertex | 4 +- build/README.md | 1 - build/android/app/CMakeLists.txt | 25 +- build/android/app/build.gradle | 14 +- .../android/app/src/main/AndroidManifest.xml | 75 +- .../com/kaliber/base/KaliberActivity.java | 132 + .../app/src/main/res/values/strings.xml | 3 +- .../app/src/main/res/xml/file_paths.xml | 4 + build/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- build/android/local.properties | 5 +- build/linux/Makefile | 10 +- build/travis.sh | 11 - privacy.md | 65 + src/base/BUILD.gn | 2 + src/base/closure.h | 11 + src/base/log.cc | 94 +- src/base/log.h | 111 +- src/base/random.cc | 19 +- src/base/random.h | 7 +- src/base/semaphore.h | 3 + src/base/sinc_resampler.cc | 446 +++ src/base/sinc_resampler.h | 154 + src/base/task_runner.cc | 35 +- src/base/task_runner.h | 20 +- src/base/timer.cc | 18 + src/base/timer.h | 2 + src/base/vecmath.cc | 6 + src/base/vecmath.h | 5 +- src/base/worker.cc | 12 +- src/base/worker.h | 14 +- src/demo/credits.cc | 12 +- src/demo/credits.h | 2 - src/demo/demo.cc | 23 +- src/demo/demo.h | 2 +- src/demo/enemy.cc | 62 +- src/demo/enemy.h | 4 + src/demo/hud.cc | 21 +- src/demo/hud.h | 2 - src/demo/menu.cc | 32 +- src/demo/menu.h | 2 - src/demo/player.cc | 71 +- src/demo/player.h | 4 +- src/demo/sky_quad.cc | 16 +- src/demo/sky_quad.h | 4 + src/engine/BUILD.gn | 13 +- src/engine/animatable.cc | 8 +- src/engine/animatable.h | 25 +- src/engine/animator.cc | 65 +- src/engine/animator.h | 12 +- src/engine/audio/audio_alsa.cc | 7 +- src/engine/audio/audio_alsa.h | 10 +- src/engine/audio/audio_base.cc | 32 +- src/engine/audio/audio_base.h | 4 +- src/engine/audio/audio_oboe.cc | 6 +- src/engine/audio/audio_oboe.h | 2 +- src/engine/audio/audio_resource.cc | 6 +- src/engine/audio/audio_sample.h | 2 +- src/engine/engine.cc | 208 +- src/engine/engine.h | 48 +- src/engine/game.h | 2 +- src/engine/image.cc | 2 + src/engine/image_quad.cc | 31 +- src/engine/image_quad.h | 20 + src/engine/input_event.h | 5 +- src/engine/mesh.cc | 6 +- src/engine/mesh.h | 4 +- src/engine/persistent_data.cc | 104 + src/engine/persistent_data.h | 40 + src/engine/platform/platform_android.cc | 149 +- src/engine/platform/platform_android.h | 6 + src/engine/platform/platform_base.cc | 27 +- src/engine/platform/platform_base.h | 8 +- src/engine/platform/platform_linux.cc | 78 +- src/engine/platform/platform_linux.h | 18 + src/engine/renderer/geometry.cc | 21 +- src/engine/renderer/geometry.h | 3 + src/engine/renderer/{ => opengl}/opengl.h | 6 +- .../renderer/{ => opengl}/render_command.cc | 6 +- .../renderer/{ => opengl}/render_command.h | 8 +- .../renderer_opengl.cc} | 301 +- src/engine/renderer/opengl/renderer_opengl.h | 215 ++ .../renderer_opengl_android.cc} | 21 +- .../renderer/opengl/renderer_opengl_linux.cc | 73 + src/engine/renderer/renderer.h | 211 +- src/engine/renderer/renderer_linux.cc | 101 - src/engine/renderer/renderer_types.cc | 2 + src/engine/renderer/renderer_types.h | 1 + src/engine/renderer/shader.cc | 81 +- src/engine/renderer/shader.h | 6 +- src/engine/renderer/texture.cc | 18 +- src/engine/shader_source.cc | 14 +- src/engine/shader_source.h | 10 + src/engine/solid_quad.cc | 8 +- src/engine/sound.cc | 117 +- src/engine/sound.h | 17 +- src/engine/sound_player.cc | 34 +- src/third_party/BUILD.gn | 15 - src/third_party/jsoncpp/json.h | 1708 +++++---- src/third_party/jsoncpp/jsoncpp.cc | 3200 +++++++++-------- src/third_party/r8b/CDSPBlockConvolver.h | 646 ---- src/third_party/r8b/CDSPFIRFilter.h | 721 ---- src/third_party/r8b/CDSPFracInterpolator.h | 1019 ------ src/third_party/r8b/CDSPHBDownsampler.h | 393 -- src/third_party/r8b/CDSPHBUpsampler.h | 857 ----- src/third_party/r8b/CDSPProcessor.h | 103 - src/third_party/r8b/CDSPRealFFT.h | 718 ---- src/third_party/r8b/CDSPResampler.h | 753 ---- src/third_party/r8b/CDSPSincFilterGen.h | 687 ---- src/third_party/r8b/pffft.cpp | 1891 ---------- src/third_party/r8b/pffft.h | 178 - src/third_party/r8b/r8bbase.cpp | 39 - src/third_party/r8b/r8bbase.h | 1240 ------- src/third_party/r8b/r8bconf.h | 198 - src/third_party/texture_compressor/README.md | 4 - 120 files changed, 5349 insertions(+), 12856 deletions(-) delete mode 100644 .travis.yml delete mode 100644 assets/engine/quad.mesh delete mode 100644 build/README.md create mode 100644 build/android/app/src/main/java/com/kaliber/base/KaliberActivity.java create mode 100644 build/android/app/src/main/res/xml/file_paths.xml delete mode 100644 build/travis.sh create mode 100644 privacy.md create mode 100644 src/base/sinc_resampler.cc create mode 100644 src/base/sinc_resampler.h create mode 100644 src/engine/persistent_data.cc create mode 100644 src/engine/persistent_data.h rename src/engine/renderer/{ => opengl}/opengl.h (74%) rename src/engine/renderer/{ => opengl}/render_command.cc (92%) rename src/engine/renderer/{ => opengl}/render_command.h (94%) rename src/engine/renderer/{renderer.cc => opengl/renderer_opengl.cc} (74%) create mode 100644 src/engine/renderer/opengl/renderer_opengl.h rename src/engine/renderer/{renderer_android.cc => opengl/renderer_opengl_android.cc} (71%) create mode 100644 src/engine/renderer/opengl/renderer_opengl_linux.cc delete mode 100644 src/engine/renderer/renderer_linux.cc delete mode 100644 src/third_party/r8b/CDSPBlockConvolver.h delete mode 100644 src/third_party/r8b/CDSPFIRFilter.h delete mode 100644 src/third_party/r8b/CDSPFracInterpolator.h delete mode 100644 src/third_party/r8b/CDSPHBDownsampler.h delete mode 100644 src/third_party/r8b/CDSPHBUpsampler.h delete mode 100644 src/third_party/r8b/CDSPProcessor.h delete mode 100644 src/third_party/r8b/CDSPRealFFT.h delete mode 100644 src/third_party/r8b/CDSPResampler.h delete mode 100644 src/third_party/r8b/CDSPSincFilterGen.h delete mode 100644 src/third_party/r8b/pffft.cpp delete mode 100644 src/third_party/r8b/pffft.h delete mode 100644 src/third_party/r8b/r8bbase.cpp delete mode 100644 src/third_party/r8b/r8bbase.h delete mode 100644 src/third_party/r8b/r8bconf.h diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index be96ccc..0000000 --- a/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -dist: trusty - -language: cpp - -os: linux - -compiler: - - gcc - - clang - -env: - - BUILD_CONFIGURATION=debug - - BUILD_CONFIGURATION=release - -install: - - source build/travis.sh - -script: - - cd build/linux - - make BUILD=$BUILD_CONFIGURATION diff --git a/README.md b/README.md index 71ca0a5..d5dfc5a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ A simple, cross-platform 2D game engine with OpenGL renderer. Supports Linux and -Android (lolipop+) platforms. +Android (lolipop+) platforms. My personal hobby project. +I've published a game on [Google Play](https://play.google.com/store/apps/details?id=com.woom.game) based on the engine. The demo included in this repository is an early prototype of the game. #### Building the demo Linux: ```text @@ -21,7 +22,6 @@ ninja -C out/release [jsoncpp](https://github.com/open-source-parsers/jsoncpp), [minimp3](https://github.com/lieff/minimp3), [oboe](https://github.com/google/oboe), -[r8brain-free-src](https://github.com/avaneev/r8brain-free-src), [stb](https://github.com/nothings/stb), [texture-compressor](https://github.com/auygun/kaliber/tree/master/src/third_party/texture_compressor), [minizip](https://github.com/madler/zlib/tree/master/contrib/minizip) diff --git a/assets/engine/pass_through.glsl_fragment b/assets/engine/pass_through.glsl_fragment index 29f1730..620d3ad 100644 --- a/assets/engine/pass_through.glsl_fragment +++ b/assets/engine/pass_through.glsl_fragment @@ -3,10 +3,10 @@ precision mediump float; #endif uniform vec4 color; -uniform sampler2D texture; +uniform sampler2D texture_0; varying vec2 tex_coord_0; void main() { - gl_FragColor = texture2D(texture, tex_coord_0) * color; + gl_FragColor = texture2D(texture_0, tex_coord_0) * color; } diff --git a/assets/engine/pass_through.glsl_vertex b/assets/engine/pass_through.glsl_vertex index a9b3956..06bca35 100644 --- a/assets/engine/pass_through.glsl_vertex +++ b/assets/engine/pass_through.glsl_vertex @@ -3,7 +3,6 @@ attribute vec2 in_tex_coord_0; uniform vec2 scale; uniform vec2 offset; -uniform vec2 pivot; uniform vec2 rotation; uniform vec2 tex_offset; uniform vec2 tex_scale; @@ -15,10 +14,9 @@ void main() { // Simple 2d transform. vec2 position = in_position; position *= scale; - position += pivot; position = vec2(position.x * rotation.y + position.y * rotation.x, position.y * rotation.y - position.x * rotation.x); - position += offset - pivot; + position += offset; tex_coord_0 = (in_tex_coord_0 + tex_offset) * tex_scale; diff --git a/assets/engine/quad.mesh b/assets/engine/quad.mesh deleted file mode 100644 index 53bb937..0000000 --- a/assets/engine/quad.mesh +++ /dev/null @@ -1,10 +0,0 @@ -// This creates a normalized unit sized quad. -{ - "primitive": "TriangleStrip", - "vertex_description": "p2f;t2f", - "num_vertices": 4, - "vertices": [-0.5, -0.5, 0.0, 1.0, - 0.5, -0.5, 1.0, 1.0, - -0.5, 0.5, 0.0, 0.0, - 0.5, 0.5, 1.0, 0.0] -} diff --git a/assets/engine/solid.glsl_vertex b/assets/engine/solid.glsl_vertex index f1ac061..5c939fd 100644 --- a/assets/engine/solid.glsl_vertex +++ b/assets/engine/solid.glsl_vertex @@ -3,7 +3,6 @@ attribute vec2 in_tex_coord_0; uniform vec2 scale; uniform vec2 offset; -uniform vec2 pivot; uniform vec2 rotation; uniform mat4 projection; @@ -11,10 +10,9 @@ void main() { // Simple 2d transform. vec2 position = in_position; position *= scale; - position += pivot; position = vec2(position.x * rotation.y + position.y * rotation.x, position.y * rotation.y - position.x * rotation.x); - position += offset - pivot; + position += offset; gl_Position = projection * vec4(position, 0.0, 1.0); } diff --git a/build/README.md b/build/README.md deleted file mode 100644 index 88ac312..0000000 --- a/build/README.md +++ /dev/null @@ -1 +0,0 @@ -[![Build Status](https://travis-ci.org/auygun/kaliber.svg?branch=master)](https://travis-ci.org/auygun/kaliber) diff --git a/build/android/app/CMakeLists.txt b/build/android/app/CMakeLists.txt index 458d52a..6c58896 100644 --- a/build/android/app/CMakeLists.txt +++ b/build/android/app/CMakeLists.txt @@ -47,10 +47,11 @@ endif () set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") -add_library(native-activity SHARED +add_library(kaliber SHARED ../../../src/base/collusion_test.cc ../../../src/base/log.cc ../../../src/base/random.cc + ../../../src/base/sinc_resampler.cc ../../../src/base/task_runner.cc ../../../src/base/timer.cc ../../../src/base/vecmath.cc @@ -73,16 +74,17 @@ add_library(native-activity SHARED ../../../src/engine/image_quad.cc ../../../src/engine/image.cc ../../../src/engine/mesh.cc + ../../../src/engine/persistent_data.cc ../../../src/engine/platform/asset_file_android.cc ../../../src/engine/platform/asset_file.cc ../../../src/engine/platform/platform_android.cc ../../../src/engine/platform/platform_base.cc ../../../src/engine/renderer/geometry.cc - ../../../src/engine/renderer/render_command.cc + ../../../src/engine/renderer/opengl/render_command.cc + ../../../src/engine/renderer/opengl/renderer_opengl_android.cc + ../../../src/engine/renderer/opengl/renderer_opengl.cc ../../../src/engine/renderer/render_resource.cc - ../../../src/engine/renderer/renderer_android.cc ../../../src/engine/renderer/renderer_types.cc - ../../../src/engine/renderer/renderer.cc ../../../src/engine/renderer/shader.cc ../../../src/engine/renderer/texture.cc ../../../src/engine/shader_source.cc @@ -94,8 +96,6 @@ add_library(native-activity SHARED ../../../src/third_party/jsoncpp/jsoncpp.cc ../../../src/third_party/minizip/ioapi.c ../../../src/third_party/minizip/unzip.c - ../../../src/third_party/r8b/pffft.cpp - ../../../src/third_party/r8b/r8bbase.cpp ../../../src/third_party/texture_compressor/dxt_encoder_internals.cc ../../../src/third_party/texture_compressor/dxt_encoder.cc ../../../src/third_party/texture_compressor/texture_compressor_etc1.cc @@ -103,24 +103,23 @@ add_library(native-activity SHARED ) if (ANDROID_ABI STREQUAL armeabi-v7a) - target_sources(native-activity PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc) - target_sources(native-activity PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc) - set_source_files_properties(../../../src/third_party/r8b/pffft.cpp PROPERTIES COMPILE_FLAGS -mfpu=neon) + target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc) + target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc) set_source_files_properties(../../../src/third_party/texture_compressor/dxt_encoder_neon.cc PROPERTIES COMPILE_FLAGS -mfpu=neon) set_source_files_properties(../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc PROPERTIES COMPILE_FLAGS -mfpu=neon) endif() if (ANDROID_ABI STREQUAL arm64-v8a) - target_sources(native-activity PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc) - target_sources(native-activity PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc) + target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc) + target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc) endif() -target_include_directories(native-activity PRIVATE +target_include_directories(kaliber PRIVATE ${ANDROID_NDK}/sources/android/native_app_glue ) # add lib dependencies -target_link_libraries(native-activity +target_link_libraries(kaliber android native_app_glue oboe diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle index aee51b9..5cd8399 100644 --- a/build/android/app/build.gradle +++ b/build/android/app/build.gradle @@ -2,22 +2,29 @@ apply plugin: 'com.android.application' android { compileSdkVersion 29 + ndkVersion '21.3.6528147' defaultConfig { - applicationId = 'com.example.native_activity' - minSdkVersion 14 - targetSdkVersion 28 + applicationId = 'com.kaliber.demo' + minSdkVersion 21 + targetSdkVersion 29 externalNativeBuild { cmake { arguments '-DANDROID_STL=c++_static' } } + ndk { + abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' + } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + ndk { + debugSymbolLevel 'FULL' + } } } externalNativeBuild { @@ -37,4 +44,5 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'com.google.android.gms:play-services-ads:19.1.0' } diff --git a/build/android/app/src/main/AndroidManifest.xml b/build/android/app/src/main/AndroidManifest.xml index 11dc6af..bd36cb6 100644 --- a/build/android/app/src/main/AndroidManifest.xml +++ b/build/android/app/src/main/AndroidManifest.xml @@ -1,36 +1,53 @@ - + package="com.kaliber.demo" + android:versionCode="1" + android:versionName="1.0"> - + - - + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - diff --git a/build/android/app/src/main/java/com/kaliber/base/KaliberActivity.java b/build/android/app/src/main/java/com/kaliber/base/KaliberActivity.java new file mode 100644 index 0000000..6f21b86 --- /dev/null +++ b/build/android/app/src/main/java/com/kaliber/base/KaliberActivity.java @@ -0,0 +1,132 @@ +package com.kaliber.base; + +import android.app.NativeActivity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.StrictMode; +import android.util.Log; +import android.view.WindowManager; +import android.widget.Toast; + +import androidx.core.content.FileProvider; + +import com.google.android.gms.ads.AdListener; +import com.google.android.gms.ads.AdRequest; +import com.google.android.gms.ads.InterstitialAd; +import com.kaliber.demo.R; + +import java.io.File; + +public class KaliberActivity extends NativeActivity { + private static final Handler sHandler = new Handler(Looper.getMainLooper()); + + static { + // Get the native Java methods bound to exported functions. + System.loadLibrary("kaliber"); + } + + private InterstitialAd mInterstitialAd; + + public static native void onShowAdResult(boolean succeeded); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mInterstitialAd = newInterstitialAd(); + loadInterstitialAd(); + } + + public void setKeepScreenOn(final boolean keepScreenOn) { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (keepScreenOn) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } else { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } + }); + } + + private InterstitialAd newInterstitialAd() { + InterstitialAd interstitialAd = new InterstitialAd(this); + interstitialAd.setAdUnitId(getString(R.string.interstitial_ad_unit_id)); + interstitialAd.setAdListener(new AdListener() { + @Override + public void onAdLoaded() { + Log.w("kaliber", "Ad loaded."); + } + + @Override + public void onAdFailedToLoad(int errorCode) { + Log.w("kaliber", "Ad failed to load. errorCode: " + errorCode); + sHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (!mInterstitialAd.isLoaded()) + loadInterstitialAd(); + } + }, 1000 * 10); + } + + @Override + public void onAdClosed() { + loadInterstitialAd(); + onShowAdResult(true); + } + }); + return interstitialAd; + } + + public void showInterstitialAd() { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (mInterstitialAd.isLoaded()) { + mInterstitialAd.show(); + } else { + loadInterstitialAd(); + onShowAdResult(false); + } + } + }); + } + + public void shareFile(final String fileName) { + runOnUiThread(new Runnable() { + @Override + public void run() { + try { + StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); + StrictMode.setVmPolicy(builder.build()); + + File dir = getExternalFilesDir(null); + File file = new File(dir, fileName); + Uri uri = FileProvider.getUriForFile(KaliberActivity.this, + "com.codepath.fileprovider", file); + + Intent emailIntent = new Intent(); + emailIntent.setAction(Intent.ACTION_SEND); + emailIntent.setType("text/plain"); + emailIntent.putExtra(Intent.EXTRA_STREAM, uri); + startActivity(Intent.createChooser(emailIntent, "Send to...")); + } catch (Throwable t) { + Toast.makeText(KaliberActivity.this, "Request failed: " + t.toString(), + Toast.LENGTH_LONG).show(); + } + } + }); + } + + private void loadInterstitialAd() { + if (!mInterstitialAd.isLoading()) { + AdRequest adRequest = new AdRequest.Builder() + .setRequestAgent("android_studio:ad_template").build(); + mInterstitialAd.loadAd(adRequest); + } + } +} diff --git a/build/android/app/src/main/res/values/strings.xml b/build/android/app/src/main/res/values/strings.xml index d8f5513..102a7ee 100644 --- a/build/android/app/src/main/res/values/strings.xml +++ b/build/android/app/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ - NativeActivity + demo + ca-app-pub-3940256099942544/1033173712 diff --git a/build/android/app/src/main/res/xml/file_paths.xml b/build/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..503aebe --- /dev/null +++ b/build/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,4 @@ + + + + diff --git a/build/android/build.gradle b/build/android/build.gradle index b4f5ce0..c032b58 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:4.1.0' } } diff --git a/build/android/gradle/wrapper/gradle-wrapper.properties b/build/android/gradle/wrapper/gradle-wrapper.properties index 48c4576..401bb9f 100644 --- a/build/android/gradle/wrapper/gradle-wrapper.properties +++ b/build/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Feb 05 19:39:12 IST 2017 +#Thu Oct 15 21:41:36 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/build/android/local.properties b/build/android/local.properties index b03a2b4..9ee0358 100644 --- a/build/android/local.properties +++ b/build/android/local.properties @@ -4,5 +4,6 @@ # Location of the SDK. This is only used by Gradle. # For customization when using a Version Control System, please read the # header note. -#Thu Apr 09 18:23:32 CEST 2020 -sdk.dir=/home/auygun/Android/Sdk +#Fri Oct 16 12:48:26 CEST 2020 +ndk.dir=/home/auygun/spinning/Android/Sdk/ndk/21.3.6528147 +sdk.dir=/home/auygun/spinning/Android/Sdk diff --git a/build/linux/Makefile b/build/linux/Makefile index b102b52..681a1af 100644 --- a/build/linux/Makefile +++ b/build/linux/Makefile @@ -68,6 +68,7 @@ GLTEST_SRC := \ $(SRC_ROOT)/base/collusion_test.cc \ $(SRC_ROOT)/base/log.cc \ $(SRC_ROOT)/base/random.cc \ + $(SRC_ROOT)/base/sinc_resampler.cc \ $(SRC_ROOT)/base/task_runner.cc \ $(SRC_ROOT)/base/timer.cc \ $(SRC_ROOT)/base/vecmath.cc \ @@ -90,16 +91,17 @@ GLTEST_SRC := \ $(SRC_ROOT)/engine/image_quad.cc \ $(SRC_ROOT)/engine/image.cc \ $(SRC_ROOT)/engine/mesh.cc \ + $(SRC_ROOT)/engine/persistent_data.cc \ $(SRC_ROOT)/engine/platform/asset_file_linux.cc \ $(SRC_ROOT)/engine/platform/asset_file.cc \ $(SRC_ROOT)/engine/platform/platform_base.cc \ $(SRC_ROOT)/engine/platform/platform_linux.cc \ $(SRC_ROOT)/engine/renderer/geometry.cc \ - $(SRC_ROOT)/engine/renderer/render_command.cc \ + $(SRC_ROOT)/engine/renderer/opengl/render_command.cc \ $(SRC_ROOT)/engine/renderer/render_resource.cc \ - $(SRC_ROOT)/engine/renderer/renderer_linux.cc \ + $(SRC_ROOT)/engine/renderer/opengl/renderer_opengl_linux.cc \ + $(SRC_ROOT)/engine/renderer/opengl/renderer_opengl.cc \ $(SRC_ROOT)/engine/renderer/renderer_types.cc \ - $(SRC_ROOT)/engine/renderer/renderer.cc \ $(SRC_ROOT)/engine/renderer/shader.cc \ $(SRC_ROOT)/engine/renderer/texture.cc \ $(SRC_ROOT)/engine/shader_source.cc \ @@ -108,8 +110,6 @@ GLTEST_SRC := \ $(SRC_ROOT)/engine/sound.cc \ $(SRC_ROOT)/third_party/glew/glew.c \ $(SRC_ROOT)/third_party/jsoncpp/jsoncpp.cc \ - $(SRC_ROOT)/third_party/r8b/pffft.cpp \ - $(SRC_ROOT)/third_party/r8b/r8bbase.cpp \ $(SRC_ROOT)/third_party/texture_compressor/dxt_encoder_internals.cc \ $(SRC_ROOT)/third_party/texture_compressor/dxt_encoder.cc \ $(SRC_ROOT)/third_party/texture_compressor/texture_compressor_etc1.cc \ diff --git a/build/travis.sh b/build/travis.sh deleted file mode 100644 index 10c048a..0000000 --- a/build/travis.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test -sudo apt-get update -qq - -sudo apt-get install -qq g++-7 -sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 90 - -sudo apt-get install -qq -y libasound2-dev - -sudo apt-get install -qq build-essential xorg-dev libc++-dev diff --git a/privacy.md b/privacy.md new file mode 100644 index 0000000..bfc6539 --- /dev/null +++ b/privacy.md @@ -0,0 +1,65 @@ +**Privacy Policy** + +Attila Uygun built the Woom app as an Ad Supported app. This SERVICE is provided by Attila Uygun at no cost and is intended for use as is. + +This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. + +If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. + +The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Woom unless otherwise defined in this Privacy Policy. + +**Information Collection and Use** + +For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information. The information that I request will be retained on your device and is not collected by me in any way. + +The app does use third party services that may collect information used to identify you. + +Link to privacy policy of third party service providers used by the app + +* [Google Play Services](https://www.google.com/policies/privacy/) +* [AdMob](https://support.google.com/admob/answer/6128543?hl=en) + +**Log Data** + +I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. + +**Cookies** + +Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. + +This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. + +**Service Providers** + +I may employ third-party companies and individuals due to the following reasons: + +* To facilitate our Service; +* To provide the Service on our behalf; +* To perform Service-related services; or +* To assist us in analyzing how our Service is used. + +I want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. + +**Security** + +I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. + +**Links to Other Sites** + +This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. + +**Children’s Privacy** + +These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13\. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do necessary actions. + +**Changes to This Privacy Policy** + +I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. + +This policy is effective as of 2020-10-15 + +**Contact Us** + +If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at coldreboot.se@gmail.com. + +This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.firebaseapp.com/) diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn index 16a9485..6603e69 100644 --- a/src/base/BUILD.gn +++ b/src/base/BUILD.gn @@ -12,6 +12,8 @@ source_set("base") { "misc.h", "random.cc", "random.h", + "sinc_resampler.cc", + "sinc_resampler.h", "task_runner.cc", "task_runner.h", "timer.cc", diff --git a/src/base/closure.h b/src/base/closure.h index 9ae9727..ede8f55 100644 --- a/src/base/closure.h +++ b/src/base/closure.h @@ -2,6 +2,7 @@ #define CLOSURE_H #include +#include #include #include @@ -43,6 +44,16 @@ using Location = std::nullptr_t; #endif +// Bind a method to an object with a std::weak_ptr. +template +std::function BindWeak(ReturnType (Class::*func)(Args...), + std::weak_ptr weak_ptr) { + return [func, weak_ptr](Args... args) { + if (auto ptr = weak_ptr.lock()) + std::invoke(func, ptr, args...); + }; +} + } // namespace base #endif // CLOSURE_H diff --git a/src/base/log.cc b/src/base/log.cc index 16d62b0..226b020 100644 --- a/src/base/log.cc +++ b/src/base/log.cc @@ -6,24 +6,17 @@ #include #endif #include -#include -#include - -#include "vecmath.h" namespace base { -// Adapted from Chromium's logging implementation. // This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have // an object of the correct type on the LHS of the unused part of the ternary // operator. -LogBase* LogBase::swallow_stream; +std::ostream* LogMessage::swallow_stream; -LogBase::LogBase(const char* file, int line) : file_(file), line_(line) {} +LogMessage::LogMessage(const char* file, int line) : file_(file), line_(line) {} -LogBase::~LogBase() = default; - -void LogBase::Flush() { +LogMessage::~LogMessage() { stream_ << std::endl; std::string text(stream_.str()); std::string filename(file_); @@ -38,78 +31,31 @@ void LogBase::Flush() { #endif } -Log::Log(const char* file, int line) : LogBase(file, line) {} - -Log::~Log() { - Flush(); +LogAbort LogAbort::Check(const char* file, int line, const char* expr) { + LogAbort instance(new LogMessage(file, line)); + instance.GetLog().stream() << "CHECK: " + << "(" << expr << ") "; + return instance; } -LogDiff::LogDiff(const char* file, int line) : LogBase(file, line) {} - -LogDiff::~LogDiff() { - static std::unordered_map log_map; - static std::mutex lock; - - auto key = std::string(file_) + std::to_string(line_); - bool flush = true; - { - std::lock_guard scoped_lock(lock); - auto it = log_map.find(key); - if (it == log_map.end()) - log_map[key] = stream_.str(); - else if (it->second != stream_.str()) - it->second = stream_.str(); - else - flush = false; - } - - if (flush) - Flush(); +LogAbort LogAbort::DCheck(const char* file, int line, const char* expr) { + LogAbort instance(new LogMessage(file, line)); + instance.GetLog().stream() << "DCHECK: " + << "(" << expr << ") "; + return instance; } -Check::Check(const char* file, - int line, - bool condition, - bool debug, - const char* expr) - : LogBase(file, line), condition_(condition) { - if (!condition_) - base() << (debug ? "DCHECK: (" : "CHECK: (") << expr << ") "; +LogAbort LogAbort::NotReached(const char* file, int line) { + LogAbort instance(new LogMessage(file, line)); + instance.GetLog().stream() << "NOTREACHED "; + return instance; } -Check::~Check() { - if (!condition_) { - Flush(); - std::abort(); - } -} +LogAbort::LogAbort(LogMessage* log) : log_(log) {} -NotReached::NotReached(const char* file, int line) : LogBase(file, line) { - base() << "NOTREACHED "; -} - -NotReached::~NotReached() { - Flush(); +LogAbort::~LogAbort() { + delete log_; std::abort(); } -template <> -LogBase& operator<<(LogBase& out, const base::Vector2& arg) { - out.stream() << "(" << arg.x << ", " << arg.y << ")"; - return out; -} - -template <> -LogBase& operator<<(LogBase& out, const base::Vector3& arg) { - out.stream() << "(" << arg.x << ", " << arg.y << ", " << arg.z << ")"; - return out; -} - -template <> -LogBase& operator<<(LogBase& out, const base::Vector4& arg) { - out.stream() << "(" << arg.x << ", " << arg.y << ", " << arg.z << ", " - << arg.w << ")"; - return out; -} - } // namespace base diff --git a/src/base/log.h b/src/base/log.h index 1180a7e..2b1b974 100644 --- a/src/base/log.h +++ b/src/base/log.h @@ -3,114 +3,95 @@ #include +// Adapted from Chromium's logging implementation. + // Macros for logging that are active in both debug and release builds. The way // to log things is to stream things to LOG. -// LOG_DIFF can be used to avoid spam and log only if the message differs. +// LOG_IF can be used for conditional logging. // CHECK(condition) terminates the process if the condition is false. // NOTREACHED annotates unreachable codepaths and terminates the process if // reached. -#define LOG base::Log(__FILE__, __LINE__).base() -#define LOG_DIFF base::LogDiff(__FILE__, __LINE__).base() -#define CHECK(expr) \ - base::Check(__FILE__, __LINE__, static_cast(expr), false, #expr).base() -#define NOTREACHED base::NotReached(__FILE__, __LINE__).base() +#define LOG base::LogMessage(__FILE__, __LINE__).stream() +#define LOG_IF(condition) \ + LAZY_STREAM(condition, base::LogMessage(__FILE__, __LINE__).stream()) +#define CHECK(condition) \ + LAZY_STREAM( \ + !(condition), \ + base::LogAbort::Check(__FILE__, __LINE__, #condition).GetLog().stream()) + +#define NOTREACHED \ + base::LogAbort::NotReached(__FILE__, __LINE__).GetLog().stream() // Macros for logging which are active only in debug builds. #ifdef _DEBUG -#define DLOG base::Log(__FILE__, __LINE__).base() -#define DLOG_DIFF base::LogDiff(__FILE__, __LINE__).base() -#define DCHECK(expr) \ - base::Check(__FILE__, __LINE__, static_cast(expr), true, #expr).base() +#define DLOG base::LogMessage(__FILE__, __LINE__).stream() +#define DLOG_IF(condition) \ + LAZY_STREAM(condition, base::LogMessage(__FILE__, __LINE__).stream()) +#define DCHECK(condition) \ + LAZY_STREAM(!(condition), \ + base::LogAbort::DCheck(__FILE__, __LINE__, #condition) \ + .GetLog() \ + .stream()) #else // "debug mode" logging is compiled away to nothing for release builds. #define DLOG EAT_STREAM_PARAMETERS -#define DLOG_DIFF EAT_STREAM_PARAMETERS -#define DCHECK(expr) EAT_STREAM_PARAMETERS +#define DLOG_IF(condition) EAT_STREAM_PARAMETERS +#define DCHECK(condition) EAT_STREAM_PARAMETERS #endif -// Adapted from Chromium's logging implementation. +// Helper macro which avoids evaluating the arguments to a stream if +// the condition doesn't hold. +#define LAZY_STREAM(condition, stream) \ + !(condition) ? (void)0 : base::LogMessage::Voidify() & (stream) + // Avoid any pointless instructions to be emitted by the compiler. #define EAT_STREAM_PARAMETERS \ - true ? (void)0 : base::LogBase::Voidify() & (*base::LogBase::swallow_stream) + LAZY_STREAM(false, *base::LogMessage::swallow_stream) namespace base { -struct Vector2; -struct Vector3; -struct Vector4; - -class LogBase { +class LogMessage { public: class Voidify { public: Voidify() = default; + // This has to be an operator with a precedence lower than << but // higher than ?: - void operator&(LogBase&) {} + void operator&(std::ostream&) {} }; - LogBase& base() { return *this; } + LogMessage(const char* file, int line); + ~LogMessage(); + + LogMessage& base() { return *this; } std::ostream& stream() { return stream_; } - static LogBase* swallow_stream; + static std::ostream* swallow_stream; protected: const char* file_; const int line_; std::ostringstream stream_; - - LogBase(const char* file, int line); - ~LogBase(); - - void Flush(); }; -class Log : public LogBase { +class LogAbort { public: - Log(const char* file, int line); - ~Log(); -}; + ~LogAbort(); -class LogDiff : public LogBase { - public: - LogDiff(const char* file, int line); - ~LogDiff(); -}; + static LogAbort Check(const char* file, int line, const char* expr); + static LogAbort DCheck(const char* file, int line, const char* expr); + static LogAbort NotReached(const char* file, int line); -class Check : public LogBase { - public: - Check(const char* file, - int line, - bool condition, - bool debug, - const char* expr); - ~Check(); + LogMessage& GetLog() { return *log_; } private: - bool condition_; + LogMessage* log_; + + LogAbort(LogMessage* log); }; -class NotReached : public LogBase { - public: - NotReached(const char* file, int line); - ~NotReached(); -}; - -template -LogBase& operator<<(LogBase& out, const T& arg) { - out.stream() << arg; - return out; -} - -// Explicit specialization for internal types. -template <> -LogBase& operator<<(LogBase& out, const base::Vector2& arg); -template <> -LogBase& operator<<(LogBase& out, const base::Vector3& arg); -template <> -LogBase& operator<<(LogBase& out, const base::Vector4& arg); - } // namespace base #endif // LOG_H diff --git a/src/base/random.cc b/src/base/random.cc index c9c7066..ac0a90e 100644 --- a/src/base/random.cc +++ b/src/base/random.cc @@ -3,24 +3,35 @@ #include #include "interpolation.h" +#include "log.h" namespace base { Random::Random() { std::random_device rd; - generator_ = std::mt19937(rd()); - real_distribution_ = std::uniform_real_distribution(0, 1); + seed_ = rd(); + DLOG << "Random seed: " << seed_; + Initialize(); } Random::Random(unsigned seed) { - generator_ = std::mt19937(seed); - real_distribution_ = std::uniform_real_distribution(0, 1); + seed_ = seed; + Initialize(); } Random::~Random() = default; +float Random::GetFloat() { + return real_distribution_(generator_); +} + int Random::Roll(int sides) { return Lerp(1, sides, GetFloat()); } +void Random::Initialize() { + generator_ = std::mt19937(seed_); + real_distribution_ = std::uniform_real_distribution(0, 1); +} + } // namespace base diff --git a/src/base/random.h b/src/base/random.h index aab1f10..13a4bef 100644 --- a/src/base/random.h +++ b/src/base/random.h @@ -12,14 +12,19 @@ class Random { ~Random(); // Returns a random float between 0 and 1. - float GetFloat() { return real_distribution_(generator_); } + float GetFloat(); // Roll dice with the given number of sides. int Roll(int sides); + unsigned seed() const { return seed_; } + private: + unsigned seed_ = 0; std::mt19937 generator_; std::uniform_real_distribution real_distribution_; + + void Initialize(); }; } // namespace base diff --git a/src/base/semaphore.h b/src/base/semaphore.h index 607714a..586ee55 100644 --- a/src/base/semaphore.h +++ b/src/base/semaphore.h @@ -4,6 +4,8 @@ #include #include +#include "../base/log.h" + namespace base { class Semaphore { @@ -14,6 +16,7 @@ class Semaphore { std::unique_lock scoped_lock(mutex_); cv_.wait(scoped_lock, [&]() { return count_ > 0; }); --count_; + DCHECK(count_ >= 0); } void Release() { diff --git a/src/base/sinc_resampler.cc b/src/base/sinc_resampler.cc new file mode 100644 index 0000000..bde1991 --- /dev/null +++ b/src/base/sinc_resampler.cc @@ -0,0 +1,446 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Initial input buffer layout, dividing into regions r0_ to r4_ (note: r0_, r3_ +// and r4_ will move after the first load): +// +// |----------------|-----------------------------------------|----------------| +// +// request_frames_ +// <---------------------------------------------------------> +// r0_ (during first load) +// +// kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 +// <---------------> <---------------> <---------------> <---------------> +// r1_ r2_ r3_ r4_ +// +// block_size_ == r4_ - r2_ +// <---------------------------------------> +// +// request_frames_ +// <------------------ ... -----------------> +// r0_ (during second load) +// +// On the second request r0_ slides to the right by kKernelSize / 2 and r3_, r4_ +// and block_size_ are reinitialized via step (3) in the algorithm below. +// +// These new regions remain constant until a Flush() occurs. While complicated, +// this allows us to reduce jitter by always requesting the same amount from the +// provided callback. +// +// The algorithm: +// +// 1) Allocate input_buffer of size: request_frames_ + kKernelSize; this ensures +// there's enough room to read request_frames_ from the callback into region +// r0_ (which will move between the first and subsequent passes). +// +// 2) Let r1_, r2_ each represent half the kernel centered around r0_: +// +// r0_ = input_buffer_ + kKernelSize / 2 +// r1_ = input_buffer_ +// r2_ = r0_ +// +// r0_ is always request_frames_ in size. r1_, r2_ are kKernelSize / 2 in +// size. r1_ must be zero initialized to avoid convolution with garbage (see +// step (5) for why). +// +// 3) Let r3_, r4_ each represent half the kernel right aligned with the end of +// r0_ and choose block_size_ as the distance in frames between r4_ and r2_: +// +// r3_ = r0_ + request_frames_ - kKernelSize +// r4_ = r0_ + request_frames_ - kKernelSize / 2 +// block_size_ = r4_ - r2_ = request_frames_ - kKernelSize / 2 +// +// 4) Consume request_frames_ frames into r0_. +// +// 5) Position kernel centered at start of r2_ and generate output frames until +// the kernel is centered at the start of r4_ or we've finished generating +// all the output frames. +// +// 6) Wrap left over data from the r3_ to r1_ and r4_ to r2_. +// +// 7) If we're on the second load, in order to avoid overwriting the frames we +// just wrapped from r4_ we need to slide r0_ to the right by the size of +// r4_, which is kKernelSize / 2: +// +// r0_ = r0_ + kKernelSize / 2 = input_buffer_ + kKernelSize +// +// r3_, r4_, and block_size_ then need to be reinitialized, so goto (3). +// +// 8) Else, if we're not on the second load, goto (4). +// +// Note: we're glossing over how the sub-sample handling works with +// |virtual_source_idx_|, etc. + +#include "sinc_resampler.h" + +#include +#include +#include + +#include "log.h" + +#if defined(_M_X64) || defined(__x86_64__) || defined(__i386__) +#include +#define CONVOLVE_FUNC Convolve_SSE +#elif defined(_M_ARM64) || defined(__aarch64__) +#include +#define CONVOLVE_FUNC Convolve_NEON +#else +#define CONVOLVE_FUNC Convolve_C +#endif + +namespace { + +constexpr double kPiDouble = 3.14159265358979323846; +constexpr float kPiFloat = 3.14159265358979323846f; + +class ScopedSubnormalFloatDisabler { + public: + ScopedSubnormalFloatDisabler() { +#if defined(_M_X64) || defined(__x86_64__) || defined(__i386__) + // Turn on "subnormals are zero" and "flush to zero" CSR flags. + orig_state_ = _mm_getcsr(); + _mm_setcsr(orig_state_ | 0x8040); +#endif + } + + ScopedSubnormalFloatDisabler(const ScopedSubnormalFloatDisabler&) = delete; + + ~ScopedSubnormalFloatDisabler() { +#if defined(ARCH_CPU_X86_FAMILY) + _mm_setcsr(orig_state_); +#endif + } + + ScopedSubnormalFloatDisabler& operator=(const ScopedSubnormalFloatDisabler&) = + delete; + +#if defined(_M_X64) || defined(__x86_64__) || defined(__i386__) + private: + unsigned int orig_state_; +#endif +}; + +double SincScaleFactor(double io_ratio) { + // |sinc_scale_factor| is basically the normalized cutoff frequency of the + // low-pass filter. + double sinc_scale_factor = io_ratio > 1.0 ? 1.0 / io_ratio : 1.0; + + // The sinc function is an idealized brick-wall filter, but since we're + // windowing it the transition from pass to stop does not happen right away. + // So we should adjust the low pass filter cutoff slightly downward to avoid + // some aliasing at the very high-end. + // TODO(crogers): this value is empirical and to be more exact should vary + // depending on kKernelSize. + sinc_scale_factor *= 0.9; + + return sinc_scale_factor; +} + +int CalculateChunkSize(int block_size_, double io_ratio) { + return block_size_ / io_ratio; +} + +} // namespace + +namespace base { + +SincResampler::SincResampler(double io_sample_rate_ratio, int request_frames) + : io_sample_rate_ratio_(io_sample_rate_ratio), + request_frames_(request_frames), + input_buffer_size_(request_frames_ + kKernelSize), + // Create input buffers with a 16-byte alignment for SSE optimizations. + kernel_storage_(static_cast( + base::AlignedAlloc<16>(sizeof(float) * kKernelStorageSize))), + kernel_pre_sinc_storage_(static_cast( + base::AlignedAlloc<16>(sizeof(float) * kKernelStorageSize))), + kernel_window_storage_(static_cast( + base::AlignedAlloc<16>(sizeof(float) * kKernelStorageSize))), + input_buffer_(static_cast( + base::AlignedAlloc<16>(sizeof(float) * input_buffer_size_))), + r1_(input_buffer_.get()), + r2_(input_buffer_.get() + kKernelSize / 2) { + DCHECK(request_frames_ > 0); + Flush(); + DCHECK(block_size_ > kKernelSize) + << "block_size must be greater than kKernelSize!"; + + memset(kernel_storage_.get(), 0, + sizeof(*kernel_storage_.get()) * kKernelStorageSize); + memset(kernel_pre_sinc_storage_.get(), 0, + sizeof(*kernel_pre_sinc_storage_.get()) * kKernelStorageSize); + memset(kernel_window_storage_.get(), 0, + sizeof(*kernel_window_storage_.get()) * kKernelStorageSize); + + InitializeKernel(); +} + +SincResampler::~SincResampler() = default; + +void SincResampler::UpdateRegions(bool second_load) { + // Setup various region pointers in the buffer (see diagram above). If we're + // on the second load we need to slide r0_ to the right by kKernelSize / 2. + r0_ = input_buffer_.get() + (second_load ? kKernelSize : kKernelSize / 2); + r3_ = r0_ + request_frames_ - kKernelSize; + r4_ = r0_ + request_frames_ - kKernelSize / 2; + block_size_ = r4_ - r2_; + chunk_size_ = CalculateChunkSize(block_size_, io_sample_rate_ratio_); + + // r1_ at the beginning of the buffer. + DCHECK(r1_ == input_buffer_.get()); + // r1_ left of r2_, r4_ left of r3_ and size correct. + DCHECK(r2_ - r1_ == r4_ - r3_); + // r2_ left of r3. + DCHECK(r2_ < r3_); +} + +void SincResampler::InitializeKernel() { + // Blackman window parameters. + static const double kAlpha = 0.16; + static const double kA0 = 0.5 * (1.0 - kAlpha); + static const double kA1 = 0.5; + static const double kA2 = 0.5 * kAlpha; + + // Generates a set of windowed sinc() kernels. + // We generate a range of sub-sample offsets from 0.0 to 1.0. + const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_); + for (int offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) { + const float subsample_offset = + static_cast(offset_idx) / kKernelOffsetCount; + + for (int i = 0; i < kKernelSize; ++i) { + const int idx = i + offset_idx * kKernelSize; + const float pre_sinc = + kPiFloat * (i - kKernelSize / 2 - subsample_offset); + kernel_pre_sinc_storage_[idx] = pre_sinc; + + // Compute Blackman window, matching the offset of the sinc(). + const float x = (i - subsample_offset) / kKernelSize; + const float window = + static_cast(kA0 - kA1 * cos(2.0 * kPiDouble * x) + + kA2 * cos(4.0 * kPiDouble * x)); + kernel_window_storage_[idx] = window; + + // Compute the sinc with offset, then window the sinc() function and store + // at the correct offset. + kernel_storage_[idx] = static_cast( + window * (pre_sinc ? sin(sinc_scale_factor * pre_sinc) / pre_sinc + : sinc_scale_factor)); + } + } +} + +void SincResampler::SetRatio(double io_sample_rate_ratio) { + if (fabs(io_sample_rate_ratio_ - io_sample_rate_ratio) < + std::numeric_limits::epsilon()) { + return; + } + + io_sample_rate_ratio_ = io_sample_rate_ratio; + chunk_size_ = CalculateChunkSize(block_size_, io_sample_rate_ratio_); + + // Optimize reinitialization by reusing values which are independent of + // |sinc_scale_factor|. Provides a 3x speedup. + const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_); + for (int offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) { + for (int i = 0; i < kKernelSize; ++i) { + const int idx = i + offset_idx * kKernelSize; + const float window = kernel_window_storage_[idx]; + const float pre_sinc = kernel_pre_sinc_storage_[idx]; + + kernel_storage_[idx] = static_cast( + window * (pre_sinc ? sin(sinc_scale_factor * pre_sinc) / pre_sinc + : sinc_scale_factor)); + } + } +} + +void SincResampler::Resample(int frames, float* destination, ReadCB read_cb) { + int remaining_frames = frames; + + // Step (1) -- Prime the input buffer at the start of the input stream. + if (!buffer_primed_ && remaining_frames) { + read_cb(request_frames_, r0_); + buffer_primed_ = true; + } + + // Step (2) -- Resample! + while (remaining_frames) { + // Silent audio can contain non-zero samples small enough to result in + // subnormals internally. Disabling subnormals can be significantly faster. + { + ScopedSubnormalFloatDisabler disable_subnormals; + + while (virtual_source_idx_ < block_size_) { + // |virtual_source_idx_| lies in between two kernel offsets so figure + // out what they are. + const int source_idx = static_cast(virtual_source_idx_); + const double virtual_offset_idx = + (virtual_source_idx_ - source_idx) * kKernelOffsetCount; + const int offset_idx = static_cast(virtual_offset_idx); + + // We'll compute "convolutions" for the two kernels which straddle + // |virtual_source_idx_|. + const float* k1 = kernel_storage_.get() + offset_idx * kKernelSize; + const float* k2 = k1 + kKernelSize; + + // Ensure |k1|, |k2| are 16-byte aligned for SIMD usage. Should always + // be true so long as kKernelSize is a multiple of 16. + DCHECK(0u == (reinterpret_cast(k1) & 0x0F)); + DCHECK(0u == (reinterpret_cast(k2) & 0x0F)); + + // Initialize input pointer based on quantized |virtual_source_idx_|. + const float* input_ptr = r1_ + source_idx; + + // Figure out how much to weight each kernel's "convolution". + const double kernel_interpolation_factor = + virtual_offset_idx - offset_idx; + *destination++ = + CONVOLVE_FUNC(input_ptr, k1, k2, kernel_interpolation_factor); + + // Advance the virtual index. + virtual_source_idx_ += io_sample_rate_ratio_; + if (!--remaining_frames) + return; + } + } + + // Wrap back around to the start. + DCHECK(virtual_source_idx_ > block_size_); + virtual_source_idx_ -= block_size_; + + // Step (3) -- Copy r3_, r4_ to r1_, r2_. + // This wraps the last input frames back to the start of the buffer. + memcpy(r1_, r3_, sizeof(*input_buffer_.get()) * kKernelSize); + + // Step (4) -- Reinitialize regions if necessary. + if (r0_ == r2_) + UpdateRegions(true); + + // Step (5) -- Refresh the buffer with more input. + read_cb(request_frames_, r0_); + } +} + +void SincResampler::PrimeWithSilence() { + // By enforcing the buffer hasn't been primed, we ensure the input buffer has + // already been zeroed during construction or by a previous Flush() call. + DCHECK(!buffer_primed_); + DCHECK(input_buffer_[0] == 0.0f); + UpdateRegions(true); +} + +void SincResampler::Flush() { + virtual_source_idx_ = 0; + buffer_primed_ = false; + memset(input_buffer_.get(), 0, + sizeof(*input_buffer_.get()) * input_buffer_size_); + UpdateRegions(false); +} + +int SincResampler::GetMaxInputFramesRequested( + int output_frames_requested) const { + const int num_chunks = static_cast( + std::ceil(static_cast(output_frames_requested) / chunk_size_)); + + return num_chunks * request_frames_; +} + +double SincResampler::BufferedFrames() const { + return buffer_primed_ ? request_frames_ - virtual_source_idx_ : 0; +} + +float SincResampler::Convolve_C(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor) { + float sum1 = 0; + float sum2 = 0; + + // Generate a single output sample. Unrolling this loop hurt performance in + // local testing. + int n = kKernelSize; + while (n--) { + sum1 += *input_ptr * *k1++; + sum2 += *input_ptr++ * *k2++; + } + + // Linearly interpolate the two "convolutions". + return static_cast((1.0 - kernel_interpolation_factor) * sum1 + + kernel_interpolation_factor * sum2); +} + +#if defined(_M_X64) || defined(__x86_64__) || defined(__i386__) +float SincResampler::Convolve_SSE(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor) { + __m128 m_input; + __m128 m_sums1 = _mm_setzero_ps(); + __m128 m_sums2 = _mm_setzero_ps(); + + // Based on |input_ptr| alignment, we need to use loadu or load. Unrolling + // these loops hurt performance in local testing. + if (reinterpret_cast(input_ptr) & 0x0F) { + for (int i = 0; i < kKernelSize; i += 4) { + m_input = _mm_loadu_ps(input_ptr + i); + m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i))); + m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i))); + } + } else { + for (int i = 0; i < kKernelSize; i += 4) { + m_input = _mm_load_ps(input_ptr + i); + m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i))); + m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i))); + } + } + + // Linearly interpolate the two "convolutions". + m_sums1 = _mm_mul_ps( + m_sums1, + _mm_set_ps1(static_cast(1.0 - kernel_interpolation_factor))); + m_sums2 = _mm_mul_ps( + m_sums2, _mm_set_ps1(static_cast(kernel_interpolation_factor))); + m_sums1 = _mm_add_ps(m_sums1, m_sums2); + + // Sum components together. + float result; + m_sums2 = _mm_add_ps(_mm_movehl_ps(m_sums1, m_sums1), m_sums1); + _mm_store_ss(&result, + _mm_add_ss(m_sums2, _mm_shuffle_ps(m_sums2, m_sums2, 1))); + + return result; +} +#elif defined(_M_ARM64) || defined(__aarch64__) +float SincResampler::Convolve_NEON(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor) { + float32x4_t m_input; + float32x4_t m_sums1 = vmovq_n_f32(0); + float32x4_t m_sums2 = vmovq_n_f32(0); + + const float* upper = input_ptr + kKernelSize; + for (; input_ptr < upper;) { + m_input = vld1q_f32(input_ptr); + input_ptr += 4; + m_sums1 = vmlaq_f32(m_sums1, m_input, vld1q_f32(k1)); + k1 += 4; + m_sums2 = vmlaq_f32(m_sums2, m_input, vld1q_f32(k2)); + k2 += 4; + } + + // Linearly interpolate the two "convolutions". + m_sums1 = vmlaq_f32( + vmulq_f32(m_sums1, vmovq_n_f32(1.0 - kernel_interpolation_factor)), + m_sums2, vmovq_n_f32(kernel_interpolation_factor)); + + // Sum components together. + float32x2_t m_half = vadd_f32(vget_high_f32(m_sums1), vget_low_f32(m_sums1)); + return vget_lane_f32(vpadd_f32(m_half, m_half), 0); +} +#endif + +} // namespace base diff --git a/src/base/sinc_resampler.h b/src/base/sinc_resampler.h new file mode 100644 index 0000000..b6102e1 --- /dev/null +++ b/src/base/sinc_resampler.h @@ -0,0 +1,154 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SINC_RESAMPLER_H_ +#define SINC_RESAMPLER_H_ + +#include +#include + +#include "mem.h" + +namespace base { + +// SincResampler is a high-quality single-channel sample-rate converter. +class SincResampler { + public: + enum { + // The kernel size can be adjusted for quality (higher is better) at the + // expense of performance. Must be a multiple of 32. + // TODO(dalecurtis): Test performance to see if we can jack this up to 64+. + kKernelSize = 32, + + // Default request size. Affects how often and for how much SincResampler + // calls back for input. Must be greater than kKernelSize. + kDefaultRequestSize = 512, + + // The kernel offset count is used for interpolation and is the number of + // sub-sample kernel shifts. Can be adjusted for quality (higher is better) + // at the expense of allocating more memory. + kKernelOffsetCount = 32, + kKernelStorageSize = kKernelSize * (kKernelOffsetCount + 1), + }; + + // Callback type for providing more data into the resampler. Expects |frames| + // of data to be rendered into |destination|; zero padded if not enough frames + // are available to satisfy the request. + typedef std::function ReadCB; + + // Constructs a SincResampler. |io_sample_rate_ratio| is the ratio + // of input / output sample rates. |request_frames| controls the size in + // frames of the buffer requested by each |read_cb| call. The value must be + // greater than kKernelSize. Specify kDefaultRequestSize if there are no + // request size constraints. + SincResampler(double io_sample_rate_ratio, int request_frames); + ~SincResampler(); + + // Resample |frames| of data from |read_cb| into |destination|. + void Resample(int frames, float* destination, ReadCB read_cb); + + // The maximum size in frames that guarantees Resample() will only make a + // single call to |read_cb_| for more data. Note: If PrimeWithSilence() is + // not called, chunk size will grow after the first two Resample() calls by + // kKernelSize / (2 * io_sample_rate_ratio). See the .cc file for details. + int ChunkSize() const { return chunk_size_; } + + // Returns the max number of frames that could be requested (via multiple + // calls to |read_cb_|) during one Resample(|output_frames_requested|) call. + int GetMaxInputFramesRequested(int output_frames_requested) const; + + // Guarantees that ChunkSize() will not change between calls by initializing + // the input buffer with silence. Note, this will cause the first few samples + // of output to be biased towards silence. Must be called again after Flush(). + void PrimeWithSilence(); + + // Flush all buffered data and reset internal indices. Not thread safe, do + // not call while Resample() is in progress. Note, if PrimeWithSilence() was + // previously called it must be called again after the Flush(). + void Flush(); + + // Update |io_sample_rate_ratio_|. SetRatio() will cause a reconstruction of + // the kernels used for resampling. Not thread safe, do not call while + // Resample() is in progress. + void SetRatio(double io_sample_rate_ratio); + + float* get_kernel_for_testing() { return kernel_storage_.get(); } + + // Return number of input frames consumed by a callback but not yet processed. + // Since input/output ratio can be fractional, so can this value. + // Zero before first call to Resample(). + double BufferedFrames() const; + + private: + void InitializeKernel(); + void UpdateRegions(bool second_load); + + // Compute convolution of |k1| and |k2| over |input_ptr|, resultant sums are + // linearly interpolated using |kernel_interpolation_factor|. On x86, the + // underlying implementation is chosen at run time based on SSE support. On + // ARM, NEON support is chosen at compile time based on compilation flags. + static float Convolve_C(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor); +#if defined(_M_X64) || defined(__x86_64__) || defined(__i386__) + static float Convolve_SSE(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor); +#elif defined(_M_ARM64) || defined(__aarch64__) + static float Convolve_NEON(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor); +#endif + + // The ratio of input / output sample rates. + double io_sample_rate_ratio_; + + // An index on the source input buffer with sub-sample precision. It must be + // double precision to avoid drift. + double virtual_source_idx_; + + // The buffer is primed once at the very beginning of processing. + bool buffer_primed_; + + // The size (in samples) to request from each |read_cb_| execution. + const int request_frames_; + + // The number of source frames processed per pass. + int block_size_; + + // Cached value used for ChunkSize(). The maximum size in frames that + // guarantees Resample() will only ask for input at most once. + int chunk_size_; + + // The size (in samples) of the internal buffer used by the resampler. + const int input_buffer_size_; + + // Contains kKernelOffsetCount kernels back-to-back, each of size kKernelSize. + // The kernel offsets are sub-sample shifts of a windowed sinc shifted from + // 0.0 to 1.0 sample. + base::AlignedMemPtr kernel_storage_; + base::AlignedMemPtr kernel_pre_sinc_storage_; + base::AlignedMemPtr kernel_window_storage_; + + // Data from the source is copied into this buffer for each processing pass. + base::AlignedMemPtr input_buffer_; + + // Pointers to the various regions inside |input_buffer_|. See the diagram at + // the top of the .cc file for more information. + float* r0_; + float* const r1_; + float* const r2_; + float* r3_; + float* r4_; + + SincResampler(SincResampler const&) = delete; + SincResampler& operator=(SincResampler const&) = delete; +}; + +} // namespace base + +#endif // SINC_RESAMPLER_H_ diff --git a/src/base/task_runner.cc b/src/base/task_runner.cc index 3dc9d50..b774bc3 100644 --- a/src/base/task_runner.cc +++ b/src/base/task_runner.cc @@ -4,14 +4,14 @@ namespace { -void EnqueueTaskAndReplyRelay(const base::Location& from, - base::Closure task_cb, - base::Closure reply_cb, - base::TaskRunner* destination) { +void PostTaskAndReplyRelay(base::Location from, + base::Closure task_cb, + base::Closure reply_cb, + base::TaskRunner* destination) { task_cb(); if (reply_cb) - destination->EnqueueTask(from, std::move(reply_cb)); + destination->PostTask(from, std::move(reply_cb)); } } // namespace @@ -30,25 +30,24 @@ TaskRunner* TaskRunner::GetThreadLocalTaskRunner() { return thread_local_task_runner.get(); } -void TaskRunner::EnqueueTask(const Location& from, Closure task) { +void TaskRunner::PostTask(const Location& from, Closure task) { DCHECK(task) << LOCATION(from); + task_count_.fetch_add(1, std::memory_order_relaxed); std::lock_guard scoped_lock(lock_); queue_.emplace_back(from, std::move(task)); } -void TaskRunner::EnqueueTaskAndReply(const Location& from, - Closure task, - Closure reply) { +void TaskRunner::PostTaskAndReply(const Location& from, + Closure task, + Closure reply) { DCHECK(task) << LOCATION(from); DCHECK(reply) << LOCATION(from); DCHECK(thread_local_task_runner) << LOCATION(from); - auto relay = std::bind(::EnqueueTaskAndReplyRelay, from, std::move(task), + auto relay = std::bind(::PostTaskAndReplyRelay, from, std::move(task), std::move(reply), thread_local_task_runner.get()); - - std::lock_guard scoped_lock(lock_); - queue_.emplace_back(from, std::move(relay)); + PostTask(from, std::move(relay)); } void TaskRunner::MultiConsumerRun() { @@ -69,6 +68,7 @@ void TaskRunner::MultiConsumerRun() { #endif task_cb(); + task_count_.fetch_sub(1, std::memory_order_relaxed); } } @@ -90,7 +90,16 @@ void TaskRunner::SingleConsumerRun() { #endif task_cb(); + task_count_.fetch_sub(1, std::memory_order_relaxed); } + cv_.notify_one(); +} + +void TaskRunner::WaitForCompletion() { + std::unique_lock scoped_lock(lock_); + cv_.wait(scoped_lock, [&]() -> bool { + return task_count_.load(std::memory_order_relaxed) == 0; + }); } } // namespace base diff --git a/src/base/task_runner.h b/src/base/task_runner.h index 3c46af7..581c008 100644 --- a/src/base/task_runner.h +++ b/src/base/task_runner.h @@ -1,6 +1,8 @@ #ifndef TASK_RUNNER_H #define TASK_RUNNER_H +#include +#include #include #include #include @@ -25,7 +27,7 @@ void ReturnAsParamAdapter(std::function func, template void ReplyAdapter(std::function callback, ReturnType* result) { - callback(std::move(*result)); + callback(*result); delete result; } @@ -41,16 +43,16 @@ class TaskRunner { TaskRunner() = default; ~TaskRunner() = default; - void EnqueueTask(const Location& from, Closure task); + void PostTask(const Location& from, Closure task); - void EnqueueTaskAndReply(const Location& from, Closure task, Closure reply); + void PostTaskAndReply(const Location& from, Closure task, Closure reply); template - void EnqueueTaskAndReplyWithResult(const Location& from, - std::function task, - std::function reply) { + void PostTaskAndReplyWithResult(const Location& from, + std::function task, + std::function reply) { auto* result = new ReturnType; - return EnqueueTaskAndReply( + return PostTaskAndReply( from, std::bind(internal::ReturnAsParamAdapter, std::move(task), result), @@ -62,6 +64,8 @@ class TaskRunner { void SingleConsumerRun(); + void WaitForCompletion(); + static void CreateThreadLocalTaskRunner(); static TaskRunner* GetThreadLocalTaskRunner(); @@ -70,6 +74,8 @@ class TaskRunner { std::deque queue_; mutable std::mutex lock_; + std::condition_variable cv_; + std::atomic task_count_{0}; static thread_local std::unique_ptr thread_local_task_runner; diff --git a/src/base/timer.cc b/src/base/timer.cc index e30b30f..90fc86b 100644 --- a/src/base/timer.cc +++ b/src/base/timer.cc @@ -1,5 +1,7 @@ #include "timer.h" +#include + namespace base { Timer::Timer() { @@ -25,4 +27,20 @@ void Timer::Update() { seconds_accumulated_ += seconds_passed_; } +void Timer::Sleep(float duration) { + Timer timer; + float accumulator = 0.0; + constexpr float epsilon = 0.0001f; + + while (accumulator < duration) { + timer.Update(); + accumulator += timer.GetSecondsPassed(); + if (duration - accumulator > epsilon) { + float sleep_time = duration - accumulator - epsilon; + std::this_thread::sleep_for( + std::chrono::microseconds((int)(sleep_time * 1000000.0f))); + } + }; +} + } // namespace base diff --git a/src/base/timer.h b/src/base/timer.h index 24d222f..569e8ff 100644 --- a/src/base/timer.h +++ b/src/base/timer.h @@ -14,6 +14,8 @@ class Timer { void Update(); + static void Sleep(float duration); + float GetSecondsPassed() const { return seconds_passed_; } float GetSecondsAccumulated() const { return seconds_accumulated_; } diff --git a/src/base/vecmath.cc b/src/base/vecmath.cc index 126106b..6f012a1 100644 --- a/src/base/vecmath.cc +++ b/src/base/vecmath.cc @@ -1,7 +1,13 @@ #include "vecmath.h" +using namespace std::string_literals; + namespace base { +std::string Vector2::ToString() { + return "("s + std::to_string(x) + ", "s + std::to_string(y) + ")"s; +} + Matrix4x4 Ortho(float left, float right, float bottom, float top) { Matrix4x4 m(1); m.c[0].x = 2.0f / (right - left); diff --git a/src/base/vecmath.h b/src/base/vecmath.h index 6649a3d..8310d40 100644 --- a/src/base/vecmath.h +++ b/src/base/vecmath.h @@ -3,6 +3,7 @@ #include #include +#include namespace base { @@ -64,6 +65,8 @@ struct Vector2 { } const float* GetData() const { return &x; } + + std::string ToString(); }; inline Vector2 operator+(const Vector2& v1, const Vector2& v2) { @@ -130,7 +133,7 @@ struct Vector4 { }; inline Vector4 operator*(const Vector4& v1, const Vector4& v2) { - return Vector4(v1.x * v2.x, v2.y * v2.y, v1.z * v2.z, v1.w * v2.w); + return Vector4(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z, v1.w * v2.w); } inline Vector4 operator*(const Vector4& v, float s) { diff --git a/src/base/worker.cc b/src/base/worker.cc index b3beb99..910647f 100644 --- a/src/base/worker.cc +++ b/src/base/worker.cc @@ -40,19 +40,19 @@ void Worker::Shutdown() { threads_.clear(); } -void Worker::EnqueueTask(const Location& from, Closure task) { +void Worker::PostTask(const Location& from, Closure task) { DCHECK((!threads_.empty())); - task_runner_.EnqueueTask(from, std::move(task)); + task_runner_.PostTask(from, std::move(task)); semaphore_.Release(); } -void Worker::EnqueueTaskAndReply(const Location& from, - Closure task, - Closure reply) { +void Worker::PostTaskAndReply(const Location& from, + Closure task, + Closure reply) { DCHECK((!threads_.empty())); - task_runner_.EnqueueTaskAndReply(from, std::move(task), std::move(reply)); + task_runner_.PostTaskAndReply(from, std::move(task), std::move(reply)); semaphore_.Release(); } diff --git a/src/base/worker.h b/src/base/worker.h index d9e5cc8..97761b3 100644 --- a/src/base/worker.h +++ b/src/base/worker.h @@ -26,16 +26,16 @@ class Worker { void Shutdown(); - void EnqueueTask(const Location& from, Closure task); + void PostTask(const Location& from, Closure task); - void EnqueueTaskAndReply(const Location& from, Closure task, Closure reply); + void PostTaskAndReply(const Location& from, Closure task, Closure reply); template - void EnqueueTaskAndReplyWithResult(const Location& from, - std::function task, - std::function reply) { - task_runner_.EnqueueTaskAndReplyWithResult(from, std::move(task), - std::move(reply)); + void PostTaskAndReplyWithResult(const Location& from, + std::function task, + std::function reply) { + task_runner_.PostTaskAndReplyWithResult(from, std::move(task), + std::move(reply)); semaphore_.Release(); } diff --git a/src/demo/credits.cc b/src/demo/credits.cc index f688948..5c5e55c 100644 --- a/src/demo/credits.cc +++ b/src/demo/credits.cc @@ -47,10 +47,6 @@ bool Credits::Initialize() { return true; } -void Credits::Update(float delta_time) { - text_animator_.Update(delta_time); -} - void Credits::OnInputEvent(std::unique_ptr event) { if ((event->GetType() == InputEvent::kDragEnd || event->GetType() == InputEvent::kNavigateBack) && @@ -67,19 +63,19 @@ void Credits::Show() { for (int i = 0; i < kNumLines; ++i) { text_[i].Create("credits", {1, kNumLines}); text_[i].SetZOrder(50); - text_[i].SetOffset({0, 0}); + text_[i].SetPosition({0, 0}); text_[i].SetColor(kTextColor * Vector4(1, 1, 1, 0)); text_[i].SetFrame(i); if (i > 0) { text_[i].PlaceToBottomOf(text_[i - 1]); - text_[i].Translate(text_[i - 1].GetOffset() * Vector2(0, 1)); - text_[i].Translate({0, text_[i - 1].GetScale().y * -kLineSpaces[i - 1]}); + text_[i].Translate(text_[i - 1].GetPosition() * Vector2(0, 1)); + text_[i].Translate({0, text_[i - 1].GetSize().y * -kLineSpaces[i - 1]}); } } float center_offset_y = - (text_[0].GetOffset().y - text_[kNumLines - 1].GetOffset().y) / 2; + (text_[0].GetPosition().y - text_[kNumLines - 1].GetPosition().y) / 2; for (int i = 0; i < kNumLines; ++i) text_[i].Translate({0, center_offset_y}); diff --git a/src/demo/credits.h b/src/demo/credits.h index 166232f..afa83c7 100644 --- a/src/demo/credits.h +++ b/src/demo/credits.h @@ -21,8 +21,6 @@ class Credits { bool Initialize(); - void Update(float delta_time); - void OnInputEvent(std::unique_ptr event); void Show(); diff --git a/src/demo/demo.cc b/src/demo/demo.cc index c2cd784..cf69eca 100644 --- a/src/demo/demo.cc +++ b/src/demo/demo.cc @@ -83,9 +83,9 @@ void Demo::Update(float delta_time) { hud_.SetScore(score_, true); } - hud_.Update(delta_time); - menu_.Update(delta_time); - credits_.Update(delta_time); + sky_.Update(delta_time); + player_.Update(delta_time); + enemy_.Update(delta_time); if (state_ == kMenu) UpdateMenuState(delta_time); @@ -102,7 +102,7 @@ void Demo::LostFocus() { EnterMenuState(); } -void Demo::GainedFocus() {} +void Demo::GainedFocus(bool from_interstitial_ad) {} void Demo::AddScore(int score) { add_score_ += score; @@ -111,6 +111,13 @@ void Demo::AddScore(int score) { void Demo::EnterMenuState() { if (state_ == kMenu) return; + + if (state_ == kState_Invalid || state_ == kGame) { + sky_.SetSpeed(0); + player_.Pause(true); + enemy_.Pause(true); + } + if (wave_ == 0) { menu_.SetOptionEnabled(Menu::kContinue, false); } else { @@ -131,7 +138,11 @@ void Demo::EnterCreditsState() { void Demo::EnterGameState() { if (state_ == kGame) return; + + sky_.SetSpeed(0.04f); hud_.Show(); + player_.Pause(false); + enemy_.Pause(false); state_ = kGame; } @@ -160,10 +171,6 @@ void Demo::UpdateMenuState(float delta_time) { } void Demo::UpdateGameState(float delta_time) { - sky_.Update(delta_time); - player_.Update(delta_time); - enemy_.Update(delta_time); - if (waiting_for_next_wave_) return; diff --git a/src/demo/demo.h b/src/demo/demo.h index a8f1a83..dddc60a 100644 --- a/src/demo/demo.h +++ b/src/demo/demo.h @@ -24,7 +24,7 @@ class Demo : public eng::Game { void LostFocus() override; - void GainedFocus() override; + void GainedFocus(bool from_interstitial_ad) override; void AddScore(int score); diff --git a/src/demo/enemy.cc b/src/demo/enemy.cc index 0ef88f5..9706020 100644 --- a/src/demo/enemy.cc +++ b/src/demo/enemy.cc @@ -57,7 +57,7 @@ bool Enemy::Initialize() { } void Enemy::Update(float delta_time) { - if (!waiting_for_next_wave_) { + if (!waiting_for_next_wave_ && !paused_) { if (spawn_factor_interpolator_ < 1) { spawn_factor_interpolator_ += delta_time * 0.1f; if (spawn_factor_interpolator_ > 1) @@ -70,17 +70,23 @@ void Enemy::Update(float delta_time) { SpawnNextEnemy(); } - for (auto it = enemies_.begin(); it != enemies_.end(); ++it) { - if (it->marked_for_removal) { + for (auto it = enemies_.begin(); it != enemies_.end();) { + if (it->marked_for_removal) it = enemies_.erase(it); - continue; - } - it->sprite_animator.Update(delta_time); - it->target_animator.Update(delta_time); - it->blast_animator.Update(delta_time); - it->health_animator.Update(delta_time); - it->score_animator.Update(delta_time); - it->movement_animator.Update(delta_time); + else + ++it; + } +} + +void Enemy::Pause(bool pause) { + paused_ = pause; + for (auto& e : enemies_) { + e.movement_animator.PauseOrResumeAll(pause); + e.sprite_animator.PauseOrResumeAll(pause); + e.target_animator.PauseOrResumeAll(pause); + e.blast_animator.PauseOrResumeAll(pause); + e.health_animator.PauseOrResumeAll(pause); + e.score_animator.PauseOrResumeAll(pause); } } @@ -95,8 +101,8 @@ Vector2 Enemy::GetTargetPos(DamageType damage_type) { EnemyUnit* target = GetTarget(damage_type); if (target) - return target->sprite.GetOffset() - - Vector2(0, target->sprite.GetScale().y / 2.5f); + return target->sprite.GetPosition() - + Vector2(0, target->sprite.GetSize().y / 2.5f); return {0, 0}; } @@ -122,11 +128,11 @@ void Enemy::SelectTarget(DamageType damage_type, e.target_animator.Stop(Animator::kAllAnimations); } - if (!base::Intersection(e.sprite.GetOffset(), - e.sprite.GetScale() * snap_factor, origin, dir)) + if (!base::Intersection(e.sprite.GetPosition(), + e.sprite.GetSize() * snap_factor, origin, dir)) continue; - Vector2 weapon_enemy_dir = e.sprite.GetOffset() - origin; + Vector2 weapon_enemy_dir = e.sprite.GetPosition() - origin; float enemy_weapon_dist = weapon_enemy_dir.Magnitude(); if (closest_dist > enemy_weapon_dist) { closest_dist = enemy_weapon_dist; @@ -232,10 +238,10 @@ void Enemy::TakeDamage(EnemyUnit* target, int damage) { } else { target->targetted_by_weapon_ = kDamageType_Invalid; - Vector2 s = target->sprite.GetScale() * Vector2(0.6f, 0.01f); + Vector2 s = target->sprite.GetSize() * Vector2(0.6f, 0.01f); s.x *= (float)target->hit_points / (float)target->total_health; - float t = (s.x - target->health_bar.GetScale().x) / 2; - target->health_bar.SetScale(s); + float t = (s.x - target->health_bar.GetSize().x) / 2; + target->health_bar.SetSize(s); target->health_bar.Translate({t, 0}); target->health_base.SetVisible(true); @@ -312,8 +318,8 @@ void Enemy::Spawn(EnemyType enemy_type, } e.sprite.SetZOrder(11); e.sprite.SetVisible(true); - Vector2 spawn_pos = pos + Vector2(0, e.sprite.GetScale().y / 2); - e.sprite.SetOffset(spawn_pos); + Vector2 spawn_pos = pos + Vector2(0, e.sprite.GetSize().y / 2); + e.sprite.SetPosition(spawn_pos); e.sprite.SetFrame(enemy_frame_start[enemy_type][damage_type]); e.sprite_animator.SetFrames(enemy_frame_count[enemy_type][damage_type], @@ -324,28 +330,28 @@ void Enemy::Spawn(EnemyType enemy_type, e.target.Create("target_tex", {6, 2}); e.target.SetZOrder(12); - e.target.SetOffset(spawn_pos); + e.target.SetPosition(spawn_pos); e.blast.Create("blast_tex", {6, 2}); e.blast.SetZOrder(12); - e.blast.SetOffset(spawn_pos); + e.blast.SetPosition(spawn_pos); e.health_base.SetZOrder(11); - e.health_base.Scale(e.sprite.GetScale() * Vector2(0.6f, 0.01f)); - e.health_base.SetOffset(spawn_pos); + e.health_base.SetSize(e.sprite.GetSize() * Vector2(0.6f, 0.01f)); + e.health_base.SetPosition(spawn_pos); e.health_base.PlaceToBottomOf(e.sprite); e.health_base.SetColor({0.5f, 0.5f, 0.5f, 1}); e.health_bar.SetZOrder(11); - e.health_bar.Scale(e.sprite.GetScale() * Vector2(0.6f, 0.01f)); - e.health_bar.SetOffset(spawn_pos); + e.health_bar.SetSize(e.sprite.GetSize() * Vector2(0.6f, 0.01f)); + e.health_bar.SetPosition(spawn_pos); e.health_bar.PlaceToBottomOf(e.sprite); e.health_bar.SetColor({0.161f, 0.89f, 0.322f, 1}); e.score.Create("score_tex"s + std::to_string(e.enemy_type)); e.score.SetZOrder(12); e.score.SetColor({1, 1, 1, 1}); - e.score.SetOffset(spawn_pos); + e.score.SetPosition(spawn_pos); e.target_animator.Attach(&e.target); diff --git a/src/demo/enemy.h b/src/demo/enemy.h index 1881440..96d0122 100644 --- a/src/demo/enemy.h +++ b/src/demo/enemy.h @@ -26,6 +26,8 @@ class Enemy { void Update(float delta_time); + void Pause(bool pause); + bool HasTarget(DamageType damage_type); base::Vector2 GetTargetPos(DamageType damage_type); @@ -87,6 +89,8 @@ class Enemy { int last_spawn_col_ = 0; + bool paused_ = false; + void TakeDamage(EnemyUnit* target, int damage); void SpawnNextEnemy(); diff --git a/src/demo/hud.cc b/src/demo/hud.cc index c5e90d3..8b41e9a 100644 --- a/src/demo/hud.cc +++ b/src/demo/hud.cc @@ -44,19 +44,19 @@ bool Hud::Initialize() { text_[i].SetZOrder(30); text_[i].SetColor(kTextColor * Vector4(1, 1, 1, 0)); - Vector2 pos = (engine.GetScreenSize() / 2 - text_[i].GetScale() / 2); + Vector2 pos = (engine.GetScreenSize() / 2 - text_[i].GetSize() / 2); pos -= engine.GetScreenSize() * Vector2(kHorizontalMargin, kVerticalMargin); Vector2 scale = engine.GetScreenSize() * Vector2(1, 0); scale -= engine.GetScreenSize() * Vector2(kHorizontalMargin * 4, 0); - scale += text_[0].GetScale() * Vector2(0, 0.3f); + scale += text_[0].GetSize() * Vector2(0, 0.3f); progress_bar_[i].SetZOrder(30); - progress_bar_[i].Scale(scale); + progress_bar_[i].SetSize(scale); progress_bar_[i].Translate(pos * Vector2(0, 1)); progress_bar_[i].SetColor(kPprogressBarColor[i] * Vector4(1, 1, 1, 0)); - pos -= progress_bar_[i].GetScale() * Vector2(0, 4); + pos -= progress_bar_[i].GetSize() * Vector2(0, 4); text_[i].Translate(pos * Vector2(i ? 1 : -1, 1)); progress_bar_animator_[i].Attach(&progress_bar_[i]); @@ -73,13 +73,6 @@ bool Hud::Initialize() { return true; } -void Hud::Update(float delta_time) { - for (int i = 0; i < 2; ++i) { - text_animator_[i].Update(delta_time); - progress_bar_animator_[i].Update(delta_time); - } -} - void Hud::Show() { if (text_[0].IsVisible()) return; @@ -118,9 +111,9 @@ void Hud::SetWave(int wave, bool flash) { void Hud::SetProgress(float progress) { progress = std::min(std::max(0.0f, progress), 1.0f); last_progress_ = progress; - Vector2 s = progress_bar_[0].GetScale() * Vector2(progress, 1); - float t = (s.x - progress_bar_[1].GetScale().x) / 2; - progress_bar_[1].SetScale(s); + Vector2 s = progress_bar_[0].GetSize() * Vector2(progress, 1); + float t = (s.x - progress_bar_[1].GetSize().x) / 2; + progress_bar_[1].SetSize(s); progress_bar_[1].Translate({t, 0}); } diff --git a/src/demo/hud.h b/src/demo/hud.h index e713cf2..d425895 100644 --- a/src/demo/hud.h +++ b/src/demo/hud.h @@ -20,8 +20,6 @@ class Hud { bool Initialize(); - void Update(float delta_time); - void Show(); void SetScore(int score, bool flash); diff --git a/src/demo/menu.cc b/src/demo/menu.cc index 0c0aece..e4a8b7b 100644 --- a/src/demo/menu.cc +++ b/src/demo/menu.cc @@ -75,19 +75,11 @@ bool Menu::Initialize() { return true; } -void Menu::Update(float delta_time) { - for (int i = 0; i < kOption_Max; ++i) { - if (items_[i].hide) - continue; - items_[i].text_animator.Update(delta_time); - } -} - void Menu::OnInputEvent(std::unique_ptr event) { if (event->GetType() == InputEvent::kDragStart) - tap_pos_[0] = tap_pos_[1] = event->GetVector(0); + tap_pos_[0] = tap_pos_[1] = event->GetVector(); else if (event->GetType() == InputEvent::kDrag) - tap_pos_[1] = event->GetVector(0); + tap_pos_[1] = event->GetVector(); if (event->GetType() != InputEvent::kDragEnd || IsAnimating()) return; @@ -95,13 +87,11 @@ void Menu::OnInputEvent(std::unique_ptr event) { for (int i = 0; i < kOption_Max; ++i) { if (items_[i].hide) continue; - if (!Intersection(items_[i].text.GetOffset(), - items_[i].text.GetScale() * Vector2(1.2f, 2), - tap_pos_[0])) + if (!Intersection(items_[i].text.GetPosition(), + items_[i].text.GetSize() * Vector2(1.2f, 2), tap_pos_[0])) continue; - if (!Intersection(items_[i].text.GetOffset(), - items_[i].text.GetScale() * Vector2(1.2f, 2), - tap_pos_[1])) + if (!Intersection(items_[i].text.GetPosition(), + items_[i].text.GetSize() * Vector2(1.2f, 2), tap_pos_[1])) continue; items_[i].text_animator.SetEndCallback(Animator::kBlending, @@ -117,12 +107,13 @@ void Menu::SetOptionEnabled(Option o, bool enable) { if (i == o) items_[i].hide = !enable; if (!items_[i].hide) { - items_[i].text.SetOffset({0, 0}); + items_[i].text.SetPosition({0, 0}); if (last >= 0) { items_[i].text.PlaceToBottomOf(items_[last].text); - items_[i].text.Translate(items_[last].text.GetOffset() * Vector2(0, 1)); + items_[i].text.Translate(items_[last].text.GetPosition() * + Vector2(0, 1)); items_[i].text.Translate( - {0, items_[last].text.GetScale().y * -kMenuOptionSpace}); + {0, items_[last].text.GetSize().y * -kMenuOptionSpace}); } if (first < 0) first = i; @@ -131,7 +122,8 @@ void Menu::SetOptionEnabled(Option o, bool enable) { } float center_offset_y = - (items_[first].text.GetOffset().y - items_[last].text.GetOffset().y) / 2; + (items_[first].text.GetPosition().y - items_[last].text.GetPosition().y) / + 2; for (int i = 0; i < kOption_Max; ++i) { if (!items_[i].hide) items_[i].text.Translate({0, center_offset_y}); diff --git a/src/demo/menu.h b/src/demo/menu.h index 46a7189..c7324d0 100644 --- a/src/demo/menu.h +++ b/src/demo/menu.h @@ -30,8 +30,6 @@ class Menu { bool Initialize(); - void Update(float delta_time); - void OnInputEvent(std::unique_ptr event); void SetOptionEnabled(Option o, bool enable); diff --git a/src/demo/player.cc b/src/demo/player.cc index cd8d77e..d601f70 100644 --- a/src/demo/player.cc +++ b/src/demo/player.cc @@ -31,24 +31,26 @@ bool Player::Initialize() { } void Player::Update(float delta_time) { - for (int i = 0; i < 2; ++i) { - warmup_animator_[i].Update(delta_time); - cooldown_animator_[i].Update(delta_time); - beam_animator_[i].Update(delta_time); - spark_animator_[i].Update(delta_time); - } - if (active_weapon_ != kDamageType_Invalid) UpdateTarget(); } +void Player::Pause(bool pause) { + for (int i = 0; i < 2; ++i) { + warmup_animator_[i].PauseOrResumeAll(pause); + cooldown_animator_[i].PauseOrResumeAll(pause); + beam_animator_[i].PauseOrResumeAll(pause); + spark_animator_[i].PauseOrResumeAll(pause); + } +} + void Player::OnInputEvent(std::unique_ptr event) { if (event->GetType() == InputEvent::kNavigateBack) NavigateBack(); else if (event->GetType() == InputEvent::kDragStart) - DragStart(event->GetVector(0)); + DragStart(event->GetVector()); else if (event->GetType() == InputEvent::kDrag) - Drag(event->GetVector(0)); + Drag(event->GetVector()); else if (event->GetType() == InputEvent::kDragEnd) DragEnd(); else if (event->GetType() == InputEvent::kDragCancel) @@ -58,18 +60,18 @@ void Player::OnInputEvent(std::unique_ptr event) { Vector2 Player::GetWeaponPos(DamageType type) const { return Engine::Get().GetScreenSize() / Vector2(type == kDamageType_Green ? 3.5f : -3.5f, -2) + - Vector2(0, weapon_[type].GetScale().y * 0.7f); + Vector2(0, weapon_[type].GetSize().y * 0.7f); } Vector2 Player::GetWeaponScale() const { - return weapon_[0].GetScale(); + return weapon_[0].GetSize(); } DamageType Player::GetWeaponType(const Vector2& pos) { DamageType closest_weapon = kDamageType_Invalid; float closest_dist = std::numeric_limits::max(); for (int i = 0; i < 2; ++i) { - float dist = (pos - weapon_[i].GetOffset()).Magnitude(); + float dist = (pos - weapon_[i].GetPosition()).Magnitude(); if (dist < closest_dist) { closest_dist = dist; closest_weapon = (DamageType)i; @@ -77,20 +79,11 @@ DamageType Player::GetWeaponType(const Vector2& pos) { } DCHECK(closest_weapon != kDamageType_Invalid); - if (closest_dist < weapon_[closest_weapon].GetScale().x * 0.9f) + if (closest_dist < weapon_[closest_weapon].GetSize().x * 0.9f) return closest_weapon; return kDamageType_Invalid; } -void Player::SetBeamLength(DamageType type, float len) { - beam_[type].SetOffset({0, 0}); - beam_[type].SetScale({len, beam_[type].GetScale().y}); - beam_[type].PlaceToRightOf(weapon_[type]); - beam_[type].Translate(weapon_[type].GetScale() * Vector2(-0.5f, 0)); - beam_[type].SetPivot(beam_[type].GetOffset()); - beam_[type].Translate(weapon_[type].GetOffset()); -} - void Player::WarmupWeapon(DamageType type) { cooldown_animator_[type].Stop(Animator::kFrames); warmup_animator_[type].Play(Animator::kFrames, false); @@ -106,25 +99,27 @@ void Player::Fire(DamageType type, Vector2 dir) { Enemy& enemy = static_cast(engine.GetGame())->GetEnemy(); if (enemy.HasTarget(type)) - dir = weapon_[type].GetOffset() - enemy.GetTargetPos(type); + dir = weapon_[type].GetPosition() - enemy.GetTargetPos(type); else dir *= engine.GetScreenSize().y * 1.3f; float len = dir.Magnitude(); - SetBeamLength(type, len); + beam_[type].SetSize({len, beam_[type].GetSize().y}); + beam_[type].SetPosition(weapon_[type].GetPosition()); dir.Normalize(); float cos_theta = dir.DotProduct(Vector2(1, 0)); float theta = acos(cos_theta) + M_PI; beam_[type].SetTheta(theta); - beam_spark_[type].SetTheta(theta); + auto offset = beam_[type].GetRotation() * (len / 2); + beam_[type].Translate({offset.y, -offset.x}); beam_[type].SetColor({1, 1, 1, 1}); beam_[type].SetVisible(true); beam_spark_[type].SetVisible(true); spark_animator_[type].Stop(Animator::kMovement); - float length = beam_[type].GetScale().x * 0.85f; + float length = beam_[type].GetSize().x * 0.9f; Vector2 movement = dir * -length; // Convert from units per second to duration. float speed = 1.0f / (18.0f / length); @@ -144,33 +139,27 @@ void Player::SetupWeapons() { drag_sign_[i].SetZOrder(21); drag_sign_[i].SetFrame(i * 8); + Vector2 pos = GetWeaponPos((DamageType)i); + // Setup weapon. weapon_[i].Create("weapon_tex", {8, 2}); weapon_[i].SetZOrder(24); weapon_[i].SetVisible(true); weapon_[i].SetFrame(wepon_warmup_frame[i]); + weapon_[i].SetPosition(pos); // Setup beam. beam_[i].Create("beam_tex", {1, 2}); beam_[i].SetZOrder(22); beam_[i].SetFrame(i); - beam_[i].PlaceToRightOf(weapon_[i]); - beam_[i].Translate(weapon_[i].GetScale() * Vector2(-0.5f, 0)); - beam_[i].SetPivot(beam_[i].GetOffset()); + beam_[i].SetPosition(pos); + beam_[i].Translate(beam_[i].GetSize() * Vector2(-0.5f, -0.5f)); // Setup beam spark. beam_spark_[i].Create("weapon_tex", {8, 2}); beam_spark_[i].SetZOrder(23); beam_spark_[i].SetFrame(i * 8 + 1); - beam_spark_[i].PlaceToRightOf(weapon_[i]); - beam_spark_[i].Translate(weapon_[i].GetScale() * Vector2(-0.5f, 0)); - beam_spark_[i].SetPivot(beam_spark_[i].GetOffset()); - - // Place parts on the screen. - Vector2 offset = GetWeaponPos((DamageType)i); - beam_[i].Translate(offset); - beam_spark_[i].Translate(offset); - weapon_[i].Translate(offset); + beam_spark_[i].SetPosition(pos); // Setup animators. weapon_[i].SetFrame(wepon_cooldown_frame[i]); @@ -227,7 +216,7 @@ void Player::DragStart(const Vector2& pos) { drag_start_ = drag_end_ = pos; - drag_sign_[active_weapon_].SetOffset(drag_start_); + drag_sign_[active_weapon_].SetPosition(drag_start_); drag_sign_[active_weapon_].SetVisible(true); } @@ -236,7 +225,7 @@ void Player::Drag(const Vector2& pos) { return; drag_end_ = pos; - drag_sign_[active_weapon_].SetOffset(drag_end_); + drag_sign_[active_weapon_].SetPosition(drag_end_); if (ValidateDrag()) { if (!drag_valid_ && !IsFiring(active_weapon_)) @@ -307,7 +296,7 @@ bool Player::ValidateDrag() { Vector2 dir = drag_end_ - drag_start_; float len = dir.Magnitude(); dir.Normalize(); - if (len < weapon_[active_weapon_].GetScale().y / 4) + if (len < weapon_[active_weapon_].GetSize().y / 4) return false; if (dir.DotProduct(Vector2(0, 1)) < 0) return false; diff --git a/src/demo/player.h b/src/demo/player.h index c34bd01..e883bb9 100644 --- a/src/demo/player.h +++ b/src/demo/player.h @@ -21,6 +21,8 @@ class Player { void Update(float delta_time); + void Pause(bool pause); + void OnInputEvent(std::unique_ptr event); base::Vector2 GetWeaponPos(DamageType type) const; @@ -45,8 +47,6 @@ class Player { DamageType GetWeaponType(const base::Vector2& pos); - void SetBeamLength(DamageType type, float len); - void WarmupWeapon(DamageType type); void CooldownWeapon(DamageType type); diff --git a/src/demo/sky_quad.cc b/src/demo/sky_quad.cc index d6b926f..cc4b0b1 100644 --- a/src/demo/sky_quad.cc +++ b/src/demo/sky_quad.cc @@ -15,6 +15,7 @@ SkyQuad::SkyQuad() : shader_(Engine::Get().CreateRenderResource()), sky_offset_{ 0, Lerp(0.0f, 10.0f, Engine::Get().GetRandomGenerator().GetFloat())} { + last_sky_offset_ = sky_offset_; } SkyQuad::~SkyQuad() = default; @@ -25,7 +26,8 @@ bool SkyQuad::Create() { auto source = std::make_unique(); if (!source->Load("sky.glsl")) return false; - shader_->Create(std::move(source), engine.GetQuad()->vertex_description()); + shader_->Create(std::move(source), engine.GetQuad()->vertex_description(), + Engine::Get().GetQuad()->primitive()); scale_ = engine.GetScreenSize(); @@ -37,8 +39,8 @@ bool SkyQuad::Create() { } void SkyQuad::Update(float delta_time) { - sky_offset_ += {0, delta_time * 0.04f}; - color_animator_.Update(delta_time); + last_sky_offset_ = sky_offset_; + sky_offset_ += {0, delta_time * speed_}; } void SkyQuad::Draw(float frame_frac) { @@ -46,13 +48,13 @@ void SkyQuad::Draw(float frame_frac) { shader_->Activate(); shader_->SetUniform("scale", scale_); - shader_->SetUniform("projection", Engine::Get().GetProjectionMarix()); + shader_->SetUniform("projection", Engine::Get().GetProjectionMatrix()); shader_->SetUniform("sky_offset", sky_offset); shader_->SetUniform("nebula_color", {nebula_color_.x, nebula_color_.y, nebula_color_.z}); + shader_->UploadUniforms(); Engine::Get().GetQuad()->Draw(); - last_sky_offset_ = sky_offset_; } void SkyQuad::ContextLost() { @@ -64,3 +66,7 @@ void SkyQuad::SwitchColor(const Vector4& color) { std::bind(SmoothStep, std::placeholders::_1)); color_animator_.Play(Animator::kBlending, false); } + +void SkyQuad::SetSpeed(float speed) { + speed_ = speed; +} diff --git a/src/demo/sky_quad.h b/src/demo/sky_quad.h index 72cac08..db1c9f4 100644 --- a/src/demo/sky_quad.h +++ b/src/demo/sky_quad.h @@ -40,6 +40,8 @@ class SkyQuad : public eng::Animatable { void SwitchColor(const base::Vector4& color); + void SetSpeed(float speed); + private: std::unique_ptr shader_; @@ -49,6 +51,8 @@ class SkyQuad : public eng::Animatable { base::Vector2 scale_ = {1, 1}; eng::Animator color_animator_; + + float speed_ = 0; }; #endif // SKY_QUAD_H diff --git a/src/engine/BUILD.gn b/src/engine/BUILD.gn index e9fd64e..2f8474e 100644 --- a/src/engine/BUILD.gn +++ b/src/engine/BUILD.gn @@ -27,6 +27,8 @@ source_set("engine") { "input_event.h", "mesh.cc", "mesh.h", + "persistent_data.cc", + "persistent_data.h", "platform/asset_file.cc", "platform/asset_file.h", "platform/platform_base.cc", @@ -35,14 +37,15 @@ source_set("engine") { "platform/platform.h", "renderer/geometry.cc", "renderer/geometry.h", - "renderer/opengl.h", - "renderer/render_command.cc", - "renderer/render_command.h", + "renderer/opengl/opengl.h", + "renderer/opengl/render_command.cc", + "renderer/opengl/render_command.h", + "renderer/opengl/renderer_opengl.cc", + "renderer/opengl/renderer_opengl.h", "renderer/render_resource.cc", "renderer/render_resource.h", "renderer/renderer_types.cc", "renderer/renderer_types.h", - "renderer/renderer.cc", "renderer/renderer.h", "renderer/shader.cc", "renderer/shader.h", @@ -68,7 +71,7 @@ source_set("engine") { "platform/asset_file_linux.cc", "platform/platform_linux.cc", "platform/platform_linux.h", - "renderer/renderer_linux.cc", + "renderer/opengl/renderer_opengl_linux.cc", ] #ldflags += ["-L/usr/X11R6/lib"] diff --git a/src/engine/animatable.cc b/src/engine/animatable.cc index 4f25921..90cd50a 100644 --- a/src/engine/animatable.cc +++ b/src/engine/animatable.cc @@ -6,16 +6,16 @@ using namespace base; namespace eng { -void Animatable::Translate(const Vector2& offset) { - offset_ += offset; +void Animatable::Translate(const Vector2& pos) { + position_ += pos; } void Animatable::Scale(const Vector2& scale) { - scale_ *= scale; + scale_ = scale; } void Animatable::Scale(float scale) { - scale_ *= scale; + scale_ = {scale, scale}; } void Animatable::Rotate(float angle) { diff --git a/src/engine/animatable.h b/src/engine/animatable.h index 1bf070b..b9d2858 100644 --- a/src/engine/animatable.h +++ b/src/engine/animatable.h @@ -11,20 +11,19 @@ class Animatable : public Drawable { Animatable() = default; ~Animatable() override = default; - void Translate(const base::Vector2& offset); + void Translate(const base::Vector2& pos); void Scale(const base::Vector2& scale); void Scale(float scale); void Rotate(float angle); - void SetOffset(const base::Vector2& offset) { offset_ = offset; } - void SetScale(const base::Vector2& scale) { scale_ = scale; } - void SetPivot(const base::Vector2& pivot) { pivot_ = pivot; } + void SetPosition(const base::Vector2& pos) { position_ = pos; } + void SetSize(const base::Vector2& size) { size_ = size; } void SetTheta(float theta); - base::Vector2 GetOffset() const { return offset_; } - base::Vector2 GetScale() const { return scale_; } - base::Vector2 GetPivot() const { return pivot_; } + base::Vector2 GetPosition() const { return position_; } + base::Vector2 GetSize() const { return size_ * scale_; } float GetTheta() const { return theta_; } + base::Vector2 GetRotation() const { return rotation_; } // Pure virtuals for frame animation support. virtual void SetFrame(size_t frame) = 0; @@ -35,25 +34,25 @@ class Animatable : public Drawable { virtual base::Vector4 GetColor() const = 0; void PlaceToLeftOf(const Animatable& s) { - Translate({s.GetScale().x / -2.0f + GetScale().x / -2.0f, 0}); + Translate({s.GetSize().x / -2.0f + GetSize().x / -2.0f, 0}); } void PlaceToRightOf(const Animatable& s) { - Translate({s.GetScale().x / 2.0f + GetScale().x / 2.0f, 0}); + Translate({s.GetSize().x / 2.0f + GetSize().x / 2.0f, 0}); } void PlaceToTopOf(const Animatable& s) { - Translate({0, s.GetScale().y / 2.0f + GetScale().y / 2.0f}); + Translate({0, s.GetSize().y / 2.0f + GetSize().y / 2.0f}); } void PlaceToBottomOf(const Animatable& s) { - Translate({0, s.GetScale().y / -2.0f + GetScale().y / -2.0f}); + Translate({0, s.GetSize().y / -2.0f + GetSize().y / -2.0f}); } protected: - base::Vector2 offset_ = {0, 0}; + base::Vector2 position_ = {0, 0}; + base::Vector2 size_ = {1, 1}; base::Vector2 scale_ = {1, 1}; - base::Vector2 pivot_ = {0, 0}; base::Vector2 rotation_ = {0, 1}; float theta_ = 0; }; diff --git a/src/engine/animator.cc b/src/engine/animator.cc index 4307e36..121a9e3 100644 --- a/src/engine/animator.cc +++ b/src/engine/animator.cc @@ -3,11 +3,20 @@ #include "../base/interpolation.h" #include "../base/log.h" #include "animatable.h" +#include "engine.h" using namespace base; namespace eng { +Animator::Animator() { + Engine::Get().AddAnimator(this); +} + +Animator::~Animator() { + Engine::Get().RemoveAnimator(this); +} + void Animator::Attach(Animatable* animatable) { elements_.push_back({animatable, {0, 0}, @@ -18,7 +27,10 @@ void Animator::Attach(Animatable* animatable) { void Animator::Play(int animation, bool loop) { play_flags_ |= animation; - loop_flags_ |= loop ? animation : 0; + if (loop) + loop_flags_ |= animation; + else + loop_flags_ &= ~animation; } void Animator::Pause(int animation) { @@ -43,6 +55,16 @@ void Animator::Stop(int animation) { loop_flags_ &= ~animation; } +void Animator::PauseOrResumeAll(bool pause) { + if (pause) { + resume_flags_ = play_flags_; + play_flags_ = 0; + } else { + play_flags_ = resume_flags_; + resume_flags_ = 0; + } +} + float Animator::GetTime(int animation) { if ((animation & kMovement) != 0) return movement_time_; @@ -55,7 +77,7 @@ float Animator::GetTime(int animation) { return timer_time_; } -void Animator::SetTime(int animation, float time) { +void Animator::SetTime(int animation, float time, bool force_update) { DCHECK(time >= 0 && time <= 1); if ((animation & kMovement) != 0) @@ -68,6 +90,13 @@ void Animator::SetTime(int animation, float time) { frame_time_ = time; if ((animation & kTimer) != 0) timer_time_ = time; + + if (force_update) { + unsigned play_flags = play_flags_; + play_flags_ = animation; + Update(0); + play_flags_ = play_flags; + } } void Animator::SetEndCallback(int animation, base::Closure cb) { @@ -95,7 +124,7 @@ void Animator::SetMovement(Vector2 direction, movement_interpolator_ = std::move(interpolator); for (auto& a : elements_) - a.movement_last_offset = {0, 0}; + a.movement_last_pos = {0, 0}; } void Animator::SetRotation(float trget, @@ -149,39 +178,41 @@ void Animator::Update(float delta_time) { UpdateFrame(delta_time); if (play_flags_ & kTimer) UpdateTimer(delta_time); +} +void Animator::EvalAnim(float frame_time) { for (auto& a : elements_) { if (play_flags_ & kMovement) { - float interpolated_time = movement_interpolator_ - ? movement_interpolator_(movement_time_) - : movement_time_; - Vector2 offset = - base::Lerp({0, 0}, movement_direction_, interpolated_time); - a.animatable->Translate(offset - a.movement_last_offset); - a.movement_last_offset = offset; + float time = movement_time_ + movement_speed_ * frame_time; + float interpolated_time = + movement_interpolator_ ? movement_interpolator_(time) : time; + Vector2 pos = base::Lerp({0, 0}, movement_direction_, interpolated_time); + a.animatable->Translate(pos - a.movement_last_pos); + a.movement_last_pos = pos; } if (play_flags_ & kRotation) { - float interpolated_time = rotation_interpolator_ - ? rotation_interpolator_(rotation_time_) - : rotation_time_; + float time = rotation_time_ + rotation_speed_ * frame_time; + float interpolated_time = + rotation_interpolator_ ? rotation_interpolator_(time) : time; float theta = base::Lerp(0.0f, rotation_target_, interpolated_time); a.animatable->Rotate(theta - a.rotation_last_theta); a.rotation_last_theta = theta; } if (play_flags_ & kBlending) { - float interpolated_time = blending_interpolator_ - ? blending_interpolator_(blending_time_) - : blending_time_; + float time = blending_time_ + blending_speed_ * frame_time; + float interpolated_time = + blending_interpolator_ ? blending_interpolator_(time) : time; Vector4 r = base::Lerp(a.blending_start, blending_target_, interpolated_time); a.animatable->SetColor(r); } if (play_flags_ & kFrames) { + float time = frame_time_ + frame_speed_ * frame_time; float interpolated_time = - frame_interpolator_ ? frame_interpolator_(frame_time_) : frame_time_; + frame_interpolator_ ? frame_interpolator_(time) : time; int target = a.frame_start_ + frame_count_; int r = base::Lerp(a.frame_start_, target, interpolated_time); if (r < target) diff --git a/src/engine/animator.h b/src/engine/animator.h index 8d0e3a4..cef3772 100644 --- a/src/engine/animator.h +++ b/src/engine/animator.h @@ -25,8 +25,8 @@ class Animator { using Interpolator = std::function; - Animator() = default; - ~Animator() = default; + Animator(); + ~Animator(); void Attach(Animatable* animatable); @@ -34,9 +34,11 @@ class Animator { void Pause(int animation); void Stop(int animation); + void PauseOrResumeAll(bool pause); + // Get/set current time of the given animation. float GetTime(int animation); - void SetTime(int animation, float time); + void SetTime(int animation, float time, bool force_update = false); // Set callback ro be called once animation ends. void SetEndCallback(int animation, base::Closure cb); @@ -67,13 +69,14 @@ class Animator { void SetVisible(bool visible); void Update(float delta_time); + void EvalAnim(float frame_time); bool IsPlaying(int animation) const { return play_flags_ & animation; } private: struct Element { Animatable* animatable; - base::Vector2 movement_last_offset = {0, 0}; + base::Vector2 movement_last_pos = {0, 0}; float rotation_last_theta = 0; base::Vector4 blending_start = {0, 0, 0, 0}; int frame_start_ = 0; @@ -81,6 +84,7 @@ class Animator { unsigned int play_flags_ = 0; unsigned int loop_flags_ = 0; + unsigned int resume_flags_ = 0; std::vector elements_; base::Vector2 movement_direction_ = {0, 0}; diff --git a/src/engine/audio/audio_alsa.cc b/src/engine/audio/audio_alsa.cc index a56fddf..ba3e1d1 100644 --- a/src/engine/audio/audio_alsa.cc +++ b/src/engine/audio/audio_alsa.cc @@ -157,11 +157,11 @@ void AudioAlsa::Resume() { suspend_audio_thread_.store(false, std::memory_order_relaxed); } -size_t AudioAlsa::GetSampleRate() { +int AudioAlsa::GetHardwareSampleRate() { return sample_rate_; } -bool AudioAlsa::StartAudioThread() { +void AudioAlsa::StartAudioThread() { LOG << "Starting audio thread."; DCHECK(!terminate_audio_thread_.load(std::memory_order_relaxed)); @@ -189,7 +189,8 @@ void AudioAlsa::AudioThreadMain() { while (suspend_audio_thread_.load(std::memory_order_relaxed)) { if (terminate_audio_thread_.load(std::memory_order_relaxed)) return; - std::this_thread::yield(); + // Avoid busy-looping. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); } RenderAudio(buffer.get(), num_frames); diff --git a/src/engine/audio/audio_alsa.h b/src/engine/audio/audio_alsa.h index bb20d83..adf8c39 100644 --- a/src/engine/audio/audio_alsa.h +++ b/src/engine/audio/audio_alsa.h @@ -24,21 +24,21 @@ class AudioAlsa : public AudioBase { void Suspend(); void Resume(); - size_t GetSampleRate(); + int GetHardwareSampleRate(); private: // Handle for the PCM device. snd_pcm_t* device_; std::thread audio_thread_; - std::atomic terminate_audio_thread_ = false; - std::atomic suspend_audio_thread_ = false; + std::atomic terminate_audio_thread_{false}; + std::atomic suspend_audio_thread_{false}; size_t num_channels_ = 0; - size_t sample_rate_ = 0; + int sample_rate_ = 0; size_t period_size_ = 0; - bool StartAudioThread(); + void StartAudioThread(); void TerminateAudioThread(); void AudioThreadMain(); diff --git a/src/engine/audio/audio_base.cc b/src/engine/audio/audio_base.cc index 1e24aa8..39f54b7 100644 --- a/src/engine/audio/audio_base.cc +++ b/src/engine/audio/audio_base.cc @@ -18,17 +18,19 @@ AudioBase::~AudioBase() = default; void AudioBase::Play(std::shared_ptr sample) { if (audio_enabled_) { - std::lock_guard scoped_lock(lock_); + std::lock_guard scoped_lock(lock_); samples_[0].push_back(sample); - } else { + } else if ((sample->flags.load(std::memory_order_relaxed) & + AudioSample::kStopped) == 0) { sample->active = false; } } void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { { - std::lock_guard scoped_lock(lock_); - samples_[1].splice(samples_[1].end(), samples_[0]); + std::unique_lock scoped_lock(lock_, std::try_to_lock); + if (scoped_lock) + samples_[1].splice(samples_[1].end(), samples_[0]); } memset(output_buffer, 0, sizeof(float) * num_frames * kChannelCount); @@ -58,7 +60,7 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { size_t channel_offset = (flags & AudioSample::kSimulateStereo) && !sound->is_streaming_sound() - ? sound->hz() / 10 + ? sound->sample_rate() / 10 : 0; DCHECK(num_samples || sound->is_streaming_sound()); @@ -88,8 +90,8 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { // Basic resampling for variations. accumulator += step; - src_index += accumulator / 10; - accumulator %= 10; + src_index += accumulator / 100; + accumulator %= 100; } // Advance source index. @@ -122,9 +124,9 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { src[1] = src[0]; // mono. num_samples = sound->GetNumSamples(); - Worker::Get().EnqueueTask(HERE, - std::bind(&AudioBase::DoStream, this, *it, - flags & AudioSample::kLoop)); + Worker::Get().PostTask(HERE, + std::bind(&AudioBase::DoStream, this, *it, + flags & AudioSample::kLoop)); } else if (num_samples) { DLOG << "Buffer underrun!"; src_index %= num_samples; @@ -141,7 +143,7 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { (!sound->is_streaming_sound() || !sample->streaming_in_progress.load(std::memory_order_relaxed))) { sample->marked_for_removal = false; - main_thread_task_runner_->EnqueueTask( + main_thread_task_runner_->PostTask( HERE, std::bind(&AudioBase::EndCallback, this, *it)); it = samples_[1].erase(it); } else { @@ -159,12 +161,10 @@ void AudioBase::DoStream(std::shared_ptr sample, bool loop) { } void AudioBase::EndCallback(std::shared_ptr sample) { - AudioSample* s = sample.get(); + sample->active = false; - s->active = false; - - if (s->end_cb) - s->end_cb(); + if (sample->end_cb) + sample->end_cb(); } } // namespace eng diff --git a/src/engine/audio/audio_base.h b/src/engine/audio/audio_base.h index f184546..0fc036d 100644 --- a/src/engine/audio/audio_base.h +++ b/src/engine/audio/audio_base.h @@ -3,9 +3,9 @@ #include #include +#include #include "../../base/closure.h" -#include "../../base/spinlock.h" #include "audio_sample.h" namespace base { @@ -32,7 +32,7 @@ class AudioBase { private: std::list> samples_[2]; - base::Spinlock lock_; + std::mutex lock_; base::TaskRunner* main_thread_task_runner_; diff --git a/src/engine/audio/audio_oboe.cc b/src/engine/audio/audio_oboe.cc index 27e19d0..2ad52d1 100644 --- a/src/engine/audio/audio_oboe.cc +++ b/src/engine/audio/audio_oboe.cc @@ -25,14 +25,14 @@ void AudioOboe::Shutdown() { } void AudioOboe::Suspend() { - stream_->pause(); + stream_->stop(); } void AudioOboe::Resume() { - stream_->start(); + RestartStream(); } -size_t AudioOboe::GetSampleRate() { +int AudioOboe::GetHardwareSampleRate() { return stream_->getSampleRate(); } diff --git a/src/engine/audio/audio_oboe.h b/src/engine/audio/audio_oboe.h index f30564e..7bdecfa 100644 --- a/src/engine/audio/audio_oboe.h +++ b/src/engine/audio/audio_oboe.h @@ -23,7 +23,7 @@ class AudioOboe : public AudioBase { void Suspend(); void Resume(); - size_t GetSampleRate(); + int GetHardwareSampleRate(); private: class StreamCallback : public oboe::AudioStreamCallback { diff --git a/src/engine/audio/audio_resource.cc b/src/engine/audio/audio_resource.cc index 418438c..71f8175 100644 --- a/src/engine/audio/audio_resource.cc +++ b/src/engine/audio/audio_resource.cc @@ -61,7 +61,7 @@ void AudioResource::SetLoop(bool loop) { if (loop) sample_->flags.fetch_or(AudioSample::kLoop, std::memory_order_relaxed); else - sample_->flags.fetch_and(AudioSample::kLoop, std::memory_order_relaxed); + sample_->flags.fetch_and(~AudioSample::kLoop, std::memory_order_relaxed); } void AudioResource::SetSimulateStereo(bool simulate) { @@ -69,12 +69,12 @@ void AudioResource::SetSimulateStereo(bool simulate) { sample_->flags.fetch_or(AudioSample::kSimulateStereo, std::memory_order_relaxed); else - sample_->flags.fetch_and(AudioSample::kSimulateStereo, + sample_->flags.fetch_and(~AudioSample::kSimulateStereo, std::memory_order_relaxed); } void AudioResource::SetResampleStep(size_t step) { - sample_->step.store(step + 10, std::memory_order_relaxed); + sample_->step.store(step + 100, std::memory_order_relaxed); } void AudioResource::SetMaxAmplitude(float max_amplitude) { diff --git a/src/engine/audio/audio_sample.h b/src/engine/audio/audio_sample.h index bbfb3c3..97035d6 100644 --- a/src/engine/audio/audio_sample.h +++ b/src/engine/audio/audio_sample.h @@ -28,7 +28,7 @@ struct AudioSample { // Write accessed by main thread, read-only accessed by audio thread. std::atomic flags{0}; - std::atomic step{10}; + std::atomic step{100}; std::atomic amplitude_inc{0}; std::atomic max_amplitude{1.0f}; diff --git a/src/engine/engine.cc b/src/engine/engine.cc index 5f80358..dbadeed 100644 --- a/src/engine/engine.cc +++ b/src/engine/engine.cc @@ -15,7 +15,6 @@ #include "mesh.h" #include "platform/platform.h" #include "renderer/geometry.h" -#include "renderer/render_command.h" #include "renderer/renderer.h" #include "renderer/shader.h" #include "renderer/texture.h" @@ -68,6 +67,8 @@ bool Engine::Initialize() { projection_ = base::Ortho(-1.0, 1.0, -aspect_ratio, aspect_ratio); } + LOG << "image scale factor: " << GetImageScaleFactor(); + if (renderer_->SupportsDXT5()) { tex_comp_alpha_ = TextureCompressor::Create(TextureCompressor::kFormatDXT5); } else if (renderer_->SupportsATC()) { @@ -95,7 +96,7 @@ bool Engine::Initialize() { game_ = GameFactoryBase::CreateGame(""); if (!game_) { - printf("No game found to run.\n"); + LOG << "No game found to run."; return false; } @@ -113,6 +114,10 @@ void Engine::Shutdown() { void Engine::Update(float delta_time) { seconds_accumulated_ += delta_time; + ++tick_; + + for (auto d : animators_) + d->Update(delta_time); game_->Update(delta_time); @@ -135,15 +140,20 @@ void Engine::Update(float delta_time) { } void Engine::Draw(float frame_frac) { + for (auto d : animators_) + d->EvalAnim(time_step_ * frame_frac); + drawables_.sort( [](auto& a, auto& b) { return a->GetZOrder() < b->GetZOrder(); }); + renderer_->PrepareForDrawing(); + for (auto d : drawables_) { if (d->IsVisible()) d->Draw(frame_frac); } - renderer_->EnqueueCommand(std::make_unique()); + renderer_->Present(); } void Engine::LostFocus() { @@ -153,11 +163,11 @@ void Engine::LostFocus() { game_->LostFocus(); } -void Engine::GainedFocus() { +void Engine::GainedFocus(bool from_interstitial_ad) { audio_->Resume(); if (game_) - game_->GainedFocus(); + game_->GainedFocus(from_interstitial_ad); } void Engine::AddDrawable(Drawable* drawable) { @@ -174,6 +184,20 @@ void Engine::RemoveDrawable(Drawable* drawable) { } } +void Engine::AddAnimator(Animator* animator) { + DCHECK(std::find(animators_.begin(), animators_.end(), animator) == + animators_.end()); + animators_.push_back(animator); +} + +void Engine::RemoveAnimator(Animator* animator) { + auto it = std::find(animators_.begin(), animators_.end(), animator); + if (it != animators_.end()) { + animators_.erase(it); + return; + } +} + void Engine::Exit() { platform_->Exit(); } @@ -190,27 +214,16 @@ Vector2 Engine::ToPosition(const Vector2& vec) { void Engine::SetImageSource(const std::string& asset_name, const std::string& file_name, bool persistent) { - std::shared_ptr texture; - auto it = textures_.find(asset_name); - if (it != textures_.end()) { - texture = it->second.texture; - it->second.asset_file = file_name; - it->second.create_image = nullptr; - it->second.persistent = persistent; - } else { - texture = CreateRenderResource(); - textures_[asset_name] = {texture, file_name, nullptr, persistent}; - } - - if (persistent) { - auto image = std::make_unique(); - if (image->Load(file_name)) { - image->Compress(); - texture->Update(std::move(image)); - } else { - texture->Destroy(); - } - } + SetImageSource( + asset_name, + [file_name]() -> std::unique_ptr { + auto image = std::make_unique(); + if (!image->Load(file_name)) + return nullptr; + image->Compress(); + return image; + }, + persistent); } void Engine::SetImageSource(const std::string& asset_name, @@ -221,11 +234,10 @@ void Engine::SetImageSource(const std::string& asset_name, if (it != textures_.end()) { texture = it->second.texture; it->second.create_image = create_image; - it->second.asset_file.clear(); it->second.persistent = persistent; } else { texture = CreateRenderResource(); - textures_[asset_name] = {texture, "", create_image, persistent}; + textures_[asset_name] = {texture, create_image, persistent}; } if (persistent) { @@ -242,17 +254,7 @@ void Engine::RefreshImage(const std::string& asset_name) { if (it == textures_.end()) return; - std::unique_ptr image; - if (!it->second.asset_file.empty()) { - image = std::make_unique(); - if (image->Load(it->second.asset_file)) - image->Compress(); - else - image.reset(); - } else if (it->second.create_image) { - image = it->second.create_image(); - } - + auto image = it->second.create_image(); if (image) it->second.texture->Update(std::move(image)); else @@ -278,9 +280,12 @@ std::unique_ptr Engine::CreateAudioResource() { } void Engine::AddInputEvent(std::unique_ptr event) { + if (replaying_) + return; + switch (event->GetType()) { case InputEvent::kDragEnd: - if (((GetScreenSize() / 2) * 0.9f - event->GetVector(0)).Magnitude() <= + if (((GetScreenSize() / 2) * 0.9f - event->GetVector()).Magnitude() <= 0.25f) { SetSatsVisible(!stats_->IsVisible()); // TODO: Enqueue DragCancel so we can consume this event. @@ -295,9 +300,9 @@ void Engine::AddInputEvent(std::unique_ptr event) { break; case InputEvent::kDrag: if (stats_->IsVisible()) { - if ((stats_->GetOffset() - event->GetVector(0)).Magnitude() <= - stats_->GetScale().y) - stats_->SetOffset(event->GetVector(0)); + if ((stats_->GetPosition() - event->GetVector()).Magnitude() <= + stats_->GetSize().y) + stats_->SetPosition(event->GetVector()); // TODO: Enqueue DragCancel so we can consume this event. } break; @@ -310,18 +315,91 @@ void Engine::AddInputEvent(std::unique_ptr event) { std::unique_ptr Engine::GetNextInputEvent() { std::unique_ptr event; + + if (replaying_) { + if (replay_index_ < replay_data_.root()["input"].size()) { + auto data = replay_data_.root()["input"][replay_index_]; + if (data["tick"].asUInt64() == tick_) { + event = std::make_unique( + (InputEvent::Type)data["input_type"].asInt(), + (size_t)data["pointer_id"].asUInt(), + Vector2(data["pos_x"].asFloat(), data["pos_y"].asFloat())); + ++replay_index_; + } + return event; + } + replaying_ = false; + replay_data_.root().clear(); + } + if (!input_queue_.empty()) { event.swap(input_queue_.front()); input_queue_.pop_front(); + + if (recording_) { + Json::Value data; + data["tick"] = tick_; + data["input_type"] = event->GetType(); + data["pointer_id"] = event->GetPointerId(); + data["pos_x"] = event->GetVector().x; + data["pos_y"] = event->GetVector().y; + replay_data_.root()["input"].append(data); + } } + return event; } +void Engine::StartRecording(const Json::Value& payload) { + if (!replaying_ && !recording_) { + recording_ = true; + random_ = Random(); + replay_data_.root()["seed"] = random_.seed(); + replay_data_.root()["payload"] = payload; + tick_ = 0; + } +} + +void Engine::EndRecording(const std::string file_name) { + if (recording_) { + DCHECK(!replaying_); + + recording_ = false; + replay_data_.SaveAs(file_name, PersistentData::kShared); + replay_data_.root().clear(); + } +} + +bool Engine::Replay(const std::string file_name, Json::Value& payload) { + if (!replaying_ && !recording_ && + replay_data_.Load(file_name, PersistentData::kShared)) { + replaying_ = true; + random_ = Random(replay_data_.root()["seed"].asUInt()); + payload = replay_data_.root()["payload"]; + tick_ = 0; + replay_index_ = 0; + } + + return replaying_; +} + void Engine::Vibrate(int duration) { if (vibration_enabled_) platform_->Vibrate(duration); } +void Engine::ShowInterstitialAd() { + platform_->ShowInterstitialAd(); +} + +void Engine::ShareFile(const std::string& file_name) { + platform_->ShareFile(file_name); +} + +void Engine::SetKeepScreenOn(bool keep_screen_on) { + platform_->SetKeepScreenOn(keep_screen_on); +} + void Engine::SetEnableAudio(bool enable) { audio_->SetEnableAudio(enable); } @@ -342,12 +420,26 @@ int Engine::GetDeviceDpi() const { return platform_->GetDeviceDpi(); } +float Engine::GetImageScaleFactor() const { + float width_inch = static_cast(renderer_->screen_width()) / + static_cast(platform_->GetDeviceDpi()); + return 2.57143f / width_inch; +} + const std::string& Engine::GetRootPath() const { return platform_->GetRootPath(); } -size_t Engine::GetAudioSampleRate() { - return audio_->GetSampleRate(); +const std::string& Engine::GetDataPath() const { + return platform_->GetDataPath(); +} + +const std::string& Engine::GetSharedDataPath() const { + return platform_->GetSharedDataPath(); +} + +int Engine::GetAudioHardwareSampleRate() { + return audio_->GetHardwareSampleRate(); } bool Engine::IsMobile() const { @@ -369,12 +461,16 @@ void Engine::ContextLost() { } bool Engine::CreateRenderResources() { + // This creates a normalized unit sized quad. + static const char vertex_description[] = "p2f;t2f"; + static const float vertices[] = { + -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, 0.5f, 1.0f, 0.0f, + }; + // Create the quad geometry we can reuse for all sprites. auto quad_mesh = std::make_unique(); - if (!quad_mesh->Load("engine/quad.mesh")) { - LOG << "Could not create quad mesh."; - return false; - } + quad_mesh->Create(kPrimitive_TriangleStrip, vertex_description, 4, vertices); quad_->Create(std::move(quad_mesh)); // Create the shader we can reuse for texture rendering. @@ -383,7 +479,8 @@ bool Engine::CreateRenderResources() { LOG << "Could not create pass through shader."; return false; } - pass_through_shader_->Create(std::move(source), quad_->vertex_description()); + pass_through_shader_->Create(std::move(source), quad_->vertex_description(), + quad_->primitive()); // Create the shader we can reuse for solid rendering. source = std::make_unique(); @@ -391,7 +488,8 @@ bool Engine::CreateRenderResources() { LOG << "Could not create solid shader."; return false; } - solid_shader_->Create(std::move(source), quad_->vertex_description()); + solid_shader_->Create(std::move(source), quad_->vertex_description(), + quad_->primitive()); return true; } @@ -414,10 +512,10 @@ std::unique_ptr Engine::PrintStats() { line = "fps: "; line += std::to_string(fps_); lines.push_back(line); - line = "cmd: "; - line += std::to_string(renderer_->global_queue_size() + - renderer_->render_queue_size()); - lines.push_back(line); + // line = "cmd: "; + // line += std::to_string(renderer_->global_queue_size() + + // renderer_->render_queue_size()); + // lines.push_back(line); constexpr int margin = 5; int line_height = system_font_->GetLineHeight(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 89248e9..63803cd 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -10,6 +10,7 @@ #include "../base/random.h" #include "../base/vecmath.h" #include "audio/audio_forward.h" +#include "persistent_data.h" #include "platform/platform_forward.h" #include "renderer/render_resource.h" @@ -17,6 +18,7 @@ class TextureCompressor; namespace eng { +class Animator; class AudioResource; class Font; class Game; @@ -46,11 +48,14 @@ class Engine { void Draw(float frame_frac); void LostFocus(); - void GainedFocus(); + void GainedFocus(bool from_interstitial_ad); void AddDrawable(Drawable* drawable); void RemoveDrawable(Drawable* drawable); + void AddAnimator(Animator* animator); + void RemoveAnimator(Animator* animator); + void Exit(); // Convert size from pixels to viewport scale. @@ -83,9 +88,20 @@ class Engine { void AddInputEvent(std::unique_ptr event); std::unique_ptr GetNextInputEvent(); + void StartRecording(const Json::Value& payload); + void EndRecording(const std::string file_name); + + bool Replay(const std::string file_name, Json::Value& payload); + // Vibrate (if supported by the platform) for the specified duration. void Vibrate(int duration); + void ShowInterstitialAd(); + + void ShareFile(const std::string& file_name); + + void SetKeepScreenOn(bool keep_screen_on); + void SetImageDpi(float dpi) { image_dpi_ = dpi; } void SetEnableAudio(bool enable); @@ -112,13 +128,19 @@ class Engine { // Return screen size in viewport scale. base::Vector2 GetScreenSize() const { return screen_size_; } - const base::Matrix4x4& GetProjectionMarix() const { return projection_; } + const base::Matrix4x4& GetProjectionMatrix() const { return projection_; } int GetDeviceDpi() const; + float GetImageScaleFactor() const; + const std::string& GetRootPath() const; - size_t GetAudioSampleRate(); + const std::string& GetDataPath() const; + + const std::string& GetSharedDataPath() const; + + int GetAudioHardwareSampleRate(); bool IsMobile() const; @@ -126,13 +148,15 @@ class Engine { float image_dpi() const { return image_dpi_; } + float time_step() { return time_step_; } + + int fps() const { return fps_; } + private: // Class holding information about texture resources managed by engine. - // Texture is created from an image asset if asset_file is valid. Otherwise - // texture is created from the image returned by create_image callback. + // Texture is created from the image returned by create_image callback. struct TextureResource { std::shared_ptr texture; - std::string asset_file; CreateImageCB create_image; bool persistent = false; }; @@ -143,7 +167,7 @@ class Engine { Renderer* renderer_ = nullptr; - Audio* audio_; + Audio* audio_ = nullptr; std::unique_ptr game_; @@ -161,6 +185,8 @@ class Engine { std::list drawables_; + std::list animators_; + // Textures mapped by asset name. std::unordered_map textures_; @@ -171,12 +197,20 @@ class Engine { float seconds_accumulated_ = 0.0f; + float time_step_ = 1.0f / 60.0f; + size_t tick_ = 0; + float image_dpi_ = 200; bool vibration_enabled_ = true; std::deque> input_queue_; + PersistentData replay_data_; + bool recording_ = false; + bool replaying_ = false; + int replay_index_ = 0; + base::Random random_; std::unique_ptr CreateRenderResourceInternal( diff --git a/src/engine/game.h b/src/engine/game.h index 08f123c..c6d0b19 100644 --- a/src/engine/game.h +++ b/src/engine/game.h @@ -16,7 +16,7 @@ class Game { virtual void LostFocus() = 0; - virtual void GainedFocus() = 0; + virtual void GainedFocus(bool from_interstitial_ad) = 0; private: Game(const Game&) = delete; diff --git a/src/engine/image.cc b/src/engine/image.cc index ee4aa7b..d50d289 100644 --- a/src/engine/image.cc +++ b/src/engine/image.cc @@ -151,6 +151,8 @@ bool Image::Load(const std::string& file_name) { return false; } + LOG << "Loaded " << file_name << ". number of color components: " << c; + uint8_t* converted_buffer = NULL; switch (c) { case 1: diff --git a/src/engine/image_quad.cc b/src/engine/image_quad.cc index a9c6b18..17c04a3 100644 --- a/src/engine/image_quad.cc +++ b/src/engine/image_quad.cc @@ -33,8 +33,16 @@ void ImageQuad::Destory() { void ImageQuad::AutoScale() { auto& engine = Engine::Get(); Vector2 dimensions = {GetFrameWidth(), GetFrameHeight()}; - SetScale(engine.ToScale(dimensions)); - Scale((float)engine.GetDeviceDpi() / engine.image_dpi()); + Vector2 size = engine.ToScale(dimensions); + float s = + static_cast(engine.image_dpi()) * engine.GetImageScaleFactor(); + size *= static_cast(engine.GetDeviceDpi()) / s; + SetSize(size); +} + +void ImageQuad::SetCustomShader(std::shared_ptr shader) { + custom_shader_ = shader; + custom_uniforms_.clear(); } void ImageQuad::SetFrame(size_t frame) { @@ -58,18 +66,25 @@ void ImageQuad::Draw(float frame_frac) { Vector2 tex_scale = {GetFrameWidth() / texture_->GetWidth(), GetFrameHeight() / texture_->GetHeight()}; - Shader* shader = Engine::Get().GetPassThroughShader(); + Shader* shader = custom_shader_ ? custom_shader_.get() + : Engine::Get().GetPassThroughShader(); shader->Activate(); - shader->SetUniform("offset", offset_); - shader->SetUniform("scale", scale_); - shader->SetUniform("pivot", pivot_); + shader->SetUniform("offset", position_); + shader->SetUniform("scale", GetSize()); shader->SetUniform("rotation", rotation_); shader->SetUniform("tex_offset", GetUVOffset(current_frame_)); shader->SetUniform("tex_scale", tex_scale); - shader->SetUniform("projection", Engine::Get().GetProjectionMarix()); + shader->SetUniform("projection", Engine::Get().GetProjectionMatrix()); shader->SetUniform("color", color_); - shader->SetUniform("texture", 0); + shader->SetUniform("texture_0", 0); + if (custom_shader_) { + for (auto& cu : custom_uniforms_) + std::visit( + [shader, &cu](auto&& arg) { shader->SetUniform(cu.first, arg); }, + cu.second); + } + shader->UploadUniforms(); Engine::Get().GetQuad()->Draw(); } diff --git a/src/engine/image_quad.h b/src/engine/image_quad.h index 2742971..ca5f1e3 100644 --- a/src/engine/image_quad.h +++ b/src/engine/image_quad.h @@ -7,9 +7,12 @@ #include #include #include +#include +#include namespace eng { +class Shader; class Texture; class ImageQuad : public Animatable { @@ -26,6 +29,13 @@ class ImageQuad : public Animatable { void AutoScale(); + void SetCustomShader(std::shared_ptr shader); + + template + void SetCustomUniform(const std::string& name, T value) { + custom_uniforms_[name] = UniformValue(value); + } + // Animatable interface. void SetFrame(size_t frame) override; size_t GetFrame() const override { return current_frame_; } @@ -37,8 +47,18 @@ class ImageQuad : public Animatable { void Draw(float frame_frac) override; private: + using UniformValue = std::variant; + std::shared_ptr texture_; + std::shared_ptr custom_shader_; + std::unordered_map custom_uniforms_; + size_t current_frame_ = 0; std::array num_frames_ = {1, 1}; // horizontal, vertical int frame_width_ = 0; diff --git a/src/engine/input_event.h b/src/engine/input_event.h index ef4b54e..8bd267c 100644 --- a/src/engine/input_event.h +++ b/src/engine/input_event.h @@ -31,10 +31,7 @@ class InputEvent { size_t GetPointerId() const { return pointer_id_; } - base::Vector2 GetVector(size_t i) const { - DCHECK(i < 2); - return vec_; - } + base::Vector2 GetVector() const { return vec_; } char GetKeyPress() const { return key_; } diff --git a/src/engine/mesh.cc b/src/engine/mesh.cc index b2301f2..570c7ed 100644 --- a/src/engine/mesh.cc +++ b/src/engine/mesh.cc @@ -9,10 +9,6 @@ namespace eng { -// Used to parse the vertex layout, -// e.g. "p3f;c4b" for "position 3 floats, color 4 bytes". -const char Mesh::kLayoutDelimiter[] = ";/ \t"; - bool Mesh::Create(Primitive primitive, const std::string& vertex_description, size_t num_vertices, @@ -105,6 +101,8 @@ bool Mesh::Load(const std::string& file_name) { return false; } + LOG << "Loaded " << file_name << ". Vertex array size: " << vertices.size(); + vertices_ = std::make_unique(vertex_buffer_size); char* dst = vertices_.get(); diff --git a/src/engine/mesh.h b/src/engine/mesh.h index e246e2d..1f866f2 100644 --- a/src/engine/mesh.h +++ b/src/engine/mesh.h @@ -9,8 +9,6 @@ namespace eng { class Mesh { public: - static const char kLayoutDelimiter[]; - Mesh() = default; ~Mesh() = default; @@ -40,7 +38,7 @@ class Mesh { bool IsValid() const { return !!vertices_.get(); } - protected: + private: Primitive primitive_ = kPrimitive_TriangleStrip; VertexDescripton vertex_description_; size_t num_vertices_ = 0; diff --git a/src/engine/persistent_data.cc b/src/engine/persistent_data.cc new file mode 100644 index 0000000..9ff3293 --- /dev/null +++ b/src/engine/persistent_data.cc @@ -0,0 +1,104 @@ +#include "persistent_data.h" + +#include + +#include "../base/file.h" +#include "engine.h" +#include "platform/asset_file.h" + +using namespace base; + +namespace eng { + +bool PersistentData::Load(const std::string& file_name, StorageType type) { + file_name_ = file_name; + type_ = type; + + size_t size = 0; + std::unique_ptr buffer; + + if (type == kAsset) { + buffer = AssetFile::ReadWholeFile(file_name, Engine::Get().GetRootPath(), + &size, true); + } else { + std::string file_path = type == kShared ? Engine::Get().GetSharedDataPath() + : Engine::Get().GetDataPath(); + file_path += file_name; + ScopedFILE file; + file.reset(fopen(file_path.c_str(), "r")); + if (!file) { + LOG << "Failed to open file " << file_path; + return false; + } + + if (file) { + if (!fseek(file.get(), 0, SEEK_END)) { + size = ftell(file.get()); + rewind(file.get()); + } + } + + buffer = std::make_unique(size + 1); + size_t bytes_read = fread(buffer.get(), 1, size, file.get()); + if (!bytes_read) { + LOG << "Failed to read a buffer of size: " << size << " from file " + << file_path; + return false; + } + buffer[size] = 0; + } + + std::string err; + Json::CharReaderBuilder builder; + const std::unique_ptr reader(builder.newCharReader()); + if (!reader->parse(buffer.get(), buffer.get() + size, &root_, &err)) { + LOG << "Failed to parse save file. Json parser error: " << err; + return false; + } + + dirty_ = false; + + return true; +} + +bool PersistentData::Save(bool force) { + if (!force && !dirty_) + return true; + + DCHECK(!file_name_.empty()); + + return SaveAs(file_name_, type_); +} + +bool PersistentData::SaveAs(const std::string& file_name, StorageType type) { + DCHECK(type != kAsset); + + file_name_ = file_name; + + Json::StreamWriterBuilder builder; + std::unique_ptr writer(builder.newStreamWriter()); + std::ostringstream stream; + writer->write(root_, &stream); + + std::string file_path = type == kShared ? Engine::Get().GetSharedDataPath() + : Engine::Get().GetDataPath(); + file_path += file_name; + ScopedFILE file; + file.reset(fopen(file_path.c_str(), "w")); + if (!file) { + LOG << "Failed to create file " << file_path; + return false; + } + + std::string data = stream.str(); + if (fwrite(data.c_str(), data.size(), 1, file.get()) != 1) { + LOG << "Failed to write to file " << file_path; + return false; + } + + dirty_ = false; + + return true; +} + +} // namespace eng diff --git a/src/engine/persistent_data.h b/src/engine/persistent_data.h new file mode 100644 index 0000000..adfdde9 --- /dev/null +++ b/src/engine/persistent_data.h @@ -0,0 +1,40 @@ +#ifndef SAVE_GAME_H +#define SAVE_GAME_H + +#include + +#include "../base/log.h" +#include "../third_party/jsoncpp/json.h" + +namespace eng { + +class PersistentData { + public: + enum StorageType { kPrivate, kShared, kAsset }; + + PersistentData() = default; + ~PersistentData() = default; + + bool Load(const std::string& file_name, StorageType type = kPrivate); + + bool Save(bool force = false); + + bool SaveAs(const std::string& file_name, StorageType type = kPrivate); + + Json::Value& root() { + dirty_ = true; + return root_; + } + + const Json::Value& root() const { return root_; } + + private: + StorageType type_ = kPrivate; + std::string file_name_; + Json::Value root_; + bool dirty_ = false; +}; + +} // namespace eng + +#endif // SAVE_GAME_H diff --git a/src/engine/platform/platform_android.cc b/src/engine/platform/platform_android.cc index 068d696..50ec498 100644 --- a/src/engine/platform/platform_android.cc +++ b/src/engine/platform/platform_android.cc @@ -1,6 +1,7 @@ #include "platform_android.h" #include +#include #include #include @@ -17,6 +18,22 @@ using namespace base; namespace { +bool g_showing_interstitial_ad = false; + +extern "C" { +JNIEXPORT void JNICALL +Java_com_kaliber_base_KaliberActivity_onShowAdResult(JNIEnv* env, + jobject obj, + jboolean succeeded); +}; + +JNIEXPORT void JNICALL +Java_com_kaliber_base_KaliberActivity_onShowAdResult(JNIEnv* env, + jobject obj, + jboolean succeeded) { + g_showing_interstitial_ad = !!succeeded; +} + std::string GetApkPath(ANativeActivity* activity) { JNIEnv* env = nullptr; activity->vm->AttachCurrentThread(&env, nullptr); @@ -45,6 +62,107 @@ std::string GetApkPath(ANativeActivity* activity) { return apk_path; } +std::string GetDataPath(ANativeActivity* activity) { + JNIEnv* env = nullptr; + activity->vm->AttachCurrentThread(&env, nullptr); + + jclass activity_clazz = env->GetObjectClass(activity->clazz); + jmethodID get_dir_id = env->GetMethodID( + activity_clazz, "getDir", "(Ljava/lang/String;I)Ljava/io/File;"); + jstring suffix = env->NewStringUTF("kaliber"); + jobject data_dir_obj = env->CallObjectMethod(activity->clazz, get_dir_id, + suffix, 0 /* MODE_PRIVATE */); + + jclass file_clazz = env->FindClass("java/io/File"); + jmethodID get_absolute_path_id = + env->GetMethodID(file_clazz, "getAbsolutePath", "()Ljava/lang/String;"); + jstring data_path_obj = + (jstring)env->CallObjectMethod(data_dir_obj, get_absolute_path_id); + + const char* tmp = env->GetStringUTFChars(data_path_obj, nullptr); + std::string data_path = std::string(tmp); + + env->ReleaseStringUTFChars(data_path_obj, tmp); + env->DeleteLocalRef(activity_clazz); + env->DeleteLocalRef(file_clazz); + env->DeleteLocalRef(suffix); + activity->vm->DetachCurrentThread(); + + if (data_path.back() != '/') + data_path += '/'; + return data_path; +} + +std::string GetSharedDataPath(ANativeActivity* activity) { + JNIEnv* env = nullptr; + activity->vm->AttachCurrentThread(&env, nullptr); + + jclass activity_clazz = env->GetObjectClass(activity->clazz); + jmethodID get_dir_id = env->GetMethodID(activity_clazz, "getExternalFilesDir", + "(Ljava/lang/String;)Ljava/io/File;"); + jobject data_dir_obj = + env->CallObjectMethod(activity->clazz, get_dir_id, nullptr); + + jclass file_clazz = env->FindClass("java/io/File"); + jmethodID get_absolute_path_id = + env->GetMethodID(file_clazz, "getAbsolutePath", "()Ljava/lang/String;"); + jstring data_path_obj = + (jstring)env->CallObjectMethod(data_dir_obj, get_absolute_path_id); + + const char* tmp = env->GetStringUTFChars(data_path_obj, nullptr); + std::string data_path = std::string(tmp); + + env->ReleaseStringUTFChars(data_path_obj, tmp); + env->DeleteLocalRef(activity_clazz); + env->DeleteLocalRef(file_clazz); + activity->vm->DetachCurrentThread(); + + if (data_path.back() != '/') + data_path += '/'; + return data_path; +} + +void ShowInterstitialAd(ANativeActivity* activity) { + JNIEnv* env = nullptr; + activity->vm->AttachCurrentThread(&env, nullptr); + + jclass activity_clazz = env->GetObjectClass(activity->clazz); + jmethodID show_interstitial_ad = + env->GetMethodID(activity_clazz, "showInterstitialAd", "()V"); + env->CallVoidMethod(activity->clazz, show_interstitial_ad); + + env->DeleteLocalRef(activity_clazz); + activity->vm->DetachCurrentThread(); +} + +void ShareFile(ANativeActivity* activity, const std::string& file_name) { + JNIEnv* env = nullptr; + activity->vm->AttachCurrentThread(&env, nullptr); + + jclass activity_clazz = env->GetObjectClass(activity->clazz); + jmethodID show_interstitial_ad = + env->GetMethodID(activity_clazz, "shareFile", "(Ljava/lang/String;)V"); + jstring file_name_js = env->NewStringUTF(file_name.c_str()); + env->CallVoidMethod(activity->clazz, show_interstitial_ad, file_name_js); + + env->DeleteLocalRef(activity_clazz); + env->DeleteLocalRef(file_name_js); + activity->vm->DetachCurrentThread(); +} + +void SetKeepScreenOn(ANativeActivity* activity, bool keep_screen_on) { + JNIEnv* env = nullptr; + activity->vm->AttachCurrentThread(&env, nullptr); + + jclass activity_clazz = env->GetObjectClass(activity->clazz); + jmethodID method_id = + env->GetMethodID(activity_clazz, "setKeepScreenOn", "(Z)V"); + env->CallVoidMethod(activity->clazz, method_id, (jboolean)keep_screen_on); + + env->DeleteLocalRef(activity_clazz); + activity->vm->DetachCurrentThread(); +} + void Vibrate(ANativeActivity* activity, int duration) { JNIEnv* env = nullptr; activity->vm->AttachCurrentThread(&env, nullptr); @@ -78,7 +196,7 @@ void Vibrate(ANativeActivity* activity, int duration) { activity->vm->DetachCurrentThread(); } -int32_t getDensityDpi(android_app* app) { +int32_t GetDensityDpi(android_app* app) { AConfiguration* config = AConfiguration_new(); AConfiguration_fromAssetManager(config, app->activity->assetManager); int32_t density = AConfiguration_getDensity(config); @@ -129,7 +247,7 @@ int32_t PlatformAndroid::HandleInput(android_app* app, AInputEvent* event) { switch (flags) { case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_POINTER_DOWN: - DLOG << "AMOTION_EVENT_ACTION_DOWN - pointer_id: " << pointer_id; + // DLOG << "AMOTION_EVENT_ACTION_DOWN - pointer_id: " << pointer_id; platform->pointer_pos_[pointer_id] = pos[pointer_id]; platform->pointer_down_[pointer_id] = true; input_event = @@ -139,7 +257,7 @@ int32_t PlatformAndroid::HandleInput(android_app* app, AInputEvent* event) { case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_POINTER_UP: - DLOG << "AMOTION_EVENT_ACTION_UP - pointer_id: " << pointer_id; + // DLOG << "AMOTION_EVENT_ACTION_UP - pointer_id: " << pointer_id; platform->pointer_pos_[pointer_id] = pos[pointer_id]; platform->pointer_down_[pointer_id] = false; input_event = std::make_unique( @@ -220,7 +338,8 @@ void PlatformAndroid::HandleCmd(android_app* app, int32_t cmd) { platform->timer_.Reset(); platform->has_focus_ = true; if (platform->engine_) - platform->engine_->GainedFocus(); + platform->engine_->GainedFocus(g_showing_interstitial_ad); + g_showing_interstitial_ad = false; break; case APP_CMD_LOST_FOCUS: @@ -243,10 +362,16 @@ void PlatformAndroid::Initialize(android_app* app) { mobile_device_ = true; - root_path_ = GetApkPath(app->activity); + root_path_ = ::GetApkPath(app->activity); LOG << "Root path: " << root_path_.c_str(); - device_dpi_ = getDensityDpi(app); + data_path_ = ::GetDataPath(app->activity); + LOG << "Data path: " << data_path_.c_str(); + + shared_data_path_ = ::GetSharedDataPath(app->activity); + LOG << "Shared data path: " << shared_data_path_.c_str(); + + device_dpi_ = ::GetDensityDpi(app); LOG << "Device DPI: " << device_dpi_; app->userData = reinterpret_cast(this); @@ -283,6 +408,18 @@ void PlatformAndroid::Vibrate(int duration) { ::Vibrate(app_->activity, duration); } +void PlatformAndroid::ShowInterstitialAd() { + ::ShowInterstitialAd(app_->activity); +} + +void PlatformAndroid::ShareFile(const std::string& file_name) { + ::ShareFile(app_->activity, file_name); +} + +void PlatformAndroid::SetKeepScreenOn(bool keep_screen_on) { + ::SetKeepScreenOn(app_->activity, keep_screen_on); +} + } // namespace eng void android_main(android_app* app) { diff --git a/src/engine/platform/platform_android.h b/src/engine/platform/platform_android.h index 430b1de..2cee0c7 100644 --- a/src/engine/platform/platform_android.h +++ b/src/engine/platform/platform_android.h @@ -22,6 +22,12 @@ class PlatformAndroid : public PlatformBase { void Vibrate(int duration); + void ShowInterstitialAd(); + + void ShareFile(const std::string& file_name); + + void SetKeepScreenOn(bool keep_screen_on); + private: android_app* app_ = nullptr; diff --git a/src/engine/platform/platform_base.cc b/src/engine/platform/platform_base.cc index e519e31..e46c9b2 100644 --- a/src/engine/platform/platform_base.cc +++ b/src/engine/platform/platform_base.cc @@ -1,15 +1,10 @@ #include "platform.h" -#include - #include "../../base/log.h" #include "../../base/task_runner.h" #include "../audio/audio.h" #include "../engine.h" -#include "../renderer/renderer.h" - -// Save battery on mobile devices. -#define USE_SLEEP +#include "../renderer/opengl/renderer_opengl.h" using namespace base; @@ -33,7 +28,7 @@ void PlatformBase::Initialize() { throw internal_error; } - renderer_ = std::make_unique(); + renderer_ = std::make_unique(); } void PlatformBase::Shutdown() { @@ -52,11 +47,7 @@ void PlatformBase::RunMainLoop() { } // Use fixed time steps. - constexpr float time_step = 1.0f / 60.0f; - -#ifdef USE_SLEEP - constexpr float epsilon = 0.0001f; -#endif // USE_SLEEP + float time_step = engine_->time_step(); timer_.Reset(); float accumulator = 0.0; @@ -66,20 +57,8 @@ void PlatformBase::RunMainLoop() { engine_->Draw(frame_frac); // Accumulate time. -#ifdef USE_SLEEP - while (accumulator < time_step) { - timer_.Update(); - accumulator += timer_.GetSecondsPassed(); - if (time_step - accumulator > epsilon) { - float sleep_time = time_step - accumulator - epsilon; - std::this_thread::sleep_for( - std::chrono::microseconds((int)(sleep_time * 1000000.0f))); - } - }; -#else timer_.Update(); accumulator += timer_.GetSecondsPassed(); -#endif // USE_SLEEP // Subdivide the frame time. while (accumulator >= time_step) { diff --git a/src/engine/platform/platform_base.h b/src/engine/platform/platform_base.h index 864fd2c..a40dfdf 100644 --- a/src/engine/platform/platform_base.h +++ b/src/engine/platform/platform_base.h @@ -26,6 +26,10 @@ class PlatformBase { const std::string& GetRootPath() const { return root_path_; } + const std::string& GetDataPath() const { return data_path_; } + + const std::string& GetSharedDataPath() const { return shared_data_path_; } + bool mobile_device() const { return mobile_device_; } static class InternalError : public std::exception { @@ -35,8 +39,10 @@ class PlatformBase { base::Timer timer_; bool mobile_device_ = false; - int device_dpi_ = 200; + int device_dpi_ = 100; std::string root_path_; + std::string data_path_; + std::string shared_data_path_; bool has_focus_ = false; bool should_exit_ = false; diff --git a/src/engine/platform/platform_linux.cc b/src/engine/platform/platform_linux.cc index 314b867..64e88bf 100644 --- a/src/engine/platform/platform_linux.cc +++ b/src/engine/platform/platform_linux.cc @@ -1,8 +1,5 @@ #include "platform_linux.h" -#include -#include - #include #include "../../base/log.h" @@ -26,25 +23,39 @@ void PlatformLinux::Initialize() { root_path_ = "../../"; LOG << "Root path: " << root_path_.c_str(); - if (!renderer_->Initialize()) { + data_path_ = "./"; + LOG << "Data path: " << data_path_.c_str(); + + shared_data_path_ = "./"; + LOG << "Shared data path: " << shared_data_path_.c_str(); + + if (!CreateWindow(800, 1205)) { + LOG << "Failed to create window."; + throw internal_error; + } + + if (!renderer_->Initialize(display_, window_)) { LOG << "Failed to initialize renderer."; throw internal_error; } - Display* display = renderer_->display(); - Window window = renderer_->window(); - XSelectInput(display, window, + XSelectInput(display_, window_, KeyPressMask | Button1MotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask); - Atom WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", false); - XSetWMProtocols(display, window, &WM_DELETE_WINDOW, 1); + Atom WM_DELETE_WINDOW = XInternAtom(display_, "WM_DELETE_WINDOW", false); + XSetWMProtocols(display_, window_, &WM_DELETE_WINDOW, 1); +} + +void PlatformLinux::Shutdown() { + PlatformBase::Shutdown(); + + DestroyWindow(); } void PlatformLinux::Update() { - Display* display = renderer_->display(); - while (XPending(display)) { + while (XPending(display_)) { XEvent e; - XNextEvent(display, &e); + XNextEvent(display_, &e); switch (e.type) { case KeyPress: { KeySym key = XLookupKeysym(&e.xkey, 0); @@ -90,7 +101,7 @@ void PlatformLinux::Update() { break; } case FocusIn: { - engine_->GainedFocus(); + engine_->GainedFocus(false); break; } case ClientMessage: { @@ -106,6 +117,47 @@ void PlatformLinux::Exit() { should_exit_ = true; } +bool PlatformLinux::CreateWindow(int width, int height) { + // Try to open the local display. + display_ = XOpenDisplay(NULL); + if (!display_) { + LOG << "Can't connect to X server. Try to set the DISPLAY environment " + "variable (hostname:number.screen_number)."; + return false; + } + + Window root_window = DefaultRootWindow(display_); + + XVisualInfo* visual_info = renderer_->GetXVisualInfo(display_); + if (!visual_info) { + LOG << "No appropriate visual found."; + return false; + } + LOG << "Visual " << (void*)visual_info->visualid << " selected"; + + // Create the main window. + XSetWindowAttributes window_attributes; + window_attributes.colormap = + XCreateColormap(display_, root_window, visual_info->visual, AllocNone); + window_attributes.event_mask = ExposureMask | KeyPressMask; + window_ = XCreateWindow(display_, root_window, 0, 0, width, height, 0, + visual_info->depth, InputOutput, visual_info->visual, + CWColormap | CWEventMask, &window_attributes); + XMapWindow(display_, window_); + XStoreName(display_, window_, "kaliber"); + + return true; +} + +void PlatformLinux::DestroyWindow() { + if (display_) { + XDestroyWindow(display_, window_); + XCloseDisplay(display_); + display_ = nullptr; + window_ = 0; + } +} + } // namespace eng int main(int argc, char** argv) { diff --git a/src/engine/platform/platform_linux.h b/src/engine/platform/platform_linux.h index a89b21d..efb7385 100644 --- a/src/engine/platform/platform_linux.h +++ b/src/engine/platform/platform_linux.h @@ -1,6 +1,9 @@ #ifndef PLATFORM_LINUX_H #define PLATFORM_LINUX_H +#include +#include + #include "platform_base.h" namespace eng { @@ -12,11 +15,26 @@ class PlatformLinux : public PlatformBase { void Initialize(); + void Shutdown(); + void Update(); void Exit(); void Vibrate(int duration) {} + + void ShowInterstitialAd() {} + + void ShareFile(const std::string& file_name) {} + + void SetKeepScreenOn(bool keep_screen_on) {} + + private: + Display* display_ = nullptr; + Window window_ = 0; + + bool CreateWindow(int width, int height); + void DestroyWindow(); }; } // namespace eng diff --git a/src/engine/renderer/geometry.cc b/src/engine/renderer/geometry.cc index 3c2f866..9ff312f 100644 --- a/src/engine/renderer/geometry.cc +++ b/src/engine/renderer/geometry.cc @@ -1,8 +1,6 @@ #include "geometry.h" -#include "../engine.h" #include "../mesh.h" -#include "render_command.h" #include "renderer.h" namespace eng { @@ -19,30 +17,21 @@ Geometry::~Geometry() { void Geometry::Create(std::unique_ptr mesh) { Destroy(); valid_ = true; - vertex_description_ = mesh->vertex_description(); - - auto cmd = std::make_unique(); - cmd->mesh = std::move(mesh); - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); + primitive_ = mesh->primitive(); + renderer_->CreateGeometry(impl_data_, std::move(mesh)); } void Geometry::Destroy() { if (valid_) { - auto cmd = std::make_unique(); - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); + renderer_->DestroyGeometry(impl_data_); valid_ = false; } } void Geometry::Draw() { - if (valid_) { - auto cmd = std::make_unique(); - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); - } + if (valid_) + renderer_->Draw(impl_data_); } } // namespace eng diff --git a/src/engine/renderer/geometry.h b/src/engine/renderer/geometry.h index 888c559..78c9f79 100644 --- a/src/engine/renderer/geometry.h +++ b/src/engine/renderer/geometry.h @@ -29,8 +29,11 @@ class Geometry : public RenderResource { return vertex_description_; } + Primitive primitive() { return primitive_; } + private: VertexDescripton vertex_description_; + Primitive primitive_ = kPrimitive_Invalid; }; } // namespace eng diff --git a/src/engine/renderer/opengl.h b/src/engine/renderer/opengl/opengl.h similarity index 74% rename from src/engine/renderer/opengl.h rename to src/engine/renderer/opengl/opengl.h index f279765..5549cbf 100644 --- a/src/engine/renderer/opengl.h +++ b/src/engine/renderer/opengl/opengl.h @@ -5,12 +5,12 @@ // Use the modified Khronos header from ndk-helper. This gives access to // additional functionality the drivers may expose but which the system headers // do not. -#include "../../third_party/android/gl3stub.h" +#include "../../../third_party/android/gl3stub.h" #include #elif defined(__linux__) -#include "../../third_party/glew/glew.h" -#include "../../third_party/glew/glxew.h" +#include "../../../third_party/glew/glew.h" +#include "../../../third_party/glew/glxew.h" // Define the missing format for the etc1 #ifndef GL_ETC1_RGB8_OES diff --git a/src/engine/renderer/render_command.cc b/src/engine/renderer/opengl/render_command.cc similarity index 92% rename from src/engine/renderer/render_command.cc rename to src/engine/renderer/opengl/render_command.cc index a4ee8ab..deed111 100644 --- a/src/engine/renderer/render_command.cc +++ b/src/engine/renderer/opengl/render_command.cc @@ -1,8 +1,8 @@ #include "render_command.h" -#include "../image.h" -#include "../mesh.h" -#include "../shader_source.h" +#include "../../image.h" +#include "../../mesh.h" +#include "../../shader_source.h" #ifdef _DEBUG #define RENDER_COMMAND_IMPL(NAME, GLOBAL) \ diff --git a/src/engine/renderer/render_command.h b/src/engine/renderer/opengl/render_command.h similarity index 94% rename from src/engine/renderer/render_command.h rename to src/engine/renderer/opengl/render_command.h index db1b91e..51289be 100644 --- a/src/engine/renderer/render_command.h +++ b/src/engine/renderer/opengl/render_command.h @@ -5,9 +5,9 @@ #include #include -#include "../../base/hash.h" -#include "../../base/vecmath.h" -#include "renderer_types.h" +#include "../../../base/hash.h" +#include "../../../base/vecmath.h" +#include "../renderer_types.h" namespace eng { @@ -39,7 +39,7 @@ struct RenderCommand { const CommandId cmd_id = INVALID_CMD_ID; // Global render commands are guaranteed to be processed. Others commands are - // frame specific and can be discared by the renderer. + // frame specific and can be discared by the renderer if not throttled. const bool global = false; #ifdef _DEBUG diff --git a/src/engine/renderer/renderer.cc b/src/engine/renderer/opengl/renderer_opengl.cc similarity index 74% rename from src/engine/renderer/renderer.cc rename to src/engine/renderer/opengl/renderer_opengl.cc index 7249a82..b6bafb4 100644 --- a/src/engine/renderer/renderer.cc +++ b/src/engine/renderer/opengl/renderer_opengl.cc @@ -1,21 +1,22 @@ -#include "renderer.h" +#include "renderer_opengl.h" #include #include #include +#include -#include "../../base/log.h" -#include "../../base/vecmath.h" +#include "../../../base/log.h" +#include "../../../base/vecmath.h" #ifdef THREADED_RENDERING -#include "../../base/task_runner.h" +#include "../../../base/task_runner.h" #endif // THREADED_RENDERING -#include "../image.h" -#include "../mesh.h" -#include "../shader_source.h" -#include "geometry.h" +#include "../../image.h" +#include "../../mesh.h" +#include "../../shader_source.h" +#include "../geometry.h" +#include "../shader.h" +#include "../texture.h" #include "render_command.h" -#include "shader.h" -#include "texture.h" using namespace base; @@ -29,44 +30,170 @@ constexpr GLenum kGlDataType[eng::kDataType_Max] = { GL_SHORT, GL_UNSIGNED_INT, GL_UNSIGNED_SHORT}; const std::string kAttributeNames[eng::kAttribType_Max] = { - "inColor", "inNormal", "inPosition", "inTexCoord"}; + "in_color", "in_normal", "in_position", "in_tex_coord"}; } // namespace namespace eng { #ifdef THREADED_RENDERING -Renderer::Renderer() +RendererOpenGL::RendererOpenGL() : main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()) {} #else -Renderer::Renderer() = default; +RendererOpenGL::RendererOpenGL() = default; #endif // THREADED_RENDERING -Renderer::~Renderer() = default; +RendererOpenGL::~RendererOpenGL() = default; -void Renderer::SetContextLostCB(Closure cb) { - context_lost_cb_ = std::move(cb); +void RendererOpenGL::CreateGeometry(std::shared_ptr impl_data, + std::unique_ptr mesh) { + auto cmd = std::make_unique(); + cmd->mesh = std::move(mesh); + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); } -void Renderer::ContextLost() { +void RendererOpenGL::DestroyGeometry(std::shared_ptr impl_data) { + auto cmd = std::make_unique(); + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::Draw(std::shared_ptr impl_data) { + auto cmd = std::make_unique(); + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::UpdateTexture(std::shared_ptr impl_data, + std::unique_ptr image) { + auto cmd = std::make_unique(); + cmd->image = std::move(image); + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::DestroyTexture(std::shared_ptr impl_data) { + auto cmd = std::make_unique(); + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::ActivateTexture(std::shared_ptr impl_data) { + auto cmd = std::make_unique(); + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::CreateShader(std::shared_ptr impl_data, + std::unique_ptr source, + const VertexDescripton& vertex_description, + Primitive primitive) { + auto cmd = std::make_unique(); + cmd->source = std::move(source); + cmd->vertex_description = vertex_description; + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::DestroyShader(std::shared_ptr impl_data) { + auto cmd = std::make_unique(); + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::ActivateShader(std::shared_ptr impl_data) { + auto cmd = std::make_unique(); + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Vector2& val) { + auto cmd = std::make_unique(); + cmd->name = name; + cmd->v = val; + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Vector3& val) { + auto cmd = std::make_unique(); + cmd->name = name; + cmd->v = val; + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Vector4& val) { + auto cmd = std::make_unique(); + cmd->name = name; + cmd->v = val; + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Matrix4x4& val) { + auto cmd = std::make_unique(); + cmd->name = name; + cmd->m = val; + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::SetUniform(std::shared_ptr impl_data, + const std::string& name, + float val) { + auto cmd = std::make_unique(); + cmd->name = name; + cmd->f = val; + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::SetUniform(std::shared_ptr impl_data, + const std::string& name, + int val) { + auto cmd = std::make_unique(); + cmd->name = name; + cmd->i = val; + cmd->impl_data = impl_data; + EnqueueCommand(std::move(cmd)); +} + +void RendererOpenGL::Present() { + EnqueueCommand(std::make_unique()); +#ifdef THREADED_RENDERING + draw_complete_semaphore_.Acquire(); +#endif // THREADED_RENDERING + fps_++; +} + +void RendererOpenGL::ContextLost() { LOG << "Context lost."; #ifdef THREADED_RENDERING global_commands_.clear(); draw_commands_[0].clear(); draw_commands_[1].clear(); -#endif // THREADED_RENDERING - InvalidateAllResources(); - -#ifdef THREADED_RENDERING - main_thread_task_runner_->EnqueueTask(HERE, context_lost_cb_); + main_thread_task_runner_->PostTask( + HERE, std::bind(&RendererOpenGL::InvalidateAllResources, this)); + main_thread_task_runner_->PostTask(HERE, context_lost_cb_); #else + InvalidateAllResources(); context_lost_cb_(); #endif // THREADED_RENDERING } -std::unique_ptr Renderer::CreateResource( +std::unique_ptr RendererOpenGL::CreateResource( RenderResourceFactoryBase& factory) { static unsigned last_id = 0; @@ -88,48 +215,19 @@ std::unique_ptr Renderer::CreateResource( return resource; } -void Renderer::ReleaseResource(unsigned resource_id) { +void RendererOpenGL::ReleaseResource(unsigned resource_id) { auto it = resources_.find(resource_id); if (it != resources_.end()) resources_.erase(it); } -void Renderer::EnqueueCommand(std::unique_ptr cmd) { -#ifdef THREADED_RENDERING - if (cmd->global) { - { - std::unique_lock scoped_lock(mutex_); - global_commands_.push_back(std::move(cmd)); - } - cv_.notify_one(); - global_queue_size_ = global_commands_.size(); - return; - } - - bool new_frame = cmd->cmd_id == CmdPresent::CMD_ID; - draw_commands_[1].push_back(std::move(cmd)); - if (new_frame) { - render_queue_size_ = draw_commands_[1].size(); - { - std::unique_lock scoped_lock(mutex_); - draw_commands_[0].swap(draw_commands_[1]); - } - cv_.notify_one(); - fps_ += draw_commands_[1].size() ? 0 : 1; - draw_commands_[1].clear(); - } -#else - ProcessCommand(cmd.get()); -#endif // THREADED_RENDERING -} - -size_t Renderer::GetAndResetFPS() { +size_t RendererOpenGL::GetAndResetFPS() { int ret = fps_; fps_ = 0; return ret; } -bool Renderer::InitCommon() { +bool RendererOpenGL::InitCommon() { // Get information about the currently active context. const char* renderer = reinterpret_cast(glGetString(GL_RENDERER)); @@ -187,10 +285,8 @@ bool Renderer::InitCommon() { } // Ancient hardware is not supported. - if (!npot_) { + if (!npot_) LOG << "NPOT not supported."; - return false; - } if (vertex_array_objects_) LOG << "Supports Vertex Array Objects."; @@ -211,12 +307,12 @@ bool Renderer::InitCommon() { return true; } -void Renderer::InvalidateAllResources() { +void RendererOpenGL::InvalidateAllResources() { for (auto& r : resources_) r.second->Destroy(); } -bool Renderer::StartRenderThread() { +bool RendererOpenGL::StartRenderThread() { #ifdef THREADED_RENDERING LOG << "Starting render thread."; @@ -228,7 +324,7 @@ bool Renderer::StartRenderThread() { std::promise promise; std::future future = promise.get_future(); render_thread_ = - std::thread(&Renderer::RenderThreadMain, this, std::move(promise)); + std::thread(&RendererOpenGL::RenderThreadMain, this, std::move(promise)); future.wait(); return future.get(); #else @@ -237,7 +333,7 @@ bool Renderer::StartRenderThread() { #endif // THREADED_RENDERING } -void Renderer::TerminateRenderThread() { +void RendererOpenGL::TerminateRenderThread() { #ifdef THREADED_RENDERING DCHECK(!terminate_render_thread_); @@ -256,7 +352,7 @@ void Renderer::TerminateRenderThread() { #ifdef THREADED_RENDERING -void Renderer::RenderThreadMain(std::promise promise) { +void RendererOpenGL::RenderThreadMain(std::promise promise) { promise.set_value(InitInternal()); std::deque> cq[2]; @@ -291,7 +387,33 @@ void Renderer::RenderThreadMain(std::promise promise) { #endif // THREADED_RENDERING -void Renderer::ProcessCommand(RenderCommand* cmd) { +void RendererOpenGL::EnqueueCommand(std::unique_ptr cmd) { +#ifdef THREADED_RENDERING + if (cmd->global) { + { + std::unique_lock scoped_lock(mutex_); + global_commands_.push_back(std::move(cmd)); + } + cv_.notify_one(); + return; + } + + bool new_frame = cmd->cmd_id == CmdPresent::CMD_ID; + draw_commands_[1].push_back(std::move(cmd)); + if (new_frame) { + { + std::unique_lock scoped_lock(mutex_); + draw_commands_[0].swap(draw_commands_[1]); + } + cv_.notify_one(); + draw_commands_[1].clear(); + } +#else + ProcessCommand(cmd.get()); +#endif // THREADED_RENDERING +} + +void RendererOpenGL::ProcessCommand(RenderCommand* cmd) { #if 0 LOG << "Processing command: " << cmd->cmd_name.c_str(); #endif @@ -350,7 +472,7 @@ void Renderer::ProcessCommand(RenderCommand* cmd) { } } -void Renderer::HandleCmdUpdateTexture(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdUpdateTexture(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); bool new_texture = impl_data->id == 0; @@ -418,7 +540,7 @@ void Renderer::HandleCmdUpdateTexture(RenderCommand* cmd) { } } -void Renderer::HandleCmdDestoryTexture(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdDestoryTexture(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->id > 0) { @@ -427,14 +549,16 @@ void Renderer::HandleCmdDestoryTexture(RenderCommand* cmd) { } } -void Renderer::HandleCmdActivateTexture(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdActivateTexture(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); - if (impl_data->id > 0) + if (impl_data->id > 0 && impl_data->id != active_texture_id_) { glBindTexture(GL_TEXTURE_2D, impl_data->id); + active_texture_id_ = impl_data->id; + } } -void Renderer::HandleCmdCreateGeometry(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdCreateGeometry(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->vertex_buffer_id > 0) @@ -448,7 +572,7 @@ void Renderer::HandleCmdCreateGeometry(RenderCommand* cmd) { } GLuint vertex_array_id = 0; - if (SupportsVAO()) { + if (vertex_array_objects_) { glGenVertexArrays(1, &vertex_array_id); glBindVertexArray(vertex_array_id); } @@ -464,7 +588,7 @@ void Renderer::HandleCmdCreateGeometry(RenderCommand* cmd) { // set up. std::vector vertex_layout; if (!SetupVertexLayout(c->mesh->vertex_description(), vertex_size, - SupportsVAO(), vertex_layout)) + vertex_array_objects_, vertex_layout)) return; // Create the index buffer and upload the data. @@ -497,7 +621,7 @@ void Renderer::HandleCmdCreateGeometry(RenderCommand* cmd) { index_buffer_id}; } -void Renderer::HandleCmdDestroyGeometry(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdDestroyGeometry(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->vertex_buffer_id == 0) @@ -513,7 +637,7 @@ void Renderer::HandleCmdDestroyGeometry(RenderCommand* cmd) { *impl_data = {}; } -void Renderer::HandleCmdDrawGeometry(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdDrawGeometry(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->vertex_buffer_id == 0) @@ -559,7 +683,7 @@ void Renderer::HandleCmdDrawGeometry(RenderCommand* cmd) { } } -void Renderer::HandleCmdCreateShader(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdCreateShader(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->id > 0) @@ -604,7 +728,7 @@ void Renderer::HandleCmdCreateShader(RenderCommand* cmd) { *impl_data = {id, {}}; } -void Renderer::HandleCmdDestroyShader(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdDestroyShader(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->id > 0) { @@ -613,14 +737,16 @@ void Renderer::HandleCmdDestroyShader(RenderCommand* cmd) { } } -void Renderer::HandleCmdActivateShader(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdActivateShader(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); - if (impl_data->id > 0) + if (impl_data->id > 0 && impl_data->id != active_shader_id_) { glUseProgram(impl_data->id); + active_shader_id_ = impl_data->id; + } } -void Renderer::HandleCmdSetUniformVec2(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdSetUniformVec2(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->id > 0) { @@ -631,7 +757,7 @@ void Renderer::HandleCmdSetUniformVec2(RenderCommand* cmd) { } } -void Renderer::HandleCmdSetUniformVec3(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdSetUniformVec3(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->id > 0) { @@ -642,7 +768,7 @@ void Renderer::HandleCmdSetUniformVec3(RenderCommand* cmd) { } } -void Renderer::HandleCmdSetUniformVec4(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdSetUniformVec4(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->id > 0) { @@ -653,7 +779,7 @@ void Renderer::HandleCmdSetUniformVec4(RenderCommand* cmd) { } } -void Renderer::HandleCmdSetUniformMat4(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdSetUniformMat4(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->id > 0) { @@ -664,7 +790,7 @@ void Renderer::HandleCmdSetUniformMat4(RenderCommand* cmd) { } } -void Renderer::HandleCmdSetUniformFloat(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdSetUniformFloat(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->id > 0) { @@ -675,7 +801,7 @@ void Renderer::HandleCmdSetUniformFloat(RenderCommand* cmd) { } } -void Renderer::HandleCmdSetUniformInt(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdSetUniformInt(RenderCommand* cmd) { auto* c = static_cast(cmd); auto impl_data = reinterpret_cast(c->impl_data.get()); if (impl_data->id > 0) { @@ -686,7 +812,7 @@ void Renderer::HandleCmdSetUniformInt(RenderCommand* cmd) { } } -bool Renderer::SetupVertexLayout( +bool RendererOpenGL::SetupVertexLayout( const VertexDescripton& vd, GLuint vertex_size, bool use_vao, @@ -726,7 +852,7 @@ bool Renderer::SetupVertexLayout( return true; } -GLuint Renderer::CreateShader(const char* source, GLenum type) { +GLuint RendererOpenGL::CreateShader(const char* source, GLenum type) { GLuint shader = glCreateShader(type); if (shader) { glShaderSource(shader, 1, &source, NULL); @@ -751,7 +877,8 @@ GLuint Renderer::CreateShader(const char* source, GLenum type) { return shader; } -bool Renderer::BindAttributeLocation(GLuint id, const VertexDescripton& vd) { +bool RendererOpenGL::BindAttributeLocation(GLuint id, + const VertexDescripton& vd) { int current = 0; int tex_coord = 0; @@ -765,7 +892,7 @@ bool Renderer::BindAttributeLocation(GLuint id, const VertexDescripton& vd) { return current > 0; } -GLint Renderer::GetUniformLocation( +GLint RendererOpenGL::GetUniformLocation( GLuint id, const std::string& name, std::unordered_map& uniforms) { diff --git a/src/engine/renderer/opengl/renderer_opengl.h b/src/engine/renderer/opengl/renderer_opengl.h new file mode 100644 index 0000000..0394199 --- /dev/null +++ b/src/engine/renderer/opengl/renderer_opengl.h @@ -0,0 +1,215 @@ +#ifndef RENDERER_OPENGL_H +#define RENDERER_OPENGL_H + +#include +#include +#include +#include + +#define THREADED_RENDERING + +#ifdef THREADED_RENDERING +#include +#include +#include +#include +#include + +#include "../../../base/semaphore.h" +#endif // THREADED_RENDERING + +#include "opengl.h" + +#include "../render_resource.h" +#include "../renderer.h" + +#if defined(__ANDROID__) +struct ANativeWindow; +#endif + +#ifdef THREADED_RENDERING +namespace base { +class TaskRunner; +} +#endif // THREADED_RENDERING + +namespace eng { + +struct RenderCommand; + +class RendererOpenGL : public Renderer { + public: + RendererOpenGL(); + ~RendererOpenGL() override; + +#if defined(__ANDROID__) + bool Initialize(ANativeWindow* window) override; +#elif defined(__linux__) + bool Initialize(Display* display, Window window) override; +#endif + + void Shutdown() override; + + void CreateGeometry(std::shared_ptr impl_data, + std::unique_ptr mesh) override; + void DestroyGeometry(std::shared_ptr impl_data) override; + void Draw(std::shared_ptr impl_data) override; + + void UpdateTexture(std::shared_ptr impl_data, + std::unique_ptr image) override; + void DestroyTexture(std::shared_ptr impl_data) override; + void ActivateTexture(std::shared_ptr impl_data) override; + + void CreateShader(std::shared_ptr impl_data, + std::unique_ptr source, + const VertexDescripton& vertex_description, + Primitive primitive) override; + void DestroyShader(std::shared_ptr impl_data) override; + void ActivateShader(std::shared_ptr impl_data) override; + + void SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Vector2& val) override; + void SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Vector3& val) override; + void SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Vector4& val) override; + void SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Matrix4x4& val) override; + void SetUniform(std::shared_ptr impl_data, + const std::string& name, + float val) override; + void SetUniform(std::shared_ptr impl_data, + const std::string& name, + int val) override; + void UploadUniforms(std::shared_ptr impl_data) override {} + + void PrepareForDrawing() override {} + void Present() override; + + std::unique_ptr CreateResource( + RenderResourceFactoryBase& factory) override; + void ReleaseResource(unsigned resource_id) override; + + size_t GetAndResetFPS() override; + +#if defined(__linux__) && !defined(__ANDROID__) + XVisualInfo* GetXVisualInfo(Display* display) override; +#endif + + private: + struct GeometryOpenGL { + struct Element { + GLsizei num_elements; + GLenum type; + size_t vertex_offset; + }; + + GLsizei num_vertices = 0; + GLsizei num_indices = 0; + GLenum primitive = 0; + GLenum index_type = 0; + std::vector vertex_layout; + GLuint vertex_size = 0; + GLuint vertex_array_id = 0; + GLuint vertex_buffer_id = 0; + GLuint index_buffer_id = 0; + }; + + struct ShaderOpenGL { + GLuint id = 0; + std::unordered_map uniforms; + }; + + struct TextureOpenGL { + GLuint id = 0; + }; + + GLuint active_shader_id_ = 0; + GLuint active_texture_id_ = 0; + + bool vertex_array_objects_ = false; + bool npot_ = false; + + std::unordered_map resources_; + +#ifdef THREADED_RENDERING + // Global commands are independent from frames and guaranteed to be processed. + std::deque> global_commands_; + // Draw commands are fame specific and can be discarded if the rendering is + // not throttled. + std::deque> draw_commands_[2]; + + std::condition_variable cv_; + std::mutex mutex_; + std::thread render_thread_; + bool terminate_render_thread_ = false; + + base::Semaphore draw_complete_semaphore_; + + base::TaskRunner* main_thread_task_runner_; +#endif // THREADED_RENDERING + + // Stats. + size_t fps_ = 0; + +#if defined(__ANDROID__) + ANativeWindow* window_; +#elif defined(__linux__) + Display* display_ = NULL; + Window window_ = 0; + GLXContext glx_context_ = NULL; +#endif + + bool InitInternal(); + bool InitCommon(); + void ShutdownInternal(); + + void ContextLost(); + + void InvalidateAllResources(); + + bool StartRenderThread(); + void TerminateRenderThread(); + +#ifdef THREADED_RENDERING + void RenderThreadMain(std::promise promise); +#endif // THREADED_RENDERING + + void EnqueueCommand(std::unique_ptr cmd); + void ProcessCommand(RenderCommand* cmd); + + void HandleCmdPresent(RenderCommand* cmd); + void HandleCmdUpdateTexture(RenderCommand* cmd); + void HandleCmdDestoryTexture(RenderCommand* cmd); + void HandleCmdActivateTexture(RenderCommand* cmd); + void HandleCmdCreateGeometry(RenderCommand* cmd); + void HandleCmdDestroyGeometry(RenderCommand* cmd); + void HandleCmdDrawGeometry(RenderCommand* cmd); + void HandleCmdCreateShader(RenderCommand* cmd); + void HandleCmdDestroyShader(RenderCommand* cmd); + void HandleCmdActivateShader(RenderCommand* cmd); + void HandleCmdSetUniformVec2(RenderCommand* cmd); + void HandleCmdSetUniformVec3(RenderCommand* cmd); + void HandleCmdSetUniformVec4(RenderCommand* cmd); + void HandleCmdSetUniformMat4(RenderCommand* cmd); + void HandleCmdSetUniformFloat(RenderCommand* cmd); + void HandleCmdSetUniformInt(RenderCommand* cmd); + + bool SetupVertexLayout(const VertexDescripton& vd, + GLuint vertex_size, + bool use_vao, + std::vector& vertex_layout); + GLuint CreateShader(const char* source, GLenum type); + bool BindAttributeLocation(GLuint id, const VertexDescripton& vd); + GLint GetUniformLocation(GLuint id, + const std::string& name, + std::unordered_map& uniforms); +}; + +} // namespace eng + +#endif // RENDERER_OPENGL_H diff --git a/src/engine/renderer/renderer_android.cc b/src/engine/renderer/opengl/renderer_opengl_android.cc similarity index 71% rename from src/engine/renderer/renderer_android.cc rename to src/engine/renderer/opengl/renderer_opengl_android.cc index c590f41..4c93e7d 100644 --- a/src/engine/renderer/renderer_android.cc +++ b/src/engine/renderer/opengl/renderer_opengl_android.cc @@ -1,20 +1,20 @@ -#include "renderer.h" +#include "renderer_opengl.h" #include -#include "../../base/log.h" -#include "../../third_party/android/GLContext.h" +#include "../../../base/log.h" +#include "../../../third_party/android/GLContext.h" namespace eng { -bool Renderer::Initialize(ANativeWindow* window) { +bool RendererOpenGL::Initialize(ANativeWindow* window) { LOG << "Initializing renderer."; window_ = window; return StartRenderThread(); } -void Renderer::Shutdown() { +void RendererOpenGL::Shutdown() { if (terminate_render_thread_) return; @@ -23,7 +23,7 @@ void Renderer::Shutdown() { TerminateRenderThread(); } -bool Renderer::InitInternal() { +bool RendererOpenGL::InitInternal() { ndk_helper::GLContext* gl_context = ndk_helper::GLContext::GetInstance(); if (!gl_context->IsInitialzed()) { @@ -52,16 +52,21 @@ bool Renderer::InitInternal() { return InitCommon(); } -void Renderer::ShutdownInternal() { +void RendererOpenGL::ShutdownInternal() { ndk_helper::GLContext::GetInstance()->Suspend(); } -void Renderer::HandleCmdPresent(RenderCommand* cmd) { +void RendererOpenGL::HandleCmdPresent(RenderCommand* cmd) { if (EGL_SUCCESS != ndk_helper::GLContext::GetInstance()->Swap()) { ContextLost(); return; } +#ifdef THREADED_RENDERING + draw_complete_semaphore_.Release(); +#endif // THREADED_RENDERING glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + active_shader_id_ = 0; + active_texture_id_ = 0; } } // namespace eng diff --git a/src/engine/renderer/opengl/renderer_opengl_linux.cc b/src/engine/renderer/opengl/renderer_opengl_linux.cc new file mode 100644 index 0000000..c6ff4c5 --- /dev/null +++ b/src/engine/renderer/opengl/renderer_opengl_linux.cc @@ -0,0 +1,73 @@ +#include "renderer_opengl.h" + +#include "../../../base/log.h" + +namespace eng { + +bool RendererOpenGL::Initialize(Display* display, Window window) { + LOG << "Initializing renderer."; + + display_ = display; + window_ = window; + + XWindowAttributes xwa; + XGetWindowAttributes(display_, window_, &xwa); + screen_width_ = xwa.width; + screen_height_ = xwa.height; + + return StartRenderThread(); +} + +void RendererOpenGL::Shutdown() { + LOG << "Shutting down renderer."; + + TerminateRenderThread(); +} + +bool RendererOpenGL::InitInternal() { + // Create the OpenGL context. + glx_context_ = + glXCreateContext(display_, GetXVisualInfo(display_), NULL, GL_TRUE); + if (!glx_context_) { + LOG << "Couldn't create the glx context."; + return false; + } + + glXMakeCurrent(display_, window_, glx_context_); + + if (GLEW_OK != glewInit()) { + LOG << "Couldn't initialize OpenGL extension wrangler."; + return false; + } + + return InitCommon(); +} + +void RendererOpenGL::ShutdownInternal() { + if (display_ && glx_context_) { + glXMakeCurrent(display_, None, NULL); + glXDestroyContext(display_, glx_context_); + glx_context_ = nullptr; + } +} + +void RendererOpenGL::HandleCmdPresent(RenderCommand* cmd) { + if (display_) { + glXSwapBuffers(display_, window_); +#ifdef THREADED_RENDERING + draw_complete_semaphore_.Release(); +#endif // THREADED_RENDERING + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + active_shader_id_ = 0; + active_texture_id_ = 0; + } +} + +XVisualInfo* RendererOpenGL::GetXVisualInfo(Display* display) { + // Look for the right visual to set up the OpenGL context. + GLint glx_attributes[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, + None}; + return glXChooseVisual(display, 0, glx_attributes); +} + +} // namespace eng diff --git a/src/engine/renderer/renderer.h b/src/engine/renderer/renderer.h index c805f0a..877fe63 100644 --- a/src/engine/renderer/renderer.h +++ b/src/engine/renderer/renderer.h @@ -1,66 +1,88 @@ #ifndef RENDERER_H #define RENDERER_H -#include #include #include -#include -#include -#include -#define THREADED_RENDERING - -#ifdef THREADED_RENDERING -#include -#include -#include -#include -#include -#endif // THREADED_RENDERING - -#include "opengl.h" +#if defined(__linux__) && !defined(__ANDROID__) +#include +#include +#endif #include "../../base/closure.h" -#include "render_resource.h" +#include "../../base/vecmath.h" #include "renderer_types.h" #if defined(__ANDROID__) struct ANativeWindow; #endif -#ifdef THREADED_RENDERING -namespace base { -class TaskRunner; -} -#endif // THREADED_RENDERING - namespace eng { -struct RenderCommand; class Image; +class ShaderSource; +class Mesh; class Renderer { public: - Renderer(); - ~Renderer(); + const unsigned kInvalidId = 0; - void SetContextLostCB(base::Closure cb); + Renderer() = default; + virtual ~Renderer() = default; + + void SetContextLostCB(base::Closure cb) { context_lost_cb_ = std::move(cb); } #if defined(__ANDROID__) - bool Initialize(ANativeWindow* window); + virtual bool Initialize(ANativeWindow* window) = 0; #elif defined(__linux__) - bool Initialize(); + virtual bool Initialize(Display* display, Window window) = 0; #endif - void Shutdown(); + virtual void Shutdown() = 0; - void ContextLost(); + virtual void CreateGeometry(std::shared_ptr impl_data, + std::unique_ptr mesh) = 0; + virtual void DestroyGeometry(std::shared_ptr impl_data) = 0; + virtual void Draw(std::shared_ptr impl_data) = 0; - std::unique_ptr CreateResource( - RenderResourceFactoryBase& factory); - void ReleaseResource(unsigned resource_id); + virtual void UpdateTexture(std::shared_ptr impl_data, + std::unique_ptr image) = 0; + virtual void DestroyTexture(std::shared_ptr impl_data) = 0; + virtual void ActivateTexture(std::shared_ptr impl_data) = 0; - void EnqueueCommand(std::unique_ptr cmd); + virtual void CreateShader(std::shared_ptr impl_data, + std::unique_ptr source, + const VertexDescripton& vertex_description, + Primitive primitive) = 0; + virtual void DestroyShader(std::shared_ptr impl_data) = 0; + virtual void ActivateShader(std::shared_ptr impl_data) = 0; + + virtual void SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Vector2& val) = 0; + virtual void SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Vector3& val) = 0; + virtual void SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Vector4& val) = 0; + virtual void SetUniform(std::shared_ptr impl_data, + const std::string& name, + const base::Matrix4x4& val) = 0; + virtual void SetUniform(std::shared_ptr impl_data, + const std::string& name, + float val) = 0; + virtual void SetUniform(std::shared_ptr impl_data, + const std::string& name, + int val) = 0; + virtual void UploadUniforms(std::shared_ptr impl_data) = 0; + + virtual void PrepareForDrawing() = 0; + virtual void Present() = 0; + + virtual std::unique_ptr CreateResource( + RenderResourceFactoryBase& factory) = 0; + virtual void ReleaseResource(unsigned resource_id) = 0; bool SupportsETC1() const { return texture_compression_.etc1; } bool SupportsDXT1() const { @@ -69,22 +91,16 @@ class Renderer { bool SupportsDXT5() const { return texture_compression_.s3tc; } bool SupportsATC() const { return texture_compression_.atc; } - bool SupportsVAO() const { return vertex_array_objects_; } - int screen_width() const { return screen_width_; } int screen_height() const { return screen_height_; } - size_t GetAndResetFPS(); - - size_t global_queue_size() { return global_queue_size_; } - size_t render_queue_size() { return render_queue_size_; } + virtual size_t GetAndResetFPS() = 0; #if defined(__linux__) && !defined(__ANDROID__) - Display* display() { return display_; } - Window window() { return window_; } + virtual XVisualInfo* GetXVisualInfo(Display* display) = 0; #endif - private: + protected: struct TextureCompression { unsigned etc1 : 1; unsigned dxt1 : 1; @@ -102,119 +118,12 @@ class Renderer { atc(false) {} }; - struct GeometryOpenGL { - struct Element { - GLsizei num_elements; - GLenum type; - size_t vertex_offset; - }; - - GLsizei num_vertices = 0; - GLsizei num_indices = 0; - GLenum primitive = 0; - GLenum index_type = 0; - std::vector vertex_layout; - GLuint vertex_size = 0; - GLuint vertex_array_id = 0; - GLuint vertex_buffer_id = 0; - GLuint index_buffer_id = 0; - }; - - struct ShaderOpenGL { - GLuint id = 0; - std::unordered_map uniforms; - }; - - struct TextureOpenGL { - GLuint id = 0; - }; - - base::Closure context_lost_cb_; - TextureCompression texture_compression_; - bool vertex_array_objects_ = false; - bool npot_ = false; int screen_width_ = 0; int screen_height_ = 0; - std::unordered_map resources_; - -#ifdef THREADED_RENDERING - // Global commands are independent from frames and guaranteed to be processed. - std::deque> global_commands_; - // Draw commands are fame specific and can be discarded if the renderer deems - // frame drop. - std::deque> draw_commands_[2]; - - std::condition_variable cv_; - std::mutex mutex_; - std::thread render_thread_; - bool terminate_render_thread_ = false; - - base::TaskRunner* main_thread_task_runner_; -#endif // THREADED_RENDERING - - // Stats. - size_t fps_ = 0; - size_t global_queue_size_ = 0; - size_t render_queue_size_ = 0; - -#if defined(__ANDROID__) - ANativeWindow* window_; -#elif defined(__linux__) - Display* display_ = NULL; - Window window_ = 0; - XVisualInfo* visual_info_; - GLXContext glx_context_ = NULL; -#endif - - bool InitInternal(); - bool InitCommon(); - void ShutdownInternal(); - - void InvalidateAllResources(); - - bool StartRenderThread(); - void TerminateRenderThread(); - -#ifdef THREADED_RENDERING - void RenderThreadMain(std::promise promise); -#endif // THREADED_RENDERING - - void ProcessCommand(RenderCommand* cmd); - - void HandleCmdPresent(RenderCommand* cmd); - void HandleCmdUpdateTexture(RenderCommand* cmd); - void HandleCmdDestoryTexture(RenderCommand* cmd); - void HandleCmdActivateTexture(RenderCommand* cmd); - void HandleCmdCreateGeometry(RenderCommand* cmd); - void HandleCmdDestroyGeometry(RenderCommand* cmd); - void HandleCmdDrawGeometry(RenderCommand* cmd); - void HandleCmdCreateShader(RenderCommand* cmd); - void HandleCmdDestroyShader(RenderCommand* cmd); - void HandleCmdActivateShader(RenderCommand* cmd); - void HandleCmdSetUniformVec2(RenderCommand* cmd); - void HandleCmdSetUniformVec3(RenderCommand* cmd); - void HandleCmdSetUniformVec4(RenderCommand* cmd); - void HandleCmdSetUniformMat4(RenderCommand* cmd); - void HandleCmdSetUniformFloat(RenderCommand* cmd); - void HandleCmdSetUniformInt(RenderCommand* cmd); - - bool SetupVertexLayout(const VertexDescripton& vd, - GLuint vertex_size, - bool use_vao, - std::vector& vertex_layout); - GLuint CreateShader(const char* source, GLenum type); - bool BindAttributeLocation(GLuint id, const VertexDescripton& vd); - GLint GetUniformLocation(GLuint id, - const std::string& name, - std::unordered_map& uniforms); - -#if defined(__linux__) && !defined(__ANDROID__) - bool CreateWindow(); - void DestroyWindow(); -#endif + base::Closure context_lost_cb_; Renderer(const Renderer&) = delete; Renderer& operator=(const Renderer&) = delete; diff --git a/src/engine/renderer/renderer_linux.cc b/src/engine/renderer/renderer_linux.cc deleted file mode 100644 index a7cd44a..0000000 --- a/src/engine/renderer/renderer_linux.cc +++ /dev/null @@ -1,101 +0,0 @@ -#include "renderer.h" - -#include "../../base/log.h" - -namespace eng { - -bool Renderer::Initialize() { - LOG << "Initializing renderer."; - - if (!CreateWindow()) - return false; - return StartRenderThread(); -} - -void Renderer::Shutdown() { - LOG << "Shutting down renderer."; - - TerminateRenderThread(); - DestroyWindow(); -} - -bool Renderer::CreateWindow() { - screen_width_ = 1280; - screen_height_ = 1024; - - // Try to open the local display. - display_ = XOpenDisplay(NULL); - if (!display_) { - LOG << "Can't connect to X server. Try to set the DISPLAY environment " - "variable (hostname:number.screen_number)."; - return false; - } - - Window root_window = DefaultRootWindow(display_); - - // Look for the right visual to set up the OpenGL context. - GLint glx_attributes[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, - None}; - visual_info_ = glXChooseVisual(display_, 0, glx_attributes); - if (!visual_info_) { - LOG << "No appropriate visual found."; - return false; - } - LOG << "Visual " << (void*)visual_info_->visualid << " selected"; - - // Create the main window. - XSetWindowAttributes window_attributes; - window_attributes.colormap = - XCreateColormap(display_, root_window, visual_info_->visual, AllocNone); - window_attributes.event_mask = ExposureMask | KeyPressMask; - window_ = - XCreateWindow(display_, root_window, 0, 0, screen_width_, screen_height_, - 0, visual_info_->depth, InputOutput, visual_info_->visual, - CWColormap | CWEventMask, &window_attributes); - XMapWindow(display_, window_); - XStoreName(display_, window_, "kaliber"); - - return true; -} - -bool Renderer::InitInternal() { - // Create the OpenGL context. - glx_context_ = glXCreateContext(display_, visual_info_, NULL, GL_TRUE); - if (!glx_context_) { - LOG << "Couldn't create the glx context."; - return false; - } - - glXMakeCurrent(display_, window_, glx_context_); - - if (GLEW_OK != glewInit()) { - LOG << "Couldn't initialize OpenGL extension wrangler."; - return false; - } - - return InitCommon(); -} - -void Renderer::DestroyWindow() { - if (display_) { - XDestroyWindow(display_, window_); - XCloseDisplay(display_); - } -} - -void Renderer::ShutdownInternal() { - if (display_ && glx_context_) { - glXMakeCurrent(display_, None, NULL); - glXDestroyContext(display_, glx_context_); - glx_context_ = nullptr; - } -} - -void Renderer::HandleCmdPresent(RenderCommand* cmd) { - if (display_) { - glXSwapBuffers(display_, window_); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } -} - -} // namespace eng diff --git a/src/engine/renderer/renderer_types.cc b/src/engine/renderer/renderer_types.cc index 94ee8e2..13f8efb 100644 --- a/src/engine/renderer/renderer_types.cc +++ b/src/engine/renderer/renderer_types.cc @@ -6,6 +6,8 @@ namespace { +// Used to parse the vertex layout, +// e.g. "p3f;c4b" for "position 3 floats, color 4 bytes". const char kLayoutDelimiter[] = ";/ \t"; } // namespace diff --git a/src/engine/renderer/renderer_types.h b/src/engine/renderer/renderer_types.h index 15700b6..dff1b77 100644 --- a/src/engine/renderer/renderer_types.h +++ b/src/engine/renderer/renderer_types.h @@ -8,6 +8,7 @@ namespace eng { enum Primitive { + kPrimitive_Invalid = -1, kPrimitive_Triangles, kPrimitive_TriangleStrip, kPrimitive_Max diff --git a/src/engine/renderer/shader.cc b/src/engine/renderer/shader.cc index 343be94..7c8980d 100644 --- a/src/engine/renderer/shader.cc +++ b/src/engine/renderer/shader.cc @@ -1,7 +1,6 @@ #include "shader.h" #include "../shader_source.h" -#include "render_command.h" #include "renderer.h" using namespace base; @@ -18,92 +17,58 @@ Shader::~Shader() { } void Shader::Create(std::unique_ptr source, - const VertexDescripton& vd) { + const VertexDescripton& vd, + Primitive primitive) { Destroy(); valid_ = true; - - auto cmd = std::make_unique(); - cmd->source = std::move(source); - cmd->vertex_description = vd; - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); + renderer_->CreateShader(impl_data_, std::move(source), vd, primitive); } void Shader::Destroy() { if (valid_) { - auto cmd = std::make_unique(); - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); + renderer_->DestroyShader(impl_data_); valid_ = false; } } void Shader::Activate() { - if (valid_) { - auto cmd = std::make_unique(); - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); - } + if (valid_) + renderer_->ActivateShader(impl_data_); } void Shader::SetUniform(const std::string& name, const Vector2& v) { - if (valid_) { - auto cmd = std::make_unique(); - cmd->name = name; - cmd->v = v; - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); - } + if (valid_) + renderer_->SetUniform(impl_data_, name, v); } void Shader::SetUniform(const std::string& name, const Vector3& v) { - if (valid_) { - auto cmd = std::make_unique(); - cmd->name = name; - cmd->v = v; - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); - } + if (valid_) + renderer_->SetUniform(impl_data_, name, v); } void Shader::SetUniform(const std::string& name, const Vector4& v) { - if (valid_) { - auto cmd = std::make_unique(); - cmd->name = name; - cmd->v = v; - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); - } + if (valid_) + renderer_->SetUniform(impl_data_, name, v); } void Shader::SetUniform(const std::string& name, const Matrix4x4& m) { - if (valid_) { - auto cmd = std::make_unique(); - cmd->name = name; - cmd->m = m; - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); - } + if (valid_) + renderer_->SetUniform(impl_data_, name, m); } void Shader::SetUniform(const std::string& name, float f) { - if (valid_) { - auto cmd = std::make_unique(); - cmd->name = name; - cmd->f = f; - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); - } + if (valid_) + renderer_->SetUniform(impl_data_, name, f); } void Shader::SetUniform(const std::string& name, int i) { - if (valid_) { - auto cmd = std::make_unique(); - cmd->name = name; - cmd->i = i; - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); - } + if (valid_) + renderer_->SetUniform(impl_data_, name, i); +} + +void Shader::UploadUniforms() { + if (valid_) + renderer_->UploadUniforms(impl_data_); } } // namespace eng diff --git a/src/engine/renderer/shader.h b/src/engine/renderer/shader.h index c110ab7..f4c6fe3 100644 --- a/src/engine/renderer/shader.h +++ b/src/engine/renderer/shader.h @@ -20,7 +20,9 @@ class Shader : public RenderResource { Renderer* renderer); ~Shader() override; - void Create(std::unique_ptr source, const VertexDescripton& vd); + void Create(std::unique_ptr source, + const VertexDescripton& vd, + Primitive primitive); void Destroy() override; @@ -32,6 +34,8 @@ class Shader : public RenderResource { void SetUniform(const std::string& name, const base::Matrix4x4& m); void SetUniform(const std::string& name, float f); void SetUniform(const std::string& name, int i); + + void UploadUniforms(); }; } // namespace eng diff --git a/src/engine/renderer/texture.cc b/src/engine/renderer/texture.cc index 699983a..d659f35 100644 --- a/src/engine/renderer/texture.cc +++ b/src/engine/renderer/texture.cc @@ -2,7 +2,6 @@ #include "../../base/log.h" #include "../image.h" -#include "render_command.h" #include "renderer.h" namespace eng { @@ -20,29 +19,20 @@ void Texture::Update(std::unique_ptr image) { valid_ = true; width_ = image->GetWidth(); height_ = image->GetHeight(); - - auto cmd = std::make_unique(); - cmd->image = std::move(image); - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); + renderer_->UpdateTexture(impl_data_, std::move(image)); } void Texture::Destroy() { if (valid_) { - auto cmd = std::make_unique(); - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); + renderer_->DestroyTexture(impl_data_); valid_ = false; DLOG << "Texture destroyed. resource_id: " << resource_id_; } } void Texture::Activate() { - if (valid_) { - auto cmd = std::make_unique(); - cmd->impl_data = impl_data_; - renderer_->EnqueueCommand(std::move(cmd)); - } + if (valid_) + renderer_->ActivateTexture(impl_data_); } } // namespace eng diff --git a/src/engine/shader_source.cc b/src/engine/shader_source.cc index 9376722..0e035b3 100644 --- a/src/engine/shader_source.cc +++ b/src/engine/shader_source.cc @@ -11,12 +11,13 @@ namespace eng { bool ShaderSource::Load(const std::string& name) { Engine& engine = Engine::Get(); - size_t size = 0; + name_ = name; std::string vertex_file_name = name; vertex_file_name += "_vertex"; - auto vertex_source = AssetFile::ReadWholeFile( - vertex_file_name.c_str(), engine.GetRootPath().c_str(), &size, true); + auto vertex_source = AssetFile::ReadWholeFile(vertex_file_name.c_str(), + engine.GetRootPath().c_str(), + &vertex_source_size_, true); if (!vertex_source) { LOG << "Failed to read file: " << vertex_file_name; return false; @@ -26,13 +27,16 @@ bool ShaderSource::Load(const std::string& name) { std::string fragment_file_name = name; fragment_file_name += "_fragment"; - auto fragment_source = AssetFile::ReadWholeFile( - fragment_file_name.c_str(), engine.GetRootPath().c_str(), &size, true); + auto fragment_source = AssetFile::ReadWholeFile(fragment_file_name.c_str(), + engine.GetRootPath().c_str(), + &fragment_source_size_, true); if (!fragment_source) { LOG << "Failed to read file: " << fragment_file_name; return false; } + LOG << "Loaded " << name; + fragment_source_ = std::move(fragment_source); return true; diff --git a/src/engine/shader_source.h b/src/engine/shader_source.h index a14afd7..8b6c621 100644 --- a/src/engine/shader_source.h +++ b/src/engine/shader_source.h @@ -16,9 +16,19 @@ class ShaderSource { const char* GetVertexSource() const { return vertex_source_.get(); } const char* GetFragmentSource() const { return fragment_source_.get(); } + size_t vertex_source_size() const { return vertex_source_size_; } + size_t fragment_source_size() const { return fragment_source_size_ ; } + + const std::string& name() const { return name_; } + private: + std::string name_; + std::unique_ptr vertex_source_; std::unique_ptr fragment_source_; + + size_t vertex_source_size_ = 0; + size_t fragment_source_size_ = 0; }; } // namespace eng diff --git a/src/engine/solid_quad.cc b/src/engine/solid_quad.cc index ff97cc2..74124ba 100644 --- a/src/engine/solid_quad.cc +++ b/src/engine/solid_quad.cc @@ -15,12 +15,12 @@ void SolidQuad::Draw(float frame_frac) { Shader* shader = Engine::Get().GetSolidShader(); shader->Activate(); - shader->SetUniform("offset", offset_); - shader->SetUniform("scale", scale_); - shader->SetUniform("pivot", pivot_); + shader->SetUniform("offset", position_); + shader->SetUniform("scale", GetSize()); shader->SetUniform("rotation", rotation_); - shader->SetUniform("projection", Engine::Get().GetProjectionMarix()); + shader->SetUniform("projection", Engine::Get().GetProjectionMatrix()); shader->SetUniform("color", color_); + shader->UploadUniforms(); Engine::Get().GetQuad()->Draw(); } diff --git a/src/engine/sound.cc b/src/engine/sound.cc index e85a000..b9e5a0b 100644 --- a/src/engine/sound.cc +++ b/src/engine/sound.cc @@ -3,13 +3,13 @@ #include #include "../base/log.h" +#include "../base/sinc_resampler.h" #define MINIMP3_ONLY_MP3 #define MINIMP3_ONLY_SIMD #define MINIMP3_FLOAT_OUTPUT #define MINIMP3_NO_STDIO #define MINIMP3_IMPLEMENTATION #include "../third_party/minimp3/minimp3_ex.h" -#include "../third_party/r8b/CDSPResampler.h" #include "engine.h" #include "platform/asset_file.h" @@ -27,22 +27,37 @@ std::array, 2> Deinterleave(size_t num_channels, if (num_channels == 1) { // Single channel. - channels[0] = std::make_unique(num_samples); - for (int i = 0; i < num_samples; ++i) - channels[0].get()[i] = input_buffer[i]; + if constexpr (std::is_same::value) { + channels[0] = std::make_unique(num_samples); + memcpy(channels[0].get(), input_buffer, num_samples * sizeof(float)); + } else { + channels[0] = std::make_unique(num_samples); + for (int i = 0; i < num_samples; ++i) + channels[0].get()[i] = static_cast(input_buffer[i]); + } } else { // Deinterleave into separate channels. channels[0] = std::make_unique(num_samples); channels[1] = std::make_unique(num_samples); for (int i = 0, j = 0; i < num_samples * 2; i += 2) { - channels[0].get()[j] = input_buffer[i]; - channels[1].get()[j++] = input_buffer[i + 1]; + channels[0].get()[j] = static_cast(input_buffer[i]); + channels[1].get()[j++] = static_cast(input_buffer[i + 1]); } } return channels; } +std::unique_ptr CreateResampler(int src_samle_rate, + int dst_sample_rate, + size_t num_samples) { + const double io_ratio = static_cast(src_samle_rate) / + static_cast(dst_sample_rate); + auto resampler = std::make_unique(io_ratio, num_samples); + resampler->PrimeWithSilence(); + return resampler; +} + } // namespace namespace eng { @@ -79,52 +94,60 @@ bool Sound::Load(const std::string& file_name, bool stream) { is_streaming_sound_ = stream; - LOG << (is_streaming_sound_ ? "Streaming " : "Loading ") << file_name << ". " + LOG << (is_streaming_sound_ ? "Streaming " : "Loaded ") << file_name << ". " << mp3_dec_->samples << " samples, " << mp3_dec_->info.channels << " channels, " << mp3_dec_->info.hz << " hz, " << "layer " << mp3_dec_->info.layer << ", " << "avg_bitrate_kbps " << mp3_dec_->info.bitrate_kbps; num_channels_ = mp3_dec_->info.channels; - hz_ = mp3_dec_->info.hz; + sample_rate_ = mp3_dec_->info.hz; num_samples_back_ = cur_sample_back_ = 0; eof_ = false; DCHECK(num_channels_ > 0 && num_channels_ <= 2); - size_t system_hz = Engine::Get().GetAudioSampleRate(); + hw_sample_rate_ = Engine::Get().GetAudioHardwareSampleRate(); if (is_streaming_sound_) { - resampler_ = std::make_unique(hz_, system_hz, - kMaxSamplesPerChunk); + if (sample_rate_ != hw_sample_rate_) { + for (int i = 0; i < mp3_dec_->info.channels; ++i) { + resampler_[i] = + CreateResampler(sample_rate_, hw_sample_rate_, + (int)kMaxSamplesPerChunk / mp3_dec_->info.channels); + } + } // Fill up buffers. StreamInternal(kMaxSamplesPerChunk, false); SwapBuffers(); StreamInternal(kMaxSamplesPerChunk, false); - if (eof_) { - // Sample is smaller than buffer. No need to stream. + // No need to stream if sample fits into the buffer. + if (eof_) is_streaming_sound_ = false; - mp3dec_ex_close(mp3_dec_.get()); - mp3_dec_.reset(); - encoded_data_.reset(); - resampler_.reset(); - } } else { - resampler_ = std::make_unique(hz_, system_hz, - mp3_dec_->samples); + if (sample_rate_ != hw_sample_rate_) { + for (int i = 0; i < mp3_dec_->info.channels; ++i) { + resampler_[i] = + CreateResampler(sample_rate_, hw_sample_rate_, + mp3_dec_->samples / mp3_dec_->info.channels); + } + } // Decode entire file. StreamInternal(mp3_dec_->samples, false); SwapBuffers(); eof_ = true; + } - // We are done with decoding for non-streaming sound. + if (!is_streaming_sound_) { + // We are done with decoding. + encoded_data_.reset(); + for (int i = 0; i < mp3_dec_->info.channels; ++i) + resampler_[i].reset(); mp3dec_ex_close(mp3_dec_.get()); mp3_dec_.reset(); - encoded_data_.reset(); - resampler_.reset(); } return true; @@ -161,6 +184,7 @@ float* Sound::GetBuffer(int channel) const { bool Sound::StreamInternal(size_t num_samples, bool loop) { auto buffer = std::make_unique(num_samples); + size_t samples_read_per_channel = 0; cur_sample_back_ = mp3_dec_->cur_sample; @@ -173,8 +197,8 @@ bool Sound::StreamInternal(size_t num_samples, bool loop) { return false; } - num_samples_back_ = samples_read / mp3_dec_->info.channels; - if (!num_samples_back_ && loop) { + samples_read_per_channel = samples_read / mp3_dec_->info.channels; + if (!samples_read_per_channel && loop) { mp3dec_ex_seek(mp3_dec_.get(), 0); loop = false; continue; @@ -182,47 +206,50 @@ bool Sound::StreamInternal(size_t num_samples, bool loop) { break; } - if (num_samples_back_) - Preprocess(std::move(buffer)); - else + if (samples_read_per_channel) { + Preprocess(std::move(buffer), samples_read_per_channel); + } else { + num_samples_back_ = 0; eof_ = true; + } return true; } -void Sound::Preprocess(std::unique_ptr input_buffer) { - size_t system_hz = Engine::Get().GetAudioSampleRate(); - - if (system_hz == hz_) { - auto channels = Deinterleave(num_channels_, num_samples_back_, - input_buffer.get()); +void Sound::Preprocess(std::unique_ptr input_buffer, + size_t samples_per_channel) { + auto channels = Deinterleave(num_channels_, samples_per_channel, + input_buffer.get()); + if (hw_sample_rate_ == sample_rate_) { // No need for resmapling. back_buffer_[0] = std::move(channels[0]); if (num_channels_ == 2) back_buffer_[1] = std::move(channels[1]); + num_samples_back_ = samples_per_channel; } else { - // r8b resampler supports only double floating point type. - auto channels = Deinterleave(num_channels_, num_samples_back_, - input_buffer.get()); - - size_t resampled_num_samples = - ((float)system_hz / (float)hz_) * num_samples_back_; + size_t num_resampled_samples = resampler_[0]->ChunkSize(); + DCHECK(num_resampled_samples == + (int)(((float)hw_sample_rate_ / (float)sample_rate_) * + samples_per_channel)); if (!back_buffer_[0]) { - if (max_samples_ < resampled_num_samples) - max_samples_ = resampled_num_samples; + if (max_samples_ < num_resampled_samples) + max_samples_ = num_resampled_samples; back_buffer_[0] = std::make_unique(max_samples_); if (num_channels_ == 2) back_buffer_[1] = std::make_unique(max_samples_); } + num_samples_back_ = num_resampled_samples; // Resample to match the system sample rate. for (int i = 0; i < num_channels_; ++i) { - resampler_->oneshot(channels[i].get(), num_samples_back_, - back_buffer_[i].get(), resampled_num_samples); + resampler_[i]->Resample(num_resampled_samples, back_buffer_[i].get(), + [&](int frames, float* destination) { + memcpy(destination, channels[i].get(), + frames * sizeof(float)); + }); } - num_samples_back_ = resampled_num_samples; } } diff --git a/src/engine/sound.h b/src/engine/sound.h index 543235a..606b4bd 100644 --- a/src/engine/sound.h +++ b/src/engine/sound.h @@ -7,9 +7,9 @@ typedef struct mp3dec_ex mp3dec_ex_t; -namespace r8b { -class CDSPResampler16; -} // namespace r8b +namespace base { +class SincResampler; +} // namespace base namespace eng { @@ -35,7 +35,7 @@ class Sound { size_t GetNumSamples() const { return num_samples_front_; } size_t num_channels() const { return num_channels_; } - size_t hz() const { return hz_; } + int sample_rate() const { return sample_rate_; } bool is_streaming_sound() const { return is_streaming_sound_; } @@ -54,13 +54,15 @@ class Sound { size_t cur_sample_back_ = 0; size_t num_channels_ = 0; - size_t hz_ = 0; + int sample_rate_ = 0; + + int hw_sample_rate_ = 0; std::unique_ptr encoded_data_; std::unique_ptr mp3_dec_; - std::unique_ptr resampler_; + std::unique_ptr resampler_[2]; bool eof_ = false; @@ -68,7 +70,8 @@ class Sound { bool StreamInternal(size_t num_samples, bool loop); - void Preprocess(std::unique_ptr input_buffer); + void Preprocess(std::unique_ptr input_buffer, + size_t samples_per_channel); }; } // namespace eng diff --git a/src/engine/sound_player.cc b/src/engine/sound_player.cc index cd988d8..164b738 100644 --- a/src/engine/sound_player.cc +++ b/src/engine/sound_player.cc @@ -25,27 +25,37 @@ void SoundPlayer::SetSound(std::unique_ptr sound) { } void SoundPlayer::Play(bool loop, float fade_in_duration) { - if (sound_) { - int step = variate_ ? Engine::Get().GetRandomGenerator().Roll(3) - 2 : 0; - resource_->SetResampleStep(step); - resource_->SetLoop(loop); - if (fade_in_duration > 0) - resource_->SetAmplitudeInc(1.0f / (sound_->hz() * fade_in_duration)); - else - resource_->SetAmplitudeInc(0); - resource_->Play(sound_, fade_in_duration > 0 ? 0 : max_amplitude_, true); - } + if (!sound_) + return; + + int step = variate_ ? Engine::Get().GetRandomGenerator().Roll(3) - 2 : 0; + resource_->SetResampleStep(step * 12); + resource_->SetLoop(loop); + if (fade_in_duration > 0) + resource_->SetAmplitudeInc(1.0f / + (sound_->sample_rate() * fade_in_duration)); + else + resource_->SetAmplitudeInc(0); + resource_->Play(sound_, fade_in_duration > 0 ? 0 : max_amplitude_, true); } void SoundPlayer::Resume(float fade_in_duration) { + if (!sound_) + return; + if (fade_in_duration > 0) - resource_->SetAmplitudeInc(1.0f / (sound_->hz() * fade_in_duration)); + resource_->SetAmplitudeInc(1.0f / + (sound_->sample_rate() * fade_in_duration)); resource_->Play(sound_, fade_in_duration > 0 ? 0 : -1, false); } void SoundPlayer::Stop(float fade_out_duration) { + if (!sound_) + return; + if (fade_out_duration > 0) - resource_->SetAmplitudeInc(-1.0f / (sound_->hz() * fade_out_duration)); + resource_->SetAmplitudeInc(-1.0f / + (sound_->sample_rate() * fade_out_duration)); else resource_->Stop(); } diff --git a/src/third_party/BUILD.gn b/src/third_party/BUILD.gn index 2b38d81..f469035 100644 --- a/src/third_party/BUILD.gn +++ b/src/third_party/BUILD.gn @@ -2,21 +2,6 @@ source_set("third_party") { sources = [ "jsoncpp/json.h", "jsoncpp/jsoncpp.cc", - "r8b/CDSPBlockConvolver.h", - "r8b/CDSPFIRFilter.h", - "r8b/CDSPFracInterpolator.h", - "r8b/CDSPHBDownsampler.h", - "r8b/CDSPHBUpsampler.h", - "r8b/CDSPProcessor.h", - "r8b/CDSPRealFFT.h", - "r8b/CDSPResampler.h", - "r8b/CDSPSincFilterGen.h", - "r8b/fft4g.h", - "r8b/pffft.cpp", - "r8b/pffft.h", - "r8b/r8bbase.cpp", - "r8b/r8bbase.h", - "r8b/r8bconf.h", "stb/stb_image.h", "stb/stb_truetype.h", "texture_compressor/dxt_encoder_implementation_autogen.h", diff --git a/src/third_party/jsoncpp/json.h b/src/third_party/jsoncpp/json.h index 32fd072..28ce653 100644 --- a/src/third_party/jsoncpp/json.h +++ b/src/third_party/jsoncpp/json.h @@ -1,4 +1,4 @@ -/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). +/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/). /// It is intended to be used with #include "json/json.h" // ////////////////////////////////////////////////////////////////////// @@ -6,32 +6,32 @@ // ////////////////////////////////////////////////////////////////////// /* -The JsonCpp library's source code, including accompanying documentation, +The JsonCpp library's source code, including accompanying documentation, tests and demonstration applications, are licensed under the following conditions... -The author (Baptiste Lepilleur) explicitly disclaims copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of -2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is -released under the terms of the MIT License (see below). +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License - + The full text of the MIT License follows: ======================================================================== -Copyright (c) 2007-2010 Baptiste Lepilleur +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -72,9 +72,9 @@ license you like. -#ifndef JSON_AMALGATED_H_INCLUDED -# define JSON_AMALGATED_H_INCLUDED -/// If defined, indicates that the source file is amalgated +#ifndef JSON_AMALGAMATED_H_INCLUDED +# define JSON_AMALGAMATED_H_INCLUDED +/// If defined, indicates that the source file is amalgamated /// to prevent private header inclusion. #define JSON_IS_AMALGAMATION @@ -82,17 +82,32 @@ license you like. // Beginning of content of file: include/json/version.h // ////////////////////////////////////////////////////////////////////// -// DO NOT EDIT. This file (and "version") is generated by CMake. -// Run CMake configure step to update it. #ifndef JSON_VERSION_H_INCLUDED -# define JSON_VERSION_H_INCLUDED +#define JSON_VERSION_H_INCLUDED -# define JSONCPP_VERSION_STRING "1.6.5" -# define JSONCPP_VERSION_MAJOR 1 -# define JSONCPP_VERSION_MINOR 6 -# define JSONCPP_VERSION_PATCH 5 -# define JSONCPP_VERSION_QUALIFIER -# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) +// Note: version must be updated in three places when doing a release. This +// annoying process ensures that amalgamate, CMake, and meson all report the +// correct version. +// 1. /meson.build +// 2. /include/json/version.h +// 3. /CMakeLists.txt +// IMPORTANT: also update the SOVERSION!! + +#define JSONCPP_VERSION_STRING "1.9.3" +#define JSONCPP_VERSION_MAJOR 1 +#define JSONCPP_VERSION_MINOR 9 +#define JSONCPP_VERSION_PATCH 3 +#define JSONCPP_VERSION_QUALIFIER +#define JSONCPP_VERSION_HEXA \ + ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ + (JSONCPP_VERSION_PATCH << 8)) + +#ifdef JSONCPP_USING_SECURE_MEMORY +#undef JSONCPP_USING_SECURE_MEMORY +#endif +#define JSONCPP_USING_SECURE_MEMORY 0 +// If non-zero, the library zeroes any memory that it has allocated before +// it frees its memory. #endif // JSON_VERSION_H_INCLUDED @@ -105,27 +120,128 @@ license you like. +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_ALLOCATOR_H_INCLUDED +#define JSON_ALLOCATOR_H_INCLUDED + +#include +#include + +#pragma pack(push, 8) + +namespace Json { +template class SecureAllocator { +public: + // Type definitions + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + /** + * Allocate memory for N items using the standard allocator. + */ + pointer allocate(size_type n) { + // allocate using "global operator new" + return static_cast(::operator new(n * sizeof(T))); + } + + /** + * Release memory which was allocated for N items at pointer P. + * + * The memory block is filled with zeroes before being released. + * The pointer argument is tagged as "volatile" to prevent the + * compiler optimizing out this critical step. + */ + void deallocate(volatile pointer p, size_type n) { + std::memset(p, 0, n * sizeof(T)); + // free using "global operator delete" + ::operator delete(p); + } + + /** + * Construct an item in-place at pointer P. + */ + template void construct(pointer p, Args&&... args) { + // construct using "placement new" and "perfect forwarding" + ::new (static_cast(p)) T(std::forward(args)...); + } + + size_type max_size() const { return size_t(-1) / sizeof(T); } + + pointer address(reference x) const { return std::addressof(x); } + + const_pointer address(const_reference x) const { return std::addressof(x); } + + /** + * Destroy an item in-place at pointer P. + */ + void destroy(pointer p) { + // destroy using "explicit destructor" + p->~T(); + } + + // Boilerplate + SecureAllocator() {} + template SecureAllocator(const SecureAllocator&) {} + template struct rebind { using other = SecureAllocator; }; +}; + +template +bool operator==(const SecureAllocator&, const SecureAllocator&) { + return true; +} + +template +bool operator!=(const SecureAllocator&, const SecureAllocator&) { + return false; +} + +} // namespace Json + +#pragma pack(pop) + +#endif // JSON_ALLOCATOR_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/allocator.h +// ////////////////////////////////////////////////////////////////////// + + + + + + // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/config.h // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef JSON_CONFIG_H_INCLUDED #define JSON_CONFIG_H_INCLUDED - -/// If defined, indicates that json library is embedded in CppTL library. -//# define JSON_IN_CPPTL 1 - -/// If defined, indicates that json may leverage CppTL library -//# define JSON_USE_CPPTL 1 -/// If defined, indicates that cpptl vector based map should be used instead of -/// std::map -/// as Value container. -//# define JSON_USE_CPPTL_SMALLMAP 1 +#include +#include +#include +#include +#include +#include +#include +#include // If non-zero, the library uses exceptions to report bad input instead of C // assertion macros. The default is to use exceptions. @@ -133,118 +249,132 @@ license you like. #define JSON_USE_EXCEPTION 1 #endif -/// If defined, indicates that the source file is amalgated +// Temporary, tracked for removal with issue #982. +#ifndef JSON_USE_NULLREF +#define JSON_USE_NULLREF 1 +#endif + +/// If defined, indicates that the source file is amalgamated /// to prevent private header inclusion. -/// Remarks: it is automatically defined in the generated amalgated header. +/// Remarks: it is automatically defined in the generated amalgamated header. // #define JSON_IS_AMALGAMATION -#ifdef JSON_IN_CPPTL -#include -#ifndef JSON_USE_CPPTL -#define JSON_USE_CPPTL 1 -#endif -#endif - -#ifdef JSON_IN_CPPTL -#define JSON_API CPPTL_API -#elif defined(JSON_DLL_BUILD) -#if defined(_MSC_VER) +// Export macros for DLL visibility +#if defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) || defined(__MINGW32__) #define JSON_API __declspec(dllexport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#elif defined(__GNUC__) || defined(__clang__) +#define JSON_API __attribute__((visibility("default"))) #endif // if defined(_MSC_VER) + #elif defined(JSON_DLL) -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW32__) #define JSON_API __declspec(dllimport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING #endif // if defined(_MSC_VER) -#endif // ifdef JSON_IN_CPPTL +#endif // ifdef JSON_DLL_BUILD + #if !defined(JSON_API) #define JSON_API #endif +#if defined(_MSC_VER) && _MSC_VER < 1800 +#error \ + "ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities" +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +// As recommended at +// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 +extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...); +#define jsoncpp_snprintf msvc_pre1900_c99_snprintf +#else +#define jsoncpp_snprintf std::snprintf +#endif + // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for // integer // Storages, and 64 bits integer support is disabled. // #define JSON_NO_INT64 1 -#if defined(_MSC_VER) // MSVC -# if _MSC_VER <= 1200 // MSVC 6 - // Microsoft Visual Studio 6 only support conversion from __int64 to double - // (no conversion from unsigned __int64). -# define JSON_USE_INT64_DOUBLE_CONVERSION 1 - // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' - // characters in the debug information) - // All projects I've ever seen with VS6 were using this globally (not bothering - // with pragma push/pop). -# pragma warning(disable : 4786) -# endif // MSVC 6 - -# if _MSC_VER >= 1500 // MSVC 2008 - /// Indicates that the following function is deprecated. -# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) -# endif - -#endif // defined(_MSC_VER) - - -#ifndef JSON_HAS_RVALUE_REFERENCES - -#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // MSVC >= 2010 +// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools. +// C++11 should be used directly in JSONCPP. +#define JSONCPP_OVERRIDE override #ifdef __clang__ -#if __has_feature(cxx_rvalue_references) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // has_feature - -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // GXX_EXPERIMENTAL - -#endif // __clang__ || __GNUC__ - -#endif // not defined JSON_HAS_RVALUE_REFERENCES - -#ifndef JSON_HAS_RVALUE_REFERENCES -#define JSON_HAS_RVALUE_REFERENCES 0 +#if __has_extension(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) #endif - -#ifdef __clang__ -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) -# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) -# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) -# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) -# endif // GNUC version -#endif // __clang__ || __GNUC__ +#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc) +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message))) +#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif // GNUC version +#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates + // MSVC) +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif // __clang__ || __GNUC__ || _MSC_VER #if !defined(JSONCPP_DEPRECATED) #define JSONCPP_DEPRECATED(message) #endif // if !defined(JSONCPP_DEPRECATED) +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6)) +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif + +#if !defined(JSON_IS_AMALGAMATION) + +#include "allocator.h" +#include "version.h" + +#endif // if !defined(JSON_IS_AMALGAMATION) + namespace Json { -typedef int Int; -typedef unsigned int UInt; +using Int = int; +using UInt = unsigned int; #if defined(JSON_NO_INT64) -typedef int LargestInt; -typedef unsigned int LargestUInt; +using LargestInt = int; +using LargestUInt = unsigned int; #undef JSON_HAS_INT64 #else // if defined(JSON_NO_INT64) // For Microsoft Visual use specific types as long long is not supported #if defined(_MSC_VER) // Microsoft Visual Studio -typedef __int64 Int64; -typedef unsigned __int64 UInt64; +using Int64 = __int64; +using UInt64 = unsigned __int64; #else // if defined(_MSC_VER) // Other platforms, use long long -typedef long long int Int64; -typedef unsigned long long int UInt64; -#endif // if defined(_MSC_VER) -typedef Int64 LargestInt; -typedef UInt64 LargestUInt; +using Int64 = int64_t; +using UInt64 = uint64_t; +#endif // if defined(_MSC_VER) +using LargestInt = Int64; +using LargestUInt = UInt64; #define JSON_HAS_INT64 #endif // if defined(JSON_NO_INT64) -} // end namespace Json + +template +using Allocator = + typename std::conditional, + std::allocator>::type; +using String = std::basic_string, Allocator>; +using IStringStream = + std::basic_istringstream; +using OStringStream = + std::basic_ostringstream; +using IStream = std::istream; +using OStream = std::ostream; +} // namespace Json + +// Legacy names (formerly macros). +using JSONCPP_STRING = Json::String; +using JSONCPP_ISTRINGSTREAM = Json::IStringStream; +using JSONCPP_OSTRINGSTREAM = Json::OStringStream; +using JSONCPP_ISTREAM = Json::IStream; +using JSONCPP_OSTREAM = Json::OStream; #endif // JSON_CONFIG_H_INCLUDED @@ -261,7 +391,7 @@ typedef UInt64 LargestUInt; // Beginning of content of file: include/json/forwards.h // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -276,17 +406,23 @@ typedef UInt64 LargestUInt; namespace Json { // writer.h +class StreamWriter; +class StreamWriterBuilder; +class Writer; class FastWriter; class StyledWriter; +class StyledStreamWriter; // reader.h class Reader; +class CharReader; +class CharReaderBuilder; -// features.h +// json_features.h class Features; // value.h -typedef unsigned int ArrayIndex; +using ArrayIndex = unsigned int; class StaticString; class Path; class PathArgument; @@ -309,21 +445,23 @@ class ValueConstIterator; // ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/features.h +// Beginning of content of file: include/json/json_features.h // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#ifndef CPPTL_JSON_FEATURES_H_INCLUDED -#define CPPTL_JSON_FEATURES_H_INCLUDED +#ifndef JSON_FEATURES_H_INCLUDED +#define JSON_FEATURES_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "forwards.h" #endif // if !defined(JSON_IS_AMALGAMATION) +#pragma pack(push, 8) + namespace Json { /** \brief Configuration passed to reader and writer. @@ -353,25 +491,27 @@ public: Features(); /// \c true if comments are allowed. Default: \c true. - bool allowComments_; + bool allowComments_{true}; /// \c true if root must be either an array or an object value. Default: \c /// false. - bool strictRoot_; + bool strictRoot_{false}; /// \c true if dropped null placeholders are allowed. Default: \c false. - bool allowDroppedNullPlaceholders_; + bool allowDroppedNullPlaceholders_{false}; /// \c true if numeric object key are allowed. Default: \c false. - bool allowNumericKeys_; + bool allowNumericKeys_{false}; }; } // namespace Json -#endif // CPPTL_JSON_FEATURES_H_INCLUDED +#pragma pack(pop) + +#endif // JSON_FEATURES_H_INCLUDED // ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/features.h +// End of content of file: include/json/json_features.h // ////////////////////////////////////////////////////////////////////// @@ -383,29 +523,53 @@ public: // Beginning of content of file: include/json/value.h // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#ifndef CPPTL_JSON_H_INCLUDED -#define CPPTL_JSON_H_INCLUDED +#ifndef JSON_H_INCLUDED +#define JSON_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "forwards.h" #endif // if !defined(JSON_IS_AMALGAMATION) + +// Conditional NORETURN attribute on the throw functions would: +// a) suppress false positives from static code analysis +// b) possibly improve optimization opportunities. +#if !defined(JSONCPP_NORETURN) +#if defined(_MSC_VER) && _MSC_VER == 1800 +#define JSONCPP_NORETURN __declspec(noreturn) +#else +#define JSONCPP_NORETURN [[noreturn]] +#endif +#endif + +// Support for '= delete' with template declarations was a late addition +// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2 +// even though these declare themselves to be c++11 compilers. +#if !defined(JSONCPP_TEMPLATE_DELETE) +#if defined(__clang__) && defined(__apple_build_version__) +#if __apple_build_version__ <= 8000042 +#define JSONCPP_TEMPLATE_DELETE +#endif +#elif defined(__clang__) +#if __clang_major__ == 3 && __clang_minor__ <= 8 +#define JSONCPP_TEMPLATE_DELETE +#endif +#endif +#if !defined(JSONCPP_TEMPLATE_DELETE) +#define JSONCPP_TEMPLATE_DELETE = delete +#endif +#endif + +#include +#include +#include +#include #include #include -#include - -#ifndef JSON_USE_CPPTL_SMALLMAP -#include -#else -#include -#endif -#ifdef JSON_USE_CPPTL -#include -#endif // Disable warning C4251: : needs to have dll-interface to // be used by... @@ -414,21 +578,25 @@ public: #pragma warning(disable : 4251) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma pack(push, 8) + /** \brief JSON (JavaScript Object Notation). */ namespace Json { +#if JSON_USE_EXCEPTION /** Base class for all exceptions we throw. * * We use nothing but these internally. Of course, STL can throw others. */ class JSON_API Exception : public std::exception { public: - Exception(std::string const& msg); - ~Exception() throw() override; - char const* what() const throw() override; + Exception(String msg); + ~Exception() noexcept override; + char const* what() const noexcept override; + protected: - std::string msg_; + String msg_; }; /** Exceptions which the user cannot easily avoid. @@ -439,7 +607,7 @@ protected: */ class JSON_API RuntimeError : public Exception { public: - RuntimeError(std::string const& msg); + RuntimeError(String const& msg); }; /** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. @@ -450,13 +618,14 @@ public: */ class JSON_API LogicError : public Exception { public: - LogicError(std::string const& msg); + LogicError(String const& msg); }; +#endif /// used internally -void throwRuntimeError(std::string const& msg); +JSONCPP_NORETURN void throwRuntimeError(String const& msg); /// used internally -void throwLogicError(std::string const& msg); +JSONCPP_NORETURN void throwLogicError(String const& msg); /** \brief Type of the value held by a Value object. */ @@ -479,14 +648,16 @@ enum CommentPlacement { numberOfCommentPlacement }; -//# ifdef JSON_USE_CPPTL -// typedef CppTL::AnyEnumerator EnumMemberNames; -// typedef CppTL::AnyEnumerator EnumValues; -//# endif +/** \brief Type of precision for formatting of real values. + */ +enum PrecisionType { + significantDigits = 0, ///< we set max number of significant digits in string + decimalPlaces ///< we set max number of digits after "." in string +}; /** \brief Lightweight wrapper to tag static string. * - * Value constructor and objectValue member assignement takes advantage of the + * Value constructor and objectValue member assignment takes advantage of the * StaticString and avoid the cost of string duplication when storing the * string or the member name. * @@ -535,7 +706,7 @@ private: * The get() methods can be used to obtain default value in the case the * required element does not exist. * - * It is possible to iterate over the list of a #objectValue values using + * It is possible to iterate over the list of member keys of an object using * the getMemberNames() method. * * \note #Value string-length fit in size_t, but keys must be < 2^30. @@ -546,66 +717,86 @@ private: */ class JSON_API Value { friend class ValueIteratorBase; -public: - typedef std::vector Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; - typedef Json::UInt UInt; - typedef Json::Int Int; -#if defined(JSON_HAS_INT64) - typedef Json::UInt64 UInt64; - typedef Json::Int64 Int64; -#endif // defined(JSON_HAS_INT64) - typedef Json::LargestInt LargestInt; - typedef Json::LargestUInt LargestUInt; - typedef Json::ArrayIndex ArrayIndex; - static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). - static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null +public: + using Members = std::vector; + using iterator = ValueIterator; + using const_iterator = ValueConstIterator; + using UInt = Json::UInt; + using Int = Json::Int; +#if defined(JSON_HAS_INT64) + using UInt64 = Json::UInt64; + using Int64 = Json::Int64; +#endif // defined(JSON_HAS_INT64) + using LargestInt = Json::LargestInt; + using LargestUInt = Json::LargestUInt; + using ArrayIndex = Json::ArrayIndex; + + // Required for boost integration, e. g. BOOST_TEST + using value_type = std::string; + +#if JSON_USE_NULLREF + // Binary compatibility kludges, do not use. + static const Value& null; + static const Value& nullRef; +#endif + + // null and nullRef are deprecated, use this instead. + static Value const& nullSingleton(); + /// Minimum signed integer value that can be stored in a Json::Value. - static const LargestInt minLargestInt; + static constexpr LargestInt minLargestInt = + LargestInt(~(LargestUInt(-1) / 2)); /// Maximum signed integer value that can be stored in a Json::Value. - static const LargestInt maxLargestInt; + static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2); /// Maximum unsigned integer value that can be stored in a Json::Value. - static const LargestUInt maxLargestUInt; + static constexpr LargestUInt maxLargestUInt = LargestUInt(-1); /// Minimum signed int value that can be stored in a Json::Value. - static const Int minInt; + static constexpr Int minInt = Int(~(UInt(-1) / 2)); /// Maximum signed int value that can be stored in a Json::Value. - static const Int maxInt; + static constexpr Int maxInt = Int(UInt(-1) / 2); /// Maximum unsigned int value that can be stored in a Json::Value. - static const UInt maxUInt; + static constexpr UInt maxUInt = UInt(-1); #if defined(JSON_HAS_INT64) /// Minimum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 minInt64; + static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2)); /// Maximum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 maxInt64; + static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2); /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. - static const UInt64 maxUInt64; + static constexpr UInt64 maxUInt64 = UInt64(-1); #endif // defined(JSON_HAS_INT64) - + /// Default precision for real value for string representation. + static constexpr UInt defaultRealPrecision = 17; + // The constant is hard-coded because some compiler have trouble + // converting Value::maxUInt64 to a double correctly (AIX/xlC). + // Assumes that UInt64 is a 64 bits integer. + static constexpr double maxUInt64AsDouble = 18446744073709551615.0; +// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler +// when using gcc and clang backend compilers. CZString +// cannot be defined as private. See issue #486 +#ifdef __NVCC__ +public: +#else private: +#endif #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION class CZString { public: - enum DuplicationPolicy { - noDuplication = 0, - duplicate, - duplicateOnCopy - }; + enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy }; CZString(ArrayIndex index); CZString(char const* str, unsigned length, DuplicationPolicy allocate); CZString(CZString const& other); -#if JSON_HAS_RVALUE_REFERENCES CZString(CZString&& other); -#endif ~CZString(); - CZString& operator=(CZString other); + CZString& operator=(const CZString& other); + CZString& operator=(CZString&& other); + bool operator<(CZString const& other) const; bool operator==(CZString const& other) const; ArrayIndex index() const; - //const char* c_str() const; ///< \deprecated + // const char* c_str() const; ///< \deprecated char const* data() const; unsigned length() const; bool isStaticString() const; @@ -614,11 +805,11 @@ private: void swap(CZString& other); struct StringStorage { - unsigned policy_: 2; - unsigned length_: 30; // 1GB max + unsigned policy_ : 2; + unsigned length_ : 30; // 1GB max }; - char const* cstr_; // actually, a prefixed string, unless policy is noDup + char const* cstr_; // actually, a prefixed string, unless policy is noDup union { ArrayIndex index_; StringStorage storage_; @@ -626,29 +817,26 @@ private: }; public: -#ifndef JSON_USE_CPPTL_SMALLMAP typedef std::map ObjectValues; -#else - typedef CppTL::SmallMap ObjectValues; -#endif // ifndef JSON_USE_CPPTL_SMALLMAP #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION public: - /** \brief Create a default Value of the given type. - - This is a very useful constructor. - To create an empty array, pass arrayValue. - To create an empty object, pass objectValue. - Another Value can then be set to this one by assignment. -This is useful since clear() and resize() will not alter types. - - Examples: -\code -Json::Value null_value; // null -Json::Value arr_value(Json::arrayValue); // [] -Json::Value obj_value(Json::objectValue); // {} -\endcode - */ + /** + * \brief Create a default Value of the given type. + * + * This is a very useful constructor. + * To create an empty array, pass arrayValue. + * To create an empty object, pass objectValue. + * Another Value can then be set to this one by assignment. + * This is useful since clear() and resize() will not alter types. + * + * Examples: + * \code + * Json::Value null_value; // null + * Json::Value arr_value(Json::arrayValue); // [] + * Json::Value obj_value(Json::objectValue); // {} + * \endcode + */ Value(ValueType type = nullValue); Value(Int value); Value(UInt value); @@ -659,43 +847,46 @@ Json::Value obj_value(Json::objectValue); // {} Value(double value); Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) Value(const char* begin, const char* end); ///< Copy all, incl zeroes. - /** \brief Constructs a value from a static string. - + /** + * \brief Constructs a value from a static string. + * * Like other value string constructor but do not duplicate the string for - * internal storage. The given string must remain alive after the call to this - * constructor. + * internal storage. The given string must remain alive after the call to + * this constructor. + * * \note This works only for null-terminated strings. (We cannot change the - * size of this class, so we have nowhere to store the length, - * which might be computed later for various operations.) + * size of this class, so we have nowhere to store the length, which might be + * computed later for various operations.) * * Example of usage: - * \code - * static StaticString foo("some text"); - * Json::Value aValue(foo); - * \endcode + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode */ Value(const StaticString& value); - Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. -#ifdef JSON_USE_CPPTL - Value(const CppTL::ConstString& value); -#endif + Value(const String& value); Value(bool value); - /// Deep copy. + Value(std::nullptr_t ptr) = delete; Value(const Value& other); -#if JSON_HAS_RVALUE_REFERENCES - /// Move constructor Value(Value&& other); -#endif ~Value(); - /// Deep copy, then swap(other). - /// \note Over-write existing comments. To preserve comments, use #swapPayload(). - Value& operator=(Value other); + /// \note Overwrite existing comments. To preserve comments, use + /// #swapPayload(). + Value& operator=(const Value& other); + Value& operator=(Value&& other); + /// Swap everything. void swap(Value& other); /// Swap values but leave comments and source offsets in place. void swapPayload(Value& other); + /// copy everything. + void copy(const Value& other); + /// copy values but leave comments and source offsets in place. + void copyPayload(const Value& other); + ValueType type() const; /// Compare payload only, not comments etc. @@ -708,15 +899,15 @@ Json::Value obj_value(Json::objectValue); // {} int compare(const Value& other) const; const char* asCString() const; ///< Embedded zeroes could cause you trouble! - std::string asString() const; ///< Embedded zeroes are possible. +#if JSONCPP_USING_SECURE_MEMORY + unsigned getCStringLength() const; // Allows you to understand the length of + // the CString +#endif + String asString() const; ///< Embedded zeroes are possible. /** Get raw char* of string-value. * \return false if !string. (Seg-fault if str or end are NULL.) */ - bool getString( - char const** begin, char const** end) const; -#ifdef JSON_USE_CPPTL - CppTL::ConstString asConstString() const; -#endif + bool getString(char const** begin, char const** end) const; Int asInt() const; UInt asUInt() const; #if defined(JSON_HAS_INT64) @@ -742,6 +933,10 @@ Json::Value obj_value(Json::objectValue); // {} bool isArray() const; bool isObject() const; + /// The `as` and `is` member function templates and specializations. + template T as() const JSONCPP_TEMPLATE_DELETE; + template bool is() const JSONCPP_TEMPLATE_DELETE; + bool isConvertibleTo(ValueType other) const; /// Number of values in array or object @@ -751,50 +946,41 @@ Json::Value obj_value(Json::objectValue); // {} /// otherwise, false. bool empty() const; - /// Return isNull() - bool operator!() const; + /// Return !isNull() + explicit operator bool() const; /// Remove all object members and array elements. /// \pre type() is arrayValue, objectValue, or nullValue /// \post type() is unchanged void clear(); - /// Resize the array to size elements. + /// Resize the array to newSize elements. /// New elements are initialized to null. /// May only be called on nullValue or arrayValue. /// \pre type() is arrayValue or nullValue /// \post type() is arrayValue - void resize(ArrayIndex size); + void resize(ArrayIndex newSize); - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. + //@{ + /// Access an array element (zero based index). If the array contains less + /// than index element, then null value are inserted in the array so that + /// its size is index+1. /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) + /// this from the operator[] which takes a string.) Value& operator[](ArrayIndex index); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) Value& operator[](int index); + //@} - /// Access an array element (zero based index ) + //@{ + /// Access an array element (zero based index). /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) + /// this from the operator[] which takes a string.) const Value& operator[](ArrayIndex index) const; - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) const Value& operator[](int index) const; + //@} /// If the array contains at least index+1 elements, returns the element - /// value, - /// otherwise returns defaultValue. + /// value, otherwise returns defaultValue. Value get(ArrayIndex index, const Value& defaultValue) const; /// Return true if index < size(). bool isValidIndex(ArrayIndex index) const; @@ -802,57 +988,51 @@ Json::Value obj_value(Json::objectValue); // {} /// /// Equivalent to jsonvalue[jsonvalue.size()] = value; Value& append(const Value& value); + Value& append(Value&& value); + + /// \brief Insert value in array at specific index + bool insert(ArrayIndex index, const Value& newValue); + bool insert(ArrayIndex index, Value&& newValue); /// Access an object value by name, create a null member if it does not exist. /// \note Because of our implementation, keys are limited to 2^30 -1 chars. - /// Exceeding that will cause an exception. + /// Exceeding that will cause an exception. Value& operator[](const char* key); /// Access an object value by name, returns null if there is no member with /// that name. const Value& operator[](const char* key) const; /// Access an object value by name, create a null member if it does not exist. /// \param key may contain embedded nulls. - Value& operator[](const std::string& key); + Value& operator[](const String& key); /// Access an object value by name, returns null if there is no member with /// that name. /// \param key may contain embedded nulls. - const Value& operator[](const std::string& key) const; + const Value& operator[](const String& key) const; /** \brief Access an object value by name, create a null member if it does not - exist. - - * If the object has no entry for that name, then the member name used to store - * the new entry is not duplicated. + * exist. + * + * If the object has no entry for that name, then the member name used to + * store the new entry is not duplicated. * Example of use: - * \code - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode */ Value& operator[](const StaticString& key); -#ifdef JSON_USE_CPPTL - /// Access an object value by name, create a null member if it does not exist. - Value& operator[](const CppTL::ConstString& key); - /// Access an object value by name, returns null if there is no member with - /// that name. - const Value& operator[](const CppTL::ConstString& key) const; -#endif /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy Value get(const char* key, const Value& defaultValue) const; /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy /// \note key may contain embedded nulls. - Value get(const char* begin, const char* end, const Value& defaultValue) const; + Value get(const char* begin, const char* end, + const Value& defaultValue) const; /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy /// \param key may contain embedded nulls. - Value get(const std::string& key, const Value& defaultValue) const; -#ifdef JSON_USE_CPPTL - /// Return the member named key if it exist, defaultValue otherwise. - /// \note deep copy - Value get(const CppTL::ConstString& key, const Value& defaultValue) const; -#endif + Value get(const String& key, const Value& defaultValue) const; /// Most general and efficient version of isMember()const, get()const, /// and operator[]const /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 @@ -860,51 +1040,44 @@ Json::Value obj_value(Json::objectValue); // {} /// Most general and efficient version of object-mutators. /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. - Value const* demand(char const* begin, char const* end); + Value* demand(char const* begin, char const* end); /// \brief Remove and return the named member. /// /// Do nothing if it did not exist. - /// \return the removed Value, or null. /// \pre type() is objectValue or nullValue /// \post type() is unchanged - /// \deprecated - Value removeMember(const char* key); + void removeMember(const char* key); /// Same as removeMember(const char*) /// \param key may contain embedded nulls. - /// \deprecated - Value removeMember(const std::string& key); + void removeMember(const String& key); /// Same as removeMember(const char* begin, const char* end, Value* removed), /// but 'key' is null-terminated. bool removeMember(const char* key, Value* removed); /** \brief Remove the named map member. - - Update 'removed' iff removed. - \param key may contain embedded nulls. - \return true iff removed (no exceptions) - */ - bool removeMember(std::string const& key, Value* removed); - /// Same as removeMember(std::string const& key, Value* removed) + * + * Update 'removed' iff removed. + * \param key may contain embedded nulls. + * \return true iff removed (no exceptions) + */ + bool removeMember(String const& key, Value* removed); + /// Same as removeMember(String const& key, Value* removed) bool removeMember(const char* begin, const char* end, Value* removed); /** \brief Remove the indexed array element. - - O(n) expensive operations. - Update 'removed' iff removed. - \return true iff removed (no exceptions) - */ - bool removeIndex(ArrayIndex i, Value* removed); + * + * O(n) expensive operations. + * Update 'removed' iff removed. + * \return true if removed (no exceptions) + */ + bool removeIndex(ArrayIndex index, Value* removed); /// Return true if the object has a member named key. /// \note 'key' must be null-terminated. bool isMember(const char* key) const; /// Return true if the object has a member named key. /// \param key may contain embedded nulls. - bool isMember(const std::string& key) const; - /// Same as isMember(std::string const& key)const + bool isMember(const String& key) const; + /// Same as isMember(String const& key)const bool isMember(const char* begin, const char* end) const; -#ifdef JSON_USE_CPPTL - /// Return true if the object has a member named key. - bool isMember(const CppTL::ConstString& key) const; -#endif /// \brief Return a list of the member names. /// @@ -913,23 +1086,22 @@ Json::Value obj_value(Json::objectValue); // {} /// \post if type() was nullValue, it remains nullValue Members getMemberNames() const; - //# ifdef JSON_USE_CPPTL - // EnumMemberNames enumMemberNames() const; - // EnumValues enumValues() const; - //# endif - /// \deprecated Always pass len. - JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.") - void setComment(const char* comment, CommentPlacement placement); + JSONCPP_DEPRECATED("Use setComment(String const&) instead.") + void setComment(const char* comment, CommentPlacement placement) { + setComment(String(comment, strlen(comment)), placement); + } /// Comments must be //... or /* ... */ - void setComment(const char* comment, size_t len, CommentPlacement placement); + void setComment(const char* comment, size_t len, CommentPlacement placement) { + setComment(String(comment, len), placement); + } /// Comments must be //... or /* ... */ - void setComment(const std::string& comment, CommentPlacement placement); + void setComment(String comment, CommentPlacement placement); bool hasComment(CommentPlacement placement) const; /// Include delimiters and embedded newlines. - std::string getComment(CommentPlacement placement) const; + String getComment(CommentPlacement placement) const; - std::string toStyledString() const; + String toStyledString() const; const_iterator begin() const; const_iterator end() const; @@ -939,26 +1111,26 @@ Json::Value obj_value(Json::objectValue); // {} // Accessors for the [start, limit) range of bytes within the JSON text from // which this value was parsed, if any. - void setOffsetStart(size_t start); - void setOffsetLimit(size_t limit); - size_t getOffsetStart() const; - size_t getOffsetLimit() const; + void setOffsetStart(ptrdiff_t start); + void setOffsetLimit(ptrdiff_t limit); + ptrdiff_t getOffsetStart() const; + ptrdiff_t getOffsetLimit() const; private: + void setType(ValueType v) { + bits_.value_type_ = static_cast(v); + } + bool isAllocated() const { return bits_.allocated_; } + void setIsAllocated(bool v) { bits_.allocated_ = v; } + void initBasic(ValueType type, bool allocated = false); + void dupPayload(const Value& other); + void releasePayload(); + void dupMeta(const Value& other); Value& resolveReference(const char* key); Value& resolveReference(const char* key, const char* end); - struct CommentInfo { - CommentInfo(); - ~CommentInfo(); - - void setComment(const char* text, size_t len); - - char* comment_; - }; - // struct MemberNamesTransform //{ // typedef const char *result_type; @@ -973,20 +1145,70 @@ private: LargestUInt uint_; double real_; bool bool_; - char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ + char* string_; // if allocated_, ptr to { unsigned, char[] }. ObjectValues* map_; } value_; - ValueType type_ : 8; - unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. - // If not allocated_, string_ must be null-terminated. - CommentInfo* comments_; + + struct { + // Really a ValueType, but types should agree for bitfield packing. + unsigned int value_type_ : 8; + // Unless allocated_, string_ must be null-terminated. + unsigned int allocated_ : 1; + } bits_; + + class Comments { + public: + Comments() = default; + Comments(const Comments& that); + Comments(Comments&& that); + Comments& operator=(const Comments& that); + Comments& operator=(Comments&& that); + bool has(CommentPlacement slot) const; + String get(CommentPlacement slot) const; + void set(CommentPlacement slot, String comment); + + private: + using Array = std::array; + std::unique_ptr ptr_; + }; + Comments comments_; // [start, limit) byte offsets in the source JSON text from which this Value // was extracted. - size_t start_; - size_t limit_; + ptrdiff_t start_; + ptrdiff_t limit_; }; +template <> inline bool Value::as() const { return asBool(); } +template <> inline bool Value::is() const { return isBool(); } + +template <> inline Int Value::as() const { return asInt(); } +template <> inline bool Value::is() const { return isInt(); } + +template <> inline UInt Value::as() const { return asUInt(); } +template <> inline bool Value::is() const { return isUInt(); } + +#if defined(JSON_HAS_INT64) +template <> inline Int64 Value::as() const { return asInt64(); } +template <> inline bool Value::is() const { return isInt64(); } + +template <> inline UInt64 Value::as() const { return asUInt64(); } +template <> inline bool Value::is() const { return isUInt64(); } +#endif + +template <> inline double Value::as() const { return asDouble(); } +template <> inline bool Value::is() const { return isDouble(); } + +template <> inline String Value::as() const { return asString(); } +template <> inline bool Value::is() const { return isString(); } + +/// These `as` specializations are type conversions, and do not have a +/// corresponding `is`. +template <> inline float Value::as() const { return asFloat(); } +template <> inline const char* Value::as() const { + return asCString(); +} + /** \brief Experimental and untested: represents an element of the "path" to * access a node. */ @@ -997,17 +1219,13 @@ public: PathArgument(); PathArgument(ArrayIndex index); PathArgument(const char* key); - PathArgument(const std::string& key); + PathArgument(String key); private: - enum Kind { - kindNone = 0, - kindIndex, - kindKey - }; - std::string key_; - ArrayIndex index_; - Kind kind_; + enum Kind { kindNone = 0, kindIndex, kindKey }; + String key_; + ArrayIndex index_{}; + Kind kind_{kindNone}; }; /** \brief Experimental and untested: represents a "path" to access a node. @@ -1019,12 +1237,11 @@ private: * - ".name1.name2.name3" * - ".[0][1][2].name1[3]" * - ".%" => member name is provided as parameter - * - ".[%]" => index is provied as parameter + * - ".[%]" => index is provided as parameter */ class JSON_API Path { public: - Path(const std::string& path, - const PathArgument& a1 = PathArgument(), + Path(const String& path, const PathArgument& a1 = PathArgument(), const PathArgument& a2 = PathArgument(), const PathArgument& a3 = PathArgument(), const PathArgument& a4 = PathArgument(), @@ -1037,15 +1254,13 @@ public: Value& make(Value& root) const; private: - typedef std::vector InArgs; - typedef std::vector Args; + using InArgs = std::vector; + using Args = std::vector; - void makePath(const std::string& path, const InArgs& in); - void addPathInArg(const std::string& path, - const InArgs& in, - InArgs::const_iterator& itInArg, - PathArgument::Kind kind); - void invalidPath(const std::string& path, int location); + void makePath(const String& path, const InArgs& in); + void addPathInArg(const String& path, const InArgs& in, + InArgs::const_iterator& itInArg, PathArgument::Kind kind); + static void invalidPath(const String& path, int location); Args args_; }; @@ -1055,10 +1270,10 @@ private: */ class JSON_API ValueIteratorBase { public: - typedef std::bidirectional_iterator_tag iterator_category; - typedef unsigned int size_t; - typedef int difference_type; - typedef ValueIteratorBase SelfType; + using iterator_category = std::bidirectional_iterator_tag; + using size_t = unsigned int; + using difference_type = int; + using SelfType = ValueIteratorBase; bool operator==(const SelfType& other) const { return isEqual(other); } @@ -1072,17 +1287,19 @@ public: /// Value. Value key() const; - /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + /// Return the index of the referenced Value, or -1 if it is not an + /// arrayValue. UInt index() const; /// Return the member name of the referenced Value, or "" if it is not an /// objectValue. /// \note Avoid `c_str()` on result, as embedded zeroes are possible. - std::string name() const; + String name() const; /// Return the member name of the referenced Value. "" if it is not an /// objectValue. - /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + /// \deprecated This cannot be used for UTF-8 strings, since there can be + /// embedded nulls. JSONCPP_DEPRECATED("Use `key = name();` instead.") char const* memberName() const; /// Return the member name of the referenced Value, or NULL if it is not an @@ -1091,7 +1308,14 @@ public: char const* memberName(char const** end) const; protected: - Value& deref() const; + /*! Internal utility functions to assist with implementing + * other iterator functions. The const and non-const versions + * of the "deref" protected methods expose the protected + * current_ member variable in a way that can often be + * optimized away by the compiler. + */ + const Value& deref() const; + Value& deref(); void increment(); @@ -1106,7 +1330,7 @@ protected: private: Value::ObjectValues::iterator current_; // Indicates that iterator is for a null value. - bool isNull_; + bool isNull_{true}; public: // For some reason, BORLAND needs these at the end, rather @@ -1122,20 +1346,21 @@ class JSON_API ValueConstIterator : public ValueIteratorBase { friend class Value; public: - typedef const Value value_type; - //typedef unsigned int size_t; - //typedef int difference_type; - typedef const Value& reference; - typedef const Value* pointer; - typedef ValueConstIterator SelfType; + using value_type = const Value; + // typedef unsigned int size_t; + // typedef int difference_type; + using reference = const Value&; + using pointer = const Value*; + using SelfType = ValueConstIterator; ValueConstIterator(); ValueConstIterator(ValueIterator const& other); private: -/*! \internal Use by Value to create an iterator. - */ + /*! \internal Use by Value to create an iterator. + */ explicit ValueConstIterator(const Value::ObjectValues::iterator& current); + public: SelfType& operator=(const ValueIteratorBase& other); @@ -1172,21 +1397,22 @@ class JSON_API ValueIterator : public ValueIteratorBase { friend class Value; public: - typedef Value value_type; - typedef unsigned int size_t; - typedef int difference_type; - typedef Value& reference; - typedef Value* pointer; - typedef ValueIterator SelfType; + using value_type = Value; + using size_t = unsigned int; + using difference_type = int; + using reference = Value&; + using pointer = Value*; + using SelfType = ValueIterator; ValueIterator(); explicit ValueIterator(const ValueConstIterator& other); ValueIterator(const ValueIterator& other); private: -/*! \internal Use by Value to create an iterator. - */ + /*! \internal Use by Value to create an iterator. + */ explicit ValueIterator(const Value::ObjectValues::iterator& current); + public: SelfType& operator=(const SelfType& other); @@ -1212,26 +1438,26 @@ public: return *this; } - reference operator*() const { return deref(); } - - pointer operator->() const { return &deref(); } + /*! The return value of non-const iterators can be + * changed, so the these functions are not const + * because the returned references/pointers can be used + * to change state of the base class. + */ + reference operator*() { return deref(); } + pointer operator->() { return &deref(); } }; +inline void swap(Value& a, Value& b) { a.swap(b); } + } // namespace Json - -namespace std { -/// Specialize std::swap() for Json::Value. -template<> -inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } -} - +#pragma pack(pop) #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#endif // CPPTL_JSON_H_INCLUDED +#endif // JSON_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/value.h @@ -1246,23 +1472,23 @@ inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } // Beginning of content of file: include/json/reader.h // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#ifndef CPPTL_JSON_READER_H_INCLUDED -#define CPPTL_JSON_READER_H_INCLUDED +#ifndef JSON_READER_H_INCLUDED +#define JSON_READER_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) -#include "features.h" +#include "json_features.h" #include "value.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include +#include #include #include -#include // Disable warning C4251: : needs to have dll-interface to // be used by... @@ -1271,135 +1497,135 @@ inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } #pragma warning(disable : 4251) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma pack(push, 8) + namespace Json { /** \brief Unserialize a JSON document into a - *Value. + * Value. * * \deprecated Use CharReader and CharReaderBuilder. */ -class JSON_API Reader { + +class JSONCPP_DEPRECATED( + "Use CharReader and CharReaderBuilder instead.") JSON_API Reader { public: - typedef char Char; - typedef const Char* Location; + using Char = char; + using Location = const Char*; /** \brief An error tagged with where in the JSON text it was encountered. * * The offsets give the [start, limit) range of bytes within the text. Note * that this is bytes, not codepoints. - * */ struct StructuredError { - size_t offset_start; - size_t offset_limit; - std::string message; + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; }; - /** \brief Constructs a Reader allowing all features - * for parsing. + /** \brief Constructs a Reader allowing all features for parsing. */ + JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") Reader(); - /** \brief Constructs a Reader allowing the specified feature set - * for parsing. + /** \brief Constructs a Reader allowing the specified feature set for parsing. */ + JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") Reader(const Features& features); /** \brief Read a Value from a JSON * document. - * \param document UTF-8 encoded string containing the document to read. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them - * back during - * serialization, \c false to discard comments. - * This parameter is ignored if - * Features::allowComments_ - * is \c false. + * + * \param document UTF-8 encoded string containing the document + * to read. + * \param[out] root Contains the root value of the document if it + * was successfully parsed. + * \param collectComments \c true to collect comment and allow writing + * them back during serialization, \c false to + * discard comments. This parameter is ignored + * if Features::allowComments_ is \c false. * \return \c true if the document was successfully parsed, \c false if an * error occurred. */ - bool - parse(const std::string& document, Value& root, bool collectComments = true); + bool parse(const std::string& document, Value& root, + bool collectComments = true); /** \brief Read a Value from a JSON - document. - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them - back during - * serialization, \c false to discard comments. - * This parameter is ignored if - Features::allowComments_ - * is \c false. + * document. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded + * string of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string + * of the document to read. Must be >= beginDoc. + * \param[out] root Contains the root value of the document if it + * was successfully parsed. + * \param collectComments \c true to collect comment and allow writing + * them back during serialization, \c false to + * discard comments. This parameter is ignored + * if Features::allowComments_ is \c false. * \return \c true if the document was successfully parsed, \c false if an - error occurred. + * error occurred. */ - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, + bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); /// \brief Parse from input stream. /// \see Json::operator>>(std::istream&, Json::Value&). - bool parse(std::istream& is, Value& root, bool collectComments = true); + bool parse(IStream& is, Value& root, bool collectComments = true); /** \brief Returns a user friendly string that list errors in the parsed * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. + * + * \return Formatted error message with the list of errors with their + * location in the parsed document. An empty string is returned if no error + * occurred during parsing. * \deprecated Use getFormattedErrorMessages() instead (typo fix). */ JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") - std::string getFormatedErrorMessages() const; + String getFormatedErrorMessages() const; /** \brief Returns a user friendly string that list errors in the parsed * document. - * \return Formatted error message with the list of errors with their location - * in - * the parsed document. An empty string is returned if no error - * occurred - * during parsing. + * + * \return Formatted error message with the list of errors with their + * location in the parsed document. An empty string is returned if no error + * occurred during parsing. */ - std::string getFormattedErrorMessages() const; + String getFormattedErrorMessages() const; - /** \brief Returns a vector of structured erros encounted while parsing. + /** \brief Returns a vector of structured errors encountered while parsing. + * * \return A (possibly empty) vector of StructuredError objects. Currently - * only one error can be returned, but the caller should tolerate - * multiple - * errors. This can occur if the parser recovers from a non-fatal - * parse error and then encounters additional errors. + * only one error can be returned, but the caller should tolerate multiple + * errors. This can occur if the parser recovers from a non-fatal parse + * error and then encounters additional errors. */ std::vector getStructuredErrors() const; /** \brief Add a semantic error message. - * \param value JSON Value location associated with the error + * + * \param value JSON Value location associated with the error * \param message The error message. - * \return \c true if the error was successfully added, \c false if the - * Value offset exceeds the document size. + * \return \c true if the error was successfully added, \c false if the Value + * offset exceeds the document size. */ - bool pushError(const Value& value, const std::string& message); + bool pushError(const Value& value, const String& message); /** \brief Add a semantic error message with extra context. - * \param value JSON Value location associated with the error + * + * \param value JSON Value location associated with the error * \param message The error message. - * \param extra Additional JSON Value location to contextualize the error + * \param extra Additional JSON Value location to contextualize the error * \return \c true if the error was successfully added, \c false if either * Value offset exceeds the document size. */ - bool pushError(const Value& value, const std::string& message, const Value& extra); + bool pushError(const Value& value, const String& message, const Value& extra); /** \brief Return whether there are any errors. - * \return \c true if there are no errors to report \c false if - * errors have occurred. + * + * \return \c true if there are no errors to report \c false if errors have + * occurred. */ bool good() const; @@ -1431,15 +1657,15 @@ private: class ErrorInfo { public: Token token_; - std::string message_; + String message_; Location extra_; }; - typedef std::deque Errors; + using Errors = std::deque; bool readToken(Token& token); void skipSpaces(); - bool match(Location pattern, int patternLength); + bool match(const Char* pattern, int patternLength); bool readComment(); bool readCStyleComment(); bool readCppStyleComment(); @@ -1451,133 +1677,132 @@ private: bool decodeNumber(Token& token); bool decodeNumber(Token& token, Value& decoded); bool decodeString(Token& token); - bool decodeString(Token& token, std::string& decoded); + bool decodeString(Token& token, String& decoded); bool decodeDouble(Token& token); bool decodeDouble(Token& token, Value& decoded); - bool decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, + bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode); - bool decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool addError(const std::string& message, Token& token, Location extra = 0); + bool decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const std::string& message, - Token& token, + bool addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken); void skipUntilSpace(); Value& currentValue(); Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - std::string getLocationLineAndColumn(Location location) const; + void getLocationLineAndColumn(Location location, int& line, + int& column) const; + String getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); - typedef std::stack Nodes; + static bool containsNewLine(Location begin, Location end); + static String normalizeEOL(Location begin, Location end); + + using Nodes = std::stack; Nodes nodes_; Errors errors_; - std::string document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value* lastValue_; - std::string commentsBefore_; + String document_; + Location begin_{}; + Location end_{}; + Location current_{}; + Location lastValueEnd_{}; + Value* lastValue_{}; + String commentsBefore_; Features features_; - bool collectComments_; -}; // Reader + bool collectComments_{}; +}; // Reader /** Interface for reading JSON from a char array. */ class JSON_API CharReader { public: - virtual ~CharReader() {} + virtual ~CharReader() = default; /** \brief Read a Value from a JSON - document. - * The document must be a UTF-8 encoded string containing the document to read. + * document. The document must be a UTF-8 encoded string containing the + * document to read. * - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the - document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the - document to read. - * Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param errs [out] Formatted error messages (if not NULL) - * a user friendly string that lists errors in the parsed - * document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string + * of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + * document to read. Must be >= beginDoc. + * \param[out] root Contains the root value of the document if it was + * successfully parsed. + * \param[out] errs Formatted error messages (if not NULL) a user + * friendly string that lists errors in the parsed + * document. * \return \c true if the document was successfully parsed, \c false if an - error occurred. + * error occurred. */ - virtual bool parse( - char const* beginDoc, char const* endDoc, - Value* root, std::string* errs) = 0; + virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) = 0; class JSON_API Factory { public: - virtual ~Factory() {} + virtual ~Factory() = default; /** \brief Allocate a CharReader via operator new(). * \throw std::exception if something goes wrong (e.g. invalid settings) */ virtual CharReader* newCharReader() const = 0; - }; // Factory -}; // CharReader + }; // Factory +}; // CharReader /** \brief Build a CharReader implementation. - -Usage: -\code - using namespace Json; - CharReaderBuilder builder; - builder["collectComments"] = false; - Value value; - std::string errs; - bool ok = parseFromStream(builder, std::cin, &value, &errs); -\endcode -*/ + * + * Usage: + * \code + * using namespace Json; + * CharReaderBuilder builder; + * builder["collectComments"] = false; + * Value value; + * String errs; + * bool ok = parseFromStream(builder, std::cin, &value, &errs); + * \endcode + */ class JSON_API CharReaderBuilder : public CharReader::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. /** Configuration of this builder. - These are case-sensitive. - Available settings (case-sensitive): - - `"collectComments": false or true` - - true to collect comment and allow writing them - back during serialization, false to discard comments. - This parameter is ignored if allowComments is false. - - `"allowComments": false or true` - - true if comments are allowed. - - `"strictRoot": false or true` - - true if root must be either an array or an object value - - `"allowDroppedNullPlaceholders": false or true` - - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) - - `"allowNumericKeys": false or true` - - true if numeric object keys are allowed. - - `"allowSingleQuotes": false or true` - - true if '' are allowed for strings (both keys and values) - - `"stackLimit": integer` - - Exceeding stackLimit (recursive depth of `readValue()`) will - cause an exception. - - This is a security issue (seg-faults caused by deeply nested JSON), - so the default is low. - - `"failIfExtra": false or true` - - If true, `parse()` returns false when extra non-whitespace trails - the JSON value in the input string. - - `"rejectDupKeys": false or true` - - If true, `parse()` returns false when a key is duplicated within an object. - - `"allowSpecialFloats": false or true` - - If true, special float values (NaNs and infinities) are allowed - and their values are lossfree restorable. - - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ + * These are case-sensitive. + * Available settings (case-sensitive): + * - `"collectComments": false or true` + * - true to collect comment and allow writing them back during + * serialization, false to discard comments. This parameter is ignored + * if allowComments is false. + * - `"allowComments": false or true` + * - true if comments are allowed. + * - `"allowTrailingCommas": false or true` + * - true if trailing commas in objects and arrays are allowed. + * - `"strictRoot": false or true` + * - true if root must be either an array or an object value + * - `"allowDroppedNullPlaceholders": false or true` + * - true if dropped null placeholders are allowed. (See + * StreamWriterBuilder.) + * - `"allowNumericKeys": false or true` + * - true if numeric object keys are allowed. + * - `"allowSingleQuotes": false or true` + * - true if '' are allowed for strings (both keys and values) + * - `"stackLimit": integer` + * - Exceeding stackLimit (recursive depth of `readValue()`) will cause an + * exception. + * - This is a security issue (seg-faults caused by deeply nested JSON), so + * the default is low. + * - `"failIfExtra": false or true` + * - If true, `parse()` returns false when extra non-whitespace trails the + * JSON value in the input string. + * - `"rejectDupKeys": false or true` + * - If true, `parse()` returns false when a key is duplicated within an + * object. + * - `"allowSpecialFloats": false or true` + * - If true, special float values (NaNs and infinities) are allowed and + * their values are lossfree restorable. + * + * You can examine 'settings_` yourself to see the defaults. You can also + * write and read them just like any JSON Value. + * \sa setDefaults() + */ Json::Value settings_; CharReaderBuilder(); @@ -1592,7 +1817,7 @@ public: /** A simple way to update a specific setting. */ - Value& operator[](std::string key); + Value& operator[](const String& key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) @@ -1609,47 +1834,47 @@ public: }; /** Consume entire stream and use its begin/end. - * Someday we might have a real StreamReader, but for now this - * is convenient. - */ -bool JSON_API parseFromStream( - CharReader::Factory const&, - std::istream&, - Value* root, std::string* errs); + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root, + String* errs); /** \brief Read from 'sin' into 'root'. - - Always keep comments from the input JSON. - - This can be used to read a file into a particular sub-object. - For example: - \code - Json::Value root; - cin >> root["dir"]["file"]; - cout << root; - \endcode - Result: - \verbatim - { - "dir": { - "file": { - // The input stream JSON would be nested here. - } - } - } - \endverbatim - \throw std::exception on parse error. - \see Json::operator<<() -*/ -JSON_API std::istream& operator>>(std::istream&, Value&); + * + * Always keep comments from the input JSON. + * + * This can be used to read a file into a particular sub-object. + * For example: + * \code + * Json::Value root; + * cin >> root["dir"]["file"]; + * cout << root; + * \endcode + * Result: + * \verbatim + * { + * "dir": { + * "file": { + * // The input stream JSON would be nested here. + * } + * } + * } + * \endverbatim + * \throw std::exception on parse error. + * \see Json::operator<<() + */ +JSON_API IStream& operator>>(IStream&, Value&); } // namespace Json +#pragma pack(pop) + #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) -#endif // CPPTL_JSON_READER_H_INCLUDED +#endif // JSON_READER_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/reader.h @@ -1664,7 +1889,7 @@ JSON_API std::istream& operator>>(std::istream&, Value&); // Beginning of content of file: include/json/writer.h // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -1675,47 +1900,49 @@ JSON_API std::istream& operator>>(std::istream&, Value&); #if !defined(JSON_IS_AMALGAMATION) #include "value.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include #include +#include +#include // Disable warning C4251: : needs to have dll-interface to // be used by... -#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4251) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma pack(push, 8) + namespace Json { class Value; /** - -Usage: -\code - using namespace Json; - void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { - std::unique_ptr const writer( - factory.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush - } -\endcode -*/ + * + * Usage: + * \code + * using namespace Json; + * void writeToStdout(StreamWriter::Factory const& factory, Value const& value) + * { std::unique_ptr const writer( factory.newStreamWriter()); + * writer->write(value, &std::cout); + * std::cout << std::endl; // add lf and flush + * } + * \endcode + */ class JSON_API StreamWriter { protected: - std::ostream* sout_; // not owned; will not delete + OStream* sout_; // not owned; will not delete public: StreamWriter(); virtual ~StreamWriter(); /** Write Value into document as configured in sub-class. - Do not take ownership of sout, but maintain a reference during function. - \pre sout != NULL - \return zero on success (For now, we always return zero, so check the stream instead.) - \throw std::exception possibly, depending on configuration + * Do not take ownership of sout, but maintain a reference during function. + * \pre sout != NULL + * \return zero on success (For now, we always return zero, so check the + * stream instead.) \throw std::exception possibly, depending on + * configuration */ - virtual int write(Value const& root, std::ostream* sout) = 0; + virtual int write(Value const& root, OStream* sout) = 0; /** \brief A simple abstract factory. */ @@ -1726,55 +1953,60 @@ public: * \throw std::exception if something goes wrong (e.g. invalid settings) */ virtual StreamWriter* newStreamWriter() const = 0; - }; // Factory -}; // StreamWriter + }; // Factory +}; // StreamWriter /** \brief Write into stringstream, then return string, for convenience. * A StreamWriter will be created from the factory, used, and then deleted. */ -std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); - +String JSON_API writeString(StreamWriter::Factory const& factory, + Value const& root); /** \brief Build a StreamWriter implementation. -Usage: -\code - using namespace Json; - Value value = ...; - StreamWriterBuilder builder; - builder["commentStyle"] = "None"; - builder["indentation"] = " "; // or whatever you like - std::unique_ptr writer( - builder.newStreamWriter()); - writer->write(value, &std::cout); - std::cout << std::endl; // add lf and flush -\endcode +* Usage: +* \code +* using namespace Json; +* Value value = ...; +* StreamWriterBuilder builder; +* builder["commentStyle"] = "None"; +* builder["indentation"] = " "; // or whatever you like +* std::unique_ptr writer( +* builder.newStreamWriter()); +* writer->write(value, &std::cout); +* std::cout << std::endl; // add lf and flush +* \endcode */ class JSON_API StreamWriterBuilder : public StreamWriter::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. /** Configuration of this builder. - Available settings (case-sensitive): - - "commentStyle": "None" or "All" - - "indentation": "" - - "enableYAMLCompatibility": false or true - - slightly change the whitespace around colons - - "dropNullPlaceholders": false or true - - Drop the "null" string from the writer's output for nullValues. - Strictly speaking, this is not valid JSON. But when the output is being - fed to a browser's Javascript, it makes for smaller output and the - browser can handle the output just fine. - - "useSpecialFloats": false or true - - If true, outputs non-finite floating point values in the following way: - NaN values as "NaN", positive infinity as "Infinity", and negative infinity - as "-Infinity". + * Available settings (case-sensitive): + * - "commentStyle": "None" or "All" + * - "indentation": "". + * - Setting this to an empty string also omits newline characters. + * - "enableYAMLCompatibility": false or true + * - slightly change the whitespace around colons + * - "dropNullPlaceholders": false or true + * - Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's JavaScript, it makes for smaller output and the + * browser can handle the output just fine. + * - "useSpecialFloats": false or true + * - If true, outputs non-finite floating point values in the following way: + * NaN values as "NaN", positive infinity as "Infinity", and negative + * infinity as "-Infinity". + * - "precision": int + * - Number of precision digits for formatting of real values. + * - "precisionType": "significant"(default) or "decimal" + * - Type of precision for formatting of real values. - You can examine 'settings_` yourself - to see the defaults. You can also write and read them just like any - JSON Value. - \sa setDefaults() - */ + * You can examine 'settings_` yourself + * to see the defaults. You can also write and read them just like any + * JSON Value. + * \sa setDefaults() + */ Json::Value settings_; StreamWriterBuilder(); @@ -1791,7 +2023,7 @@ public: bool validate(Json::Value* invalid) const; /** A simple way to update a specific setting. */ - Value& operator[](std::string key); + Value& operator[](const String& key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) @@ -1804,11 +2036,11 @@ public: /** \brief Abstract class for writers. * \deprecated Use StreamWriter. (And really, this is an implementation detail.) */ -class JSON_API Writer { +class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer { public: virtual ~Writer(); - virtual std::string write(const Value& root) = 0; + virtual String write(const Value& root) = 0; }; /** \brief Outputs a Value in JSON format @@ -1816,21 +2048,25 @@ public: * * The JSON document is written in a single line. It is not intended for 'human' *consumption, - * but may be usefull to support feature such as RPC where bandwith is limited. + * but may be useful to support feature such as RPC where bandwidth is limited. * \sa Reader, Value * \deprecated Use StreamWriterBuilder. */ -class JSON_API FastWriter : public Writer { - +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter + : public Writer { public: FastWriter(); - ~FastWriter() override {} + ~FastWriter() override = default; void enableYAMLCompatibility(); /** \brief Drop the "null" string from the writer's output for nullValues. * Strictly speaking, this is not valid JSON. But when the output is being - * fed to a browser's Javascript, it makes for smaller output and the + * fed to a browser's JavaScript, it makes for smaller output and the * browser can handle the output just fine. */ void dropNullPlaceholders(); @@ -1838,16 +2074,19 @@ public: void omitEndingLineFeed(); public: // overridden from Writer - std::string write(const Value& root) override; + String write(const Value& root) override; private: void writeValue(const Value& value); - std::string document_; - bool yamlCompatiblityEnabled_; - bool dropNullPlaceholders_; - bool omitEndingLineFeed_; + String document_; + bool yamlCompatibilityEnabled_{false}; + bool dropNullPlaceholders_{false}; + bool omitEndingLineFeed_{false}; }; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif /** \brief Writes a Value in JSON format in a *human friendly way. @@ -1873,41 +2112,49 @@ private: * \sa Reader, Value, Value::setComment() * \deprecated Use StreamWriterBuilder. */ -class JSON_API StyledWriter : public Writer { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API + StyledWriter : public Writer { public: StyledWriter(); - ~StyledWriter() override {} + ~StyledWriter() override = default; public: // overridden from Writer /** \brief Serialize a Value in JSON format. * \param root Value to serialize. * \return String containing the JSON document that represents the root value. */ - std::string write(const Value& root) override; + String write(const Value& root) override; private: void writeValue(const Value& value); void writeArrayValue(const Value& value); - bool isMultineArray(const Value& value); - void pushValue(const std::string& value); + bool isMultilineArray(const Value& value); + void pushValue(const String& value); void writeIndent(); - void writeWithIndent(const std::string& value); + void writeWithIndent(const String& value); void indent(); void unindent(); void writeCommentBeforeValue(const Value& root); void writeCommentAfterValueOnSameLine(const Value& root); - bool hasCommentForValue(const Value& value); - static std::string normalizeEOL(const std::string& text); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); - typedef std::vector ChildValues; + using ChildValues = std::vector; ChildValues childValues_; - std::string document_; - std::string indentString_; - int rightMargin_; - int indentSize_; - bool addChildValues_; + String document_; + String indentString_; + unsigned int rightMargin_{74}; + unsigned int indentSize_{3}; + bool addChildValues_{false}; }; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif /** \brief Writes a Value in JSON format in a human friendly way, @@ -1931,14 +2178,21 @@ private: * If the Value have comments then they are outputed according to their #CommentPlacement. * - * \param indentation Each level will be indented by this amount extra. * \sa Reader, Value, Value::setComment() * \deprecated Use StreamWriterBuilder. */ -class JSON_API StyledStreamWriter { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // Deriving from deprecated class +#endif +class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API + StyledStreamWriter { public: - StyledStreamWriter(std::string indentation = "\t"); - ~StyledStreamWriter() {} + /** + * \param indentation Each level will be indented by this amount extra. + */ + StyledStreamWriter(String indentation = "\t"); + ~StyledStreamWriter() = default; public: /** \brief Serialize a Value in JSON format. @@ -1947,49 +2201,56 @@ public: * \note There is no point in deriving from Writer, since write() should not * return a value. */ - void write(std::ostream& out, const Value& root); + void write(OStream& out, const Value& root); private: void writeValue(const Value& value); void writeArrayValue(const Value& value); - bool isMultineArray(const Value& value); - void pushValue(const std::string& value); + bool isMultilineArray(const Value& value); + void pushValue(const String& value); void writeIndent(); - void writeWithIndent(const std::string& value); + void writeWithIndent(const String& value); void indent(); void unindent(); void writeCommentBeforeValue(const Value& root); void writeCommentAfterValueOnSameLine(const Value& root); - bool hasCommentForValue(const Value& value); - static std::string normalizeEOL(const std::string& text); + static bool hasCommentForValue(const Value& value); + static String normalizeEOL(const String& text); - typedef std::vector ChildValues; + using ChildValues = std::vector; ChildValues childValues_; - std::ostream* document_; - std::string indentString_; - int rightMargin_; - std::string indentation_; + OStream* document_; + String indentString_; + unsigned int rightMargin_{74}; + String indentation_; bool addChildValues_ : 1; bool indented_ : 1; }; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif #if defined(JSON_HAS_INT64) -std::string JSON_API valueToString(Int value); -std::string JSON_API valueToString(UInt value); +String JSON_API valueToString(Int value); +String JSON_API valueToString(UInt value); #endif // if defined(JSON_HAS_INT64) -std::string JSON_API valueToString(LargestInt value); -std::string JSON_API valueToString(LargestUInt value); -std::string JSON_API valueToString(double value); -std::string JSON_API valueToString(bool value); -std::string JSON_API valueToQuotedString(const char* value); +String JSON_API valueToString(LargestInt value); +String JSON_API valueToString(LargestUInt value); +String JSON_API valueToString( + double value, unsigned int precision = Value::defaultRealPrecision, + PrecisionType precisionType = PrecisionType::significantDigits); +String JSON_API valueToString(bool value); +String JSON_API valueToQuotedString(const char* value); /// \brief Output using the StyledStreamWriter. /// \see Json::operator>>() -JSON_API std::ostream& operator<<(std::ostream&, const Value& root); +JSON_API OStream& operator<<(OStream&, const Value& root); } // namespace Json +#pragma pack(pop) + #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) @@ -2009,15 +2270,15 @@ JSON_API std::ostream& operator<<(std::ostream&, const Value& root); // Beginning of content of file: include/json/assertions.h // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE -#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED -#define CPPTL_JSON_ASSERTIONS_H_INCLUDED +#ifndef JSON_ASSERTIONS_H_INCLUDED +#define JSON_ASSERTIONS_H_INCLUDED -#include +#include #include #if !defined(JSON_IS_AMALGAMATION) @@ -2031,38 +2292,45 @@ JSON_API std::ostream& operator<<(std::ostream&, const Value& root); #if JSON_USE_EXCEPTION // @todo <= add detail about condition in exception -# define JSON_ASSERT(condition) \ - {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} +#define JSON_ASSERT(condition) \ + do { \ + if (!(condition)) { \ + Json::throwLogicError("assert json failed"); \ + } \ + } while (0) -# define JSON_FAIL_MESSAGE(message) \ - { \ - std::ostringstream oss; oss << message; \ +#define JSON_FAIL_MESSAGE(message) \ + do { \ + OStringStream oss; \ + oss << message; \ Json::throwLogicError(oss.str()); \ abort(); \ - } + } while (0) #else // JSON_USE_EXCEPTION -# define JSON_ASSERT(condition) assert(condition) +#define JSON_ASSERT(condition) assert(condition) // The call to assert() will show the failure message in debug builds. In // release builds we abort, for a core-dump or debugger. -# define JSON_FAIL_MESSAGE(message) \ +#define JSON_FAIL_MESSAGE(message) \ { \ - std::ostringstream oss; oss << message; \ + OStringStream oss; \ + oss << message; \ assert(false && oss.str().c_str()); \ abort(); \ } - #endif #define JSON_ASSERT_MESSAGE(condition, message) \ - if (!(condition)) { \ - JSON_FAIL_MESSAGE(message); \ - } + do { \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } \ + } while (0) -#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED +#endif // JSON_ASSERTIONS_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/assertions.h @@ -2072,4 +2340,4 @@ JSON_API std::ostream& operator<<(std::ostream&, const Value& root); -#endif //ifndef JSON_AMALGATED_H_INCLUDED +#endif //ifndef JSON_AMALGAMATED_H_INCLUDED diff --git a/src/third_party/jsoncpp/jsoncpp.cc b/src/third_party/jsoncpp/jsoncpp.cc index 39131de..a1adf09 100644 --- a/src/third_party/jsoncpp/jsoncpp.cc +++ b/src/third_party/jsoncpp/jsoncpp.cc @@ -1,4 +1,4 @@ -/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). +/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/). /// It is intended to be used with #include "json/json.h" // ////////////////////////////////////////////////////////////////////// @@ -6,32 +6,32 @@ // ////////////////////////////////////////////////////////////////////// /* -The JsonCpp library's source code, including accompanying documentation, +The JsonCpp library's source code, including accompanying documentation, tests and demonstration applications, are licensed under the following conditions... -The author (Baptiste Lepilleur) explicitly disclaims copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, +Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of -2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is -released under the terms of the MIT License (see below). +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and +The JsonCpp Authors, and is released under the terms of the MIT License (see below). -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License - + The full text of the MIT License follows: ======================================================================== -Copyright (c) 2007-2010 Baptiste Lepilleur +Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -84,7 +84,7 @@ license you like. // Beginning of content of file: src/lib_json/json_tool.h // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -92,6 +92,19 @@ license you like. #ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED #define LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#if !defined(JSON_IS_AMALGAMATION) +#include +#endif + +// Also support old flag NO_LOCALE_SUPPORT +#ifdef NO_LOCALE_SUPPORT +#define JSONCPP_NO_LOCALE_SUPPORT +#endif + +#ifndef JSONCPP_NO_LOCALE_SUPPORT +#include +#endif + /* This header provides common string manipulation support, such as UTF-8, * portable conversion from/to string... * @@ -99,10 +112,18 @@ license you like. */ namespace Json { +// static inline char getDecimalPoint() { +// #ifdef JSONCPP_NO_LOCALE_SUPPORT +// return '\0'; +// #else +// struct lconv* lc = localeconv(); +// return lc ? *(lc->decimal_point) : '\0'; +// #endif +// } /// Converts a unicode code-point to UTF-8. -static inline std::string codePointToUTF8(unsigned int cp) { - std::string result; +static inline String codePointToUTF8(unsigned int cp) { + String result; // based on description from http://en.wikipedia.org/wiki/UTF-8 @@ -129,9 +150,6 @@ static inline std::string codePointToUTF8(unsigned int cp) { return result; } -/// Returns true if ch is a control character (in range [1,31]). -static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } - enum { /// Constant that specify the size of the buffer that must be passed to /// uintToString. @@ -139,17 +157,17 @@ enum { }; // Defines a char buffer for use with uintToString(). -typedef char UIntToStringBuffer[uintToStringBufferSize]; +using UIntToStringBuffer = char[uintToStringBufferSize]; /** Converts an unsigned integer to string. - * @param value Unsigned interger to convert to string + * @param value Unsigned integer to convert to string * @param current Input/Output string buffer. * Must have at least uintToStringBufferSize chars free. */ static inline void uintToString(LargestUInt value, char*& current) { *--current = 0; do { - *--current = static_cast(value % 10U + static_cast('0')); + *--current = static_cast(value % 10U + static_cast('0')); value /= 10; } while (value != 0); } @@ -159,16 +177,45 @@ static inline void uintToString(LargestUInt value, char*& current) { * We had a sophisticated way, but it did not work in WinCE. * @see https://github.com/open-source-parsers/jsoncpp/pull/9 */ -static inline void fixNumericLocale(char* begin, char* end) { - while (begin < end) { +template Iter fixNumericLocale(Iter begin, Iter end) { + for (; begin != end; ++begin) { if (*begin == ',') { *begin = '.'; } - ++begin; } + return begin; } -} // namespace Json { +// template void fixNumericLocaleInput(Iter begin, Iter end) { +// char decimalPoint = getDecimalPoint(); +// if (decimalPoint == '\0' || decimalPoint == '.') { +// return; +// } +// for (; begin != end; ++begin) { +// if (*begin == '.') { +// *begin = decimalPoint; +// } +// } +// } + +/** + * Return iterator that would be the new end of the range [begin,end), if we + * were to delete zeros in the end of string, but not the last zero before '.'. + */ +template Iter fixZerosInTheEnd(Iter begin, Iter end) { + for (; begin != end; --end) { + if (*(end - 1) != '0') { + return end; + } + // Don't delete the last zero before the decimal point. + if (begin != (end - 1) && *(end - 2) == '.') { + return end; + } + } + return end; +} + +} // namespace Json #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED @@ -185,69 +232,72 @@ static inline void fixNumericLocale(char* begin, char* end) { // Beginning of content of file: src/lib_json/json_reader.cpp // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2011 Baptiste Lepilleur +// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors +// Copyright (C) 2016 InfoTeCS JSC. All rights reserved. // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) +#include "json_tool.h" #include #include #include -#include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include +#include #include #include +#include #include -#include +#include #include #include -#include +#include +#include -#if defined(_MSC_VER) -#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above -#define snprintf sprintf_s -#elif _MSC_VER >= 1900 // VC++ 14.0 and above -#define snprintf std::snprintf -#else -#define snprintf _snprintf -#endif -#elif defined(__ANDROID__) || defined(__QNXNTO__) -#define snprintf snprintf -#elif __cplusplus >= 201103L -#define snprintf std::snprintf -#endif +#include +#if __cplusplus >= 201103L -#if defined(__QNXNTO__) +#if !defined(sscanf) #define sscanf std::sscanf #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +#endif //__cplusplus + +#if defined(_MSC_VER) +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES +#endif //_MSC_VER + +#if defined(_MSC_VER) // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif -static int const stackLimit_g = 1000; -static int stackDepth_g = 0; // see readValue() +// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile +// time to change the stack limit +#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) +#define JSONCPP_DEPRECATED_STACK_LIMIT 1000 +#endif + +static size_t const stackLimit_g = + JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() namespace Json { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr CharReaderPtr; +using CharReaderPtr = std::unique_ptr; #else -typedef std::auto_ptr CharReaderPtr; +using CharReaderPtr = std::auto_ptr; #endif // Implementation of class Features // //////////////////////////////// -Features::Features() - : allowComments_(true), strictRoot_(false), - allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} +Features::Features() = default; -Features Features::all() { return Features(); } +Features Features::all() { return {}; } Features Features::strictMode() { Features features; @@ -261,50 +311,39 @@ Features Features::strictMode() { // Implementation of class Reader // //////////////////////////////// -static bool containsNewLine(Reader::Location begin, Reader::Location end) { - for (; begin < end; ++begin) - if (*begin == '\n' || *begin == '\r') - return true; - return false; +bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); } // Class Reader // ////////////////////////////////////////////////////////////////// -Reader::Reader() - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(Features::all()), - collectComments_() {} +Reader::Reader() : features_(Features::all()) {} -Reader::Reader(const Features& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), features_(features), collectComments_() { -} +Reader::Reader(const Features& features) : features_(features) {} -bool -Reader::parse(const std::string& document, Value& root, bool collectComments) { - document_ = document; +bool Reader::parse(const std::string& document, Value& root, + bool collectComments) { + document_.assign(document.begin(), document.end()); const char* begin = document_.c_str(); const char* end = begin + document_.length(); return parse(begin, end, root, collectComments); } -bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { - // std::istream_iterator begin(sin); +bool Reader::parse(std::istream& is, Value& root, bool collectComments) { + // std::istream_iterator begin(is); // std::istream_iterator end; // Those would allow streamed input from a file, if parse() were a // template function. - // Since std::string is reference-counted, this at least does not + // Since String is reference-counted, this at least does not // create an extra copy. - std::string doc; - std::getline(sin, doc, (char)EOF); - return parse(doc, root, collectComments); + String doc; + std::getline(is, doc, static_cast EOF); + return parse(doc.data(), doc.data() + doc.size(), root, collectComments); } -bool Reader::parse(const char* beginDoc, - const char* endDoc, - Value& root, +bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments) { if (!features_.allowComments_) { collectComments = false; @@ -314,15 +353,14 @@ bool Reader::parse(const char* beginDoc, end_ = endDoc; collectComments_ = collectComments; current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - commentsBefore_ = ""; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; + commentsBefore_.clear(); errors_.clear(); while (!nodes_.empty()) nodes_.pop(); nodes_.push(&root); - stackDepth_g = 0; // Yes, this is bad coding, but options are limited. bool successful = readValue(); Token token; skipCommentTokens(token); @@ -345,12 +383,12 @@ bool Reader::parse(const char* beginDoc, } bool Reader::readValue() { - // This is a non-reentrant way to support a stackLimit. Terrible! - // But this deprecated class has a security problem: Bad input can - // cause a seg-fault. This seems like a fair, binary-compatible way - // to prevent the problem. - if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); - ++stackDepth_g; + // readValue() may call itself only if it calls readObject() or ReadArray(). + // These methods execute nodes_.push() just before and nodes_.pop)() just + // after calling readValue(). parse() executes one nodes_.push(), so > instead + // of >=. + if (nodes_.size() > stackLimit_g) + throwRuntimeError("Exceeded stackLimit in readValue()."); Token token; skipCommentTokens(token); @@ -358,7 +396,7 @@ bool Reader::readValue() { if (collectComments_ && !commentsBefore_.empty()) { currentValue().setComment(commentsBefore_, commentBefore); - commentsBefore_ = ""; + commentsBefore_.clear(); } switch (token.type_) { @@ -376,30 +414,24 @@ bool Reader::readValue() { case tokenString: successful = decodeString(token); break; - case tokenTrue: - { + case tokenTrue: { Value v(true); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { + } break; + case tokenFalse: { Value v(false); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { + } break; + case tokenNull: { Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; + } break; case tokenArraySeparator: case tokenObjectEnd: case tokenArrayEnd: @@ -424,7 +456,6 @@ bool Reader::readValue() { lastValue_ = ¤tValue(); } - --stackDepth_g; return successful; } @@ -506,7 +537,7 @@ bool Reader::readToken(Token& token) { if (!ok) token.type_ = tokenError; token.end_ = current_; - return true; + return ok; } void Reader::skipSpaces() { @@ -519,7 +550,7 @@ void Reader::skipSpaces() { } } -bool Reader::match(Location pattern, int patternLength) { +bool Reader::match(const Char* pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; @@ -553,16 +584,16 @@ bool Reader::readComment() { return true; } -static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { - std::string normalized; - normalized.reserve(end - begin); +String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { + String normalized; + normalized.reserve(static_cast(end - begin)); Reader::Location current = begin; while (current != end) { char c = *current++; if (c == '\r') { if (current != end && *current == '\n') - // convert dos EOL - ++current; + // convert dos EOL + ++current; // convert Mac EOL normalized += '\n'; } else { @@ -572,12 +603,12 @@ static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { return normalized; } -void -Reader::addComment(Location begin, Location end, CommentPlacement placement) { +void Reader::addComment(Location begin, Location end, + CommentPlacement placement) { assert(collectComments_); - const std::string& normalized = normalizeEOL(begin, end); + const String& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); + assert(lastValue_ != nullptr); lastValue_->setComment(normalized, placement); } else { commentsBefore_ += normalized; @@ -585,7 +616,7 @@ Reader::addComment(Location begin, Location end, CommentPlacement placement) { } bool Reader::readCStyleComment() { - while (current_ != end_) { + while ((current_ + 1) < end_) { Char c = getNextChar(); if (c == '*' && *current_ == '/') break; @@ -610,29 +641,29 @@ bool Reader::readCppStyleComment() { } void Reader::readNumber() { - const char *p = current_; + Location p = current_; char c = '0'; // stopgap for already consumed character // integral part while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; // fractional part if (c == '.') { - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; } // exponential part if (c == 'e' || c == 'E') { - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; if (c == '+' || c == '-') - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; } } bool Reader::readString() { - Char c = 0; + Char c = '\0'; while (current_ != end_) { c = getNextChar(); if (c == '\\') @@ -643,12 +674,12 @@ bool Reader::readString() { return c == '"'; } -bool Reader::readObject(Token& tokenStart) { +bool Reader::readObject(Token& token) { Token tokenName; - std::string name; + String name; Value init(objectValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); + currentValue().setOffsetStart(token.start_ - begin_); while (readToken(tokenName)) { bool initialTokenOk = true; while (tokenName.type_ == tokenComment && initialTokenOk) @@ -657,7 +688,7 @@ bool Reader::readObject(Token& tokenStart) { break; if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object return true; - name = ""; + name.clear(); if (tokenName.type_ == tokenString) { if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); @@ -672,8 +703,8 @@ bool Reader::readObject(Token& tokenStart) { Token colon; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover( - "Missing ':' after object member name", colon, tokenObjectEnd); + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); } Value& value = currentValue()[name]; nodes_.push(&value); @@ -686,8 +717,8 @@ bool Reader::readObject(Token& tokenStart) { if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); } bool finalizeTokenOk = true; while (comma.type_ == tokenComment && finalizeTokenOk) @@ -695,16 +726,16 @@ bool Reader::readObject(Token& tokenStart) { if (comma.type_ == tokenObjectEnd) return true; } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); } -bool Reader::readArray(Token& tokenStart) { +bool Reader::readArray(Token& token) { Value init(arrayValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); + currentValue().setOffsetStart(token.start_ - begin_); skipSpaces(); - if (*current_ == ']') // empty array + if (current_ != end_ && *current_ == ']') // empty array { Token endArray; readToken(endArray); @@ -719,19 +750,19 @@ bool Reader::readArray(Token& tokenStart) { if (!ok) // error already set return recoverFromError(tokenArrayEnd); - Token token; + Token currentToken; // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); } - if (token.type_ == tokenArrayEnd) + if (currentToken.type_ == tokenArrayEnd) break; } return true; @@ -755,7 +786,8 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { bool isNegative = *current == '-'; if (isNegative) ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of them. + // TODO: Help the compiler do the div and mod at compile time or get rid of + // them. Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 : Value::maxLargestUInt; @@ -765,7 +797,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); - Value::UInt digit(c - '0'); + auto digit(static_cast(c - '0')); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If // a) we've only just touched the limit, b) this is the last digit, and @@ -801,18 +833,17 @@ bool Reader::decodeDouble(Token& token) { bool Reader::decodeDouble(Token& token, Value& decoded) { double value = 0; - std::string buffer(token.start_, token.end_); - std::istringstream is(buffer); + String buffer(token.start_, token.end_); + IStringStream is(buffer); if (!(is >> value)) - return addError("'" + std::string(token.start_, token.end_) + - "' is not a number.", - token); + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); decoded = value; return true; } bool Reader::decodeString(Token& token) { - std::string decoded_string; + String decoded_string; if (!decodeString(token, decoded_string)) return false; Value decoded(decoded_string); @@ -822,15 +853,15 @@ bool Reader::decodeString(Token& token) { return true; } -bool Reader::decodeString(Token& token, std::string& decoded) { - decoded.reserve(token.end_ - token.start_ - 2); +bool Reader::decodeString(Token& token, String& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' while (current != end) { Char c = *current++; if (c == '"') break; - else if (c == '\\') { + if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); Char escape = *current++; @@ -875,10 +906,8 @@ bool Reader::decodeString(Token& token, std::string& decoded) { return true; } -bool Reader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { +bool Reader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false; @@ -887,10 +916,9 @@ bool Reader::decodeUnicodeCodePoint(Token& token, if (end - current < 6) return addError( "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; + token, current); if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } else @@ -898,22 +926,19 @@ bool Reader::decodeUnicodeCodePoint(Token& token, } else return addError("expecting another \\u token to begin the second half of " "a unicode surrogate pair", - token, - current); + token, current); } return true; } -bool Reader::decodeUnicodeEscapeSequence(Token& token, - Location& current, +bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, - unsigned int& unicode) { + unsigned int& ret_unicode) { if (end - current < 4) return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, + "Bad unicode escape sequence in string: four digits expected.", token, current); - unicode = 0; + int unicode = 0; for (int index = 0; index < 4; ++index) { Char c = *current++; unicode *= 16; @@ -926,14 +951,13 @@ bool Reader::decodeUnicodeEscapeSequence(Token& token, else return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); + token, current); } + ret_unicode = static_cast(unicode); return true; } -bool -Reader::addError(const std::string& message, Token& token, Location extra) { +bool Reader::addError(const String& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -943,7 +967,7 @@ Reader::addError(const std::string& message, Token& token, Location extra) { } bool Reader::recoverFromError(TokenType skipUntilToken) { - int errorCount = int(errors_.size()); + size_t const errorCount = errors_.size(); Token skip; for (;;) { if (!readToken(skip)) @@ -955,8 +979,7 @@ bool Reader::recoverFromError(TokenType skipUntilToken) { return false; } -bool Reader::addErrorAndRecover(const std::string& message, - Token& token, +bool Reader::addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); @@ -970,8 +993,7 @@ Reader::Char Reader::getNextChar() { return *current_++; } -void Reader::getLocationLineAndColumn(Location location, - int& line, +void Reader::getLocationLineAndColumn(Location location, int& line, int& column) const { Location current = begin_; Location lastLineStart = current; @@ -993,25 +1015,22 @@ void Reader::getLocationLineAndColumn(Location location, ++line; } -std::string Reader::getLocationLineAndColumn(Location location) const { +String Reader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; - snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); return buffer; } // Deprecated. Preserved for backward compatibility -std::string Reader::getFormatedErrorMessages() const { +String Reader::getFormatedErrorMessages() const { return getFormattedErrorMessages(); } -std::string Reader::getFormattedErrorMessages() const { - std::string formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; +String Reader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; formattedMessage += " " + error.message_ + "\n"; @@ -1024,10 +1043,7 @@ std::string Reader::getFormattedErrorMessages() const { std::vector Reader::getStructuredErrors() const { std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; + for (const auto& error : errors_) { Reader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; @@ -1037,28 +1053,27 @@ std::vector Reader::getStructuredErrors() const { return allErrors; } -bool Reader::pushError(const Value& value, const std::string& message) { - size_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length) +bool Reader::pushError(const Value& value, const String& message) { + ptrdiff_t const length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length) return false; Token token; token.type_ = tokenError; token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); + token.end_ = begin_ + value.getOffsetLimit(); ErrorInfo info; info.token_ = token; info.message_ = message; - info.extra_ = 0; + info.extra_ = nullptr; errors_.push_back(info); return true; } -bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) { - size_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length - || extra.getOffsetLimit() > length) +bool Reader::pushError(const Value& value, const String& message, + const Value& extra) { + ptrdiff_t const length = end_ - begin_; + if (value.getOffsetStart() > length || value.getOffsetLimit() > length || + extra.getOffsetLimit() > length) return false; Token token; token.type_ = tokenError; @@ -1072,15 +1087,15 @@ bool Reader::pushError(const Value& value, const std::string& message, const Val return true; } -bool Reader::good() const { - return !errors_.size(); -} +bool Reader::good() const { return errors_.empty(); } -// exact copy of Features +// Originally copied from the Features class (now deprecated), used internally +// for features implementation. class OurFeatures { public: static OurFeatures all(); bool allowComments_; + bool allowTrailingCommas_; bool strictRoot_; bool allowDroppedNullPlaceholders_; bool allowNumericKeys_; @@ -1088,42 +1103,36 @@ public: bool failIfExtra_; bool rejectDupKeys_; bool allowSpecialFloats_; - int stackLimit_; -}; // OurFeatures + bool skipBom_; + size_t stackLimit_; +}; // OurFeatures -// exact copy of Implementation of class Features -// //////////////////////////////// - -OurFeatures OurFeatures::all() { return OurFeatures(); } +OurFeatures OurFeatures::all() { return {}; } // Implementation of class Reader // //////////////////////////////// -// exact copy of Reader, renamed to OurReader +// Originally copied from the Reader class (now deprecated), used internally +// for implementing JSON reading. class OurReader { public: - typedef char Char; - typedef const Char* Location; + using Char = char; + using Location = const Char*; struct StructuredError { - size_t offset_start; - size_t offset_limit; - std::string message; + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + String message; }; - OurReader(OurFeatures const& features); - bool parse(const char* beginDoc, - const char* endDoc, - Value& root, + explicit OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); - std::string getFormattedErrorMessages() const; + String getFormattedErrorMessages() const; std::vector getStructuredErrors() const; - bool pushError(const Value& value, const std::string& message); - bool pushError(const Value& value, const std::string& message, const Value& extra); - bool good() const; private: - OurReader(OurReader const&); // no impl - void operator=(OurReader const&); // no impl + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl enum TokenType { tokenEndOfStream = 0, @@ -1155,17 +1164,18 @@ private: class ErrorInfo { public: Token token_; - std::string message_; + String message_; Location extra_; }; - typedef std::deque Errors; + using Errors = std::deque; bool readToken(Token& token); void skipSpaces(); - bool match(Location pattern, int patternLength); + void skipBom(bool skipBom); + bool match(const Char* pattern, int patternLength); bool readComment(); - bool readCStyleComment(); + bool readCStyleComment(bool* containsNewLineResult); bool readCppStyleComment(); bool readString(); bool readStringSingleQuote(); @@ -1176,60 +1186,57 @@ private: bool decodeNumber(Token& token); bool decodeNumber(Token& token, Value& decoded); bool decodeString(Token& token); - bool decodeString(Token& token, std::string& decoded); + bool decodeString(Token& token, String& decoded); bool decodeDouble(Token& token); bool decodeDouble(Token& token, Value& decoded); - bool decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, + bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode); - bool decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode); - bool addError(const std::string& message, Token& token, Location extra = 0); + bool decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, unsigned int& unicode); + bool addError(const String& message, Token& token, Location extra = nullptr); bool recoverFromError(TokenType skipUntilToken); - bool addErrorAndRecover(const std::string& message, - Token& token, + bool addErrorAndRecover(const String& message, Token& token, TokenType skipUntilToken); void skipUntilSpace(); Value& currentValue(); Char getNextChar(); - void - getLocationLineAndColumn(Location location, int& line, int& column) const; - std::string getLocationLineAndColumn(Location location) const; + void getLocationLineAndColumn(Location location, int& line, + int& column) const; + String getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); - typedef std::stack Nodes; - Nodes nodes_; - Errors errors_; - std::string document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value* lastValue_; - std::string commentsBefore_; - int stackDepth_; + static String normalizeEOL(Location begin, Location end); + static bool containsNewLine(Location begin, Location end); + + using Nodes = std::stack; + + Nodes nodes_{}; + Errors errors_{}; + String document_{}; + Location begin_ = nullptr; + Location end_ = nullptr; + Location current_ = nullptr; + Location lastValueEnd_ = nullptr; + Value* lastValue_ = nullptr; + bool lastValueHasAComment_ = false; + String commentsBefore_{}; OurFeatures const features_; - bool collectComments_; -}; // OurReader + bool collectComments_ = false; +}; // OurReader // complete copy of Read impl, for OurReader -OurReader::OurReader(OurFeatures const& features) - : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), - lastValue_(), commentsBefore_(), - stackDepth_(0), - features_(features), collectComments_() { +bool OurReader::containsNewLine(OurReader::Location begin, + OurReader::Location end) { + return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; }); } -bool OurReader::parse(const char* beginDoc, - const char* endDoc, - Value& root, - bool collectComments) { +OurReader::OurReader(OurFeatures const& features) : features_(features) {} + +bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root, + bool collectComments) { if (!features_.allowComments_) { collectComments = false; } @@ -1238,23 +1245,23 @@ bool OurReader::parse(const char* beginDoc, end_ = endDoc; collectComments_ = collectComments; current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - commentsBefore_ = ""; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; + commentsBefore_.clear(); errors_.clear(); while (!nodes_.empty()) nodes_.pop(); nodes_.push(&root); - stackDepth_ = 0; + // skip byte order mark if it exists at the beginning of the UTF-8 text. + skipBom(features_.skipBom_); bool successful = readValue(); + nodes_.pop(); Token token; skipCommentTokens(token); - if (features_.failIfExtra_) { - if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { - addError("Extra non-whitespace after JSON value.", token); - return false; - } + if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) { + addError("Extra non-whitespace after JSON value.", token); + return false; } if (collectComments_ && !commentsBefore_.empty()) root.setComment(commentsBefore_, commentAfter); @@ -1275,15 +1282,16 @@ bool OurReader::parse(const char* beginDoc, } bool OurReader::readValue() { - if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); - ++stackDepth_; + // To preserve the old behaviour we cast size_t to int. + if (nodes_.size() > features_.stackLimit_) + throwRuntimeError("Exceeded stackLimit in readValue()."); Token token; skipCommentTokens(token); bool successful = true; if (collectComments_ && !commentsBefore_.empty()) { currentValue().setComment(commentsBefore_, commentBefore); - commentsBefore_ = ""; + commentsBefore_.clear(); } switch (token.type_) { @@ -1301,54 +1309,42 @@ bool OurReader::readValue() { case tokenString: successful = decodeString(token); break; - case tokenTrue: - { + case tokenTrue: { Value v(true); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenFalse: - { + } break; + case tokenFalse: { Value v(false); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNull: - { + } break; + case tokenNull: { Value v; currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNaN: - { + } break; + case tokenNaN: { Value v(std::numeric_limits::quiet_NaN()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenPosInf: - { + } break; + case tokenPosInf: { Value v(std::numeric_limits::infinity()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; - case tokenNegInf: - { + } break; + case tokenNegInf: { Value v(-std::numeric_limits::infinity()); currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); - } - break; + } break; case tokenArraySeparator: case tokenObjectEnd: case tokenArrayEnd: @@ -1370,10 +1366,10 @@ bool OurReader::readValue() { if (collectComments_) { lastValueEnd_ = current_; + lastValueHasAComment_ = false; lastValue_ = ¤tValue(); } - --stackDepth_; return successful; } @@ -1411,10 +1407,13 @@ bool OurReader::readToken(Token& token) { break; case '\'': if (features_.allowSingleQuotes_) { - token.type_ = tokenString; - ok = readStringSingleQuote(); + token.type_ = tokenString; + ok = readStringSingleQuote(); + } else { + // If we don't allow single quotes, this is a failure case. + ok = false; + } break; - } // else continue case '/': token.type_ = tokenComment; ok = readComment(); @@ -1440,6 +1439,14 @@ bool OurReader::readToken(Token& token) { ok = features_.allowSpecialFloats_ && match("nfinity", 7); } break; + case '+': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenPosInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; case 't': token.type_ = tokenTrue; ok = match("rue", 3); @@ -1484,7 +1491,7 @@ bool OurReader::readToken(Token& token) { if (!ok) token.type_ = tokenError; token.end_ = current_; - return true; + return ok; } void OurReader::skipSpaces() { @@ -1497,7 +1504,17 @@ void OurReader::skipSpaces() { } } -bool OurReader::match(Location pattern, int patternLength) { +void OurReader::skipBom(bool skipBom) { + // The default behavior is to skip BOM. + if (skipBom) { + if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) { + begin_ += 3; + current_ = begin_; + } + } +} + +bool OurReader::match(const Char* pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; @@ -1509,21 +1526,32 @@ bool OurReader::match(Location pattern, int patternLength) { } bool OurReader::readComment() { - Location commentBegin = current_ - 1; - Char c = getNextChar(); + const Location commentBegin = current_ - 1; + const Char c = getNextChar(); bool successful = false; - if (c == '*') - successful = readCStyleComment(); - else if (c == '/') + bool cStyleWithEmbeddedNewline = false; + + const bool isCStyleComment = (c == '*'); + const bool isCppStyleComment = (c == '/'); + if (isCStyleComment) { + successful = readCStyleComment(&cStyleWithEmbeddedNewline); + } else if (isCppStyleComment) { successful = readCppStyleComment(); + } + if (!successful) return false; if (collectComments_) { CommentPlacement placement = commentBefore; - if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { - if (c != '*' || !containsNewLine(commentBegin, current_)) - placement = commentAfterOnSameLine; + + if (!lastValueHasAComment_) { + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (isCppStyleComment || !cStyleWithEmbeddedNewline) { + placement = commentAfterOnSameLine; + lastValueHasAComment_ = true; + } + } } addComment(commentBegin, current_, placement); @@ -1531,24 +1559,49 @@ bool OurReader::readComment() { return true; } -void -OurReader::addComment(Location begin, Location end, CommentPlacement placement) { +String OurReader::normalizeEOL(OurReader::Location begin, + OurReader::Location end) { + String normalized; + normalized.reserve(static_cast(end - begin)); + OurReader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void OurReader::addComment(Location begin, Location end, + CommentPlacement placement) { assert(collectComments_); - const std::string& normalized = normalizeEOL(begin, end); + const String& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { - assert(lastValue_ != 0); + assert(lastValue_ != nullptr); lastValue_->setComment(normalized, placement); } else { commentsBefore_ += normalized; } } -bool OurReader::readCStyleComment() { - while (current_ != end_) { +bool OurReader::readCStyleComment(bool* containsNewLineResult) { + *containsNewLineResult = false; + + while ((current_ + 1) < end_) { Char c = getNextChar(); if (c == '*' && *current_ == '/') break; + if (c == '\n') + *containsNewLineResult = true; } + return getNextChar() == '/'; } @@ -1569,7 +1622,7 @@ bool OurReader::readCppStyleComment() { } bool OurReader::readNumber(bool checkInf) { - const char *p = current_; + Location p = current_; if (checkInf && p != end_ && *p == 'I') { current_ = ++p; return false; @@ -1577,20 +1630,20 @@ bool OurReader::readNumber(bool checkInf) { char c = '0'; // stopgap for already consumed character // integral part while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; // fractional part if (c == '.') { - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; } // exponential part if (c == 'e' || c == 'E') { - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; if (c == '+' || c == '-') - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; while (c >= '0' && c <= '9') - c = (current_ = p) < end_ ? *p++ : 0; + c = (current_ = p) < end_ ? *p++ : '\0'; } return true; } @@ -1606,7 +1659,6 @@ bool OurReader::readString() { return c == '"'; } - bool OurReader::readStringSingleQuote() { Char c = 0; while (current_ != end_) { @@ -1619,21 +1671,23 @@ bool OurReader::readStringSingleQuote() { return c == '\''; } -bool OurReader::readObject(Token& tokenStart) { +bool OurReader::readObject(Token& token) { Token tokenName; - std::string name; + String name; Value init(objectValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); + currentValue().setOffsetStart(token.start_ - begin_); while (readToken(tokenName)) { bool initialTokenOk = true; while (tokenName.type_ == tokenComment && initialTokenOk) initialTokenOk = readToken(tokenName); if (!initialTokenOk) break; - if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + if (tokenName.type_ == tokenObjectEnd && + (name.empty() || + features_.allowTrailingCommas_)) // empty object or trailing comma return true; - name = ""; + name.clear(); if (tokenName.type_ == tokenString) { if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); @@ -1645,17 +1699,17 @@ bool OurReader::readObject(Token& tokenStart) { } else { break; } + if (name.length() >= (1U << 30)) + throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + String msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover(msg, tokenName, tokenObjectEnd); + } Token colon; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { - return addErrorAndRecover( - "Missing ':' after object member name", colon, tokenObjectEnd); - } - if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); - if (features_.rejectDupKeys_ && currentValue().isMember(name)) { - std::string msg = "Duplicate key: '" + name + "'"; - return addErrorAndRecover( - msg, tokenName, tokenObjectEnd); + return addErrorAndRecover("Missing ':' after object member name", colon, + tokenObjectEnd); } Value& value = currentValue()[name]; nodes_.push(&value); @@ -1668,8 +1722,8 @@ bool OurReader::readObject(Token& tokenStart) { if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) { - return addErrorAndRecover( - "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + return addErrorAndRecover("Missing ',' or '}' in object declaration", + comma, tokenObjectEnd); } bool finalizeTokenOk = true; while (comma.type_ == tokenComment && finalizeTokenOk) @@ -1677,23 +1731,27 @@ bool OurReader::readObject(Token& tokenStart) { if (comma.type_ == tokenObjectEnd) return true; } - return addErrorAndRecover( - "Missing '}' or object member name", tokenName, tokenObjectEnd); + return addErrorAndRecover("Missing '}' or object member name", tokenName, + tokenObjectEnd); } -bool OurReader::readArray(Token& tokenStart) { +bool OurReader::readArray(Token& token) { Value init(arrayValue); currentValue().swapPayload(init); - currentValue().setOffsetStart(tokenStart.start_ - begin_); - skipSpaces(); - if (*current_ == ']') // empty array - { - Token endArray; - readToken(endArray); - return true; - } + currentValue().setOffsetStart(token.start_ - begin_); int index = 0; for (;;) { + skipSpaces(); + if (current_ != end_ && *current_ == ']' && + (index == 0 || + (features_.allowTrailingCommas_ && + !features_.allowDroppedNullPlaceholders_))) // empty array or trailing + // comma + { + Token endArray; + readToken(endArray); + return true; + } Value& value = currentValue()[index++]; nodes_.push(&value); bool ok = readValue(); @@ -1701,19 +1759,19 @@ bool OurReader::readArray(Token& tokenStart) { if (!ok) // error already set return recoverFromError(tokenArrayEnd); - Token token; + Token currentToken; // Accept Comment after last item in the array. - ok = readToken(token); - while (token.type_ == tokenComment && ok) { - ok = readToken(token); + ok = readToken(currentToken); + while (currentToken.type_ == tokenComment && ok) { + ok = readToken(currentToken); } - bool badTokenType = - (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + bool badTokenType = (currentToken.type_ != tokenArraySeparator && + currentToken.type_ != tokenArrayEnd); if (!ok || badTokenType) { - return addErrorAndRecover( - "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + return addErrorAndRecover("Missing ',' or ']' in array declaration", + currentToken, tokenArrayEnd); } - if (token.type_ == tokenArrayEnd) + if (currentToken.type_ == tokenArrayEnd) break; } return true; @@ -1734,38 +1792,78 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { // larger than the maximum supported value of an integer then // we decode the number as a double. Location current = token.start_; - bool isNegative = *current == '-'; - if (isNegative) + const bool isNegative = *current == '-'; + if (isNegative) { ++current; - // TODO: Help the compiler do the div and mod at compile time or get rid of them. - Value::LargestUInt maxIntegerValue = - isNegative ? Value::LargestUInt(-Value::minLargestInt) - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; + } + + // We assume we can represent the largest and smallest integer types as + // unsigned integers with separate sign. This is only true if they can fit + // into an unsigned integer. + static_assert(Value::maxLargestInt <= Value::maxLargestUInt, + "Int must be smaller than UInt"); + + // We need to convert minLargestInt into a positive number. The easiest way + // to do this conversion is to assume our "threshold" value of minLargestInt + // divided by 10 can fit in maxLargestInt when absolute valued. This should + // be a safe assumption. + static_assert(Value::minLargestInt <= -Value::maxLargestInt, + "The absolute value of minLargestInt must be greater than or " + "equal to maxLargestInt"); + static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt, + "The absolute value of minLargestInt must be only 1 magnitude " + "larger than maxLargest Int"); + + static constexpr Value::LargestUInt positive_threshold = + Value::maxLargestUInt / 10; + static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10; + + // For the negative values, we have to be more careful. Since typically + // -Value::minLargestInt will cause an overflow, we first divide by 10 and + // then take the inverse. This assumes that minLargestInt is only a single + // power of 10 different in magnitude, which we check above. For the last + // digit, we take the modulus before negating for the same reason. + static constexpr auto negative_threshold = + Value::LargestUInt(-(Value::minLargestInt / 10)); + static constexpr auto negative_last_digit = + Value::UInt(-(Value::minLargestInt % 10)); + + const Value::LargestUInt threshold = + isNegative ? negative_threshold : positive_threshold; + const Value::UInt max_last_digit = + isNegative ? negative_last_digit : positive_last_digit; + Value::LargestUInt value = 0; while (current < token.end_) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); - Value::UInt digit(c - '0'); + + const auto digit(static_cast(c - '0')); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If - // a) we've only just touched the limit, b) this is the last digit, and + // a) we've only just touched the limit, meaing value == threshold, + // b) this is the last digit, or // c) it's small enough to fit in that rounding delta, we're okay. // Otherwise treat this number as a double to avoid overflow. if (value > threshold || current != token.end_ || - digit > maxIntegerValue % 10) { + digit > max_last_digit) { return decodeDouble(token, decoded); } } value = value * 10 + digit; } - if (isNegative) - decoded = -Value::LargestInt(value); - else if (value <= Value::LargestUInt(Value::maxInt)) + + if (isNegative) { + // We use the same magnitude assumption here, just in case. + const auto last_digit = static_cast(value % 10); + decoded = -Value::LargestInt(value / 10) * 10 - last_digit; + } else if (value <= Value::LargestUInt(Value::maxLargestInt)) { decoded = Value::LargestInt(value); - else + } else { decoded = value; + } + return true; } @@ -1781,42 +1879,18 @@ bool OurReader::decodeDouble(Token& token) { bool OurReader::decodeDouble(Token& token, Value& decoded) { double value = 0; - const int bufferSize = 32; - int count; - int length = int(token.end_ - token.start_); - - // Sanity check to avoid buffer overflow exploits. - if (length < 0) { - return addError("Unable to parse token length", token); + const String buffer(token.start_, token.end_); + IStringStream is(buffer); + if (!(is >> value)) { + return addError( + "'" + String(token.start_, token.end_) + "' is not a number.", token); } - - // Avoid using a string constant for the format control string given to - // sscanf, as this can cause hard to debug crashes on OS X. See here for more - // info: - // - // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html - char format[] = "%lf"; - - if (length <= bufferSize) { - Char buffer[bufferSize + 1]; - memcpy(buffer, token.start_, length); - buffer[length] = 0; - count = sscanf(buffer, format, &value); - } else { - std::string buffer(token.start_, token.end_); - count = sscanf(buffer.c_str(), format, &value); - } - - if (count != 1) - return addError("'" + std::string(token.start_, token.end_) + - "' is not a number.", - token); decoded = value; return true; } bool OurReader::decodeString(Token& token) { - std::string decoded_string; + String decoded_string; if (!decodeString(token, decoded_string)) return false; Value decoded(decoded_string); @@ -1826,15 +1900,15 @@ bool OurReader::decodeString(Token& token) { return true; } -bool OurReader::decodeString(Token& token, std::string& decoded) { - decoded.reserve(token.end_ - token.start_ - 2); +bool OurReader::decodeString(Token& token, String& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' while (current != end) { Char c = *current++; if (c == '"') break; - else if (c == '\\') { + if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); Char escape = *current++; @@ -1879,10 +1953,8 @@ bool OurReader::decodeString(Token& token, std::string& decoded) { return true; } -bool OurReader::decodeUnicodeCodePoint(Token& token, - Location& current, - Location end, - unsigned int& unicode) { +bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, + Location end, unsigned int& unicode) { if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false; @@ -1891,10 +1963,9 @@ bool OurReader::decodeUnicodeCodePoint(Token& token, if (end - current < 6) return addError( "additional six characters expected to parse unicode surrogate pair.", - token, - current); - unsigned int surrogatePair; + token, current); if (*(current++) == '\\' && *(current++) == 'u') { + unsigned int surrogatePair; if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } else @@ -1902,22 +1973,19 @@ bool OurReader::decodeUnicodeCodePoint(Token& token, } else return addError("expecting another \\u token to begin the second half of " "a unicode surrogate pair", - token, - current); + token, current); } return true; } -bool OurReader::decodeUnicodeEscapeSequence(Token& token, - Location& current, - Location end, - unsigned int& unicode) { +bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, + Location end, + unsigned int& ret_unicode) { if (end - current < 4) return addError( - "Bad unicode escape sequence in string: four digits expected.", - token, + "Bad unicode escape sequence in string: four digits expected.", token, current); - unicode = 0; + int unicode = 0; for (int index = 0; index < 4; ++index) { Char c = *current++; unicode *= 16; @@ -1930,14 +1998,13 @@ bool OurReader::decodeUnicodeEscapeSequence(Token& token, else return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", - token, - current); + token, current); } + ret_unicode = static_cast(unicode); return true; } -bool -OurReader::addError(const std::string& message, Token& token, Location extra) { +bool OurReader::addError(const String& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -1947,7 +2014,7 @@ OurReader::addError(const std::string& message, Token& token, Location extra) { } bool OurReader::recoverFromError(TokenType skipUntilToken) { - int errorCount = int(errors_.size()); + size_t errorCount = errors_.size(); Token skip; for (;;) { if (!readToken(skip)) @@ -1959,9 +2026,8 @@ bool OurReader::recoverFromError(TokenType skipUntilToken) { return false; } -bool OurReader::addErrorAndRecover(const std::string& message, - Token& token, - TokenType skipUntilToken) { +bool OurReader::addErrorAndRecover(const String& message, Token& token, + TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); } @@ -1974,9 +2040,8 @@ OurReader::Char OurReader::getNextChar() { return *current_++; } -void OurReader::getLocationLineAndColumn(Location location, - int& line, - int& column) const { +void OurReader::getLocationLineAndColumn(Location location, int& line, + int& column) const { Location current = begin_; Location lastLineStart = current; line = 0; @@ -1997,20 +2062,17 @@ void OurReader::getLocationLineAndColumn(Location location, ++line; } -std::string OurReader::getLocationLineAndColumn(Location location) const { +String OurReader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; - snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); return buffer; } -std::string OurReader::getFormattedErrorMessages() const { - std::string formattedMessage; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; +String OurReader::getFormattedErrorMessages() const { + String formattedMessage; + for (const auto& error : errors_) { formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; formattedMessage += " " + error.message_ + "\n"; @@ -2023,10 +2085,7 @@ std::string OurReader::getFormattedErrorMessages() const { std::vector OurReader::getStructuredErrors() const { std::vector allErrors; - for (Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError) { - const ErrorInfo& error = *itError; + for (const auto& error : errors_) { OurReader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; @@ -2036,59 +2095,15 @@ std::vector OurReader::getStructuredErrors() const { return allErrors; } -bool OurReader::pushError(const Value& value, const std::string& message) { - size_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = end_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = 0; - errors_.push_back(info); - return true; -} - -bool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) { - size_t length = end_ - begin_; - if(value.getOffsetStart() > length - || value.getOffsetLimit() > length - || extra.getOffsetLimit() > length) - return false; - Token token; - token.type_ = tokenError; - token.start_ = begin_ + value.getOffsetStart(); - token.end_ = begin_ + value.getOffsetLimit(); - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = begin_ + extra.getOffsetStart(); - errors_.push_back(info); - return true; -} - -bool OurReader::good() const { - return !errors_.size(); -} - - class OurCharReader : public CharReader { bool const collectComments_; OurReader reader_; + public: - OurCharReader( - bool collectComments, - OurFeatures const& features) - : collectComments_(collectComments) - , reader_(features) - {} - bool parse( - char const* beginDoc, char const* endDoc, - Value* root, std::string* errs) override { + OurCharReader(bool collectComments, OurFeatures const& features) + : collectComments_(collectComments), reader_(features) {} + bool parse(char const* beginDoc, char const* endDoc, Value* root, + String* errs) override { bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); if (errs) { *errs = reader_.getFormattedErrorMessages(); @@ -2097,67 +2112,64 @@ public: } }; -CharReaderBuilder::CharReaderBuilder() -{ - setDefaults(&settings_); -} -CharReaderBuilder::~CharReaderBuilder() -{} -CharReader* CharReaderBuilder::newCharReader() const -{ +CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } +CharReaderBuilder::~CharReaderBuilder() = default; +CharReader* CharReaderBuilder::newCharReader() const { bool collectComments = settings_["collectComments"].asBool(); OurFeatures features = OurFeatures::all(); features.allowComments_ = settings_["allowComments"].asBool(); + features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool(); features.strictRoot_ = settings_["strictRoot"].asBool(); - features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowDroppedNullPlaceholders_ = + settings_["allowDroppedNullPlaceholders"].asBool(); features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); - features.stackLimit_ = settings_["stackLimit"].asInt(); + + // Stack limit is always a size_t, so we get this as an unsigned int + // regardless of it we have 64-bit integer support enabled. + features.stackLimit_ = static_cast(settings_["stackLimit"].asUInt()); features.failIfExtra_ = settings_["failIfExtra"].asBool(); features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + features.skipBom_ = settings_["skipBom"].asBool(); return new OurCharReader(collectComments, features); } -static void getValidReaderKeys(std::set* valid_keys) -{ - valid_keys->clear(); - valid_keys->insert("collectComments"); - valid_keys->insert("allowComments"); - valid_keys->insert("strictRoot"); - valid_keys->insert("allowDroppedNullPlaceholders"); - valid_keys->insert("allowNumericKeys"); - valid_keys->insert("allowSingleQuotes"); - valid_keys->insert("stackLimit"); - valid_keys->insert("failIfExtra"); - valid_keys->insert("rejectDupKeys"); - valid_keys->insert("allowSpecialFloats"); -} -bool CharReaderBuilder::validate(Json::Value* invalid) const -{ - Json::Value my_invalid; - if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set valid_keys; - getValidReaderKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - std::string const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } + +bool CharReaderBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set{ + "collectComments", + "allowComments", + "allowTrailingCommas", + "strictRoot", + "allowDroppedNullPlaceholders", + "allowNumericKeys", + "allowSingleQuotes", + "stackLimit", + "failIfExtra", + "rejectDupKeys", + "allowSpecialFloats", + "skipBom", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[std::move(key)] = *si; + else + return false; } - return 0u == inv.size(); + return invalid ? invalid->empty() : true; } -Value& CharReaderBuilder::operator[](std::string key) -{ + +Value& CharReaderBuilder::operator[](const String& key) { return settings_[key]; } // static -void CharReaderBuilder::strictMode(Json::Value* settings) -{ -//! [CharReaderBuilderStrictMode] +void CharReaderBuilder::strictMode(Json::Value* settings) { + //! [CharReaderBuilderStrictMode] (*settings)["allowComments"] = false; + (*settings)["allowTrailingCommas"] = false; (*settings)["strictRoot"] = true; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; @@ -2166,14 +2178,15 @@ void CharReaderBuilder::strictMode(Json::Value* settings) (*settings)["failIfExtra"] = true; (*settings)["rejectDupKeys"] = true; (*settings)["allowSpecialFloats"] = false; -//! [CharReaderBuilderStrictMode] + (*settings)["skipBom"] = true; + //! [CharReaderBuilderStrictMode] } // static -void CharReaderBuilder::setDefaults(Json::Value* settings) -{ -//! [CharReaderBuilderDefaults] +void CharReaderBuilder::setDefaults(Json::Value* settings) { + //! [CharReaderBuilderDefaults] (*settings)["collectComments"] = true; (*settings)["allowComments"] = true; + (*settings)["allowTrailingCommas"] = true; (*settings)["strictRoot"] = false; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; @@ -2182,19 +2195,18 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) (*settings)["failIfExtra"] = false; (*settings)["rejectDupKeys"] = false; (*settings)["allowSpecialFloats"] = false; -//! [CharReaderBuilderDefaults] + (*settings)["skipBom"] = true; + //! [CharReaderBuilderDefaults] } ////////////////////////////////// // global functions -bool parseFromStream( - CharReader::Factory const& fact, std::istream& sin, - Value* root, std::string* errs) -{ - std::ostringstream ssin; +bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root, + String* errs) { + OStringStream ssin; ssin << sin.rdbuf(); - std::string doc = ssin.str(); + String doc = ssin.str(); char const* begin = doc.data(); char const* end = begin + doc.size(); // Note that we do not actually need a null-terminator. @@ -2202,15 +2214,11 @@ bool parseFromStream( return reader->parse(begin, end, root, errs); } -std::istream& operator>>(std::istream& sin, Value& root) { +IStream& operator>>(IStream& sin, Value& root) { CharReaderBuilder b; - std::string errs; + String errs; bool ok = parseFromStream(b, sin, &root, &errs); if (!ok) { - fprintf(stderr, - "Error from reader: %s", - errs.c_str()); - throwRuntimeError(errs); } return sin; @@ -2231,7 +2239,7 @@ std::istream& operator>>(std::istream& sin, Value& root) { // Beginning of content of file: src/lib_json/json_valueiterator.inl // ////////////////////////////////////////////////////////////////////// -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -2248,31 +2256,21 @@ namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIteratorBase::ValueIteratorBase() - : current_(), isNull_(true) { -} +ValueIteratorBase::ValueIteratorBase() : current_() {} ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator& current) : current_(current), isNull_(false) {} -Value& ValueIteratorBase::deref() const { - return current_->second; -} +Value& ValueIteratorBase::deref() { return current_->second; } +const Value& ValueIteratorBase::deref() const { return current_->second; } -void ValueIteratorBase::increment() { - ++current_; -} +void ValueIteratorBase::increment() { ++current_; } -void ValueIteratorBase::decrement() { - --current_; -} +void ValueIteratorBase::decrement() { --current_; } ValueIteratorBase::difference_type ValueIteratorBase::computeDistance(const SelfType& other) const { -#ifdef JSON_USE_CPPTL_SMALLMAP - return other.current_ - current_; -#else // Iterator for null value are initialized using the default // constructor, which initialize current_ to the default // std::map::iterator. As begin() and end() are two instance @@ -2293,7 +2291,6 @@ ValueIteratorBase::computeDistance(const SelfType& other) const { ++myDistance; } return myDistance; -#endif } bool ValueIteratorBase::isEqual(const SelfType& other) const { @@ -2325,12 +2322,13 @@ UInt ValueIteratorBase::index() const { return Value::UInt(-1); } -std::string ValueIteratorBase::name() const { +String ValueIteratorBase::name() const { char const* keey; char const* end; keey = memberName(&end); - if (!keey) return std::string(); - return std::string(keey, end); + if (!keey) + return String(); + return String(keey, end); } char const* ValueIteratorBase::memberName() const { @@ -2341,8 +2339,8 @@ char const* ValueIteratorBase::memberName() const { char const* ValueIteratorBase::memberName(char const** end) const { const char* cname = (*current_).first.data(); if (!cname) { - *end = NULL; - return NULL; + *end = nullptr; + return nullptr; } *end = cname + (*current_).first.length(); return cname; @@ -2356,7 +2354,7 @@ char const* ValueIteratorBase::memberName(char const** end) const { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueConstIterator::ValueConstIterator() {} +ValueConstIterator::ValueConstIterator() = default; ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator& current) @@ -2379,7 +2377,7 @@ operator=(const ValueIteratorBase& other) { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -ValueIterator::ValueIterator() {} +ValueIterator::ValueIterator() = default; ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) : ValueIteratorBase(current) {} @@ -2389,8 +2387,7 @@ ValueIterator::ValueIterator(const ValueConstIterator& other) throwRuntimeError("ConstIterator to Iterator should never be allowed."); } -ValueIterator::ValueIterator(const ValueIterator& other) - : ValueIteratorBase(other) {} +ValueIterator::ValueIterator(const ValueIterator& other) = default; ValueIterator& ValueIterator::operator=(const SelfType& other) { copy(other); @@ -2412,7 +2409,7 @@ ValueIterator& ValueIterator::operator=(const SelfType& other) { // Beginning of content of file: src/lib_json/json_value.cpp // ////////////////////////////////////////////////////////////////////// -// Copyright 2011 Baptiste Lepilleur +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -2422,20 +2419,54 @@ ValueIterator& ValueIterator::operator=(const SelfType& other) { #include #include #endif // if !defined(JSON_IS_AMALGAMATION) -#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#ifdef JSON_USE_CPPTL -#include + +// Provide implementation equivalent of std::snprintf for older _MSC compilers +#if defined(_MSC_VER) && _MSC_VER < 1900 +#include +static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size, + const char* format, va_list ap) { + int count = -1; + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + return count; +} + +int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size, + const char* format, ...) { + va_list ap; + va_start(ap, format); + const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + return count; +} +#endif + +// Disable warning C4702 : unreachable code +#if defined(_MSC_VER) +#pragma warning(disable : 4702) #endif -#include // size_t -#include // min() #define JSON_ASSERT_UNREACHABLE assert(false) namespace Json { +template +static std::unique_ptr cloneUnique(const std::unique_ptr& p) { + std::unique_ptr r; + if (p) { + r = std::unique_ptr(new T(*p)); + } + return r; +} // This is a walkaround to avoid the static initialization of Value::null. // kNull must be word-aligned to avoid crashing on ARM. We use an alignment of @@ -2445,35 +2476,34 @@ namespace Json { #else #define ALIGNAS(byte_alignment) #endif -static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; -const unsigned char& kNullRef = kNull[0]; -const Value& Value::null = reinterpret_cast(kNullRef); -const Value& Value::nullRef = null; -const Int Value::minInt = Int(~(UInt(-1) / 2)); -const Int Value::maxInt = Int(UInt(-1) / 2); -const UInt Value::maxUInt = UInt(-1); -#if defined(JSON_HAS_INT64) -const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); -const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); -const UInt64 Value::maxUInt64 = UInt64(-1); -// The constant is hard-coded because some compiler have trouble -// converting Value::maxUInt64 to a double correctly (AIX/xlC). -// Assumes that UInt64 is a 64 bits integer. -static const double maxUInt64AsDouble = 18446744073709551615.0; -#endif // defined(JSON_HAS_INT64) -const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); -const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); -const LargestUInt Value::maxLargestUInt = LargestUInt(-1); +// static +Value const& Value::nullSingleton() { + static Value const nullStatic; + return nullStatic; +} + +#if JSON_USE_NULLREF +// for backwards compatibility, we'll leave these global references around, but +// DO NOT use them in JSONCPP library code any more! +// static +Value const& Value::null = Value::nullSingleton(); + +// static +Value const& Value::nullRef = Value::nullSingleton(); +#endif #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) template static inline bool InRange(double d, T min, U max) { - return d >= min && d <= max; + // The casts can lose precision, but we are looking only for + // an approximate range. Might fail on edge cases though. ~cdunn + return d >= static_cast(min) && d <= static_cast(max); } #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) static inline double integerToDouble(Json::UInt64 value) { - return static_cast(Int64(value / 2)) * 2.0 + Int64(value & 1); + return static_cast(Int64(value / 2)) * 2.0 + + static_cast(Int64(value & 1)); } template static inline double integerToDouble(T value) { @@ -2493,18 +2523,16 @@ static inline bool InRange(double d, T min, U max) { * computed using strlen(value). * @return Pointer on the duplicate instance of string. */ -static inline char* duplicateStringValue(const char* value, - size_t length) { +static inline char* duplicateStringValue(const char* value, size_t length) { // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. - if (length >= (size_t)Value::maxInt) + if (length >= static_cast(Value::maxInt)) length = Value::maxInt - 1; - char* newString = static_cast(malloc(length + 1)); - if (newString == NULL) { - throwRuntimeError( - "in Json::Value::duplicateStringValue(): " - "Failed to allocate string value buffer"); + auto newString = static_cast(malloc(length + 1)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); } memcpy(newString, value, length); newString[length] = 0; @@ -2513,31 +2541,28 @@ static inline char* duplicateStringValue(const char* value, /* Record the length as a prefix. */ -static inline char* duplicateAndPrefixStringValue( - const char* value, - unsigned int length) -{ +static inline char* duplicateAndPrefixStringValue(const char* value, + unsigned int length) { // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. - JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U, + JSON_ASSERT_MESSAGE(length <= static_cast(Value::maxInt) - + sizeof(unsigned) - 1U, "in Json::Value::duplicateAndPrefixStringValue(): " "length too big for prefixing"); - unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; - char* newString = static_cast(malloc(actualLength)); - if (newString == 0) { - throwRuntimeError( - "in Json::Value::duplicateAndPrefixStringValue(): " - "Failed to allocate string value buffer"); + size_t actualLength = sizeof(length) + length + 1; + auto newString = static_cast(malloc(actualLength)); + if (newString == nullptr) { + throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); } *reinterpret_cast(newString) = length; memcpy(newString + sizeof(unsigned), value, length); - newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + newString[actualLength - 1U] = + 0; // to avoid buffer over-run accidents by users later return newString; } -inline static void decodePrefixedString( - bool isPrefixed, char const* prefixed, - unsigned* length, char const** value) -{ +inline static void decodePrefixedString(bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) { if (!isPrefixed) { *length = static_cast(strlen(prefixed)); *value = prefixed; @@ -2546,9 +2571,28 @@ inline static void decodePrefixedString( *value = prefixed + sizeof(unsigned); } } -/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). +/** Free the string duplicated by + * duplicateStringValue()/duplicateAndPrefixStringValue(). */ -static inline void releaseStringValue(char* value) { free(value); } +#if JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + unsigned length = 0; + char const* valueDecoded; + decodePrefixedString(true, value, &length, &valueDecoded); + size_t const size = sizeof(unsigned) + length + 1U; + memset(value, 0, size); + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + // length==0 => we allocated the strings memory + size_t size = (length == 0) ? strlen(value) : length; + memset(value, 0, size); + free(value); +} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { free(value); } +static inline void releaseStringValue(char* value, unsigned) { free(value); } +#endif // JSONCPP_USING_SECURE_MEMORY } // namespace Json @@ -2566,57 +2610,28 @@ static inline void releaseStringValue(char* value) { free(value); } namespace Json { -Exception::Exception(std::string const& msg) - : msg_(msg) -{} -Exception::~Exception() throw() -{} -char const* Exception::what() const throw() -{ - return msg_.c_str(); -} -RuntimeError::RuntimeError(std::string const& msg) - : Exception(msg) -{} -LogicError::LogicError(std::string const& msg) - : Exception(msg) -{} -void throwRuntimeError(std::string const& msg) -{ +#if JSON_USE_EXCEPTION +Exception::Exception(String msg) : msg_(std::move(msg)) {} +Exception::~Exception() noexcept = default; +char const* Exception::what() const noexcept { return msg_.c_str(); } +RuntimeError::RuntimeError(String const& msg) : Exception(msg) {} +LogicError::LogicError(String const& msg) : Exception(msg) {} +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { throw RuntimeError(msg); } -void throwLogicError(std::string const& msg) -{ +JSONCPP_NORETURN void throwLogicError(String const& msg) { throw LogicError(msg); } - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CommentInfo -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -Value::CommentInfo::CommentInfo() : comment_(0) {} - -Value::CommentInfo::~CommentInfo() { - if (comment_) - releaseStringValue(comment_); +#else // !JSON_USE_EXCEPTION +JSONCPP_NORETURN void throwRuntimeError(String const& msg) { + std::cerr << msg << std::endl; + abort(); } - -void Value::CommentInfo::setComment(const char* text, size_t len) { - if (comment_) { - releaseStringValue(comment_); - comment_ = 0; - } - JSON_ASSERT(text != 0); - JSON_ASSERT_MESSAGE( - text[0] == '\0' || text[0] == '/', - "in Json::Value::setComment(): Comments must start with /"); - // It seems that /**/ style comments are acceptable as well. - comment_ = duplicateStringValue(text, len); +JSONCPP_NORETURN void throwLogicError(String const& msg) { + std::cerr << msg << std::endl; + abort(); } +#endif // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -2629,36 +2644,45 @@ void Value::CommentInfo::setComment(const char* text, size_t len) { // Notes: policy_ indicates if the string was allocated when // a string is stored. -Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} +Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {} -Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) +Value::CZString::CZString(char const* str, unsigned length, + DuplicationPolicy allocate) : cstr_(str) { // allocate != duplicate storage_.policy_ = allocate & 0x3; - storage_.length_ = ulength & 0x3FFFFFFF; + storage_.length_ = length & 0x3FFFFFFF; } -Value::CZString::CZString(const CZString& other) - : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0 - ? duplicateStringValue(other.cstr_, other.storage_.length_) - : other.cstr_) { - storage_.policy_ = (other.cstr_ - ? (static_cast(other.storage_.policy_) == noDuplication - ? noDuplication : duplicate) - : static_cast(other.storage_.policy_)); +Value::CZString::CZString(const CZString& other) { + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = + static_cast( + other.cstr_ + ? (static_cast(other.storage_.policy_) == + noDuplication + ? noDuplication + : duplicate) + : static_cast(other.storage_.policy_)) & + 3U; storage_.length_ = other.storage_.length_; } -#if JSON_HAS_RVALUE_REFERENCES Value::CZString::CZString(CZString&& other) - : cstr_(other.cstr_), index_(other.index_) { + : cstr_(other.cstr_), index_(other.index_) { other.cstr_ = nullptr; } -#endif Value::CZString::~CZString() { - if (cstr_ && storage_.policy_ == duplicate) - releaseStringValue(const_cast(cstr_)); + if (cstr_ && storage_.policy_ == duplicate) { + releaseStringValue(const_cast(cstr_), + storage_.length_ + 1U); // +1 for null terminating + // character for sake of + // completeness but not actually + // necessary + } } void Value::CZString::swap(CZString& other) { @@ -2666,41 +2690,58 @@ void Value::CZString::swap(CZString& other) { std::swap(index_, other.index_); } -Value::CZString& Value::CZString::operator=(CZString other) { - swap(other); +Value::CZString& Value::CZString::operator=(const CZString& other) { + cstr_ = other.cstr_; + index_ = other.index_; + return *this; +} + +Value::CZString& Value::CZString::operator=(CZString&& other) { + cstr_ = other.cstr_; + index_ = other.index_; + other.cstr_ = nullptr; return *this; } bool Value::CZString::operator<(const CZString& other) const { - if (!cstr_) return index_ < other.index_; - //return strcmp(cstr_, other.cstr_) < 0; + if (!cstr_) + return index_ < other.index_; + // return strcmp(cstr_, other.cstr_) < 0; // Assume both are strings. unsigned this_len = this->storage_.length_; unsigned other_len = other.storage_.length_; - unsigned min_len = std::min(this_len, other_len); + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this->cstr_ && other.cstr_); int comp = memcmp(this->cstr_, other.cstr_, min_len); - if (comp < 0) return true; - if (comp > 0) return false; + if (comp < 0) + return true; + if (comp > 0) + return false; return (this_len < other_len); } bool Value::CZString::operator==(const CZString& other) const { - if (!cstr_) return index_ == other.index_; - //return strcmp(cstr_, other.cstr_) == 0; + if (!cstr_) + return index_ == other.index_; + // return strcmp(cstr_, other.cstr_) == 0; // Assume both are strings. unsigned this_len = this->storage_.length_; unsigned other_len = other.storage_.length_; - if (this_len != other_len) return false; + if (this_len != other_len) + return false; + JSON_ASSERT(this->cstr_ && other.cstr_); int comp = memcmp(this->cstr_, other.cstr_, this_len); return comp == 0; } ArrayIndex Value::CZString::index() const { return index_; } -//const char* Value::CZString::c_str() const { return cstr_; } +// const char* Value::CZString::c_str() const { return cstr_; } const char* Value::CZString::data() const { return cstr_; } unsigned Value::CZString::length() const { return storage_.length_; } -bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } +bool Value::CZString::isStaticString() const { + return storage_.policy_ == noDuplication; +} // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -2714,9 +2755,10 @@ bool Value::CZString::isStaticString() const { return storage_.policy_ == noDupl * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ -Value::Value(ValueType vtype) { - initBasic(vtype); - switch (vtype) { +Value::Value(ValueType type) { + static char const emptyString[] = ""; + initBasic(type); + switch (type) { case nullValue: break; case intValue: @@ -2727,7 +2769,8 @@ Value::Value(ValueType vtype) { value_.real_ = 0.0; break; case stringValue: - value_.string_ = 0; + // allocated_ == false, so this is safe. + value_.string_ = const_cast(static_cast(emptyString)); break; case arrayValue: case objectValue: @@ -2768,19 +2811,22 @@ Value::Value(double value) { Value::Value(const char* value) { initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); + JSON_ASSERT_MESSAGE(value != nullptr, + "Null Value Passed to Value Constructor"); + value_.string_ = duplicateAndPrefixStringValue( + value, static_cast(strlen(value))); } -Value::Value(const char* beginValue, const char* endValue) { +Value::Value(const char* begin, const char* end) { initBasic(stringValue, true); value_.string_ = - duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); + duplicateAndPrefixStringValue(begin, static_cast(end - begin)); } -Value::Value(const std::string& value) { +Value::Value(const String& value) { initBasic(stringValue, true); - value_.string_ = - duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); + value_.string_ = duplicateAndPrefixStringValue( + value.data(), static_cast(value.length())); } Value::Value(const StaticString& value) { @@ -2788,107 +2834,44 @@ Value::Value(const StaticString& value) { value_.string_ = const_cast(value.c_str()); } -#ifdef JSON_USE_CPPTL -Value::Value(const CppTL::ConstString& value) { - initBasic(stringValue, true); - value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); -} -#endif - Value::Value(bool value) { initBasic(booleanValue); value_.bool_ = value; } -Value::Value(Value const& other) - : type_(other.type_), allocated_(false) - , - comments_(0), start_(other.start_), limit_(other.limit_) -{ - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - value_ = other.value_; - break; - case stringValue: - if (other.value_.string_ && other.allocated_) { - unsigned len; - char const* str; - decodePrefixedString(other.allocated_, other.value_.string_, - &len, &str); - value_.string_ = duplicateAndPrefixStringValue(str, len); - allocated_ = true; - } else { - value_.string_ = other.value_.string_; - allocated_ = false; - } - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(*other.value_.map_); - break; - default: - JSON_ASSERT_UNREACHABLE; - } - if (other.comments_) { - comments_ = new CommentInfo[numberOfCommentPlacement]; - for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { - const CommentInfo& otherComment = other.comments_[comment]; - if (otherComment.comment_) - comments_[comment].setComment( - otherComment.comment_, strlen(otherComment.comment_)); - } - } +Value::Value(const Value& other) { + dupPayload(other); + dupMeta(other); } -#if JSON_HAS_RVALUE_REFERENCES -// Move constructor Value::Value(Value&& other) { initBasic(nullValue); swap(other); } -#endif Value::~Value() { - switch (type_) { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue: - if (allocated_) - releaseStringValue(value_.string_); - break; - case arrayValue: - case objectValue: - delete value_.map_; - break; - default: - JSON_ASSERT_UNREACHABLE; - } - - if (comments_) - delete[] comments_; + releasePayload(); + value_.uint_ = 0; } -Value& Value::operator=(Value other) { - swap(other); +Value& Value::operator=(const Value& other) { + Value(other).swap(*this); + return *this; +} + +Value& Value::operator=(Value&& other) { + other.swap(*this); return *this; } void Value::swapPayload(Value& other) { - ValueType temp = type_; - type_ = other.type_; - other.type_ = temp; + std::swap(bits_, other.bits_); std::swap(value_, other.value_); - int temp2 = allocated_; - allocated_ = other.allocated_; - other.allocated_ = temp2 & 0x1; +} + +void Value::copyPayload(const Value& other) { + releasePayload(); + dupPayload(other); } void Value::swap(Value& other) { @@ -2898,7 +2881,14 @@ void Value::swap(Value& other) { std::swap(limit_, other.limit_); } -ValueType Value::type() const { return type_; } +void Value::copy(const Value& other) { + copyPayload(other); + dupMeta(other); +} + +ValueType Value::type() const { + return static_cast(bits_.value_type_); +} int Value::compare(const Value& other) const { if (*this < other) @@ -2909,10 +2899,10 @@ int Value::compare(const Value& other) const { } bool Value::operator<(const Value& other) const { - int typeDelta = type_ - other.type_; + int typeDelta = type() - other.type(); if (typeDelta) - return typeDelta < 0 ? true : false; - switch (type_) { + return typeDelta < 0; + switch (type()) { case nullValue: return false; case intValue: @@ -2923,29 +2913,33 @@ bool Value::operator<(const Value& other) const { return value_.real_ < other.value_.real_; case booleanValue: return value_.bool_ < other.value_.bool_; - case stringValue: - { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { - if (other.value_.string_) return true; - else return false; + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { + return other.value_.string_ != nullptr; } unsigned this_len; unsigned other_len; char const* this_str; char const* other_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); - unsigned min_len = std::min(this_len, other_len); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this_str && other_str); int comp = memcmp(this_str, other_str, min_len); - if (comp < 0) return true; - if (comp > 0) return false; + if (comp < 0) + return true; + if (comp > 0) + return false; return (this_len < other_len); } case arrayValue: case objectValue: { - int delta = int(value_.map_->size() - other.value_.map_->size()); - if (delta) - return delta < 0; + auto thisSize = value_.map_->size(); + auto otherSize = other.value_.map_->size(); + if (thisSize != otherSize) + return thisSize < otherSize; return (*value_.map_) < (*other.value_.map_); } default: @@ -2961,14 +2955,9 @@ bool Value::operator>=(const Value& other) const { return !(*this < other); } bool Value::operator>(const Value& other) const { return other < *this; } bool Value::operator==(const Value& other) const { - // if ( type_ != other.type_ ) - // GCC 2.95.3 says: - // attempt to take address of bit-field structure member `Json::Value::type_' - // Beats me, but a temp solves the problem. - int temp = other.type_; - if (type_ != temp) + if (type() != other.type()) return false; - switch (type_) { + switch (type()) { case nullValue: return true; case intValue: @@ -2979,18 +2968,21 @@ bool Value::operator==(const Value& other) const { return value_.real_ == other.value_.real_; case booleanValue: return value_.bool_ == other.value_.bool_; - case stringValue: - { - if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + case stringValue: { + if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) { return (value_.string_ == other.value_.string_); } unsigned this_len; unsigned other_len; char const* this_str; char const* other_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); - if (this_len != other_len) return false; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len, + &other_str); + if (this_len != other_len) + return false; + JSON_ASSERT(this_str && other_str); int comp = memcmp(this_str, other_str, this_len); return comp == 0; } @@ -3007,35 +2999,55 @@ bool Value::operator==(const Value& other) const { bool Value::operator!=(const Value& other) const { return !(*this == other); } const char* Value::asCString() const { - JSON_ASSERT_MESSAGE(type_ == stringValue, + JSON_ASSERT_MESSAGE(type() == stringValue, "in Json::Value::asCString(): requires stringValue"); - if (value_.string_ == 0) return 0; + if (value_.string_ == nullptr) + return nullptr; unsigned this_len; char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); return this_str; } -bool Value::getString(char const** str, char const** cend) const { - if (type_ != stringValue) return false; - if (value_.string_ == 0) return false; +#if JSONCPP_USING_SECURE_MEMORY +unsigned Value::getCStringLength() const { + JSON_ASSERT_MESSAGE(type() == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) + return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return this_len; +} +#endif + +bool Value::getString(char const** begin, char const** end) const { + if (type() != stringValue) + return false; + if (value_.string_ == nullptr) + return false; unsigned length; - decodePrefixedString(this->allocated_, this->value_.string_, &length, str); - *cend = *str + length; + decodePrefixedString(this->isAllocated(), this->value_.string_, &length, + begin); + *end = *begin + length; return true; } -std::string Value::asString() const { - switch (type_) { +String Value::asString() const { + switch (type()) { case nullValue: return ""; - case stringValue: - { - if (value_.string_ == 0) return ""; + case stringValue: { + if (value_.string_ == nullptr) + return ""; unsigned this_len; char const* this_str; - decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); - return std::string(this_str, this_len); + decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len, + &this_str); + return String(this_str, this_len); } case booleanValue: return value_.bool_ ? "true" : "false"; @@ -3050,18 +3062,8 @@ std::string Value::asString() const { } } -#ifdef JSON_USE_CPPTL -CppTL::ConstString Value::asConstString() const { - unsigned len; - char const* str; - decodePrefixedString(allocated_, value_.string_, - &len, &str); - return CppTL::ConstString(str, len); -} -#endif - Value::Int Value::asInt() const { - switch (type_) { + switch (type()) { case intValue: JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); return Int(value_.int_); @@ -3083,7 +3085,7 @@ Value::Int Value::asInt() const { } Value::UInt Value::asUInt() const { - switch (type_) { + switch (type()) { case intValue: JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); return UInt(value_.int_); @@ -3107,7 +3109,7 @@ Value::UInt Value::asUInt() const { #if defined(JSON_HAS_INT64) Value::Int64 Value::asInt64() const { - switch (type_) { + switch (type()) { case intValue: return Int64(value_.int_); case uintValue: @@ -3128,7 +3130,7 @@ Value::Int64 Value::asInt64() const { } Value::UInt64 Value::asUInt64() const { - switch (type_) { + switch (type()) { case intValue: JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); return UInt64(value_.int_); @@ -3166,7 +3168,7 @@ LargestUInt Value::asLargestUInt() const { } double Value::asDouble() const { - switch (type_) { + switch (type()) { case intValue: return static_cast(value_.int_); case uintValue: @@ -3188,21 +3190,22 @@ double Value::asDouble() const { } float Value::asFloat() const { - switch (type_) { + switch (type()) { case intValue: return static_cast(value_.int_); case uintValue: #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return static_cast(value_.uint_); #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return integerToDouble(value_.uint_); + // This can fail (silently?) if the value is bigger than MAX_FLOAT. + return static_cast(integerToDouble(value_.uint_)); #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) case realValue: return static_cast(value_.real_); case nullValue: return 0.0; case booleanValue: - return value_.bool_ ? 1.0f : 0.0f; + return value_.bool_ ? 1.0F : 0.0F; default: break; } @@ -3210,18 +3213,20 @@ float Value::asFloat() const { } bool Value::asBool() const { - switch (type_) { + switch (type()) { case booleanValue: return value_.bool_; case nullValue: return false; case intValue: - return value_.int_ ? true : false; + return value_.int_ != 0; case uintValue: - return value_.uint_ ? true : false; - case realValue: - // This is kind of strange. Not recommended. - return (value_.real_ != 0.0) ? true : false; + return value_.uint_ != 0; + case realValue: { + // According to JavaScript language zero or NaN is regarded as false + const auto value_classification = std::fpclassify(value_.real_); + return value_classification != FP_ZERO && value_classification != FP_NAN; + } default: break; } @@ -3232,30 +3237,30 @@ bool Value::isConvertibleTo(ValueType other) const { switch (other) { case nullValue: return (isNumeric() && asDouble() == 0.0) || - (type_ == booleanValue && value_.bool_ == false) || - (type_ == stringValue && asString() == "") || - (type_ == arrayValue && value_.map_->size() == 0) || - (type_ == objectValue && value_.map_->size() == 0) || - type_ == nullValue; + (type() == booleanValue && !value_.bool_) || + (type() == stringValue && asString().empty()) || + (type() == arrayValue && value_.map_->empty()) || + (type() == objectValue && value_.map_->empty()) || + type() == nullValue; case intValue: return isInt() || - (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || - type_ == booleanValue || type_ == nullValue; + (type() == realValue && InRange(value_.real_, minInt, maxInt)) || + type() == booleanValue || type() == nullValue; case uintValue: return isUInt() || - (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || - type_ == booleanValue || type_ == nullValue; + (type() == realValue && InRange(value_.real_, 0, maxUInt)) || + type() == booleanValue || type() == nullValue; case realValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; + return isNumeric() || type() == booleanValue || type() == nullValue; case booleanValue: - return isNumeric() || type_ == booleanValue || type_ == nullValue; + return isNumeric() || type() == booleanValue || type() == nullValue; case stringValue: - return isNumeric() || type_ == booleanValue || type_ == stringValue || - type_ == nullValue; + return isNumeric() || type() == booleanValue || type() == stringValue || + type() == nullValue; case arrayValue: - return type_ == arrayValue || type_ == nullValue; + return type() == arrayValue || type() == nullValue; case objectValue: - return type_ == objectValue || type_ == nullValue; + return type() == objectValue || type() == nullValue; } JSON_ASSERT_UNREACHABLE; return false; @@ -3263,7 +3268,7 @@ bool Value::isConvertibleTo(ValueType other) const { /// Number of values in array or object ArrayIndex Value::size() const { - switch (type_) { + switch (type()) { case nullValue: case intValue: case uintValue: @@ -3287,20 +3292,19 @@ ArrayIndex Value::size() const { bool Value::empty() const { if (isNull() || isArray() || isObject()) - return size() == 0u; - else - return false; + return size() == 0U; + return false; } -bool Value::operator!() const { return isNull(); } +Value::operator bool() const { return !isNull(); } void Value::clear() { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || - type_ == objectValue, + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue || + type() == objectValue, "in Json::Value::clear(): requires complex value"); start_ = 0; limit_ = 0; - switch (type_) { + switch (type()) { case arrayValue: case objectValue: value_.map_->clear(); @@ -3311,35 +3315,35 @@ void Value::clear() { } void Value::resize(ArrayIndex newSize) { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, "in Json::Value::resize(): requires arrayValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(arrayValue); ArrayIndex oldSize = size(); if (newSize == 0) clear(); else if (newSize > oldSize) - (*this)[newSize - 1]; + this->operator[](newSize - 1); else { for (ArrayIndex index = newSize; index < oldSize; ++index) { value_.map_->erase(index); } - assert(size() == newSize); + JSON_ASSERT(size() == newSize); } } Value& Value::operator[](ArrayIndex index) { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, + type() == nullValue || type() == arrayValue, "in Json::Value::operator[](ArrayIndex): requires arrayValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(arrayValue); CZString key(index); - ObjectValues::iterator it = value_.map_->lower_bound(key); + auto it = value_.map_->lower_bound(key); if (it != value_.map_->end() && (*it).first == key) return (*it).second; - ObjectValues::value_type defaultValue(key, nullRef); + ObjectValues::value_type defaultValue(key, nullSingleton()); it = value_.map_->insert(it, defaultValue); return (*it).second; } @@ -3353,14 +3357,14 @@ Value& Value::operator[](int index) { const Value& Value::operator[](ArrayIndex index) const { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == arrayValue, + type() == nullValue || type() == arrayValue, "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); - if (type_ == nullValue) - return nullRef; + if (type() == nullValue) + return nullSingleton(); CZString key(index); ObjectValues::const_iterator it = value_.map_->find(key); if (it == value_.map_->end()) - return nullRef; + return nullSingleton(); return (*it).second; } @@ -3371,50 +3375,108 @@ const Value& Value::operator[](int index) const { return (*this)[ArrayIndex(index)]; } -void Value::initBasic(ValueType vtype, bool allocated) { - type_ = vtype; - allocated_ = allocated; - comments_ = 0; +void Value::initBasic(ValueType type, bool allocated) { + setType(type); + setIsAllocated(allocated); + comments_ = Comments{}; start_ = 0; limit_ = 0; } +void Value::dupPayload(const Value& other) { + setType(other.type()); + setIsAllocated(false); + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.isAllocated()) { + unsigned len; + char const* str; + decodePrefixedString(other.isAllocated(), other.value_.string_, &len, + &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + setIsAllocated(true); + } else { + value_.string_ = other.value_.string_; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::releasePayload() { + switch (type()) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (isAllocated()) + releasePrefixedStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +void Value::dupMeta(const Value& other) { + comments_ = other.comments_; + start_ = other.start_; + limit_ = other.limit_; +} + // Access an object value by name, create a null member if it does not exist. // @pre Type of '*this' is object or null. // @param key is null-terminated. Value& Value::resolveReference(const char* key) { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, + type() == nullValue || type() == objectValue, "in Json::Value::resolveReference(): requires objectValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(objectValue); - CZString actualKey( - key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + CZString actualKey(key, static_cast(strlen(key)), + CZString::noDuplication); // NOTE! + auto it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; - ObjectValues::value_type defaultValue(actualKey, nullRef); + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; return value; } // @param key is not null-terminated. -Value& Value::resolveReference(char const* key, char const* cend) -{ +Value& Value::resolveReference(char const* key, char const* end) { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, + type() == nullValue || type() == objectValue, "in Json::Value::resolveReference(key, end): requires objectValue"); - if (type_ == nullValue) + if (type() == nullValue) *this = Value(objectValue); - CZString actualKey( - key, static_cast(cend-key), CZString::duplicateOnCopy); - ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + CZString actualKey(key, static_cast(end - key), + CZString::duplicateOnCopy); + auto it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; - ObjectValues::value_type defaultValue(actualKey, nullRef); + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; return value; @@ -3422,32 +3484,40 @@ Value& Value::resolveReference(char const* key, char const* cend) Value Value::get(ArrayIndex index, const Value& defaultValue) const { const Value* value = &((*this)[index]); - return value == &nullRef ? defaultValue : *value; + return value == &nullSingleton() ? defaultValue : *value; } bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } -Value const* Value::find(char const* key, char const* cend) const -{ - JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, - "in Json::Value::find(key, end, found): requires objectValue or nullValue"); - if (type_ == nullValue) return NULL; - CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); +Value const* Value::find(char const* begin, char const* end) const { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::find(begin, end): requires " + "objectValue or nullValue"); + if (type() == nullValue) + return nullptr; + CZString actualKey(begin, static_cast(end - begin), + CZString::noDuplication); ObjectValues::const_iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) return NULL; + if (it == value_.map_->end()) + return nullptr; return &(*it).second; } -const Value& Value::operator[](const char* key) const -{ +Value* Value::demand(char const* begin, char const* end) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, + "in Json::Value::demand(begin, end): requires " + "objectValue or nullValue"); + return &resolveReference(begin, end); +} +const Value& Value::operator[](const char* key) const { Value const* found = find(key, key + strlen(key)); - if (!found) return nullRef; + if (!found) + return nullSingleton(); return *found; } -Value const& Value::operator[](std::string const& key) const -{ +Value const& Value::operator[](const String& key) const { Value const* found = find(key.data(), key.data() + key.length()); - if (!found) return nullRef; + if (!found) + return nullSingleton(); return *found; } @@ -3455,7 +3525,7 @@ Value& Value::operator[](const char* key) { return resolveReference(key, key + strlen(key)); } -Value& Value::operator[](const std::string& key) { +Value& Value::operator[](const String& key) { return resolveReference(key.data(), key.data() + key.length()); } @@ -3463,177 +3533,146 @@ Value& Value::operator[](const StaticString& key) { return resolveReference(key.c_str()); } -#ifdef JSON_USE_CPPTL -Value& Value::operator[](const CppTL::ConstString& key) { - return resolveReference(key.c_str(), key.end_c_str()); -} -Value const& Value::operator[](CppTL::ConstString const& key) const -{ - Value const* found = find(key.c_str(), key.end_c_str()); - if (!found) return nullRef; - return *found; -} -#endif +Value& Value::append(const Value& value) { return append(Value(value)); } -Value& Value::append(const Value& value) { return (*this)[size()] = value; } +Value& Value::append(Value&& value) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::append: requires arrayValue"); + if (type() == nullValue) { + *this = Value(arrayValue); + } + return this->value_.map_->emplace(size(), std::move(value)).first->second; +} -Value Value::get(char const* key, char const* cend, Value const& defaultValue) const -{ - Value const* found = find(key, cend); +bool Value::insert(ArrayIndex index, const Value& newValue) { + return insert(index, Value(newValue)); +} + +bool Value::insert(ArrayIndex index, Value&& newValue) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue, + "in Json::Value::insert: requires arrayValue"); + ArrayIndex length = size(); + if (index > length) { + return false; + } + for (ArrayIndex i = length; i > index; i--) { + (*this)[i] = std::move((*this)[i - 1]); + } + (*this)[index] = std::move(newValue); + return true; +} + +Value Value::get(char const* begin, char const* end, + Value const& defaultValue) const { + Value const* found = find(begin, end); return !found ? defaultValue : *found; } -Value Value::get(char const* key, Value const& defaultValue) const -{ +Value Value::get(char const* key, Value const& defaultValue) const { return get(key, key + strlen(key), defaultValue); } -Value Value::get(std::string const& key, Value const& defaultValue) const -{ +Value Value::get(String const& key, Value const& defaultValue) const { return get(key.data(), key.data() + key.length(), defaultValue); } - -bool Value::removeMember(const char* key, const char* cend, Value* removed) -{ - if (type_ != objectValue) { +bool Value::removeMember(const char* begin, const char* end, Value* removed) { + if (type() != objectValue) { return false; } - CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); - ObjectValues::iterator it = value_.map_->find(actualKey); + CZString actualKey(begin, static_cast(end - begin), + CZString::noDuplication); + auto it = value_.map_->find(actualKey); if (it == value_.map_->end()) return false; - *removed = it->second; + if (removed) + *removed = std::move(it->second); value_.map_->erase(it); return true; } -bool Value::removeMember(const char* key, Value* removed) -{ +bool Value::removeMember(const char* key, Value* removed) { return removeMember(key, key + strlen(key), removed); } -bool Value::removeMember(std::string const& key, Value* removed) -{ +bool Value::removeMember(String const& key, Value* removed) { return removeMember(key.data(), key.data() + key.length(), removed); } -Value Value::removeMember(const char* key) -{ - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, +void Value::removeMember(const char* key) { + JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue, "in Json::Value::removeMember(): requires objectValue"); - if (type_ == nullValue) - return nullRef; + if (type() == nullValue) + return; - Value removed; // null - removeMember(key, key + strlen(key), &removed); - return removed; // still null if removeMember() did nothing -} -Value Value::removeMember(const std::string& key) -{ - return removeMember(key.c_str()); + CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication); + value_.map_->erase(actualKey); } +void Value::removeMember(const String& key) { removeMember(key.c_str()); } bool Value::removeIndex(ArrayIndex index, Value* removed) { - if (type_ != arrayValue) { + if (type() != arrayValue) { return false; } CZString key(index); - ObjectValues::iterator it = value_.map_->find(key); + auto it = value_.map_->find(key); if (it == value_.map_->end()) { return false; } - *removed = it->second; + if (removed) + *removed = it->second; ArrayIndex oldSize = size(); // shift left all items left, into the place of the "removed" - for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + for (ArrayIndex i = index; i < (oldSize - 1); ++i) { CZString keey(i); (*value_.map_)[keey] = (*this)[i + 1]; } // erase the last one ("leftover") CZString keyLast(oldSize - 1); - ObjectValues::iterator itLast = value_.map_->find(keyLast); + auto itLast = value_.map_->find(keyLast); value_.map_->erase(itLast); return true; } -#ifdef JSON_USE_CPPTL -Value Value::get(const CppTL::ConstString& key, - const Value& defaultValue) const { - return get(key.c_str(), key.end_c_str(), defaultValue); +bool Value::isMember(char const* begin, char const* end) const { + Value const* value = find(begin, end); + return nullptr != value; } -#endif - -bool Value::isMember(char const* key, char const* cend) const -{ - Value const* value = find(key, cend); - return NULL != value; -} -bool Value::isMember(char const* key) const -{ +bool Value::isMember(char const* key) const { return isMember(key, key + strlen(key)); } -bool Value::isMember(std::string const& key) const -{ +bool Value::isMember(String const& key) const { return isMember(key.data(), key.data() + key.length()); } -#ifdef JSON_USE_CPPTL -bool Value::isMember(const CppTL::ConstString& key) const { - return isMember(key.c_str(), key.end_c_str()); -} -#endif - Value::Members Value::getMemberNames() const { JSON_ASSERT_MESSAGE( - type_ == nullValue || type_ == objectValue, + type() == nullValue || type() == objectValue, "in Json::Value::getMemberNames(), value must be objectValue"); - if (type_ == nullValue) + if (type() == nullValue) return Value::Members(); Members members; members.reserve(value_.map_->size()); ObjectValues::const_iterator it = value_.map_->begin(); ObjectValues::const_iterator itEnd = value_.map_->end(); for (; it != itEnd; ++it) { - members.push_back(std::string((*it).first.data(), - (*it).first.length())); + members.push_back(String((*it).first.data(), (*it).first.length())); } return members; } -// -//# ifdef JSON_USE_CPPTL -// EnumMemberNames -// Value::enumMemberNames() const -//{ -// if ( type_ == objectValue ) -// { -// return CppTL::Enum::any( CppTL::Enum::transform( -// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), -// MemberNamesTransform() ) ); -// } -// return EnumMemberNames(); -//} -// -// -// EnumValues -// Value::enumValues() const -//{ -// if ( type_ == objectValue || type_ == arrayValue ) -// return CppTL::Enum::anyValues( *(value_.map_), -// CppTL::Type() ); -// return EnumValues(); -//} -// -//# endif static bool IsIntegral(double d) { double integral_part; return modf(d, &integral_part) == 0.0; } -bool Value::isNull() const { return type_ == nullValue; } +bool Value::isNull() const { return type() == nullValue; } -bool Value::isBool() const { return type_ == booleanValue; } +bool Value::isBool() const { return type() == booleanValue; } bool Value::isInt() const { - switch (type_) { + switch (type()) { case intValue: +#if defined(JSON_HAS_INT64) return value_.int_ >= minInt && value_.int_ <= maxInt; +#else + return true; +#endif case uintValue: return value_.uint_ <= UInt(maxInt); case realValue: @@ -3646,11 +3685,19 @@ bool Value::isInt() const { } bool Value::isUInt() const { - switch (type_) { + switch (type()) { case intValue: +#if defined(JSON_HAS_INT64) return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); +#else + return value_.int_ >= 0; +#endif case uintValue: +#if defined(JSON_HAS_INT64) return value_.uint_ <= maxUInt; +#else + return true; +#endif case realValue: return value_.real_ >= 0 && value_.real_ <= maxUInt && IsIntegral(value_.real_); @@ -3662,7 +3709,7 @@ bool Value::isUInt() const { bool Value::isInt64() const { #if defined(JSON_HAS_INT64) - switch (type_) { + switch (type()) { case intValue: return true; case uintValue: @@ -3682,7 +3729,7 @@ bool Value::isInt64() const { bool Value::isUInt64() const { #if defined(JSON_HAS_INT64) - switch (type_) { + switch (type()) { case intValue: return value_.int_ >= 0; case uintValue: @@ -3701,66 +3748,114 @@ bool Value::isUInt64() const { } bool Value::isIntegral() const { + switch (type()) { + case intValue: + case uintValue: + return true; + case realValue: #if defined(JSON_HAS_INT64) - return isInt64() || isUInt64(); + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); #else - return isInt() || isUInt(); -#endif -} - -bool Value::isDouble() const { return type_ == realValue || isIntegral(); } - -bool Value::isNumeric() const { return isIntegral() || isDouble(); } - -bool Value::isString() const { return type_ == stringValue; } - -bool Value::isArray() const { return type_ == arrayValue; } - -bool Value::isObject() const { return type_ == objectValue; } - -void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { - if (!comments_) - comments_ = new CommentInfo[numberOfCommentPlacement]; - if ((len > 0) && (comment[len-1] == '\n')) { - // Always discard trailing newline, to aid indentation. - len -= 1; + return value_.real_ >= minInt && value_.real_ <= maxUInt && + IsIntegral(value_.real_); +#endif // JSON_HAS_INT64 + default: + break; } - comments_[placement].setComment(comment, len); + return false; } -void Value::setComment(const char* comment, CommentPlacement placement) { - setComment(comment, strlen(comment), placement); +bool Value::isDouble() const { + return type() == intValue || type() == uintValue || type() == realValue; } -void Value::setComment(const std::string& comment, CommentPlacement placement) { - setComment(comment.c_str(), comment.length(), placement); +bool Value::isNumeric() const { return isDouble(); } + +bool Value::isString() const { return type() == stringValue; } + +bool Value::isArray() const { return type() == arrayValue; } + +bool Value::isObject() const { return type() == objectValue; } + +Value::Comments::Comments(const Comments& that) + : ptr_{cloneUnique(that.ptr_)} {} + +Value::Comments::Comments(Comments&& that) : ptr_{std::move(that.ptr_)} {} + +Value::Comments& Value::Comments::operator=(const Comments& that) { + ptr_ = cloneUnique(that.ptr_); + return *this; +} + +Value::Comments& Value::Comments::operator=(Comments&& that) { + ptr_ = std::move(that.ptr_); + return *this; +} + +bool Value::Comments::has(CommentPlacement slot) const { + return ptr_ && !(*ptr_)[slot].empty(); +} + +String Value::Comments::get(CommentPlacement slot) const { + if (!ptr_) + return {}; + return (*ptr_)[slot]; +} + +void Value::Comments::set(CommentPlacement slot, String comment) { + if (!ptr_) { + ptr_ = std::unique_ptr(new Array()); + } + // check comments array boundry. + if (slot < CommentPlacement::numberOfCommentPlacement) { + (*ptr_)[slot] = std::move(comment); + } +} + +void Value::setComment(String comment, CommentPlacement placement) { + if (!comment.empty() && (comment.back() == '\n')) { + // Always discard trailing newline, to aid indentation. + comment.pop_back(); + } + JSON_ASSERT(!comment.empty()); + JSON_ASSERT_MESSAGE( + comment[0] == '\0' || comment[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + comments_.set(placement, std::move(comment)); } bool Value::hasComment(CommentPlacement placement) const { - return comments_ != 0 && comments_[placement].comment_ != 0; + return comments_.has(placement); } -std::string Value::getComment(CommentPlacement placement) const { - if (hasComment(placement)) - return comments_[placement].comment_; - return ""; +String Value::getComment(CommentPlacement placement) const { + return comments_.get(placement); } -void Value::setOffsetStart(size_t start) { start_ = start; } +void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } -void Value::setOffsetLimit(size_t limit) { limit_ = limit; } +void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } -size_t Value::getOffsetStart() const { return start_; } +ptrdiff_t Value::getOffsetStart() const { return start_; } -size_t Value::getOffsetLimit() const { return limit_; } +ptrdiff_t Value::getOffsetLimit() const { return limit_; } -std::string Value::toStyledString() const { - StyledWriter writer; - return writer.write(*this); +String Value::toStyledString() const { + StreamWriterBuilder builder; + + String out = this->hasComment(commentBefore) ? "\n" : ""; + out += Json::writeString(builder, *this); + out += '\n'; + + return out; } Value::const_iterator Value::begin() const { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3769,11 +3864,11 @@ Value::const_iterator Value::begin() const { default: break; } - return const_iterator(); + return {}; } Value::const_iterator Value::end() const { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3782,11 +3877,11 @@ Value::const_iterator Value::end() const { default: break; } - return const_iterator(); + return {}; } Value::iterator Value::begin() { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3799,7 +3894,7 @@ Value::iterator Value::begin() { } Value::iterator Value::end() { - switch (type_) { + switch (type()) { case arrayValue: case objectValue: if (value_.map_) @@ -3814,27 +3909,23 @@ Value::iterator Value::end() { // class PathArgument // ////////////////////////////////////////////////////////////////// -PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} +PathArgument::PathArgument() = default; PathArgument::PathArgument(ArrayIndex index) - : key_(), index_(index), kind_(kindIndex) {} + : index_(index), kind_(kindIndex) {} -PathArgument::PathArgument(const char* key) - : key_(key), index_(), kind_(kindKey) {} +PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {} -PathArgument::PathArgument(const std::string& key) - : key_(key.c_str()), index_(), kind_(kindKey) {} +PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {} // class Path // ////////////////////////////////////////////////////////////////// -Path::Path(const std::string& path, - const PathArgument& a1, - const PathArgument& a2, - const PathArgument& a3, - const PathArgument& a4, +Path::Path(const String& path, const PathArgument& a1, const PathArgument& a2, + const PathArgument& a3, const PathArgument& a4, const PathArgument& a5) { InArgs in; + in.reserve(5); in.push_back(&a1); in.push_back(&a2); in.push_back(&a3); @@ -3843,10 +3934,10 @@ Path::Path(const std::string& path, makePath(path, in); } -void Path::makePath(const std::string& path, const InArgs& in) { +void Path::makePath(const String& path, const InArgs& in) { const char* current = path.c_str(); const char* end = current + path.length(); - InArgs::const_iterator itInArg = in.begin(); + auto itInArg = in.begin(); while (current != end) { if (*current == '[') { ++current; @@ -3858,24 +3949,23 @@ void Path::makePath(const std::string& path, const InArgs& in) { index = index * 10 + ArrayIndex(*current - '0'); args_.push_back(index); } - if (current == end || *current++ != ']') + if (current == end || *++current != ']') invalidPath(path, int(current - path.c_str())); } else if (*current == '%') { addPathInArg(path, in, itInArg, PathArgument::kindKey); ++current; - } else if (*current == '.') { + } else if (*current == '.' || *current == ']') { ++current; } else { const char* beginName = current; while (current != end && !strchr("[.", *current)) ++current; - args_.push_back(std::string(beginName, current)); + args_.push_back(String(beginName, current)); } } } -void Path::addPathInArg(const std::string& /*path*/, - const InArgs& in, +void Path::addPathInArg(const String& /*path*/, const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind) { if (itInArg == in.end()) { @@ -3883,31 +3973,33 @@ void Path::addPathInArg(const std::string& /*path*/, } else if ((*itInArg)->kind_ != kind) { // Error: bad argument type } else { - args_.push_back(**itInArg); + args_.push_back(**itInArg++); } } -void Path::invalidPath(const std::string& /*path*/, int /*location*/) { +void Path::invalidPath(const String& /*path*/, int /*location*/) { // Error: invalid path. } const Value& Path::resolve(const Value& root) const { const Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; + for (const auto& arg : args_) { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) { - // Error: unable to resolve path (array value expected at position... + // Error: unable to resolve path (array value expected at position... ) + return Value::nullSingleton(); } node = &((*node)[arg.index_]); } else if (arg.kind_ == PathArgument::kindKey) { if (!node->isObject()) { // Error: unable to resolve path (object value expected at position...) + return Value::nullSingleton(); } node = &((*node)[arg.key_]); - if (node == &Value::nullRef) { + if (node == &Value::nullSingleton()) { // Error: unable to resolve path (object has no member named '' at // position...) + return Value::nullSingleton(); } } } @@ -3916,8 +4008,7 @@ const Value& Path::resolve(const Value& root) const { Value Path::resolve(const Value& root, const Value& defaultValue) const { const Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; + for (const auto& arg : args_) { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) return defaultValue; @@ -3926,7 +4017,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const { if (!node->isObject()) return defaultValue; node = &((*node)[arg.key_]); - if (node == &Value::nullRef) + if (node == &Value::nullSingleton()) return defaultValue; } } @@ -3935,8 +4026,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const { Value& Path::make(Value& root) const { Value* node = &root; - for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { - const PathArgument& arg = *it; + for (const auto& arg : args_) { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray()) { // Error: node is not an array at position ... @@ -3967,75 +4057,87 @@ Value& Path::make(Value& root) const { // Beginning of content of file: src/lib_json/json_writer.cpp // ////////////////////////////////////////////////////////////////////// -// Copyright 2011 Baptiste Lepilleur +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) -#include #include "json_tool.h" +#include #endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include #include #include +#include #include #include -#include -#include -#include + +#if __cplusplus >= 201103L +#include #include -#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#if !defined(isnan) +#define isnan std::isnan +#endif + +#if !defined(isfinite) +#define isfinite std::isfinite +#endif + +#else +#include +#include + +#if defined(_MSC_VER) +#if !defined(isnan) +#include +#define isnan _isnan +#endif + +#if !defined(isfinite) #include #define isfinite _finite -#elif defined(__sun) && defined(__SVR4) //Solaris +#endif + +#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES + +#endif //_MSC_VER + +#if defined(__sun) && defined(__SVR4) // Solaris #if !defined(isfinite) #include #define isfinite finite #endif -#elif defined(_AIX) -#if !defined(isfinite) -#include -#define isfinite finite #endif -#elif defined(__hpux) + +#if defined(__hpux) #if !defined(isfinite) #if defined(__ia64) && !defined(finite) -#define isfinite(x) ((sizeof(x) == sizeof(float) ? \ - _Isfinitef(x) : _IsFinite(x))) -#else -#include +#define isfinite(x) \ + ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x))) +#endif +#endif +#endif + +#if !defined(isnan) +// IEEE standard states that NaN values will not compare to themselves +#define isnan(x) (x != x) +#endif + +#if !defined(__APPLE__) +#if !defined(isfinite) #define isfinite finite #endif #endif -#else -#include -#if !(defined(__QNXNTO__)) // QNX already defines isfinite -#define isfinite std::isfinite -#endif #endif #if defined(_MSC_VER) -#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above -#define snprintf sprintf_s -#elif _MSC_VER >= 1900 // VC++ 14.0 and above -#define snprintf std::snprintf -#else -#define snprintf _snprintf -#endif -#elif defined(__ANDROID__) || defined(__QNXNTO__) -#define snprintf snprintf -#elif __cplusplus >= 201103L -#define snprintf std::snprintf -#endif - -#if defined(__BORLANDC__) -#include -#define isfinite _finite -#define snprintf _snprintf -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif @@ -4043,30 +4145,12 @@ Value& Path::make(Value& root) const { namespace Json { #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) -typedef std::unique_ptr StreamWriterPtr; +using StreamWriterPtr = std::unique_ptr; #else -typedef std::auto_ptr StreamWriterPtr; +using StreamWriterPtr = std::auto_ptr; #endif -static bool containsControlCharacter(const char* str) { - while (*str) { - if (isControlCharacter(*(str++))) - return true; - } - return false; -} - -static bool containsControlCharacter0(const char* str, unsigned len) { - char const* end = str + len; - while (end != str) { - if (isControlCharacter(*str) || 0==*str) - return true; - ++str; - } - return false; -} - -std::string valueToString(LargestInt value) { +String valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); if (value == Value::minLargestInt) { @@ -4082,7 +4166,7 @@ std::string valueToString(LargestInt value) { return current; } -std::string valueToString(LargestUInt value) { +String valueToString(LargestUInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); uintToString(value, current); @@ -4092,140 +4176,171 @@ std::string valueToString(LargestUInt value) { #if defined(JSON_HAS_INT64) -std::string valueToString(Int value) { - return valueToString(LargestInt(value)); -} +String valueToString(Int value) { return valueToString(LargestInt(value)); } -std::string valueToString(UInt value) { - return valueToString(LargestUInt(value)); -} +String valueToString(UInt value) { return valueToString(LargestUInt(value)); } #endif // # if defined(JSON_HAS_INT64) -std::string valueToString(double value, bool useSpecialFloats, unsigned int precision) { - // Allocate a buffer that is more than large enough to store the 16 digits of - // precision requested below. - char buffer[32]; - int len = -1; - - char formatString[6]; - sprintf(formatString, "%%.%dg", precision); - +namespace { +String valueToString(double value, bool useSpecialFloats, + unsigned int precision, PrecisionType precisionType) { // Print into the buffer. We need not request the alternative representation - // that always has a decimal point because JSON doesn't distingish the + // that always has a decimal point because JSON doesn't distinguish the // concepts of reals and integers. - if (isfinite(value)) { - len = snprintf(buffer, sizeof(buffer), formatString, value); - } else { - // IEEE standard states that NaN values will not compare to themselves - if (value != value) { - len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null"); - } else if (value < 0) { - len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999"); - } else { - len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999"); - } - // For those, we do not need to call fixNumLoc, but it is fast. + if (!isfinite(value)) { + static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"}, + {"null", "-1e+9999", "1e+9999"}}; + return reps[useSpecialFloats ? 0 : 1] + [isnan(value) ? 0 : (value < 0) ? 1 : 2]; + } + + String buffer(size_t(36), '\0'); + while (true) { + int len = jsoncpp_snprintf( + &*buffer.begin(), buffer.size(), + (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f", + precision, value); + assert(len >= 0); + auto wouldPrint = static_cast(len); + if (wouldPrint >= buffer.size()) { + buffer.resize(wouldPrint + 1); + continue; + } + buffer.resize(wouldPrint); + break; + } + + buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end()); + + // strip the zero padding from the right + if (precisionType == PrecisionType::decimalPlaces) { + buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end()); + } + + // try to ensure we preserve the fact that this was given to us as a double on + // input + if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) { + buffer += ".0"; } - assert(len >= 0); - fixNumericLocale(buffer, buffer + len); return buffer; } +} // namespace -std::string valueToString(double value) { return valueToString(value, false, 17); } +String valueToString(double value, unsigned int precision, + PrecisionType precisionType) { + return valueToString(value, false, precision, precisionType); +} -std::string valueToString(bool value) { return value ? "true" : "false"; } +String valueToString(bool value) { return value ? "true" : "false"; } -std::string valueToQuotedString(const char* value) { - if (value == NULL) - return ""; - // Not sure how to handle unicode... - if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && - !containsControlCharacter(value)) - return std::string("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = - strlen(value) * 2 + 3; // allescaped+quotes+NULL - std::string result; - result.reserve(maxsize); // to avoid lots of mallocs - result += "\""; - for (const char* c = value; *c != 0; ++c) { - switch (*c) { - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - // case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something. - // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); - result += oss.str(); - } else { - result += *c; - } - break; - } +static bool doesAnyCharRequireEscaping(char const* s, size_t n) { + assert(s || !n); + + return std::any_of(s, s + n, [](unsigned char c) { + return c == '\\' || c == '"' || c < 0x20 || c > 0x7F; + }); +} + +static unsigned int utf8ToCodepoint(const char*& s, const char* e) { + const unsigned int REPLACEMENT_CHARACTER = 0xFFFD; + + unsigned int firstByte = static_cast(*s); + + if (firstByte < 0x80) + return firstByte; + + if (firstByte < 0xE0) { + if (e - s < 2) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = + ((firstByte & 0x1F) << 6) | (static_cast(s[1]) & 0x3F); + s += 1; + // oversized encoded characters are invalid + return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated; } - result += "\""; + + if (firstByte < 0xF0) { + if (e - s < 3) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x0F) << 12) | + ((static_cast(s[1]) & 0x3F) << 6) | + (static_cast(s[2]) & 0x3F); + s += 2; + // surrogates aren't valid codepoints itself + // shouldn't be UTF-8 encoded + if (calculated >= 0xD800 && calculated <= 0xDFFF) + return REPLACEMENT_CHARACTER; + // oversized encoded characters are invalid + return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated; + } + + if (firstByte < 0xF8) { + if (e - s < 4) + return REPLACEMENT_CHARACTER; + + unsigned int calculated = ((firstByte & 0x07) << 18) | + ((static_cast(s[1]) & 0x3F) << 12) | + ((static_cast(s[2]) & 0x3F) << 6) | + (static_cast(s[3]) & 0x3F); + s += 3; + // oversized encoded characters are invalid + return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated; + } + + return REPLACEMENT_CHARACTER; +} + +static const char hex2[] = "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + +static String toHex16Bit(unsigned int x) { + const unsigned int hi = (x >> 8) & 0xff; + const unsigned int lo = x & 0xff; + String result(4, ' '); + result[0] = hex2[2 * hi]; + result[1] = hex2[2 * hi + 1]; + result[2] = hex2[2 * lo]; + result[3] = hex2[2 * lo + 1]; return result; } -// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp -static char const* strnpbrk(char const* s, char const* accept, size_t n) { - assert((s || !n) && accept); - - char const* const end = s + n; - for (char const* cur = s; cur < end; ++cur) { - int const c = *cur; - for (char const* a = accept; *a; ++a) { - if (*a == c) { - return cur; - } - } - } - return NULL; +static void appendRaw(String& result, unsigned ch) { + result += static_cast(ch); } -static std::string valueToQuotedStringN(const char* value, unsigned length) { - if (value == NULL) + +static void appendHex(String& result, unsigned ch) { + result.append("\\u").append(toHex16Bit(ch)); +} + +static String valueToQuotedStringN(const char* value, unsigned length, + bool emitUTF8 = false) { + if (value == nullptr) return ""; - // Not sure how to handle unicode... - if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && - !containsControlCharacter0(value, length)) - return std::string("\"") + value + "\""; + + if (!doesAnyCharRequireEscaping(value, length)) + return String("\"") + value + "\""; // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. + // Appending to String is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = - length * 2 + 3; // allescaped+quotes+NULL - std::string result; + String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL + String result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; char const* end = value + length; @@ -4260,44 +4375,63 @@ static std::string valueToQuotedStringN(const char* value, unsigned length) { // sequence. // Should add a flag to allow this compatibility mode and prevent this // sequence from occurring. - default: - if ((isControlCharacter(*c)) || (*c == 0)) { - std::ostringstream oss; - oss << "\\u" << std::hex << std::uppercase << std::setfill('0') - << std::setw(4) << static_cast(*c); - result += oss.str(); + default: { + if (emitUTF8) { + unsigned codepoint = static_cast(*c); + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else { + appendRaw(result, codepoint); + } } else { - result += *c; + unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c` + if (codepoint < 0x20) { + appendHex(result, codepoint); + } else if (codepoint < 0x80) { + appendRaw(result, codepoint); + } else if (codepoint < 0x10000) { + // Basic Multilingual Plane + appendHex(result, codepoint); + } else { + // Extended Unicode. Encode 20 bits as a surrogate pair. + codepoint -= 0x10000; + appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff)); + appendHex(result, 0xdc00 + (codepoint & 0x3ff)); + } } - break; + } break; } } result += "\""; return result; } +String valueToQuotedString(const char* value) { + return valueToQuotedStringN(value, static_cast(strlen(value))); +} + // Class Writer // ////////////////////////////////////////////////////////////////// -Writer::~Writer() {} +Writer::~Writer() = default; // Class FastWriter // ////////////////////////////////////////////////////////////////// FastWriter::FastWriter() - : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), - omitEndingLineFeed_(false) {} -void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } + = default; + +void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; } void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } -std::string FastWriter::write(const Value& root) { - document_ = ""; +String FastWriter::write(const Value& root) { + document_.clear(); writeValue(root); if (!omitEndingLineFeed_) - document_ += "\n"; + document_ += '\n'; return document_; } @@ -4316,13 +4450,13 @@ void FastWriter::writeValue(const Value& value) { case realValue: document_ += valueToString(value.asDouble()); break; - case stringValue: - { - // Is NULL possible for value.string_? + case stringValue: { + // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); + if (ok) + document_ += valueToQuotedStringN(str, static_cast(end - str)); break; } case booleanValue: @@ -4330,8 +4464,8 @@ void FastWriter::writeValue(const Value& value) { break; case arrayValue: { document_ += '['; - int size = value.size(); - for (int index = 0; index < size; ++index) { + ArrayIndex size = value.size(); + for (ArrayIndex index = 0; index < size; ++index) { if (index > 0) document_ += ','; writeValue(value[index]); @@ -4341,13 +4475,13 @@ void FastWriter::writeValue(const Value& value) { case objectValue: { Value::Members members(value.getMemberNames()); document_ += '{'; - for (Value::Members::iterator it = members.begin(); it != members.end(); - ++it) { - const std::string& name = *it; + for (auto it = members.begin(); it != members.end(); ++it) { + const String& name = *it; if (it != members.begin()) document_ += ','; - document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); - document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + document_ += valueToQuotedStringN(name.data(), + static_cast(name.length())); + document_ += yamlCompatibilityEnabled_ ? ": " : ":"; writeValue(value[name]); } document_ += '}'; @@ -4358,17 +4492,16 @@ void FastWriter::writeValue(const Value& value) { // Class StyledWriter // ////////////////////////////////////////////////////////////////// -StyledWriter::StyledWriter() - : rightMargin_(74), indentSize_(3), addChildValues_() {} +StyledWriter::StyledWriter() = default; -std::string StyledWriter::write(const Value& root) { - document_ = ""; +String StyledWriter::write(const Value& root) { + document_.clear(); addChildValues_ = false; - indentString_ = ""; + indentString_.clear(); writeCommentBeforeValue(root); writeValue(root); writeCommentAfterValueOnSameLine(root); - document_ += "\n"; + document_ += '\n'; return document_; } @@ -4386,14 +4519,15 @@ void StyledWriter::writeValue(const Value& value) { case realValue: pushValue(valueToString(value.asDouble())); break; - case stringValue: - { - // Is NULL possible for value.string_? + case stringValue: { + // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str))); + else + pushValue(""); break; } case booleanValue: @@ -4409,9 +4543,9 @@ void StyledWriter::writeValue(const Value& value) { else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + auto it = members.begin(); for (;;) { - const std::string& name = *it; + const String& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); @@ -4436,7 +4570,7 @@ void StyledWriter::writeArrayValue(const Value& value) { if (size == 0) pushValue("[]"); else { - bool isArrayMultiLine = isMultineArray(value); + bool isArrayMultiLine = isMultilineArray(value); if (isArrayMultiLine) { writeWithIndent("["); indent(); @@ -4474,26 +4608,26 @@ void StyledWriter::writeArrayValue(const Value& value) { } } -bool StyledWriter::isMultineArray(const Value& value) { - int size = value.size(); +bool StyledWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); + !childValue.empty()); } if (!isMultiLine) // check if line length > max line length { childValues_.reserve(size); addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (int index = 0; index < size; ++index) { + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { if (hasCommentForValue(value[index])) { isMultiLine = true; } writeValue(value[index]); - lineLength += int(childValues_[index].length()); + lineLength += static_cast(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; @@ -4501,7 +4635,7 @@ bool StyledWriter::isMultineArray(const Value& value) { return isMultiLine; } -void StyledWriter::pushValue(const std::string& value) { +void StyledWriter::pushValue(const String& value) { if (addChildValues_) childValues_.push_back(value); else @@ -4519,15 +4653,15 @@ void StyledWriter::writeIndent() { document_ += indentString_; } -void StyledWriter::writeWithIndent(const std::string& value) { +void StyledWriter::writeWithIndent(const String& value) { writeIndent(); document_ += value; } -void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } +void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); } void StyledWriter::unindent() { - assert(int(indentString_.size()) >= indentSize_); + assert(indentString_.size() >= indentSize_); indentString_.resize(indentString_.size() - indentSize_); } @@ -4535,20 +4669,19 @@ void StyledWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; - document_ += "\n"; + document_ += '\n'; writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); while (iter != comment.end()) { document_ += *iter; - if (*iter == '\n' && - (iter != comment.end() && *(iter + 1) == '/')) + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) writeIndent(); ++iter; } // Comments are stripped of trailing newlines, so add one here - document_ += "\n"; + document_ += '\n'; } void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { @@ -4556,9 +4689,9 @@ void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { document_ += " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { - document_ += "\n"; + document_ += '\n'; document_ += root.getComment(commentAfter); - document_ += "\n"; + document_ += '\n'; } } @@ -4571,22 +4704,23 @@ bool StyledWriter::hasCommentForValue(const Value& value) { // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// -StyledStreamWriter::StyledStreamWriter(std::string indentation) - : document_(NULL), rightMargin_(74), indentation_(indentation), - addChildValues_() {} +StyledStreamWriter::StyledStreamWriter(String indentation) + : document_(nullptr), indentation_(std::move(indentation)), + addChildValues_(), indented_(false) {} -void StyledStreamWriter::write(std::ostream& out, const Value& root) { +void StyledStreamWriter::write(OStream& out, const Value& root) { document_ = &out; addChildValues_ = false; - indentString_ = ""; + indentString_.clear(); indented_ = true; writeCommentBeforeValue(root); - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *document_ << "\n"; - document_ = NULL; // Forget the stream, for safety. + document_ = nullptr; // Forget the stream, for safety. } void StyledStreamWriter::writeValue(const Value& value) { @@ -4603,14 +4737,15 @@ void StyledStreamWriter::writeValue(const Value& value) { case realValue: pushValue(valueToString(value.asDouble())); break; - case stringValue: - { - // Is NULL possible for value.string_? + case stringValue: { + // Is NULL possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str))); + else + pushValue(""); break; } case booleanValue: @@ -4626,9 +4761,9 @@ void StyledStreamWriter::writeValue(const Value& value) { else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + auto it = members.begin(); for (;;) { - const std::string& name = *it; + const String& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); @@ -4653,7 +4788,7 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { if (size == 0) pushValue("[]"); else { - bool isArrayMultiLine = isMultineArray(value); + bool isArrayMultiLine = isMultilineArray(value); if (isArrayMultiLine) { writeWithIndent("["); indent(); @@ -4665,7 +4800,8 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { if (hasChildValue) writeWithIndent(childValues_[index]); else { - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; @@ -4693,26 +4829,26 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { } } -bool StyledStreamWriter::isMultineArray(const Value& value) { - int size = value.size(); +bool StyledStreamWriter::isMultilineArray(const Value& value) { + ArrayIndex const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); + !childValue.empty()); } if (!isMultiLine) // check if line length > max line length { childValues_.reserve(size); addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (int index = 0; index < size; ++index) { + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { if (hasCommentForValue(value[index])) { isMultiLine = true; } writeValue(value[index]); - lineLength += int(childValues_[index].length()); + lineLength += static_cast(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; @@ -4720,7 +4856,7 @@ bool StyledStreamWriter::isMultineArray(const Value& value) { return isMultiLine; } -void StyledStreamWriter::pushValue(const std::string& value) { +void StyledStreamWriter::pushValue(const String& value) { if (addChildValues_) childValues_.push_back(value); else @@ -4735,8 +4871,9 @@ void StyledStreamWriter::writeIndent() { *document_ << '\n' << indentString_; } -void StyledStreamWriter::writeWithIndent(const std::string& value) { - if (!indented_) writeIndent(); +void StyledStreamWriter::writeWithIndent(const String& value) { + if (!indented_) + writeIndent(); *document_ << value; indented_ = false; } @@ -4752,13 +4889,13 @@ void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; - if (!indented_) writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); while (iter != comment.end()) { *document_ << *iter; - if (*iter == '\n' && - (iter != comment.end() && *(iter + 1) == '/')) + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would include newline *document_ << indentString_; ++iter; @@ -4790,84 +4927,73 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { struct CommentStyle { /// Decide whether to write comments. enum Enum { - None, ///< Drop all comments. - Most, ///< Recover odd behavior of previous versions (not implemented yet). - All ///< Keep all comments. + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. }; }; -struct BuiltStyledStreamWriter : public StreamWriter -{ - BuiltStyledStreamWriter( - std::string const& indentation, - CommentStyle::Enum cs, - std::string const& colonSymbol, - std::string const& nullSymbol, - std::string const& endingLineFeedSymbol, - bool useSpecialFloats, - unsigned int precision); - int write(Value const& root, std::ostream* sout) override; +struct BuiltStyledStreamWriter : public StreamWriter { + BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs, + String colonSymbol, String nullSymbol, + String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, + PrecisionType precisionType); + int write(Value const& root, OStream* sout) override; + private: void writeValue(Value const& value); void writeArrayValue(Value const& value); - bool isMultineArray(Value const& value); - void pushValue(std::string const& value); + bool isMultilineArray(Value const& value); + void pushValue(String const& value); void writeIndent(); - void writeWithIndent(std::string const& value); + void writeWithIndent(String const& value); void indent(); void unindent(); void writeCommentBeforeValue(Value const& root); void writeCommentAfterValueOnSameLine(Value const& root); static bool hasCommentForValue(const Value& value); - typedef std::vector ChildValues; + using ChildValues = std::vector; ChildValues childValues_; - std::string indentString_; - int rightMargin_; - std::string indentation_; + String indentString_; + unsigned int rightMargin_; + String indentation_; CommentStyle::Enum cs_; - std::string colonSymbol_; - std::string nullSymbol_; - std::string endingLineFeedSymbol_; + String colonSymbol_; + String nullSymbol_; + String endingLineFeedSymbol_; bool addChildValues_ : 1; bool indented_ : 1; bool useSpecialFloats_ : 1; + bool emitUTF8_ : 1; unsigned int precision_; + PrecisionType precisionType_; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( - std::string const& indentation, - CommentStyle::Enum cs, - std::string const& colonSymbol, - std::string const& nullSymbol, - std::string const& endingLineFeedSymbol, - bool useSpecialFloats, - unsigned int precision) - : rightMargin_(74) - , indentation_(indentation) - , cs_(cs) - , colonSymbol_(colonSymbol) - , nullSymbol_(nullSymbol) - , endingLineFeedSymbol_(endingLineFeedSymbol) - , addChildValues_(false) - , indented_(false) - , useSpecialFloats_(useSpecialFloats) - , precision_(precision) -{ -} -int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) -{ + String indentation, CommentStyle::Enum cs, String colonSymbol, + String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats, + bool emitUTF8, unsigned int precision, PrecisionType precisionType) + : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs), + colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)), + endingLineFeedSymbol_(std::move(endingLineFeedSymbol)), + addChildValues_(false), indented_(false), + useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8), + precision_(precision), precisionType_(precisionType) {} +int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) { sout_ = sout; addChildValues_ = false; indented_ = true; - indentString_ = ""; + indentString_.clear(); writeCommentBeforeValue(root); - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *sout_ << endingLineFeedSymbol_; - sout_ = NULL; + sout_ = nullptr; return 0; } void BuiltStyledStreamWriter::writeValue(Value const& value) { @@ -4882,16 +5008,19 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { pushValue(valueToString(value.asLargestUInt())); break; case realValue: - pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_, + precisionType_)); break; - case stringValue: - { - // Is NULL is possible for value.string_? + case stringValue: { + // Is NULL is possible for value.string_? No. char const* str; char const* end; bool ok = value.getString(&str, &end); - if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); - else pushValue(""); + if (ok) + pushValue(valueToQuotedStringN(str, static_cast(end - str), + emitUTF8_)); + else + pushValue(""); break; } case booleanValue: @@ -4907,12 +5036,13 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) { else { writeWithIndent("{"); indent(); - Value::Members::iterator it = members.begin(); + auto it = members.begin(); for (;;) { - std::string const& name = *it; + String const& name = *it; Value const& childValue = value[name]; writeCommentBeforeValue(childValue); - writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); + writeWithIndent(valueToQuotedStringN( + name.data(), static_cast(name.length()), emitUTF8_)); *sout_ << colonSymbol_; writeValue(childValue); if (++it == members.end()) { @@ -4934,7 +5064,7 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { if (size == 0) pushValue("[]"); else { - bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); + bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value); if (isMultiLine) { writeWithIndent("["); indent(); @@ -4946,7 +5076,8 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { if (hasChildValue) writeWithIndent(childValues_[index]); else { - if (!indented_) writeIndent(); + if (!indented_) + writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; @@ -4964,38 +5095,40 @@ void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { { assert(childValues_.size() == size); *sout_ << "["; - if (!indentation_.empty()) *sout_ << " "; + if (!indentation_.empty()) + *sout_ << " "; for (unsigned index = 0; index < size; ++index) { if (index > 0) - *sout_ << ", "; + *sout_ << ((!indentation_.empty()) ? ", " : ","); *sout_ << childValues_[index]; } - if (!indentation_.empty()) *sout_ << " "; + if (!indentation_.empty()) + *sout_ << " "; *sout_ << "]"; } } } -bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { - int size = value.size(); +bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) { + ArrayIndex const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { Value const& childValue = value[index]; isMultiLine = ((childValue.isArray() || childValue.isObject()) && - childValue.size() > 0); + !childValue.empty()); } if (!isMultiLine) // check if line length > max line length { childValues_.reserve(size); addChildValues_ = true; - int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' - for (int index = 0; index < size; ++index) { + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { if (hasCommentForValue(value[index])) { isMultiLine = true; } writeValue(value[index]); - lineLength += int(childValues_[index].length()); + lineLength += static_cast(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; @@ -5003,7 +5136,7 @@ bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { return isMultiLine; } -void BuiltStyledStreamWriter::pushValue(std::string const& value) { +void BuiltStyledStreamWriter::pushValue(String const& value) { if (addChildValues_) childValues_.push_back(value); else @@ -5022,8 +5155,9 @@ void BuiltStyledStreamWriter::writeIndent() { } } -void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { - if (!indented_) writeIndent(); +void BuiltStyledStreamWriter::writeWithIndent(String const& value) { + if (!indented_) + writeIndent(); *sout_ << value; indented_ = false; } @@ -5036,17 +5170,18 @@ void BuiltStyledStreamWriter::unindent() { } void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { - if (cs_ == CommentStyle::None) return; + if (cs_ == CommentStyle::None) + return; if (!root.hasComment(commentBefore)) return; - if (!indented_) writeIndent(); - const std::string& comment = root.getComment(commentBefore); - std::string::const_iterator iter = comment.begin(); + if (!indented_) + writeIndent(); + const String& comment = root.getComment(commentBefore); + String::const_iterator iter = comment.begin(); while (iter != comment.end()) { *sout_ << *iter; - if (*iter == '\n' && - (iter != comment.end() && *(iter + 1) == '/')) + if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would write extra newline *sout_ << indentString_; ++iter; @@ -5054,8 +5189,10 @@ void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { indented_ = false; } -void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { - if (cs_ == CommentStyle::None) return; +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine( + Value const& root) { + if (cs_ == CommentStyle::None) + return; if (root.hasComment(commentAfterOnSameLine)) *sout_ << " " + root.getComment(commentAfterOnSameLine); @@ -5075,28 +5212,19 @@ bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { /////////////// // StreamWriter -StreamWriter::StreamWriter() - : sout_(NULL) -{ -} -StreamWriter::~StreamWriter() -{ -} -StreamWriter::Factory::~Factory() -{} -StreamWriterBuilder::StreamWriterBuilder() -{ - setDefaults(&settings_); -} -StreamWriterBuilder::~StreamWriterBuilder() -{} -StreamWriter* StreamWriterBuilder::newStreamWriter() const -{ - std::string indentation = settings_["indentation"].asString(); - std::string cs_str = settings_["commentStyle"].asString(); - bool eyc = settings_["enableYAMLCompatibility"].asBool(); - bool dnp = settings_["dropNullPlaceholders"].asBool(); - bool usf = settings_["useSpecialFloats"].asBool(); +StreamWriter::StreamWriter() : sout_(nullptr) {} +StreamWriter::~StreamWriter() = default; +StreamWriter::Factory::~Factory() = default; +StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } +StreamWriterBuilder::~StreamWriterBuilder() = default; +StreamWriter* StreamWriterBuilder::newStreamWriter() const { + const String indentation = settings_["indentation"].asString(); + const String cs_str = settings_["commentStyle"].asString(); + const String pt_str = settings_["precisionType"].asString(); + const bool eyc = settings_["enableYAMLCompatibility"].asBool(); + const bool dnp = settings_["dropNullPlaceholders"].asBool(); + const bool usf = settings_["useSpecialFloats"].asBool(); + const bool emitUTF8 = settings_["emitUTF8"].asBool(); unsigned int pre = settings_["precision"].asUInt(); CommentStyle::Enum cs = CommentStyle::All; if (cs_str == "All") { @@ -5106,74 +5234,80 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const } else { throwRuntimeError("commentStyle must be 'All' or 'None'"); } - std::string colonSymbol = " : "; + PrecisionType precisionType(significantDigits); + if (pt_str == "significant") { + precisionType = PrecisionType::significantDigits; + } else if (pt_str == "decimal") { + precisionType = PrecisionType::decimalPlaces; + } else { + throwRuntimeError("precisionType must be 'significant' or 'decimal'"); + } + String colonSymbol = " : "; if (eyc) { colonSymbol = ": "; } else if (indentation.empty()) { colonSymbol = ":"; } - std::string nullSymbol = "null"; + String nullSymbol = "null"; if (dnp) { - nullSymbol = ""; + nullSymbol.clear(); } - if (pre > 17) pre = 17; - std::string endingLineFeedSymbol = ""; - return new BuiltStyledStreamWriter( - indentation, cs, - colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); + if (pre > 17) + pre = 17; + String endingLineFeedSymbol; + return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol, + endingLineFeedSymbol, usf, emitUTF8, pre, + precisionType); } -static void getValidWriterKeys(std::set* valid_keys) -{ - valid_keys->clear(); - valid_keys->insert("indentation"); - valid_keys->insert("commentStyle"); - valid_keys->insert("enableYAMLCompatibility"); - valid_keys->insert("dropNullPlaceholders"); - valid_keys->insert("useSpecialFloats"); - valid_keys->insert("precision"); -} -bool StreamWriterBuilder::validate(Json::Value* invalid) const -{ - Json::Value my_invalid; - if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL - Json::Value& inv = *invalid; - std::set valid_keys; - getValidWriterKeys(&valid_keys); - Value::Members keys = settings_.getMemberNames(); - size_t n = keys.size(); - for (size_t i = 0; i < n; ++i) { - std::string const& key = keys[i]; - if (valid_keys.find(key) == valid_keys.end()) { - inv[key] = settings_[key]; - } + +bool StreamWriterBuilder::validate(Json::Value* invalid) const { + static const auto& valid_keys = *new std::set{ + "indentation", + "commentStyle", + "enableYAMLCompatibility", + "dropNullPlaceholders", + "useSpecialFloats", + "emitUTF8", + "precision", + "precisionType", + }; + for (auto si = settings_.begin(); si != settings_.end(); ++si) { + auto key = si.name(); + if (valid_keys.count(key)) + continue; + if (invalid) + (*invalid)[std::move(key)] = *si; + else + return false; } - return 0u == inv.size(); + return invalid ? invalid->empty() : true; } -Value& StreamWriterBuilder::operator[](std::string key) -{ + +Value& StreamWriterBuilder::operator[](const String& key) { return settings_[key]; } // static -void StreamWriterBuilder::setDefaults(Json::Value* settings) -{ +void StreamWriterBuilder::setDefaults(Json::Value* settings) { //! [StreamWriterBuilderDefaults] (*settings)["commentStyle"] = "All"; (*settings)["indentation"] = "\t"; (*settings)["enableYAMLCompatibility"] = false; (*settings)["dropNullPlaceholders"] = false; (*settings)["useSpecialFloats"] = false; + (*settings)["emitUTF8"] = false; (*settings)["precision"] = 17; + (*settings)["precisionType"] = "significant"; //! [StreamWriterBuilderDefaults] } -std::string writeString(StreamWriter::Factory const& builder, Value const& root) { - std::ostringstream sout; - StreamWriterPtr const writer(builder.newStreamWriter()); +String writeString(StreamWriter::Factory const& factory, Value const& root) { + OStringStream sout; + StreamWriterPtr const writer(factory.newStreamWriter()); writer->write(root, &sout); return sout.str(); } -std::ostream& operator<<(std::ostream& sout, Value const& root) { +OStream& operator<<(OStream& sout, Value const& root) { StreamWriterBuilder builder; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); diff --git a/src/third_party/r8b/CDSPBlockConvolver.h b/src/third_party/r8b/CDSPBlockConvolver.h deleted file mode 100644 index fe27eaa..0000000 --- a/src/third_party/r8b/CDSPBlockConvolver.h +++ /dev/null @@ -1,646 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file CDSPBlockConvolver.h - * - * @brief Single-block overlap-save convolution processor class. - * - * This file includes single-block overlap-save convolution processor class. - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8B_CDSPBLOCKCONVOLVER_INCLUDED -#define R8B_CDSPBLOCKCONVOLVER_INCLUDED - -#include "CDSPFIRFilter.h" -#include "CDSPProcessor.h" - -namespace r8b { - -/** - * @brief Single-block overlap-save convolution processing class. - * - * Class that implements single-block overlap-save convolution processing. The - * length of a single FFT block used depends on the length of the filter - * kernel. - * - * The rationale behind "single-block" processing is that increasing the FFT - * block length by 2 is more efficient than performing convolution at the same - * FFT block length but using two blocks. - * - * This class also implements a built-in resampling by any whole-number - * factor, which simplifies the overall resampling objects topology. - */ - -class CDSPBlockConvolver : public CDSPProcessor -{ -public: - /** - * Constructor initializes internal variables and constants of *this - * object. - * - * @param aFilter Pre-calculated filter data. Reference to this object is - * inhertied by *this object, and the object will be released when *this - * object is destroyed. If upsampling is used, filter's gain should be - * equal to the upsampling factor. - * @param aUpFactor The upsampling factor, positive value. E.g. value of 2 - * means 2x upsampling should be performed over the input data. - * @param aDownFactor The downsampling factor, positive value. E.g. value - * of 2 means 2x downsampling should be performed over the output data. - * @param PrevLatency Latency, in samples (any value >=0), which was left - * in the output signal by a previous process. This value is usually - * non-zero if the minimum-phase filters are in use. This value is always - * zero if the linear-phase filters are in use. - * @param aDoConsumeLatency "True" if the output latency should be - * consumed. Does not apply to the fractional part of the latency (if such - * part is available). - */ - - CDSPBlockConvolver( CDSPFIRFilter& aFilter, const int aUpFactor, - const int aDownFactor, const double PrevLatency = 0.0, - const bool aDoConsumeLatency = true ) - : Filter( &aFilter ) - , UpFactor( aUpFactor ) - , DownFactor( aDownFactor ) - , DoConsumeLatency( aDoConsumeLatency ) - , BlockLen2( 2 << Filter -> getBlockLenBits() ) - { - R8BASSERT( UpFactor > 0 ); - R8BASSERT( DownFactor > 0 ); - R8BASSERT( PrevLatency >= 0.0 ); - - int fftinBits; - UpShift = getBitOccupancy( UpFactor ) - 1; - - if(( 1 << UpShift ) == UpFactor ) - { - fftinBits = Filter -> getBlockLenBits() + 1 - UpShift; - PrevInputLen = ( Filter -> getKernelLen() - 1 + UpFactor - 1 ) / - UpFactor; - - InputLen = BlockLen2 - PrevInputLen * UpFactor; - } - else - { - UpShift = -1; - fftinBits = Filter -> getBlockLenBits() + 1; - PrevInputLen = Filter -> getKernelLen() - 1; - InputLen = BlockLen2 - PrevInputLen; - } - - OutOffset = ( Filter -> isZeroPhase() ? Filter -> getLatency() : 0 ); - LatencyFrac = Filter -> getLatencyFrac() + PrevLatency * UpFactor; - Latency = (int) LatencyFrac; - const int InLatency = Latency + Filter -> getLatency() - OutOffset; - LatencyFrac -= Latency; - LatencyFrac /= DownFactor; - - Latency += InputLen + Filter -> getLatency(); - - int fftoutBits; - InputDelay = 0; - UpSkipInit = 0; - DownSkipInit = 0; - DownShift = getBitOccupancy( DownFactor ) - 1; - - if(( 1 << DownShift ) == DownFactor ) - { - fftoutBits = Filter -> getBlockLenBits() + 1 - DownShift; - - if( DownFactor > 1 ) - { - if( UpShift > 0 ) - { - // This case never happens in practice due to mutual - // exclusion of "power of 2" DownFactor and UpFactor - // values. - - R8BASSERT( UpShift == 0 ); - } - else - { - // Make sure InputLen is divisible by DownFactor. - - const int ilc = InputLen & ( DownFactor - 1 ); - PrevInputLen += ilc; - InputLen -= ilc; - Latency -= ilc; - - // Correct InputDelay for input and filter's latency. - - const int lc = InLatency & ( DownFactor - 1 ); - - if( lc > 0 ) - { - InputDelay = DownFactor - lc; - } - - if( !DoConsumeLatency ) - { - Latency /= DownFactor; - } - } - } - } - else - { - fftoutBits = Filter -> getBlockLenBits() + 1; - DownShift = -1; - - if( !DoConsumeLatency && DownFactor > 1 ) - { - DownSkipInit = Latency % DownFactor; - Latency /= DownFactor; - } - } - - fftin = new CDSPRealFFTKeeper( fftinBits ); - - if( fftoutBits == fftinBits ) - { - fftout = fftin; - } - else - { - ffto2 = new CDSPRealFFTKeeper( fftoutBits ); - fftout = ffto2; - } - - WorkBlocks.alloc( BlockLen2 * 2 + PrevInputLen ); - CurInput = &WorkBlocks[ 0 ]; - CurOutput = &WorkBlocks[ BlockLen2 ]; // CurInput and - // CurOutput are memory-aligned. - PrevInput = &WorkBlocks[ BlockLen2 * 2 ]; - - clear(); - - R8BCONSOLE( "CDSPBlockConvolver: flt_len=%i in_len=%i io=%i/%i " - "fft=%i/%i latency=%i\n", Filter -> getKernelLen(), InputLen, - UpFactor, DownFactor, (*fftin) -> getLen(), (*fftout) -> getLen(), - getLatency() ); - } - - virtual ~CDSPBlockConvolver() - { - Filter -> unref(); - } - - virtual int getLatency() const - { - return( DoConsumeLatency ? 0 : Latency ); - } - - virtual double getLatencyFrac() const - { - return( LatencyFrac ); - } - - virtual int getMaxOutLen( const int MaxInLen ) const - { - R8BASSERT( MaxInLen >= 0 ); - - return(( MaxInLen * UpFactor + DownFactor - 1 ) / DownFactor ); - } - - virtual void clear() - { - memset( &PrevInput[ 0 ], 0, PrevInputLen * sizeof( double )); - - if( DoConsumeLatency ) - { - LatencyLeft = Latency; - } - else - { - LatencyLeft = 0; - - if( DownShift > 0 ) - { - memset( &CurOutput[ 0 ], 0, ( BlockLen2 >> DownShift ) * - sizeof( double )); - } - else - { - memset( &CurOutput[ BlockLen2 - OutOffset ], 0, OutOffset * - sizeof( double )); - - memset( &CurOutput[ 0 ], 0, ( InputLen - OutOffset ) * - sizeof( double )); - } - } - - memset( CurInput, 0, InputDelay * sizeof( double )); - - InDataLeft = InputLen - InputDelay; - UpSkip = UpSkipInit; - DownSkip = DownSkipInit; - } - - virtual int process( double* ip, int l0, double*& op0 ) - { - R8BASSERT( l0 >= 0 ); - R8BASSERT( UpFactor / DownFactor <= 1 || ip != op0 || l0 == 0 ); - - double* op = op0; - int l = l0 * UpFactor; - l0 = 0; - - while( l > 0 ) - { - const int Offs = InputLen - InDataLeft; - - if( l < InDataLeft ) - { - InDataLeft -= l; - - if( UpShift >= 0 ) - { - memcpy( &CurInput[ Offs >> UpShift ], ip, - ( l >> UpShift ) * sizeof( double )); - } - else - { - copyUpsample( ip, &CurInput[ Offs ], l ); - } - - copyToOutput( Offs - OutOffset, op, l, l0 ); - break; - } - - const int b = InDataLeft; - l -= b; - InDataLeft = InputLen; - int ilu; - - if( UpShift >= 0 ) - { - const int bu = b >> UpShift; - memcpy( &CurInput[ Offs >> UpShift ], ip, - bu * sizeof( double )); - - ip += bu; - ilu = InputLen >> UpShift; - } - else - { - copyUpsample( ip, &CurInput[ Offs ], b ); - ilu = InputLen; - } - - const int pil = (int) ( PrevInputLen * sizeof( double )); - memcpy( &CurInput[ ilu ], PrevInput, pil ); - memcpy( PrevInput, &CurInput[ ilu - PrevInputLen ], pil ); - - (*fftin) -> forward( CurInput ); - - if( UpShift > 0 ) - { - #if R8B_FLOATFFT - mirrorInputSpectrum( (float*) CurInput ); - #else // R8B_FLOATFFT - mirrorInputSpectrum( CurInput ); - #endif // R8B_FLOATFFT - } - - if( Filter -> isZeroPhase() ) - { - (*fftout) -> multiplyBlocksZ( Filter -> getKernelBlock(), - CurInput ); - } - else - { - (*fftout) -> multiplyBlocks( Filter -> getKernelBlock(), - CurInput ); - } - - if( DownShift > 0 ) - { - const int z = BlockLen2 >> DownShift; - - #if R8B_FLOATFFT - float* const kb = (float*) Filter -> getKernelBlock(); - float* const p = (float*) CurInput; - #else // R8B_FLOATFFT - const double* const kb = Filter -> getKernelBlock(); - double* const p = CurInput; - #endif // R8B_FLOATFFT - - p[ 1 ] = kb[ z ] * p[ z ] - kb[ z + 1 ] * p[ z + 1 ]; - } - - (*fftout) -> inverse( CurInput ); - - copyToOutput( Offs - OutOffset, op, b, l0 ); - - double* const tmp = CurInput; - CurInput = CurOutput; - CurOutput = tmp; - } - - return( l0 ); - } - -private: - CDSPFIRFilter* Filter; ///< Filter in use. - ///< - CPtrKeeper< CDSPRealFFTKeeper* > fftin; ///< FFT object 1, used to produce - ///< the input spectrum (can embed the "power of 2" upsampling). - ///< - CPtrKeeper< CDSPRealFFTKeeper* > ffto2; ///< FFT object 2 (can be NULL). - ///< - CDSPRealFFTKeeper* fftout; ///< FFT object used to produce the output - ///< signal (can embed the "power of 2" downsampling), may point to - ///< either "fftin" or "ffto2". - ///< - int UpFactor; ///< Upsampling factor. - ///< - int DownFactor; ///< Downsampling factor. - ///< - bool DoConsumeLatency; ///< "True" if the output latency should be - ///< consumed. Does not apply to the fractional part of the latency - ///< (if such part is available). - ///< - int BlockLen2; ///< Equals block length * 2. - ///< - int OutOffset; ///< Output offset, depends on filter's introduced latency. - ///< - int PrevInputLen; ///< The length of previous input data saved, used for - ///< overlap. - ///< - int InputLen; ///< The number of input samples that should be accumulated - ///< before the input block is processed. - ///< - int Latency; ///< Processing latency, in samples. - ///< - double LatencyFrac; ///< Fractional latency, in samples, that is left in - ///< the output signal. - ///< - int UpShift; ///< "Power of 2" upsampling shift. Equals -1 if UpFactor is - ///< not a "power of 2" value. Equals 0 if UpFactor equals 1. - ///< - int DownShift; ///< "Power of 2" downsampling shift. Equals -1 if - ///< DownFactor is not a "power of 2". Equals 0 if DownFactor equals - ///< 1. - ///< - int InputDelay; ///< Additional input delay, in samples. Used to make the - ///< output delay divisible by DownShift. Used only if UpShift <= 0 - ///< and DownShift > 0. - ///< - CFixedBuffer< double > WorkBlocks; ///< Previous input data, input and - ///< output data blocks, overall capacity = BlockLen2 * 2 + - ///< PrevInputLen. Used in the flip-flop manner. - ///< - double* PrevInput; ///< Previous input data buffer, capacity = BlockLen. - ///< - double* CurInput; ///< Input data buffer, capacity = BlockLen2. - ///< - double* CurOutput; ///< Output data buffer, capacity = BlockLen2. - ///< - int InDataLeft; ///< Samples left before processing input and output FFT - ///< blocks. Initialized to InputLen on clear. - ///< - int LatencyLeft; ///< Latency in samples left to skip. - ///< - int UpSkip; ///< The current upsampling sample skip (value in the range - ///< 0 to UpFactor - 1). - ///< - int UpSkipInit; ///< The initial UpSkip value after clear(). - ///< - int DownSkip; ///< The current downsampling sample skip (value in the - ///< range 0 to DownFactor - 1). Not used if DownShift > 0. - ///< - int DownSkipInit; ///< The initial DownSkip value after clear(). - ///< - - /** - * Function copies samples from the input buffer to the output buffer - * while inserting zeros inbetween them to perform the whole-numbered - * upsampling. - * - * @param[in,out] ip0 Input buffer. Will be advanced on function's return. - * @param[out] op Output buffer. - * @param l0 The number of samples to fill in the output buffer, including - * both input samples and interpolation (zero) samples. - */ - - void copyUpsample( double*& ip0, double* op, int l0 ) - { - int b = min( UpSkip, l0 ); - - if( b > 0 ) - { - l0 -= b; - UpSkip -= b; - *op = 0.0; - op++; - b--; - - while( b > 0 ) - { - *op = 0.0; - op++; - b--; - } - } - - double* ip = ip0; - int l = l0 / UpFactor; - int lz = l0 - l * UpFactor; - const int upf = UpFactor; - - if( upf == 3 ) - { - while( l > 0 ) - { - op[ 0 ] = *ip; - op[ 1 ] = 0.0; - op[ 2 ] = 0.0; - ip++; - op += upf; - l--; - } - } - else - if( upf == 5 ) - { - while( l > 0 ) - { - op[ 0 ] = *ip; - op[ 1 ] = 0.0; - op[ 2 ] = 0.0; - op[ 3 ] = 0.0; - op[ 4 ] = 0.0; - ip++; - op += upf; - l--; - } - } - else - { - while( l > 0 ) - { - op[ 0 ] = *ip; - int j; - - for( j = 1; j < upf; j++ ) - { - op[ j ] = 0.0; - } - - ip++; - op += upf; - l--; - } - } - - if( lz > 0 ) - { - *op = *ip; - op++; - ip++; - UpSkip = UpFactor - lz; - - while( lz > 1 ) - { - *op = 0.0; - op++; - lz--; - } - } - - ip0 = ip; - } - - /** - * Function copies sample data from the CurOutput buffer to the specified - * output buffer and advances its position. If necessary, this function - * "consumes" latency and performs downsampling. - * - * @param Offs CurOutput buffer offset, can be negative. - * @param[out] op0 Output buffer pointer, will be advanced. - * @param b The number of output samples available, including those which - * are discarded during whole-number downsampling. - * @param l0 The overall output sample count, will be increased. - */ - - void copyToOutput( int Offs, double*& op0, int b, int& l0 ) - { - if( Offs < 0 ) - { - if( Offs + b <= 0 ) - { - Offs += BlockLen2; - } - else - { - copyToOutput( Offs + BlockLen2, op0, -Offs, l0 ); - b += Offs; - Offs = 0; - } - } - - if( LatencyLeft > 0 ) - { - if( LatencyLeft >= b ) - { - LatencyLeft -= b; - return; - } - - Offs += LatencyLeft; - b -= LatencyLeft; - LatencyLeft = 0; - } - - const int df = DownFactor; - - if( DownShift > 0 ) - { - int Skip = Offs & ( df - 1 ); - - if( Skip > 0 ) - { - Skip = df - Skip; - b -= Skip; - Offs += Skip; - } - - if( b > 0 ) - { - b = ( b + df - 1 ) >> DownShift; - memcpy( op0, &CurOutput[ Offs >> DownShift ], - b * sizeof( double )); - - op0 += b; - l0 += b; - } - } - else - { - if( df > 1 ) - { - const double* ip = &CurOutput[ Offs + DownSkip ]; - int l = ( b + df - 1 - DownSkip ) / df; - DownSkip += l * df - b; - - double* op = op0; - l0 += l; - op0 += l; - - while( l > 0 ) - { - *op = *ip; - op++; - ip += df; - l--; - } - } - else - { - memcpy( op0, &CurOutput[ Offs ], b * sizeof( double )); - op0 += b; - l0 += b; - } - } - } - - /** - * Function performs input spectrum mirroring which is used to perform a - * fast "power of 2" upsampling. Such mirroring is equivalent to insertion - * of zeros into the input signal. - * - * @param p Spectrum data block to mirror. - */ - - template< class T > - void mirrorInputSpectrum( T* const p ) - { - const int bl1 = BlockLen2 >> UpShift; - const int bl2 = bl1 + bl1; - int i; - - for( i = bl1 + 2; i < bl2; i += 2 ) - { - p[ i ] = p[ bl2 - i ]; - p[ i + 1 ] = -p[ bl2 - i + 1 ]; - } - - p[ bl1 ] = p[ 1 ]; - p[ bl1 + 1 ] = 0.0; - p[ 1 ] = p[ 0 ]; - - for( i = 1; i < UpShift; i++ ) - { - const int z = bl1 << i; - memcpy( &p[ z ], p, z * sizeof( T )); - p[ z + 1 ] = 0.0; - } - } -}; - -} // namespace r8b - -#endif // R8B_CDSPBLOCKCONVOLVER_INCLUDED diff --git a/src/third_party/r8b/CDSPFIRFilter.h b/src/third_party/r8b/CDSPFIRFilter.h deleted file mode 100644 index 8930fcb..0000000 --- a/src/third_party/r8b/CDSPFIRFilter.h +++ /dev/null @@ -1,721 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file CDSPFIRFilter.h - * - * @brief FIR filter generator and filter cache classes. - * - * This file includes low-pass FIR filter generator and filter cache. - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8B_CDSPFIRFILTER_INCLUDED -#define R8B_CDSPFIRFILTER_INCLUDED - -#include "CDSPSincFilterGen.h" -#include "CDSPRealFFT.h" - -namespace r8b { - -/** - * Enumeration of filter's phase responses. - */ - -enum EDSPFilterPhaseResponse -{ - fprLinearPhase = 0, ///< Linear-phase response. Features a linear-phase - ///< high-latency response, with the latency expressed as integer - ///< value. - ///< - fprMinPhase ///< Minimum-phase response. Features a minimal latency - ///< response, but the response's phase is non-linear. The latency is - ///< usually expressed as non-integer value, and usually is small, but - ///< is never equal to zero. The minimum-phase filter is transformed - ///< from the linear-phase filter. The transformation has precision - ///< limits which may skew both the -3 dB point and attenuation of the - ///< filter being transformed: as it was measured, the skew happens - ///< purely at random, and in most cases it is within tolerable range. - ///< In a small (1%) random subset of cases the skew is bigger and - ///< cannot be predicted. Minimum-phase transform requires 64-bit - ///< floating point FFT precision, results with 32-bit float FFT are - ///< far from optimal. - ///< -}; - -/** - * @brief Calculation and storage class for FIR filters. - * - * Class that implements calculation and storing of a FIR filter (currently - * contains low-pass filter calculation routine designed for sample rate - * conversion). Objects of this class cannot be created directly, but can be - * obtained via the CDSPFilterCache::getLPFilter() static function. - */ - -class CDSPFIRFilter : public R8B_BASECLASS -{ - R8BNOCTOR( CDSPFIRFilter ); - - friend class CDSPFIRFilterCache; - -public: - ~CDSPFIRFilter() - { - R8BASSERT( RefCount == 0 ); - - delete Next; - } - - /** - * @return The minimal allowed low-pass filter's transition band, in - * percent. - */ - - static double getLPMinTransBand() - { - return( 0.5 ); - } - - /** - * @return The maximal allowed low-pass filter's transition band, in - * percent. - */ - - static double getLPMaxTransBand() - { - return( 45.0 ); - } - - /** - * @return The minimal allowed low-pass filter's stop-band attenuation, in - * decibel. - */ - - static double getLPMinAtten() - { - return( 49.0 ); - } - - /** - * @return The maximal allowed low-pass filter's stop-band attenuation, in - * decibel. - */ - - static double getLPMaxAtten() - { - return( 218.0 ); - } - - /** - * @return "True" if kernel block of *this filter has zero-phase response. - */ - - bool isZeroPhase() const - { - return( IsZeroPhase ); - } - - /** - * @return Filter's latency, in samples (integer part). - */ - - int getLatency() const - { - return( Latency ); - } - - /** - * @return Filter's latency, in samples (fractional part). Always zero for - * linear-phase filters. - */ - - double getLatencyFrac() const - { - return( LatencyFrac ); - } - - /** - * @return Filter kernel length, in samples. Not to be confused with the - * block length. - */ - - int getKernelLen() const - { - return( KernelLen ); - } - - /** - * @return Filter's block length, espressed as Nth power of 2. The actual - * length is twice as large due to zero-padding. - */ - - int getBlockLenBits() const - { - return( BlockLenBits ); - } - - /** - * @return Filter's kernel block, in complex-numbered form obtained via - * the CDSPRealFFT::forward() function call, zero-padded, gain-adjusted - * with the CDSPRealFFT::getInvMulConst() * ReqGain constant, immediately - * suitable for convolution. Kernel block may have "zero-phase" response, - * depending on the isZeroPhase() function's result. - */ - - const double* getKernelBlock() const - { - return( KernelBlock ); - } - - /** - * This function should be called when the filter obtained via the - * filter cache is no longer needed. - */ - - void unref(); - -private: - double ReqNormFreq; ///< Required normalized frequency, 0 to 1 inclusive. - ///< - double ReqTransBand; ///< Required transition band in percent, as passed - ///< by the user. - ///< - double ReqAtten; ///< Required stop-band attenuation in decibel, as passed - ///< by the user (positive value). - ///< - EDSPFilterPhaseResponse ReqPhase; ///< Required filter's phase response. - ///< - double ReqGain; ///< Required overall filter's gain. - ///< - CDSPFIRFilter* Next; ///< Next FIR filter in cache's list. - ///< - int RefCount; ///< The number of references made to *this FIR filter. - ///< - bool IsZeroPhase; ///< "True" if kernel block of *this filter has - ///< zero-phase response. - ///< - int Latency; ///< Filter's latency in samples (integer part). - ///< - double LatencyFrac; ///< Filter's latency in samples (fractional part). - ///< - int KernelLen; ///< Filter kernel length, in samples. - ///< - int BlockLenBits; ///< Block length used to store *this FIR filter, - ///< expressed as Nth power of 2. This value is used directly by the - ///< convolver. - ///< - CFixedBuffer< double > KernelBlock; ///< FIR filter buffer, capacity - ///< equals to 1 << ( BlockLenBits + 1 ). Second part of the buffer - ///< contains zero-padding to allow alias-free convolution. - ///< Memory-aligned. - ///< - - CDSPFIRFilter() - : RefCount( 1 ) - { - } - - /** - * Function builds filter kernel based on the "Req" parameters. - * - * @param ExtAttenCorrs External attentuation correction table, for - * internal use. - */ - - void buildLPFilter( const double* const ExtAttenCorrs ) - { - const double tb = ReqTransBand * 0.01; - double pwr; - double fo1; - double hl; - double atten = -ReqAtten; - - if( tb >= 0.25 ) - { - if( ReqAtten >= 117.0 ) - { - atten -= 1.60; - } - else - if( ReqAtten >= 60.0 ) - { - atten -= 1.91; - } - else - { - atten -= 2.25; - } - } - else - if( tb >= 0.10 ) - { - if( ReqAtten >= 117.0 ) - { - atten -= 0.69; - } - else - if( ReqAtten >= 60.0 ) - { - atten -= 0.73; - } - else - { - atten -= 1.13; - } - } - else - { - if( ReqAtten >= 117.0 ) - { - atten -= 0.21; - } - else - if( ReqAtten >= 60.0 ) - { - atten -= 0.25; - } - else - { - atten -= 0.36; - } - } - - static const int AttenCorrCount = 264; - static const double AttenCorrMin = 49.0; - static const double AttenCorrDiff = 176.25; - int AttenCorr = (int) floor(( -atten - AttenCorrMin ) * - AttenCorrCount / AttenCorrDiff + 0.5 ); - - AttenCorr = min( AttenCorrCount, max( 0, AttenCorr )); - - if( ExtAttenCorrs != NULL ) - { - atten -= ExtAttenCorrs[ AttenCorr ]; - } - else - if( tb >= 0.25 ) - { - static const double AttenCorrScale = 101.0; - static const signed char AttenCorrs[] = { - -127, -127, -125, -125, -122, -119, -115, -110, -104, -97, - -91, -82, -75, -24, -16, -6, 4, 14, 24, 29, 30, 32, 37, 44, - 51, 57, 63, 67, 65, 50, 53, 56, 58, 60, 63, 64, 66, 68, 74, - 77, 78, 78, 78, 79, 79, 60, 60, 60, 61, 59, 52, 47, 41, 36, - 30, 24, 17, 9, 0, -8, -10, -11, -14, -13, -18, -25, -31, -38, - -44, -50, -57, -63, -68, -74, -81, -89, -96, -101, -104, -107, - -109, -110, -86, -84, -85, -82, -80, -77, -73, -67, -62, -55, - -48, -42, -35, -30, -20, -11, -2, 5, 6, 6, 7, 11, 16, 21, 26, - 34, 41, 46, 49, 52, 55, 56, 48, 49, 51, 51, 52, 52, 52, 52, - 52, 51, 51, 50, 47, 47, 50, 48, 46, 42, 38, 35, 31, 27, 24, - 20, 16, 12, 11, 12, 10, 8, 4, -1, -6, -11, -16, -19, -17, -21, - -24, -27, -32, -34, -37, -38, -40, -41, -40, -40, -42, -41, - -44, -45, -43, -41, -34, -31, -28, -24, -21, -18, -14, -10, - -5, -1, 2, 5, 8, 7, 4, 3, 2, 2, 4, 6, 8, 9, 9, 10, 10, 10, 10, - 9, 8, 9, 11, 14, 13, 12, 11, 10, 8, 7, 6, 5, 3, 2, 2, -1, -1, - -3, -3, -4, -4, -5, -4, -6, -7, -9, -5, -1, -1, 0, 1, 0, -2, - -3, -4, -5, -5, -8, -13, -13, -13, -12, -13, -12, -11, -11, - -9, -8, -7, -5, -3, -1, 2, 4, 6, 9, 10, 11, 14, 18, 21, 24, - 27, 30, 34, 37, 37, 39, 40 }; - - atten -= AttenCorrs[ AttenCorr ] / AttenCorrScale; - } - else - if( tb >= 0.10 ) - { - static const double AttenCorrScale = 210.0; - static const signed char AttenCorrs[] = { - -113, -118, -122, -125, -126, -97, -95, -92, -92, -89, -82, - -75, -69, -48, -42, -36, -30, -22, -14, -5, -2, 1, 6, 13, 22, - 28, 35, 41, 48, 55, 56, 56, 61, 65, 71, 77, 81, 83, 85, 85, - 74, 74, 73, 72, 71, 70, 68, 64, 59, 56, 49, 52, 46, 42, 36, - 32, 26, 20, 13, 7, -2, -6, -10, -15, -20, -27, -33, -38, -44, - -43, -48, -53, -57, -63, -69, -73, -75, -79, -81, -74, -76, - -77, -77, -78, -81, -80, -80, -78, -76, -65, -62, -59, -56, - -51, -48, -44, -38, -33, -25, -19, -13, -5, -1, 2, 7, 13, 17, - 21, 25, 30, 35, 40, 45, 50, 53, 56, 57, 55, 58, 59, 62, 64, - 67, 67, 68, 68, 62, 61, 61, 59, 59, 57, 57, 55, 52, 48, 42, - 38, 35, 31, 26, 20, 15, 13, 10, 7, 3, -2, -8, -13, -17, -23, - -28, -34, -37, -40, -41, -45, -48, -50, -53, -57, -59, -62, - -63, -63, -57, -57, -56, -56, -54, -54, -53, -49, -48, -41, - -38, -33, -31, -26, -23, -18, -12, -9, -7, -7, -3, 0, 5, 9, - 14, 16, 20, 22, 21, 23, 25, 27, 28, 29, 34, 33, 35, 33, 31, - 30, 29, 29, 26, 26, 25, 24, 20, 19, 15, 10, 8, 4, 1, -2, -6, - -10, -16, -19, -23, -26, -27, -30, -34, -39, -43, -47, -51, - -52, -54, -56, -58, -59, -62, -63, -66, -65, -65, -64, -59, - -57, -54, -52, -48, -44, -42, -37, -32, -22, -17, -10, -3, 5, - 13, 22, 30, 40, 50, 60, 72 }; - - atten -= AttenCorrs[ AttenCorr ] / AttenCorrScale; - } - else - { - static const double AttenCorrScale = 196.0; - static const signed char AttenCorrs[] = { - -15, -17, -20, -20, -20, -21, -20, -16, -17, -18, -17, -13, - -12, -11, -9, -7, -5, -4, -1, 1, 3, 4, 5, 6, 7, 9, 9, 10, 10, - 10, 11, 11, 11, 12, 12, 12, 10, 11, 10, 10, 8, 10, 11, 10, 11, - 11, 13, 14, 15, 19, 27, 26, 23, 18, 14, 8, 4, -2, -6, -12, - -17, -23, -28, -33, -37, -42, -46, -49, -53, -57, -60, -61, - -64, -65, -67, -66, -66, -66, -65, -64, -61, -59, -56, -52, - -48, -42, -38, -31, -27, -19, -13, -7, -1, 8, 14, 22, 29, 37, - 45, 52, 59, 66, 73, 80, 86, 91, 96, 100, 104, 108, 111, 114, - 115, 117, 118, 120, 120, 118, 117, 114, 113, 111, 107, 103, - 99, 95, 89, 84, 78, 72, 66, 60, 52, 44, 37, 30, 21, 14, 6, -3, - -11, -18, -26, -34, -43, -51, -58, -65, -73, -78, -85, -90, - -97, -102, -107, -113, -115, -118, -121, -125, -125, -126, - -126, -126, -125, -124, -121, -119, -115, -111, -109, -101, - -102, -95, -88, -81, -73, -67, -63, -54, -47, -40, -33, -26, - -18, -11, -5, 2, 8, 14, 19, 25, 31, 36, 37, 43, 47, 49, 51, - 52, 57, 57, 56, 57, 58, 58, 58, 57, 56, 52, 52, 50, 48, 44, - 41, 39, 37, 33, 31, 26, 24, 21, 18, 14, 11, 8, 4, 2, -2, -5, - -7, -9, -11, -13, -15, -16, -18, -19, -20, -23, -24, -24, -25, - -27, -26, -27, -29, -30, -31, -32, -35, -36, -39, -40, -44, - -46, -51, -54, -59, -63, -69, -76, -83, -91, -98 }; - - atten -= AttenCorrs[ AttenCorr ] / AttenCorrScale; - } - - pwr = 7.43932822146293e-8 * sqr( atten ) + 0.000102747434588003 * - cos( 0.00785021930010397 * atten ) * cos( 0.633854318781239 + - 0.103208573657699 * atten ) - 0.00798132247867036 - - 0.000903555213543865 * atten - 0.0969365532127236 * exp( - 0.0779275237937911 * atten ) - 1.37304948662012e-5 * atten * cos( - 0.00785021930010397 * atten ); - - if( pwr <= 0.067665322581 ) - { - if( tb >= 0.25 ) - { - hl = 2.6778150875894 / tb + 300.547590563091 * atan( atan( - 2.68959772209918 * pwr )) / ( 5.5099277187035 * tb - tb * - tanh( cos( asinh( atten )))); - - fo1 = 0.987205355829873 * tb + 1.00011788929851 * atan2( - -0.321432067051302 - 6.19131357321578 * sqrt( pwr ), - hl + -1.14861472207245 / ( hl - 14.1821147585957 ) + pow( - 0.9521145021664, pow( atan2( 1.12018764830637, tb ), - 2.10988901686912 * hl - 20.9691278378345 ))); - } - else - if( tb >= 0.10 ) - { - hl = ( 1.56688617018066 + 142.064321294568 * pwr + - 0.00419441117131136 * cos( 243.633511747297 * pwr ) - - 0.022953443903576 * atten - 0.026629568860284 * cos( - 127.715550622571 * pwr )) / tb; - - fo1 = 0.982299356642411 * tb + 0.999441744774215 * asinh(( - -0.361783054039583 - 5.80540593623676 * sqrt( pwr )) / - hl ); - } - else - { - hl = ( 2.45739657014937 + 269.183679500541 * pwr * cos( - 5.73225668178813 + atan2( cosh( 0.988861169868941 - - 17.2201556280744 * pwr ), 1.08340138240431 * pwr ))) / tb; - - fo1 = 2.291956939 * tb + 0.01942450693 * sqr( tb ) * hl - - 4.67538973161837 * pwr * tb - 1.668433124 * tb * - pow( pwr, pwr ); - } - } - else - { - if( tb >= 0.25 ) - { - hl = ( 1.50258368698213 + 158.556968859477 * asinh( pwr ) * - tanh( 57.9466246871383 * tanh( pwr )) - - 0.0105440479814834 * atten ) / tb; - - fo1 = 0.994024401639321 * tb + ( -0.236282717577215 - - 6.8724924545387 * sqrt( sin( pwr ))) / hl; - } - else - if( tb >= 0.10 ) - { - hl = ( 1.50277377248945 + 158.222625721046 * asinh( pwr ) * - tanh( 1.02875299001715 + 42.072277322604 * pwr ) - - 0.0108380943845632 * atten ) / tb; - - fo1 = 0.992539376734551 * tb + ( -0.251747813037178 - - 6.74159892452584 * sqrt( tanh( tanh( tan( pwr ))))) / hl; - } - else - { - hl = ( 1.15990238966306 * pwr - 5.02124037125213 * sqr( - pwr ) - 0.158676856669827 * atten * cos( 1.1609073390614 * - pwr - 6.33932586197475 * pwr * sqr( pwr ))) / tb; - - fo1 = 0.867344453126885 * tb + 0.052693817907757 * tb * log( - pwr ) + 0.0895511178735932 * tb * atan( 59.7538527741309 * - pwr ) - 0.0745653568081453 * pwr * tb; - } - } - - double WinParams[ 2 ]; - WinParams[ 0 ] = 125.0; - WinParams[ 1 ] = pwr; - - CDSPSincFilterGen sinc; - sinc.Len2 = 0.25 * hl / ReqNormFreq; - sinc.Freq1 = 0.0; - sinc.Freq2 = M_PI * ( 1.0 - fo1 ) * ReqNormFreq; - sinc.initBand( CDSPSincFilterGen :: wftKaiser, WinParams, true ); - - KernelLen = sinc.KernelLen; - BlockLenBits = getBitOccupancy( KernelLen - 1 ) + R8B_EXTFFT; - const int BlockLen = 1 << BlockLenBits; - - KernelBlock.alloc( BlockLen * 2 ); - sinc.generateBand( &KernelBlock[ 0 ], - &CDSPSincFilterGen :: calcWindowKaiser ); - - if( ReqPhase == fprLinearPhase ) - { - IsZeroPhase = true; - Latency = sinc.fl2; - LatencyFrac = 0.0; - } - else - { - IsZeroPhase = false; - double DCGroupDelay; - - calcMinPhaseTransform( &KernelBlock[ 0 ], KernelLen, 16, false, - &DCGroupDelay ); - - Latency = (int) DCGroupDelay; - LatencyFrac = DCGroupDelay - Latency; - } - - CDSPRealFFTKeeper ffto( BlockLenBits + 1 ); - - if( IsZeroPhase ) - { - // Calculate DC gain. - - double s = 0.0; - int i; - - for( i = 0; i < KernelLen; i++ ) - { - s += KernelBlock[ i ]; - } - - s = ffto -> getInvMulConst() * ReqGain / s; - - // Time-shift the filter so that zero-phase response is produced. - // Simultaneously multiply by "s". - - for( i = 0; i <= sinc.fl2; i++ ) - { - KernelBlock[ i ] = KernelBlock[ sinc.fl2 + i ] * s; - } - - for( i = 1; i <= sinc.fl2; i++ ) - { - KernelBlock[ BlockLen * 2 - i ] = KernelBlock[ i ]; - } - - memset( &KernelBlock[ sinc.fl2 + 1 ], 0, - ( BlockLen * 2 - KernelLen ) * sizeof( double )); - } - else - { - normalizeFIRFilter( &KernelBlock[ 0 ], KernelLen, - ffto -> getInvMulConst() * ReqGain ); - - memset( &KernelBlock[ KernelLen ], 0, - ( BlockLen * 2 - KernelLen ) * sizeof( double )); - } - - ffto -> forward( KernelBlock ); - - if( IsZeroPhase ) - { - ffto -> convertToZ( KernelBlock ); - } - - R8BCONSOLE( "CDSPFIRFilter: flt_len=%i latency=%i nfreq=%.4f " - "tb=%.1f att=%.1f gain=%.3f\n", KernelLen, Latency, - ReqNormFreq, ReqTransBand, ReqAtten, ReqGain ); - } -}; - -/** - * @brief FIR filter cache class. - * - * Class that implements cache for calculated FIR filters. The required FIR - * filter should be obtained via the getLPFilter() static function. - */ - -class CDSPFIRFilterCache : public R8B_BASECLASS -{ - R8BNOCTOR( CDSPFIRFilterCache ); - - friend class CDSPFIRFilter; - -public: - /** - * @return The number of filters present in the cache now. This value can - * be monitored for debugging "forgotten" filters. - */ - - static int getObjCount() - { - R8BSYNC( StateSync ); - - return( ObjCount ); - } - - /** - * Function calculates or returns reference to a previously calculated - * (cached) low-pass FIR filter. Note that the real transition band and - * attenuation achieved by the filter varies with the magnitude of the - * required attenuation, and are never 100% exact. - * - * @param ReqNormFreq Required normalized frequency, in the range 0 to 1, - * inclusive. This is the point after which the stop-band spans. - * @param ReqTransBand Required transition band, in percent of the - * 0 to ReqNormFreq spectral bandwidth, in the range - * CDSPFIRFilter::getLPMinTransBand() to - * CDSPFIRFilter::getLPMaxTransBand(), inclusive. The transition band - * specifies the part of the spectrum between the -3 dB and ReqNormFreq - * points. The real resulting -3 dB point varies in the range from -3.00 - * to -3.05 dB, but is generally very close to -3 dB. - * @param ReqAtten Required stop-band attenuation in decibel, in the range - * CDSPFIRFilter::getLPMinAtten() to CDSPFIRFilter::getLPMaxAtten(), - * inclusive. Note that the actual stop-band attenuation of the resulting - * filter may be 0.40-4.46 dB higher. - * @param ReqPhase Required filter's phase response. - * @param ReqGain Required overall filter's gain (1.0 for unity gain). - * @param AttenCorrs Attentuation correction table, to pass to the filter - * generation function. For internal use. - * @return A reference to a new or a previously calculated low-pass FIR - * filter object with the required characteristics. A reference count is - * incremented in the returned filter object which should be released - * after use via the CDSPFIRFilter::unref() function. - */ - - static CDSPFIRFilter& getLPFilter( const double ReqNormFreq, - const double ReqTransBand, const double ReqAtten, - const EDSPFilterPhaseResponse ReqPhase, const double ReqGain, - const double* const AttenCorrs = NULL ) - { - R8BASSERT( ReqNormFreq > 0.0 && ReqNormFreq <= 1.0 ); - R8BASSERT( ReqTransBand >= CDSPFIRFilter :: getLPMinTransBand() ); - R8BASSERT( ReqTransBand <= CDSPFIRFilter :: getLPMaxTransBand() ); - R8BASSERT( ReqAtten >= CDSPFIRFilter :: getLPMinAtten() ); - R8BASSERT( ReqAtten <= CDSPFIRFilter :: getLPMaxAtten() ); - R8BASSERT( ReqGain > 0.0 ); - - R8BSYNC( StateSync ); - - CDSPFIRFilter* PrevObj = NULL; - CDSPFIRFilter* CurObj = Objects; - - while( CurObj != NULL ) - { - if( CurObj -> ReqNormFreq == ReqNormFreq && - CurObj -> ReqTransBand == ReqTransBand && - CurObj -> ReqAtten == ReqAtten && - CurObj -> ReqPhase == ReqPhase && - CurObj -> ReqGain == ReqGain ) - { - break; - } - - if( CurObj -> Next == NULL && ObjCount >= R8B_FILTER_CACHE_MAX ) - { - if( CurObj -> RefCount == 0 ) - { - // Delete the last filter which is not used. - - PrevObj -> Next = NULL; - delete CurObj; - ObjCount--; - } - else - { - // Move the last filter to the top of the list since it - // seems to be in use for a long time. - - PrevObj -> Next = NULL; - CurObj -> Next = Objects.unkeep(); - Objects = CurObj; - } - - CurObj = NULL; - break; - } - - PrevObj = CurObj; - CurObj = CurObj -> Next; - } - - if( CurObj != NULL ) - { - CurObj -> RefCount++; - - if( PrevObj == NULL ) - { - return( *CurObj ); - } - - // Remove the filter from the list temporarily. - - PrevObj -> Next = CurObj -> Next; - } - else - { - // Create a new filter object (with RefCount == 1) and build the - // filter kernel. - - CurObj = new CDSPFIRFilter(); - CurObj -> ReqNormFreq = ReqNormFreq; - CurObj -> ReqTransBand = ReqTransBand; - CurObj -> ReqAtten = ReqAtten; - CurObj -> ReqPhase = ReqPhase; - CurObj -> ReqGain = ReqGain; - ObjCount++; - - CurObj -> buildLPFilter( AttenCorrs ); - } - - // Insert the filter at the start of the list. - - CurObj -> Next = Objects.unkeep(); - Objects = CurObj; - - return( *CurObj ); - } - -private: - static CSyncObject StateSync; ///< Cache state synchronizer. - ///< - static CPtrKeeper< CDSPFIRFilter* > Objects; ///< The chain of cached - ///< objects. - ///< - static int ObjCount; ///< The number of objects currently preset in the - ///< cache. - ///< -}; - -// --------------------------------------------------------------------------- -// CDSPFIRFilter PUBLIC -// --------------------------------------------------------------------------- - -inline void CDSPFIRFilter :: unref() -{ - R8BSYNC( CDSPFIRFilterCache :: StateSync ); - - RefCount--; -} - -// --------------------------------------------------------------------------- - -} // namespace r8b - -#endif // R8B_CDSPFIRFILTER_INCLUDED diff --git a/src/third_party/r8b/CDSPFracInterpolator.h b/src/third_party/r8b/CDSPFracInterpolator.h deleted file mode 100644 index 097ccd3..0000000 --- a/src/third_party/r8b/CDSPFracInterpolator.h +++ /dev/null @@ -1,1019 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file CDSPFracInterpolator.h - * - * @brief Fractional delay interpolator and filter bank classes. - * - * This file includes fractional delay interpolator class. - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8B_CDSPFRACINTERPOLATOR_INCLUDED -#define R8B_CDSPFRACINTERPOLATOR_INCLUDED - -#include "CDSPSincFilterGen.h" -#include "CDSPProcessor.h" - -namespace r8b { - -#if R8B_FLTTEST - extern int InterpFilterFracs; ///< Force this number of fractional filter - ///< positions. -1 - use default. - ///< - extern int InterpFilterFracsThird; ///< Force this number of fractional - ///< filter positions for one-third filters. -1 - use default. - ///< -#endif // R8B_FLTTEST - -/** - * @brief Sinc function-based fractional delay filter bank class. - * - * Class implements storage and initialization of a bank of sinc-based - * fractional delay filters, expressed as 0th, 1st, 2nd or 3rd order - * polynomial interpolation coefficients. The filters are windowed by the - * "Kaiser" power-raised window function. - */ - -class CDSPFracDelayFilterBank : public R8B_BASECLASS -{ - R8BNOCTOR( CDSPFracDelayFilterBank ); - - friend class CDSPFracDelayFilterBankCache; - -public: - /** - * Constructor. - * - * @param aFilterFracs The number of fractional delay positions to sample, - * -1 - use default. - * @param aElementSize The size of each filter's tap, in "double" values. - * This parameter corresponds to the complexity of interpolation. 4 should - * be set for 3rd order, 3 for 2nd order, 2 for linear interpolation, 1 - * for whole-numbered stepping. - * @param aInterpPoints The number of points the interpolation is based - * on. This value should not be confused with the ElementSize. Set to 2 - * for linear or no interpolation. - * @param aReqAtten Required filter attentuation. - * @param aIsThird "True" if one-third filter is required. - */ - - CDSPFracDelayFilterBank( const int aFilterFracs, const int aElementSize, - const int aInterpPoints, const double aReqAtten, const bool aIsThird ) - : InitFilterFracs( aFilterFracs ) - , ElementSize( aElementSize ) - , InterpPoints( aInterpPoints ) - , ReqAtten( aReqAtten ) - , IsThird( aIsThird ) - , Next( NULL ) - , RefCount( 1 ) - { - R8BASSERT( ElementSize >= 1 && ElementSize <= 4 ); - - // Kaiser window function Params, for half and third-band. - - const double* const Params = getWinParams( ReqAtten, IsThird, - FilterLen ); - - FilterSize = FilterLen * ElementSize; - - if( InitFilterFracs == -1 ) - { - FilterFracs = (int) ceil( 1.792462178761753 * - exp( 0.033300466782047 * ReqAtten )); - - #if R8B_FLTTEST - - if( IsThird ) - { - if( InterpFilterFracsThird != -1 ) - { - FilterFracs = InterpFilterFracsThird; - } - } - else - { - if( InterpFilterFracs != -1 ) - { - FilterFracs = InterpFilterFracs; - } - } - - #endif // R8B_FLTTEST - } - else - { - FilterFracs = InitFilterFracs; - } - - Table.alloc( FilterSize * ( FilterFracs + InterpPoints )); - - CDSPSincFilterGen sinc; - sinc.Len2 = FilterLen / 2; - - double* p = Table; - const int pc2 = InterpPoints / 2; - int i; - - for( i = -pc2 + 1; i <= FilterFracs + pc2; i++ ) - { - sinc.FracDelay = (double) ( FilterFracs - i ) / FilterFracs; - sinc.initFrac( CDSPSincFilterGen :: wftKaiser, Params, true ); - sinc.generateFrac( p, &CDSPSincFilterGen :: calcWindowKaiser, - ElementSize ); - - normalizeFIRFilter( p, FilterLen, 1.0, ElementSize ); - p += FilterSize; - } - - const int TablePos2 = FilterSize; - const int TablePos3 = FilterSize * 2; - const int TablePos4 = FilterSize * 3; - const int TablePos5 = FilterSize * 4; - const int TablePos6 = FilterSize * 5; - const int TablePos7 = FilterSize * 6; - const int TablePos8 = FilterSize * 7; - double* const TableEnd = Table + ( FilterFracs + 1 ) * FilterSize; - p = Table; - - if( InterpPoints == 8 ) - { - if( ElementSize == 3 ) - { - // Calculate 2nd order spline (polynomial) interpolation - // coefficients using 8 points. - - while( p < TableEnd ) - { - calcSpline2p8Coeffs( p, p[ 0 ], p[ TablePos2 ], - p[ TablePos3 ], p[ TablePos4 ], p[ TablePos5 ], - p[ TablePos6 ], p[ TablePos7 ], p[ TablePos8 ]); - - p += ElementSize; - } - } - else - if( ElementSize == 4 ) - { - // Calculate 3rd order spline (polynomial) interpolation - // coefficients using 8 points. - - while( p < TableEnd ) - { - calcSpline3p8Coeffs( p, p[ 0 ], p[ TablePos2 ], - p[ TablePos3 ], p[ TablePos4 ], p[ TablePos5 ], - p[ TablePos6 ], p[ TablePos7 ], p[ TablePos8 ]); - - p += ElementSize; - } - } - } - else - { - if( ElementSize == 2 ) - { - // Calculate linear interpolation coefficients. - - while( p < TableEnd ) - { - p[ 1 ] = p[ TablePos2 ] - p[ 0 ]; - p += ElementSize; - } - } - } - - R8BCONSOLE( "CDSPFracDelayFilterBank: fracs=%i order=%i taps=%i " - "att=%.1f third=%i\n", FilterFracs, ElementSize - 1, FilterLen, - ReqAtten, (int) IsThird ); - } - - ~CDSPFracDelayFilterBank() - { - delete Next; - } - - /** - * Function "rounds" the specified attenuation to the nearest effective - * value. - * - * @param[in,out] att Required filter attentuation. Will be rounded to the - * nearest value. - * @param aIsThird "True" if one-third filter is required. - */ - - static void roundReqAtten( double& att, const bool aIsThird ) - { - int tmp; - getWinParams( att, aIsThird, tmp ); - } - - /** - * Function returns the length of the filter. - */ - - int getFilterLen() const - { - return( FilterLen ); - } - - /** - * Function returns the number of fractional positions sampled by the - * bank. - */ - - int getFilterFracs() const - { - return( FilterFracs ); - } - - /** - * @param i Filter index, in the range 0 to FilterFracs, inclusive. - * @return Reference to the filter. - */ - - const double& operator []( const int i ) const - { - R8BASSERT( i >= 0 && i <= FilterFracs ); - - return( Table[ i * FilterSize ]); - } - - /** - * This function should be called when the filter obtained via the - * filter bank cache is no longer needed. - */ - - void unref(); - -private: - int FilterLen; ///< Filter length. - ///< - int FilterFracs; ///< Fractional position count. - ///< - int InitFilterFracs; ///< Fractional position count as supplied to the - ///< constructor, may equal -1. - ///< - int ElementSize; ///< Filter element size. - ///< - int InterpPoints; ///< Interpolation points to use. - ///< - double ReqAtten; ///< Filter's attentuation. - ///< - bool IsThird; ///< "True" if one-third filter is in use. - ///< - int FilterSize; ///< This constant specifies the "size" of a single filter - ///< in "double" elements. - ///< - CFixedBuffer< double > Table; ///< The table of fractional delay filters - ///< for all discrete fractional x = 0..1 sample positions, and - ///< interpolation coefficients. - ///< - CDSPFracDelayFilterBank* Next; ///< Next filter bank in cache's list. - ///< - int RefCount; ///< The number of references made to *this filter bank. - ///< Not considered for "static" filter bank objects. - ///< - - /** - * Function returns windowing function parameters for the specified - * attenuation and filter type. - * - * @param[in,out] att Required filter attentuation. Will be rounded to the - * nearest value. - * @param aIsThird "True" if one-third filter is required. - * @param[out] fltlen Resulting filter length. - */ - - static const double* getWinParams( double& att, const bool aIsThird, - int& fltlen ) - { - const int CoeffCount = 13; - static const double Coeffs[ CoeffCount ][ 3 ] = { - { 2.6504246356892924, 1.9035845248358245, 51.7280 }, // 0.0516 - { 4.0759654812373016, 1.5747323142948524, 67.1095 }, // 0.0048 - { 4.9036508646352033, 1.6207644759455790, 81.8379 }, // 0.0009 - { 5.6131421124830716, 1.6947677220415129, 96.4021 }, // 0.0002 - { 5.9433751253133691, 1.8730186383321272, 111.1300 }, // 0.0000 - { 6.8308658253825660, 1.8549555120377224, 125.4649 }, // 0.0000 - { 7.6648458853758372, 1.8565765953924642, 139.7378 }, // 0.0000 - { 8.2038730802326842, 1.9269521308895179, 154.0532 }, // 0.0000 - { 8.7865151489187561, 1.9775307528231671, 168.2101 }, // 0.0000 - { 9.5945013206755156, 1.9718457932433306, 182.1076 }, // 0.0000 - { 10.5163048616210250, 1.9504085061576968, 195.5668 }, // 0.0000 - { 10.2382664677006100, 2.1608878780497056, 209.0609 }, // 0.0000 - { 10.9976663155261660, 2.1536415815428249, 222.5009 }, // 0.0000 - }; - - const int CoeffCountThird = 10; - static const double CoeffsThird[ CoeffCountThird ][ 3 ] = { - { 4.0738201365282452, 1.5774150265957998, 67.2431 }, // 0.0050 - { 4.9502289040040495, 1.7149006172407628, 86.4870 }, // 0.0008 - { 5.5995071332976192, 1.8930163359641823, 106.1171 }, // 0.0001 - { 6.3627287856776054, 1.9945748303811506, 125.2304 }, // 0.0000 - { 7.4299554386534528, 1.9893399585993299, 144.3469 }, // 0.0000 - { 8.0667710807396436, 2.0928202837610885, 163.4098 }, // 0.0000 - { 8.7469991933128526, 2.1640274270903488, 181.0694 }, // 0.0000 - { 10.0823164330540570, 2.0896732996403280, 199.2880 }, // 0.0000 - { 19.1718281840114810, 1.2030083075440616, 215.2990 }, // 0.0000 - { 21.0914128488567630, 1.1919045429676862, 233.9152 }, // 0.0000 - }; - - const double* Params; - int i = 0; - - if( aIsThird ) - { - while( i != CoeffCountThird - 1 && CoeffsThird[ i ][ 2 ] < att ) - { - i++; - } - - Params = &CoeffsThird[ i ][ 0 ]; - att = CoeffsThird[ i ][ 2 ]; - } - else - { - while( i != CoeffCount - 1 && Coeffs[ i ][ 2 ] < att ) - { - i++; - } - - Params = &Coeffs[ i ][ 0 ]; - att = Coeffs[ i ][ 2 ]; - } - - fltlen = ( i + 3 ) * 2; - - return( Params ); - } -}; - -/** - * @brief Fractional delay filter cache class. - * - * Class implements cache storage of fractional delay filter banks. - */ - -class CDSPFracDelayFilterBankCache : public R8B_BASECLASS -{ - R8BNOCTOR( CDSPFracDelayFilterBankCache ); - - friend class CDSPFracDelayFilterBank; - -public: - /** - * @return The number of filters present in the cache now. This value can - * be monitored for debugging "forgotten" filters. - */ - - static int getObjCount() - { - R8BSYNC( StateSync ); - - return( ObjCount ); - } - - /** - * Function calculates or returns reference to a previously calculated - * (cached) fractional delay filter bank. - * - * @param aFilterFracs The number of fractional delay positions to sample, - * -1 - use default. - * @param aElementSize The size of each filter's tap, in "double" values. - * @param aInterpPoints The number of points the interpolation is based - * on. - * @param ReqAtten Required filter attentuation. - * @param IsThird "True" if one-third filter is required. - * @param IsStatic "True" if a permanent static filter should be returned - * that is never removed from the cache until application terminates. - */ - - static CDSPFracDelayFilterBank& getFilterBank( const int aFilterFracs, - const int aElementSize, const int aInterpPoints, - double ReqAtten, const bool IsThird, const bool IsStatic ) - { - CDSPFracDelayFilterBank :: roundReqAtten( ReqAtten, IsThird ); - - R8BSYNC( StateSync ); - - if( IsStatic ) - { - CDSPFracDelayFilterBank* CurObj = StaticObjects; - - while( CurObj != NULL ) - { - if( CurObj -> InitFilterFracs == aFilterFracs && - CurObj -> ElementSize == aElementSize && - CurObj -> InterpPoints == aInterpPoints && - CurObj -> ReqAtten == ReqAtten && - CurObj -> IsThird == IsThird ) - { - return( *CurObj ); - } - - CurObj = CurObj -> Next; - } - - // Create a new filter bank and build it. - - CurObj = new CDSPFracDelayFilterBank( aFilterFracs, aElementSize, - aInterpPoints, ReqAtten, IsThird ); - - // Insert the bank at the start of the list. - - CurObj -> Next = StaticObjects.unkeep(); - StaticObjects = CurObj; - - return( *CurObj ); - } - - CDSPFracDelayFilterBank* PrevObj = NULL; - CDSPFracDelayFilterBank* CurObj = Objects; - - while( CurObj != NULL ) - { - if( CurObj -> InitFilterFracs == aFilterFracs && - CurObj -> ElementSize == aElementSize && - CurObj -> InterpPoints == aInterpPoints && - CurObj -> ReqAtten == ReqAtten && - CurObj -> IsThird == IsThird ) - { - break; - } - - if( CurObj -> Next == NULL && ObjCount >= R8B_FRACBANK_CACHE_MAX ) - { - if( CurObj -> RefCount == 0 ) - { - // Delete the last bank which is not used. - - PrevObj -> Next = NULL; - delete CurObj; - ObjCount--; - } - else - { - // Move the last bank to the top of the list since it - // seems to be in use for a long time. - - PrevObj -> Next = NULL; - CurObj -> Next = Objects.unkeep(); - Objects = CurObj; - } - - CurObj = NULL; - break; - } - - PrevObj = CurObj; - CurObj = CurObj -> Next; - } - - if( CurObj != NULL ) - { - CurObj -> RefCount++; - - if( PrevObj == NULL ) - { - return( *CurObj ); - } - - // Remove the bank from the list temporarily. - - PrevObj -> Next = CurObj -> Next; - } - else - { - // Create a new filter bank (with RefCount == 1) and build it. - - CurObj = new CDSPFracDelayFilterBank( aFilterFracs, aElementSize, - aInterpPoints, ReqAtten, IsThird ); - - ObjCount++; - } - - // Insert the bank at the start of the list. - - CurObj -> Next = Objects.unkeep(); - Objects = CurObj; - - return( *CurObj ); - } - -private: - static CSyncObject StateSync; ///< Cache state synchronizer. - ///< - static CPtrKeeper< CDSPFracDelayFilterBank* > Objects; ///< The chain of - ///< cached objects. - ///< - static CPtrKeeper< CDSPFracDelayFilterBank* > StaticObjects; ///< The - ///< chain of static objects. - ///< - static int ObjCount; ///< The number of objects currently preset in the - ///< Objects cache. - ///< -}; - -// --------------------------------------------------------------------------- -// CDSPFracDelayFilterBank PUBLIC -// --------------------------------------------------------------------------- - -inline void CDSPFracDelayFilterBank :: unref() -{ - R8BSYNC( CDSPFracDelayFilterBankCache :: StateSync ); - - RefCount--; -} - -/** - * @param l Number 1. - * @param s Number 2. - * @param[out] GCD Resulting GCD. - * @return "True" if the greatest common denominator of 2 numbers was - * found. - */ - -inline bool findGCD( double l, double s, double& GCD ) -{ - int it = 0; - - while( it < 50 ) - { - if( s <= 0.0 ) - { - GCD = l; - return( true ); - } - - const double r = l - s; - l = s; - s = ( r < 0.0 ? -r : r ); - it++; - } - - return( false ); -} - -/** - * Function evaluates source and destination sample rate ratio and returns - * the required input and output stepping. Function returns "false" if - * *this class cannot be used to perform interpolation using these sample - * rates. - * - * @param SSampleRate Source sample rate. - * @param DSampleRate Destination sample rate. - * @param[out] ResInStep Resulting input step. - * @param[out] ResOutStep Resulting output step. - * @return "True" if stepping was acquired. - */ - -inline bool getWholeStepping( const double SSampleRate, - const double DSampleRate, int& ResInStep, int& ResOutStep ) -{ - double GCD; - - if( !findGCD( SSampleRate, DSampleRate, GCD ) || GCD < 1.0 ) - { - return( false ); - } - - const double InStep0 = SSampleRate / GCD; - ResInStep = (int) InStep0; - const double OutStep0 = DSampleRate / GCD; - ResOutStep = (int) OutStep0; - - if( InStep0 != ResInStep || OutStep0 != ResOutStep ) - { - return( false ); - } - - if( ResOutStep > 1500 ) - { - // Do not allow large output stepping due to low cache - // performance of large filter banks. - - return( false ); - } - - return( true ); -} - -/** - * @brief Fractional delay filter bank-based interpolator class. - * - * Class implements the fractional delay interpolator. This implementation at - * first puts the input signal into a ring buffer and then performs - * interpolation. The interpolation is performed using sinc-based fractional - * delay filters. These filters are contained in a bank, and for higher - * precision they are interpolated between adjacent filters. - * - * To increase sample timing precision, this class uses "resettable counter" - * approach. This gives zero overall sample timing error. With the - * R8B_FASTTIMING configuration option enabled, the sample timing experiences - * a very minor drift. - */ - -class CDSPFracInterpolator : public CDSPProcessor -{ -public: - /** - * Constructor initalizes the interpolator. It is important to call the - * getMaxOutLen() function afterwards to obtain the optimal output buffer - * length. - * - * @param aSrcSampleRate Source sample rate. - * @param aDstSampleRate Destination sample rate. - * @param ReqAtten Required filter attentuation. - * @param IsThird "True" if one-third filter is required. - * @param PrevLatency Latency, in samples (any value >=0), which was left - * in the output signal by a previous process. This latency will be - * consumed completely. - */ - - CDSPFracInterpolator( const double aSrcSampleRate, - const double aDstSampleRate, const double ReqAtten, - const bool IsThird, const double PrevLatency ) - : SrcSampleRate( aSrcSampleRate ) - , DstSampleRate( aDstSampleRate ) - #if R8B_FASTTIMING - , FracStep( aSrcSampleRate / aDstSampleRate ) - #endif // R8B_FASTTIMING - { - R8BASSERT( SrcSampleRate > 0.0 ); - R8BASSERT( DstSampleRate > 0.0 ); - R8BASSERT( PrevLatency >= 0.0 ); - R8BASSERT( BufLenBits >= 5 ); - R8BASSERT(( 1 << BufLenBits ) >= FilterLen * 3 ); - - InitFracPos = PrevLatency; - Latency = (int) InitFracPos; - InitFracPos -= Latency; - - #if R8B_FLTTEST - - IsWhole = false; - LatencyFrac = 0.0; - FilterBank = new CDSPFracDelayFilterBank( -1, 3, 8, ReqAtten, - IsThird ); - - #else // R8B_FLTTEST - - IsWhole = getWholeStepping( SrcSampleRate, DstSampleRate, InStep, - OutStep ); - - if( IsWhole ) - { - InitFracPosW = (int) ( InitFracPos * OutStep ); - LatencyFrac = InitFracPos - (double) InitFracPosW / OutStep; - FilterBank = &CDSPFracDelayFilterBankCache :: getFilterBank( - OutStep, 1, 2, ReqAtten, IsThird, false ); - } - else - { - LatencyFrac = 0.0; - FilterBank = &CDSPFracDelayFilterBankCache :: getFilterBank( - -1, 3, 8, ReqAtten, IsThird, true ); - } - - #endif // R8B_FLTTEST - - FilterLen = FilterBank -> getFilterLen(); - fl2 = FilterLen >> 1; - fll = fl2 - 1; - flo = fll + fl2; - - static const CConvolveFn FltConvFn0[ 13 ] = { - &CDSPFracInterpolator :: convolve0< 6 >, - &CDSPFracInterpolator :: convolve0< 8 >, - &CDSPFracInterpolator :: convolve0< 10 >, - &CDSPFracInterpolator :: convolve0< 12 >, - &CDSPFracInterpolator :: convolve0< 14 >, - &CDSPFracInterpolator :: convolve0< 16 >, - &CDSPFracInterpolator :: convolve0< 18 >, - &CDSPFracInterpolator :: convolve0< 20 >, - &CDSPFracInterpolator :: convolve0< 22 >, - &CDSPFracInterpolator :: convolve0< 24 >, - &CDSPFracInterpolator :: convolve0< 26 >, - &CDSPFracInterpolator :: convolve0< 28 >, - &CDSPFracInterpolator :: convolve0< 30 > - }; - - convfn = ( IsWhole ? FltConvFn0[ fl2 - 3 ] : - &CDSPFracInterpolator :: convolve2 ); - - R8BCONSOLE( "CDSPFracInterpolator: src=%.2f dst=%.2f taps=%i " - "fracs=%i third=%i step=%.6f\n", SrcSampleRate, DstSampleRate, - FilterLen, ( IsWhole ? OutStep : FilterBank -> getFilterFracs() ), - (int) IsThird, aSrcSampleRate / aDstSampleRate ); - - clear(); - } - - virtual ~CDSPFracInterpolator() - { - #if R8B_FLTTEST - - delete FilterBank; - - #else // R8B_FLTTEST - - FilterBank -> unref(); - - #endif // R8B_FLTTEST - } - - virtual int getLatency() const - { - return( 0 ); - } - - virtual double getLatencyFrac() const - { - return( LatencyFrac ); - } - - virtual int getMaxOutLen( const int MaxInLen ) const - { - R8BASSERT( MaxInLen >= 0 ); - - return( (int) ceil( MaxInLen * DstSampleRate / SrcSampleRate ) + 1 ); - } - - virtual void clear() - { - LatencyLeft = Latency; - BufLeft = 0; - WritePos = 0; - ReadPos = BufLen - fll; // Set "read" position to account for filter's - // latency at zero fractional delay. - - memset( &Buf[ ReadPos ], 0, fll * sizeof( double )); - - if( IsWhole ) - { - InPosFracW = InitFracPosW; - } - else - { - InPosFrac = InitFracPos; - - #if !R8B_FASTTIMING - InCounter = 0; - InPosInt = 0; - InPosShift = InitFracPos * DstSampleRate / SrcSampleRate; - #endif // !R8B_FASTTIMING - } - } - - virtual int process( double* ip, int l, double*& op0 ) - { - R8BASSERT( l >= 0 ); - R8BASSERT( ip != op0 || l == 0 || SrcSampleRate > DstSampleRate ); - - if( LatencyLeft > 0 ) - { - if( LatencyLeft >= l ) - { - LatencyLeft -= l; - return( 0 ); - } - - l -= LatencyLeft; - ip += LatencyLeft; - LatencyLeft = 0; - } - - double* op = op0; - - while( l > 0 ) - { - // Add new input samples to both halves of the ring buffer. - - const int b = min( min( l, BufLen - WritePos ), - BufLen - fll - BufLeft ); - - double* const wp1 = Buf + WritePos; - memcpy( wp1, ip, b * sizeof( double )); - - if( WritePos < flo ) - { - const int c = min( b, flo - WritePos ); - memcpy( wp1 + BufLen, wp1, c * sizeof( double )); - } - - ip += b; - WritePos = ( WritePos + b ) & BufLenMask; - l -= b; - BufLeft += b; - - // Produce as many output samples as possible. - - op = ( *this.*convfn )( op ); - } - - #if !R8B_FASTTIMING - - if( !IsWhole && InCounter > 1000 ) - { - // Reset the interpolation position counter to achieve a - // higher sample timing precision. - - InCounter = 0; - InPosInt = 0; - InPosShift = InPosFrac * DstSampleRate / SrcSampleRate; - } - - #endif // !R8B_FASTTIMING - - return( (int) ( op - op0 )); - } - -private: - static const int BufLenBits = 8; ///< The length of the ring buffer, - ///< expressed as Nth power of 2. This value can be reduced if it is - ///< known that only short input buffers will be passed to the - ///< interpolator. The minimum value of this parameter is 5, and - ///< 1 << BufLenBits should be at least 3 times larger than the - ///< FilterLen. However, this condition can be easily met if the input - ///< signal is suitably downsampled first before the interpolation is - ///< performed. - ///< - static const int BufLen = 1 << BufLenBits; ///< The length of the ring - ///< buffer. The actual length is twice as long to allow "beyond max - ///< position" positioning. - ///< - static const int BufLenMask = BufLen - 1; ///< Mask used for quick buffer - ///< position wrapping. - ///< - int FilterLen; ///< Filter length, in taps. Even value. - ///< - int fl2; ///< Right-side (half) filter length. - ///< - int fll; ///< Input latency. - ///< - int flo; ///< Overrun length. - ///< - double Buf[ BufLen + 29 ]; ///< The ring buffer, including overrun - ///< protection for maximal filter length. - ///< - double SrcSampleRate; ///< Source sample rate. - ///< - double DstSampleRate; ///< Destination sample rate. - ///< - bool IsWhole; ///< "True" if whole-number stepping is in use. - ///< - int InStep; ///< Input whole-number stepping. - ///< - int OutStep; ///< Output whole-number stepping (corresponds to filter bank - ///< size). - ///< - double InitFracPos; ///< Initial fractional position, in samples, in the - ///< range [0; 1). - ///< - int InitFracPosW; ///< Initial fractional position for whole-number - ///< stepping. - ///< - int Latency; ///< Initial latency that should be removed from the input. - ///< - double LatencyFrac; ///< Left-over fractional latency. - ///< - int BufLeft; ///< The number of samples left in the buffer to process. - ///< - int WritePos; ///< The current buffer write position. Incremented together - ///< with the BufLeft variable. - ///< - int ReadPos; ///< The current buffer read position. - ///< - int LatencyLeft; ///< Input latency left to remove. - ///< - double InPosFrac; ///< Interpolation position (fractional part). - ///< - int InPosFracW; ///< Interpolation position (fractional part) for - ///< whole-number stepping. Corresponds to the index into the filter - ///< bank. - ///< - CDSPFracDelayFilterBank* FilterBank; ///< Filter bank in use, may be - ///< whole-number stepping filter bank or static bank. - ///< -#if R8B_FASTTIMING - double FracStep; ///< Fractional sample timing step. -#else // R8B_FASTTIMING - int InCounter; ///< Interpolation step counter. - ///< - int InPosInt; ///< Interpolation position (integer part). - ///< - double InPosShift; ///< Interpolation position fractional shift. - ///< -#endif // R8B_FASTTIMING - - typedef double*( CDSPFracInterpolator :: *CConvolveFn )( double* op ); ///< - ///< Convolution funtion type. - ///< - CConvolveFn convfn; ///< Convolution function in use. - ///< - - /** - * Convolution function for 0th order resampling. - * - * @param[out] op Output buffer. - * @return Advanced "op" value. - * @tparam fltlen Filter length. - */ - - template< int fltlen > - double* convolve0( double* op ) - { - while( BufLeft > fl2 ) - { - const double* const ftp = &(*FilterBank)[ InPosFracW ]; - const double* const rp = Buf + ReadPos; - double s = 0.0; - int i; - - for( i = 0; i < fltlen; i++ ) - { - s += ftp[ i ] * rp[ i ]; - } - - *op = s; - op++; - - InPosFracW += InStep; - const int PosIncr = InPosFracW / OutStep; - InPosFracW -= PosIncr * OutStep; - - ReadPos = ( ReadPos + PosIncr ) & BufLenMask; - BufLeft -= PosIncr; - } - - return( op ); - } - - /** - * Convolution function for 2nd order resampling. - * - * @param[out] op Output buffer. - * @return Advanced "op" value. - */ - - double* convolve2( double* op ) - { - while( BufLeft > fl2 ) - { - double x = InPosFrac * FilterBank -> getFilterFracs(); - const int fti = (int) x; // Function table index. - x -= fti; // Coefficient for interpolation between - // adjacent fractional delay filters. - const double x2 = x * x; - const double* const ftp = &(*FilterBank)[ fti ]; - const double* const rp = Buf + ReadPos; - double s = 0.0; - int ii = 0; - int i; - - for( i = 0; i < FilterLen; i++ ) - { - s += ( ftp[ ii ] + ftp[ ii + 1 ] * x + - ftp[ ii + 2 ] * x2 ) * rp[ i ]; - - ii += 3; - } - - *op = s; - op++; - - #if R8B_FASTTIMING - - InPosFrac += FracStep; - const int PosIncr = (int) InPosFrac; - InPosFrac -= PosIncr; - - #else // R8B_FASTTIMING - - InCounter++; - const double NextInPos = ( InCounter + InPosShift ) * - SrcSampleRate / DstSampleRate; - - const int NextInPosInt = (int) NextInPos; - const int PosIncr = NextInPosInt - InPosInt; - InPosInt = NextInPosInt; - InPosFrac = NextInPos - NextInPosInt; - - #endif // R8B_FASTTIMING - - ReadPos = ( ReadPos + PosIncr ) & BufLenMask; - BufLeft -= PosIncr; - } - - return( op ); - } -}; - -// --------------------------------------------------------------------------- - -} // namespace r8b - -#endif // R8B_CDSPFRACINTERPOLATOR_INCLUDED diff --git a/src/third_party/r8b/CDSPHBDownsampler.h b/src/third_party/r8b/CDSPHBDownsampler.h deleted file mode 100644 index 75a9b50..0000000 --- a/src/third_party/r8b/CDSPHBDownsampler.h +++ /dev/null @@ -1,393 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file CDSPHBDownsampler.h - * - * @brief Half-band downsampling convolver class. - * - * This file includes half-band downsampling convolver class. - * - * r8brain-free-src Copyright (c) 2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8B_CDSPHBDOWNSAMPLER_INCLUDED -#define R8B_CDSPHBDOWNSAMPLER_INCLUDED - -#include "CDSPHBUpsampler.h" - -namespace r8b { - -/** - * @brief Half-band downsampler class. - * - * Class implements brute-force half-band 2X downsampling that uses small - * sparse symmetric FIR filters. The output has 2.0 gain. - */ - -class CDSPHBDownsampler : public CDSPProcessor -{ -public: - /** - * Constructor initalizes the half-band downsampler. - * - * @param ReqAtten Required half-band filter attentuation. - * @param SteepIndex Steepness index - 0=steepest. Corresponds to general - * downsampling ratio, e.g. at 4x downsampling 0 is used, at 8x - * downsampling 1 is used, etc. - * @param IsThird "True" if 1/3 resampling is performed. - * @param PrevLatency Latency, in samples (any value >=0), which was left - * in the output signal by a previous process. Whole-number latency will - * be consumed by *this object while remaining fractional latency can be - * obtained via the getLatencyFrac() function. - */ - - CDSPHBDownsampler( const double ReqAtten, const int SteepIndex, - const bool IsThird, const double PrevLatency ) - { - static const CConvolveFn FltConvFn[ 14 ] = { - &CDSPHBDownsampler :: convolve1, &CDSPHBDownsampler :: convolve2, - &CDSPHBDownsampler :: convolve3, &CDSPHBDownsampler :: convolve4, - &CDSPHBDownsampler :: convolve5, &CDSPHBDownsampler :: convolve6, - &CDSPHBDownsampler :: convolve7, &CDSPHBDownsampler :: convolve8, - &CDSPHBDownsampler :: convolve9, &CDSPHBDownsampler :: convolve10, - &CDSPHBDownsampler :: convolve11, &CDSPHBDownsampler :: convolve12, - &CDSPHBDownsampler :: convolve13, - &CDSPHBDownsampler :: convolve14 }; - - int fltt; - double att; - - if( IsThird ) - { - CDSPHBUpsampler :: getHBFilterThird( ReqAtten, SteepIndex, fltp, - fltt, att ); - } - else - { - CDSPHBUpsampler :: getHBFilter( ReqAtten, SteepIndex, fltp, fltt, - att ); - } - - convfn = FltConvFn[ fltt - 1 ]; - fll = fltt * 2 - 1; - fl2 = fll; - flo = fll + fl2; - - LatencyFrac = PrevLatency * 0.5; - Latency = (int) LatencyFrac; - LatencyFrac -= Latency; - - R8BCONSOLE( "CDSPHBDownsampler: taps=%i third=%i att=%.1f io=1/2\n", - fltt, (int) IsThird, att ); - - clear(); - } - - virtual int getLatency() const - { - return( 0 ); - } - - virtual double getLatencyFrac() const - { - return( LatencyFrac ); - } - - virtual int getMaxOutLen( const int MaxInLen ) const - { - R8BASSERT( MaxInLen >= 0 ); - - return(( MaxInLen + 1 ) / 2 ); - } - - virtual void clear() - { - LatencyLeft = Latency; - BufLeft = 0; - WritePos = 0; - ReadPos = BufLen - fll; // Set "read" position to - // account for filter's latency. - - memset( &Buf[ ReadPos ], 0, fll * sizeof( double )); - } - - virtual int process( double* ip, int l, double*& op0 ) - { - R8BASSERT( l >= 0 ); - - double* op = op0; - - while( l > 0 ) - { - // Add new input samples to both halves of the ring buffer. - - const int b = min( min( l, BufLen - WritePos ), - BufLen - fll - BufLeft ); - - double* const wp1 = Buf + WritePos; - memcpy( wp1, ip, b * sizeof( double )); - - if( WritePos < flo ) - { - const int c = min( b, flo - WritePos ); - memcpy( wp1 + BufLen, wp1, c * sizeof( double )); - } - - ip += b; - WritePos = ( WritePos + b ) & BufLenMask; - l -= b; - BufLeft += b; - - if( BufLeft > fl2 ) - { - const int c = ( BufLeft - fl2 + 1 ) >> 1; - - double* const opend = op + c; - ( *convfn )( op, opend, fltp, Buf + fll, ReadPos ); - - op = opend; - BufLeft -= c + c; - } - } - - int ol = (int) ( op - op0 ); - - if( LatencyLeft > 0 ) - { - if( LatencyLeft >= ol ) - { - LatencyLeft -= ol; - return( 0 ); - } - - ol -= LatencyLeft; - op0 += LatencyLeft; - LatencyLeft = 0; - } - - return( ol ); - } - -private: - static const int BufLenBits = 8; ///< The length of the ring buffer, - ///< expressed as Nth power of 2. This value can be reduced if it is - ///< known that only short input buffers will be passed to the - ///< interpolator. The minimum value of this parameter is 5, and - ///< 1 << BufLenBits should be at least 3 times larger than the - ///< FilterLen. - ///< - static const int BufLen = 1 << BufLenBits; ///< The length of the ring - ///< buffer. The actual length is twice as long to allow "beyond max - ///< position" positioning. - ///< - static const int BufLenMask = BufLen - 1; ///< Mask used for quick buffer - ///< position wrapping. - ///< - double Buf[ BufLen + 54 ]; ///< The ring buffer, including overrun - ///< protection for the largest filter. - ///< - const double* fltp; ///< Half-band filter taps. - ///< - int fll; ///< Input latency. - ///< - int fl2; ///< Right-side filter length. - ///< - int flo; ///< Overrun length. - ///< - int Latency; ///< Initial latency that should be removed from the output. - ///< - double LatencyFrac; ///< Fractional latency left on the output. - ///< - int BufLeft; ///< The number of samples left in the buffer to process. - ///< When this value is below FilterLenD2Plus1, the interpolation - ///< cycle ends. - ///< - int WritePos; ///< The current buffer write position. Incremented together - ///< with the BufLeft variable. - ///< - int ReadPos; ///< The current buffer read position. - ///< - int LatencyLeft; ///< Latency left to remove. - ///< - typedef void( *CConvolveFn )( double* op, double* const opend, - const double* const flt, const double* const rp0, int& ReadPos0 ); ///< - ///< Convolution funtion type. - ///< - CConvolveFn convfn; ///< Convolution function in use. - ///< - -#define R8BHBC1( fn ) \ - static void fn( double* op, double* const opend, const double* const flt, \ - const double* const rp0, int& ReadPos0 ) \ - { \ - int rpos = ReadPos0; \ - while( op < opend ) \ - { \ - const double* const rp = rp0 + rpos; \ - *op = rp[ 0 ] + - -#define R8BHBC2 \ - rpos = ( rpos + 2 ) & BufLenMask; \ - op++; \ - } \ - ReadPos0 = rpos; \ - } - - R8BHBC1( convolve1 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]); - R8BHBC2 - - R8BHBC1( convolve2 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]); - R8BHBC2 - - R8BHBC1( convolve3 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]); - R8BHBC2 - - R8BHBC1( convolve4 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]); - R8BHBC2 - - R8BHBC1( convolve5 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]); - R8BHBC2 - - R8BHBC1( convolve6 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]) + - flt[ 5 ] * ( rp[ 11 ] + rp[ -11 ]); - R8BHBC2 - - R8BHBC1( convolve7 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]) + - flt[ 5 ] * ( rp[ 11 ] + rp[ -11 ]) + - flt[ 6 ] * ( rp[ 13 ] + rp[ -13 ]); - R8BHBC2 - - R8BHBC1( convolve8 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]) + - flt[ 5 ] * ( rp[ 11 ] + rp[ -11 ]) + - flt[ 6 ] * ( rp[ 13 ] + rp[ -13 ]) + - flt[ 7 ] * ( rp[ 15 ] + rp[ -15 ]); - R8BHBC2 - - R8BHBC1( convolve9 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]) + - flt[ 5 ] * ( rp[ 11 ] + rp[ -11 ]) + - flt[ 6 ] * ( rp[ 13 ] + rp[ -13 ]) + - flt[ 7 ] * ( rp[ 15 ] + rp[ -15 ]) + - flt[ 8 ] * ( rp[ 17 ] + rp[ -17 ]); - R8BHBC2 - - R8BHBC1( convolve10 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]) + - flt[ 5 ] * ( rp[ 11 ] + rp[ -11 ]) + - flt[ 6 ] * ( rp[ 13 ] + rp[ -13 ]) + - flt[ 7 ] * ( rp[ 15 ] + rp[ -15 ]) + - flt[ 8 ] * ( rp[ 17 ] + rp[ -17 ]) + - flt[ 9 ] * ( rp[ 19 ] + rp[ -19 ]); - R8BHBC2 - - R8BHBC1( convolve11 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]) + - flt[ 5 ] * ( rp[ 11 ] + rp[ -11 ]) + - flt[ 6 ] * ( rp[ 13 ] + rp[ -13 ]) + - flt[ 7 ] * ( rp[ 15 ] + rp[ -15 ]) + - flt[ 8 ] * ( rp[ 17 ] + rp[ -17 ]) + - flt[ 9 ] * ( rp[ 19 ] + rp[ -19 ]) + - flt[ 10 ] * ( rp[ 21 ] + rp[ -21 ]); - R8BHBC2 - - R8BHBC1( convolve12 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]) + - flt[ 5 ] * ( rp[ 11 ] + rp[ -11 ]) + - flt[ 6 ] * ( rp[ 13 ] + rp[ -13 ]) + - flt[ 7 ] * ( rp[ 15 ] + rp[ -15 ]) + - flt[ 8 ] * ( rp[ 17 ] + rp[ -17 ]) + - flt[ 9 ] * ( rp[ 19 ] + rp[ -19 ]) + - flt[ 10 ] * ( rp[ 21 ] + rp[ -21 ]) + - flt[ 11 ] * ( rp[ 23 ] + rp[ -23 ]); - R8BHBC2 - - R8BHBC1( convolve13 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]) + - flt[ 5 ] * ( rp[ 11 ] + rp[ -11 ]) + - flt[ 6 ] * ( rp[ 13 ] + rp[ -13 ]) + - flt[ 7 ] * ( rp[ 15 ] + rp[ -15 ]) + - flt[ 8 ] * ( rp[ 17 ] + rp[ -17 ]) + - flt[ 9 ] * ( rp[ 19 ] + rp[ -19 ]) + - flt[ 10 ] * ( rp[ 21 ] + rp[ -21 ]) + - flt[ 11 ] * ( rp[ 23 ] + rp[ -23 ]) + - flt[ 12 ] * ( rp[ 25 ] + rp[ -25 ]); - R8BHBC2 - - R8BHBC1( convolve14 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ -1 ]) + - flt[ 1 ] * ( rp[ 3 ] + rp[ -3 ]) + - flt[ 2 ] * ( rp[ 5 ] + rp[ -5 ]) + - flt[ 3 ] * ( rp[ 7 ] + rp[ -7 ]) + - flt[ 4 ] * ( rp[ 9 ] + rp[ -9 ]) + - flt[ 5 ] * ( rp[ 11 ] + rp[ -11 ]) + - flt[ 6 ] * ( rp[ 13 ] + rp[ -13 ]) + - flt[ 7 ] * ( rp[ 15 ] + rp[ -15 ]) + - flt[ 8 ] * ( rp[ 17 ] + rp[ -17 ]) + - flt[ 9 ] * ( rp[ 19 ] + rp[ -19 ]) + - flt[ 10 ] * ( rp[ 21 ] + rp[ -21 ]) + - flt[ 11 ] * ( rp[ 23 ] + rp[ -23 ]) + - flt[ 12 ] * ( rp[ 25 ] + rp[ -25 ]) + - flt[ 13 ] * ( rp[ 27 ] + rp[ -27 ]); - R8BHBC2 - -#undef R8BHBC1 -#undef R8BHBC2 -}; - -// --------------------------------------------------------------------------- - -} // namespace r8b - -#endif // R8B_CDSPHBDOWNSAMPLER_INCLUDED diff --git a/src/third_party/r8b/CDSPHBUpsampler.h b/src/third_party/r8b/CDSPHBUpsampler.h deleted file mode 100644 index 5e07bf0..0000000 --- a/src/third_party/r8b/CDSPHBUpsampler.h +++ /dev/null @@ -1,857 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file CDSPHBUpsampler.h - * - * @brief Half-band upsampling class. - * - * This file includes half-band upsampling class. - * - * r8brain-free-src Copyright (c) 2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8B_CDSPHBUPSAMPLER_INCLUDED -#define R8B_CDSPHBUPSAMPLER_INCLUDED - -#include "CDSPProcessor.h" - -namespace r8b { - -/** - * @brief Half-band upsampling class. - * - * Class implements brute-force half-band 2X upsampling that uses small - * sparse symmetric FIR filters. It is very efficient and should be used at - * latter upsampling steps after initial steep 2X upsampling. - */ - -class CDSPHBUpsampler : public CDSPProcessor -{ -public: - /** - * Function that provides filter data for various steepness indices and - * attenuations. - * - * @param ReqAtten Required half-band filter attentuation. - * @param SteepIndex Steepness index - 0=steepest. Corresponds to general - * upsampling/downsampling ratio, e.g. at 4x 0 is used, at 8x 1 is used, - * etc. - */ - - static void getHBFilter( const double ReqAtten, const int SteepIndex, - const double*& flt, int& fltt, double& att ) - { - static const int FltCount = 11; - static const double HBKernel_4[ 4 ] = { // att -64.9241 dB, frac 4.0 - 6.1073830069265711e-001, -1.4463982571471876e-001, - 4.1136036923118187e-002, -7.4740105856914872e-003 }; - static const double HBKernel_5[ 5 ] = { // att -87.4775 dB, frac 4.0 - 6.1553054142504338e-001, -1.5591352339398118e-001, - 5.2404802661298266e-002, -1.4230574348726146e-002, - 2.2457593805377831e-003 }; - static const double HBKernel_6[ 6 ] = { // att -104.5154 dB, frac 4.0 - 6.1883766561934184e-001, -1.6396282655874558e-001, - 6.1104571279325129e-002, -2.0317756445217543e-002, - 5.0264527466018826e-003, -6.9392938429507279e-004 }; - static const double HBKernel_7[ 7 ] = { // att -120.6199 dB, frac 4.0 - 6.2125313688727779e-001, -1.6999763849273491e-001, - 6.8014108060738196e-002, -2.5679821316697125e-002, - 7.9798828249699784e-003, -1.7871060154498470e-003, - 2.1836606459564009e-004 }; - static const double HBKernel_8[ 8 ] = { // att -136.5151 dB, frac 4.0 - 6.2309299085367287e-001, -1.7468969193368433e-001, - 7.3628746444973150e-002, -3.0378268550055314e-002, - 1.0908085227657214e-002, -3.1287343330312556e-003, - 6.3632014609722092e-004, -6.9597139145649502e-005 }; - static const double HBKernel_9[ 9 ] = { // att -152.3240 dB, frac 4.0 - 6.2454069594794803e-001, -1.7844303649890664e-001, - 7.8279410808762842e-002, -3.4501119561829857e-002, - 1.3717889826645487e-002, -4.6090109007760798e-003, - 1.2192752061406873e-003, -2.2647618541786664e-004, - 2.2395554542567748e-005 }; - static const double HBKernel_10[ 10 ] = { // att -168.0859 dB, frac 4.0 - 6.2570883988448611e-001, -1.8151274643053061e-001, - 8.2191863294185458e-002, -3.8131779329357615e-002, - 1.6367492549512565e-002, -6.1530178832078578e-003, - 1.9277693942420303e-003, -4.7165916432255402e-004, - 8.0491894752808465e-005, -7.2581515842465856e-006 }; - static const double HBKernel_11[ 11 ] = { // att -183.7962 dB, frac 4.0 - 6.2667167706646965e-001, -1.8407153341833782e-001, - 8.5529995600327216e-002, -4.1346831452173063e-002, - 1.8844831683400131e-002, -7.7125170314919214e-003, - 2.7268674834296570e-003, -7.9745028391855826e-004, - 1.8116344571770699e-004, -2.8569149678673122e-005, - 2.3667021922861879e-006 }; - static const double HBKernel_12[ 12 ] = { // att -199.4768 dB, frac 4.0 - 6.2747849729182659e-001, -1.8623616781335248e-001, - 8.8409755856508648e-002, -4.4207468780136254e-002, - 2.1149175912217915e-002, -9.2551508154301194e-003, - 3.5871562052326249e-003, -1.1923167600753576e-003, - 3.2627812001613326e-004, -6.9106902008709490e-005, - 1.0122897772888322e-005, -7.7531878091292963e-007 }; - static const double HBKernel_13[ 13 ] = { // att -215.1364 dB, frac 4.0 - 6.2816416238782957e-001, -1.8809076918442266e-001, - 9.0918539368474965e-002, -4.6765502172995604e-002, - 2.3287520069933797e-002, -1.0760626940880943e-002, - 4.4853921118213676e-003, -1.6438774496992904e-003, - 5.1441308429384374e-004, -1.3211724349740752e-004, - 2.6191316362108199e-005, -3.5802424384280469e-006, - 2.5491272423372411e-007 }; - static const double HBKernel_14[ 14 ] = { // att -230.7526 dB, frac 4.0 - 6.2875473147254901e-001, -1.8969942008858576e-001, - 9.3126095475258408e-002, -4.9067252227455962e-002, - 2.5273009767563311e-002, -1.2218646838258702e-002, - 5.4048946497798353e-003, -2.1409921992386689e-003, - 7.4250304371305991e-004, -2.1924546773651068e-004, - 5.3015823597863675e-005, -9.8743070771832892e-006, - 1.2650397198764347e-006, -8.4146728313072455e-008 }; - static const double FltAttens[ FltCount ] = { - 64.9241, 87.4775, 104.5154, 120.6199, 136.5151, 152.3240, - 168.0859, 183.7962, 199.4768, 215.1364, 230.7526 }; - static const double* const FltPtrs[ FltCount ] = { HBKernel_4, - HBKernel_5, HBKernel_6, HBKernel_7, HBKernel_8, HBKernel_9, - HBKernel_10, HBKernel_11, HBKernel_12, HBKernel_13, - HBKernel_14 }; - - static const int FltCountB = 7; // 0.125 - static const double HBKernel_2b[ 2 ] = { // StopAtten = -46.2556 dB - 5.6965643437574798e-001, -7.0243561190601822e-002 }; - static const double HBKernel_3b[ 3 ] = { // StopAtten = -93.6536 dB - 5.9040785316467748e-001, -1.0462338733801557e-001, - 1.4234900265846395e-002 }; - static const double HBKernel_4b[ 4 ] = { // StopAtten = -123.4514 dB - 6.0140277542879073e-001, -1.2564483854573050e-001, - 2.7446500598030887e-002, -3.2051079559036744e-003 }; - static const double HBKernel_5b[ 5 ] = { // StopAtten = -152.4403 dB - 6.0818642429044178e-001, -1.3981140187082763e-001, - 3.8489164053787661e-002, -7.6218861795043225e-003, - 7.5772358126258155e-004 }; - static const double HBKernel_6b[ 6 ] = { // StopAtten = -181.2501 dB - 6.1278392271870352e-001, -1.5000053763409249e-001, - 4.7575323519283508e-002, -1.2320702806281281e-002, - 2.1462442604041065e-003, -1.8425092396978648e-004 }; - static const double HBKernel_7b[ 7 ] = { // StopAtten = -209.9472 dB - 6.1610372237019151e-001, -1.5767891821295410e-001, - 5.5089690570484962e-002, -1.6895755290596615e-002, - 3.9416641999499014e-003, -6.0603620400878633e-004, - 4.5632598748568398e-005 }; - static const double HBKernel_8b[ 8 ] = { // StopAtten = -238.5612 dB - 6.1861282849648180e-001, -1.6367179296640288e-001, - 6.1369859727781417e-002, -2.1184465440918565e-002, - 5.9623352367661475e-003, -1.2483096884685629e-003, - 1.7099294398059683e-004, -1.1448310399897466e-005 }; - static const double FltAttensB[ FltCountB ] = { - 46.2556, 93.6536, 123.4514, 152.4403, 181.2501, 209.9472, - 238.5612 }; - static const double* const FltPtrsB[ FltCountB ] = { HBKernel_2b, - HBKernel_3b, HBKernel_4b, HBKernel_5b, HBKernel_6b, HBKernel_7b, - HBKernel_8b }; - - static const int FltCountC = 5; // 0.0625 - static const double HBKernel_2c[ 2 ] = { // StopAtten = -87.9438 dB - 5.6430278013478086e-001, -6.4338068855764208e-002 }; - static const double HBKernel_3c[ 3 ] = { // StopAtten = -130.8862 dB - 5.8706402915553113e-001, -9.9362380958695873e-002, - 1.2298637065878193e-002 }; - static const double HBKernel_4c[ 4 ] = { // StopAtten = -172.3191 dB - 5.9896586135108265e-001, -1.2111680603660968e-001, - 2.4763118077755664e-002, -2.6121758134936002e-003 }; - static const double HBKernel_5c[ 5 ] = { // StopAtten = -213.4984 dB - 6.0626808278478261e-001, -1.3588224019070938e-001, - 3.5544305138258458e-002, -6.5127022013993230e-003, - 5.8255449020627736e-004 }; - static const double HBKernel_6c[ 6 ] = { // StopAtten = -254.5179 dB - 6.1120171157732273e-001, -1.4654486624691154e-001, - 4.4582957343679119e-002, -1.0840542911916273e-002, - 1.7343703931622656e-003, -1.3363015552414481e-004 }; - static const double FltAttensC[ FltCountC ] = { - 87.9438, 130.8862, 172.3191, 213.4984, 254.5179 }; - static const double* const FltPtrsC[ FltCountC ] = { HBKernel_2c, - HBKernel_3c, HBKernel_4c, HBKernel_5c, HBKernel_6c }; - - static const int FltCountD = 3; // 0.03125 - static const double HBKernel_2d[ 2 ] = { // StopAtten = -113.1456 dB - 5.6295152180538044e-001, -6.2953706070191726e-002 }; - static const double HBKernel_3d[ 3 ] = { // StopAtten = -167.1446 dB - 5.8621968728761675e-001, -9.8080551656624410e-002, - 1.1860868762030571e-002 }; - static const double HBKernel_4d[ 4 ] = { // StopAtten = -220.6519 dB - 5.9835028661892165e-001, -1.1999986095168852e-001, - 2.4132530901858028e-002, -2.4829565783680927e-003 }; - static const double FltAttensD[ FltCountD ] = { - 113.1456, 167.1446, 220.6519 }; - static const double* const FltPtrsD[ FltCountD ] = { HBKernel_2d, - HBKernel_3d, HBKernel_4d }; - - static const int FltCountE = 4; // 0.015625 - static const double HBKernel_1e[ 1 ] = { // StopAtten = -60.9962 dB - 5.0030136284718241e-001 }; - static const double HBKernel_2e[ 2 ] = { // StopAtten = -137.3130 dB - 5.6261293163934145e-001, -6.2613067826625832e-002 }; - static const double HBKernel_3e[ 3 ] = { // StopAtten = -203.2997 dB - 5.8600808139033378e-001, -9.7762185874608526e-002, - 1.1754104552667852e-002 }; - static const double HBKernel_4e[ 4 ] = { // StopAtten = -268.8561 dB - 5.9819599535791312e-001, -1.1972157884617740e-001, - 2.3977307400990484e-002, -2.4517239127622593e-003 }; - static const double FltAttensE[ FltCountE ] = { - 60.9962, 137.3130, 203.2997, 268.8561 }; - static const double* const FltPtrsE[ FltCountE ] = { HBKernel_1e, - HBKernel_2e, HBKernel_3e, HBKernel_4e }; - - static const int FltCountG = 3; - static const double HBKernel_1g[ 1 ] = { // att -93.9165 dB, frac 256.0 - 5.0001882524896712e-001 }; - static const double HBKernel_2g[ 2 ] = { // att -185.4886 dB, frac 256.0 - 5.6250705922473820e-001, -6.2507059756319761e-002 }; - static const double HBKernel_3g[ 3 ] = { // att -275.5531 dB, frac 256.0 - 5.8594191093025305e-001, -9.7662866644414148e-002, - 1.1720955714177778e-002 }; - static const double FltAttensG[ FltCountG ] = { - 93.9165, 185.4886, 275.5531 }; - static const double* const FltPtrsG[ FltCountG ] = { HBKernel_1g, - HBKernel_2g, HBKernel_3g }; - - int k = 0; - - if( SteepIndex <= 0 ) - { - while( k != FltCount - 1 && FltAttens[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrs[ k ]; - fltt = 4 + k; - att = FltAttens[ k ]; - } - else - if( SteepIndex == 1 ) - { - while( k != FltCountB - 1 && FltAttensB[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsB[ k ]; - fltt = 2 + k; - att = FltAttensB[ k ]; - } - else - if( SteepIndex == 2 ) - { - while( k != FltCountC - 1 && FltAttensC[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsC[ k ]; - fltt = 2 + k; - att = FltAttensC[ k ]; - } - else - if( SteepIndex == 3 ) - { - while( k != FltCountD - 1 && FltAttensD[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsD[ k ]; - fltt = 2 + k; - att = FltAttensD[ k ]; - } - else - if( SteepIndex == 4 || SteepIndex == 5 ) - { - while( k != FltCountE - 1 && FltAttensE[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsE[ k ]; - fltt = 1 + k; - att = FltAttensE[ k ]; - } - else - { - while( k != FltCountG - 1 && FltAttensG[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsG[ k ]; - fltt = 1 + k; - att = FltAttensG[ k ]; - } - } - - /** - * Function that provides filter data for various steepness indices and - * attenuations. For 1/3 resamplings. - * - * @param ReqAtten Required half-band filter attentuation. - * @param SteepIndex Steepness index - 0=steepest. Corresponds to general - * upsampling/downsampling ratio, e.g. at 4x 0 is used, at 8x 1 is used, - * etc. - */ - - static void getHBFilterThird( const double ReqAtten, const int SteepIndex, - const double*& flt, int& fltt, double& att ) - { - static const int FltCount = 7; - static const double HBKernel_3[ 3 ] = { // att -75.0994 dB, frac 6.0 - 5.9381789425210385e-001, -1.1030344037819353e-001, - 1.6601396044066741e-002 }; - static const double HBKernel_4[ 4 ] = { // att -102.5310 dB, frac 6.0 - 6.0388679447131843e-001, -1.3043900369548017e-001, - 3.0518777984447295e-002, -3.9738477033171900e-003 }; - static const double HBKernel_5[ 5 ] = { // att -126.5360 dB, frac 6.0 - 6.1014115058940344e-001, -1.4393081816630204e-001, - 4.1760642892854860e-002, -8.9692183234068596e-003, - 9.9871340618369218e-004 }; - static const double HBKernel_6[ 6 ] = { // att -150.1830 dB, frac 6.0 - 6.1439563420561982e-001, -1.5360187826939378e-001, - 5.0840891346007507e-002, -1.4053648740740480e-002, - 2.6771286587896391e-003, -2.5815816045721899e-004 }; - static const double HBKernel_7[ 7 ] = { // att -173.7067 dB, frac 6.0 - 6.1747493476475102e-001, -1.6087373733655960e-001, - 5.8263075644905349e-002, -1.8872408175697929e-002, - 4.7421376553202421e-003, -8.0196529637661137e-004, - 6.7964807425180754e-005 }; - static const double HBKernel_8[ 8 ] = { // att -197.1454 dB, frac 6.0 - 6.1980610946074488e-001, -1.6654070574184196e-001, - 6.4416567396953492e-002, -2.3307744316524541e-002, - 6.9909157209589430e-003, -1.5871946236745982e-003, - 2.4017727258609085e-004, -1.8125308111373566e-005 }; - static const double HBKernel_9[ 9 ] = { // att -220.5199 dB, frac 6.0 - 6.2163188987470752e-001, -1.7108115412330563e-001, - 6.9588371105224839e-002, -2.7339625869282957e-002, - 9.2954473703765472e-003, -2.5537181861669997e-003, - 5.2572296540671394e-004, -7.1813366796731157e-005, - 4.8802392556669750e-006 }; - static const double FltAttens[ FltCount ] = { - 75.0994, 102.5310, 126.5360, 150.1830, 173.7067, 197.1454, - 220.5199 }; - static const double* const FltPtrs[ FltCount ] = { HBKernel_3, - HBKernel_4, HBKernel_5, HBKernel_6, HBKernel_7, HBKernel_8, - HBKernel_9 }; - - static const int FltCountB = 5; - static const double HBKernel_2b[ 2 ] = { // att -75.4413 dB, frac 12.0 - 5.6569875353984056e-001, -6.5811416441328888e-002 }; - static const double HBKernel_3b[ 3 ] = { // att -115.7198 dB, frac 12.0 - 5.8793612182667099e-001, -1.0070583248877137e-001, - 1.2771337947163270e-002 }; - static const double HBKernel_4b[ 4 ] = { // att -152.1528 dB, frac 12.0 - 5.9960155600859322e-001, -1.2228154335192955e-001, - 2.5433718917658079e-002, -2.7537562530760588e-003 }; - static const double HBKernel_5b[ 5 ] = { // att -188.2914 dB, frac 12.0 - 6.0676859170270769e-001, -1.3689667009297382e-001, - 3.6288512627614941e-002, -6.7838855288962756e-003, - 6.2345167652090897e-004 }; - static const double HBKernel_6b[ 6 ] = { // att -224.2705 dB, frac 12.0 - 6.1161456377889145e-001, -1.4743902036519768e-001, - 4.5344160828746795e-002, -1.1207372108402218e-002, - 1.8328498006058664e-003, -1.4518194076022933e-004 }; - static const double FltAttensB[ FltCountB ] = { - 75.4413, 115.7198, 152.1528, 188.2914, 224.2705 }; - static const double* const FltPtrsB[ FltCountB ] = { HBKernel_2b, - HBKernel_3b, HBKernel_4b, HBKernel_5b, HBKernel_6b }; - - static const int FltCountC = 4; - static const double HBKernel_2c[ 2 ] = { // att -102.9806 dB, frac 24.0 - 5.6330232648142842e-001, -6.3309247177420730e-002 }; - static const double HBKernel_3c[ 3 ] = { // att -152.1187 dB, frac 24.0 - 5.8643891113575064e-001, -9.8411593011501639e-002, - 1.1972706651455891e-002 }; - static const double HBKernel_4c[ 4 ] = { // att -200.6182 dB, frac 24.0 - 5.9851012364429712e-001, -1.2028885240905723e-001, - 2.4294521088349529e-002, -2.5157924167197453e-003 }; - static const double HBKernel_5c[ 5 ] = { // att -248.8728 dB, frac 24.0 - 6.0590922849004858e-001, -1.3515953371903033e-001, - 3.5020856634677522e-002, -6.3256195330255094e-003, - 5.5506812768978109e-004 }; - static const double FltAttensC[ FltCountC ] = { - 102.9806, 152.1187, 200.6182, 248.8728 }; - static const double* const FltPtrsC[ FltCountC ] = { HBKernel_2c, - HBKernel_3c, HBKernel_4c, HBKernel_5c }; - - static const int FltCountD = 4; - static const double HBKernel_1d[ 1 ] = { // att -48.6615 dB, frac 48.0 - 5.0053598654836240e-001 }; - static const double HBKernel_2d[ 2 ] = { // att -127.3033 dB, frac 48.0 - 5.6270074379958679e-001, -6.2701174487726163e-002 }; - static const double HBKernel_3d[ 3 ] = { // att -188.2989 dB, frac 48.0 - 5.8606296210257025e-001, -9.7844644764129421e-002, - 1.1781683046197223e-002 }; - static const double HBKernel_4d[ 4 ] = { // att -248.8578 dB, frac 48.0 - 5.9823601283411165e-001, -1.1979369067338455e-001, - 2.4017459011435899e-002, -2.4597811725236445e-003 }; - static const double FltAttensD[ FltCountD ] = { - 48.6615, 127.3033, 188.2989, 248.8578 }; - static const double* const FltPtrsD[ FltCountD ] = { HBKernel_1d, - HBKernel_2d, HBKernel_3d, HBKernel_4d }; - - static const int FltCountE = 3; - static const double HBKernel_1e[ 1 ] = { // att -73.2782 dB, frac 96.0 - 5.0013388897382527e-001 }; - static const double HBKernel_2e[ 2 ] = { // att -151.4076 dB, frac 96.0 - 5.6255019604318290e-001, -6.2550222932385172e-002 }; - static const double HBKernel_3e[ 3 ] = { // att -224.4366 dB, frac 96.0 - 5.8596887233874539e-001, -9.7703321108182931e-002, - 1.1734448775437802e-002 }; - static const double FltAttensE[ FltCountE ] = { - 73.2782, 151.4076, 224.4366 }; - static const double* const FltPtrsE[ FltCountE ] = { HBKernel_1e, - HBKernel_2e, HBKernel_3e }; - - static const int FltCountG = 3; - static const double HBKernel_1g[ 1 ] = { // att -101.2873 dB, frac 384.0 - 5.0000836666064941e-001 }; - static const double HBKernel_2g[ 2 ] = { // att -199.5761 dB, frac 384.0 - 5.6250313744967606e-001, -6.2503137554676916e-002 }; - static const double HBKernel_3g[ 3 ] = { // att -296.4833 dB, frac 384.0 - 5.8593945769687561e-001, -9.7659186594368730e-002, - 1.1719728897494584e-002 }; - static const double FltAttensG[ FltCountG ] = { - 101.2873, 199.5761, 296.4833 }; - static const double* const FltPtrsG[ FltCountG ] = { HBKernel_1g, - HBKernel_2g, HBKernel_3g }; - - int k = 0; - - if( SteepIndex <= 0 ) - { - while( k != FltCount - 1 && FltAttens[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrs[ k ]; - fltt = 3 + k; - att = FltAttens[ k ]; - } - else - if( SteepIndex == 1 ) - { - while( k != FltCountB - 1 && FltAttensB[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsB[ k ]; - fltt = 2 + k; - att = FltAttensB[ k ]; - } - else - if( SteepIndex == 2 ) - { - while( k != FltCountC - 1 && FltAttensC[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsC[ k ]; - fltt = 2 + k; - att = FltAttensC[ k ]; - } - else - if( SteepIndex == 3 ) - { - while( k != FltCountD - 1 && FltAttensD[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsD[ k ]; - fltt = 1 + k; - att = FltAttensD[ k ]; - } - else - if( SteepIndex == 4 || SteepIndex == 5 ) - { - while( k != FltCountE - 1 && FltAttensE[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsE[ k ]; - fltt = 1 + k; - att = FltAttensE[ k ]; - } - else - { - while( k != FltCountG - 1 && FltAttensG[ k ] < ReqAtten ) - { - k++; - } - - flt = FltPtrsG[ k ]; - fltt = 1 + k; - att = FltAttensG[ k ]; - } - } - - /** - * Constructor initalizes the half-band upsampler. - * - * @param ReqAtten Required half-band filter attentuation. - * @param SteepIndex Steepness index - 0=steepest. Corresponds to general - * upsampling ratio, e.g. at 4x upsampling 0 is used, at 8x upsampling 1 - * is used, etc. - * @param IsThird "True" if 1/3 resampling is performed. - * @param PrevLatency Latency, in samples (any value >=0), which was left - * in the output signal by a previous process. Whole-number latency will - * be consumed by *this object while remaining fractional latency can be - * obtained via the getLatencyFrac() function. - */ - - CDSPHBUpsampler( const double ReqAtten, const int SteepIndex, - const bool IsThird, const double PrevLatency ) - { - static const CConvolveFn FltConvFn[ 14 ] = { - &CDSPHBUpsampler :: convolve1, &CDSPHBUpsampler :: convolve2, - &CDSPHBUpsampler :: convolve3, &CDSPHBUpsampler :: convolve4, - &CDSPHBUpsampler :: convolve5, &CDSPHBUpsampler :: convolve6, - &CDSPHBUpsampler :: convolve7, &CDSPHBUpsampler :: convolve8, - &CDSPHBUpsampler :: convolve9, &CDSPHBUpsampler :: convolve10, - &CDSPHBUpsampler :: convolve11, &CDSPHBUpsampler :: convolve12, - &CDSPHBUpsampler :: convolve13, &CDSPHBUpsampler :: convolve14 }; - - int fltt; - double att; - - if( IsThird ) - { - getHBFilterThird( ReqAtten, SteepIndex, fltp, fltt, att ); - } - else - { - getHBFilter( ReqAtten, SteepIndex, fltp, fltt, att ); - } - - convfn = FltConvFn[ fltt - 1 ]; - fll = fltt - 1; - fl2 = fltt; - flo = fll + fl2; - - LatencyFrac = PrevLatency * 2.0; - Latency = (int) LatencyFrac; - LatencyFrac -= Latency; - - R8BCONSOLE( "CDSPHBUpsampler: sti=%i third=%i taps=%i att=%.1f " - "io=2/1\n", SteepIndex, (int) IsThird, fltt, att ); - - clear(); - } - - virtual int getLatency() const - { - return( 0 ); - } - - virtual double getLatencyFrac() const - { - return( LatencyFrac ); - } - - virtual int getMaxOutLen( const int MaxInLen ) const - { - R8BASSERT( MaxInLen >= 0 ); - - return( MaxInLen * 2 ); - } - - virtual void clear() - { - LatencyLeft = Latency; - BufLeft = 0; - WritePos = 0; - ReadPos = BufLen - fll; // Set "read" position to - // account for filter's latency. - - memset( &Buf[ ReadPos ], 0, fll * sizeof( double )); - } - - virtual int process( double* ip, int l, double*& op0 ) - { - R8BASSERT( l >= 0 ); - - double* op = op0; - - while( l > 0 ) - { - // Add new input samples to both halves of the ring buffer. - - const int b = min( min( l, BufLen - WritePos ), - BufLen - fll - BufLeft ); - - double* const wp1 = Buf + WritePos; - memcpy( wp1, ip, b * sizeof( double )); - - if( WritePos < flo ) - { - const int c = min( b, flo - WritePos ); - memcpy( wp1 + BufLen, wp1, c * sizeof( double )); - } - - ip += b; - WritePos = ( WritePos + b ) & BufLenMask; - l -= b; - BufLeft += b; - - if( BufLeft > fl2 ) - { - const int c = BufLeft - fl2; - - double* const opend = op + c + c; - ( *convfn )( op, opend, fltp, Buf + fll, ReadPos ); - - op = opend; - BufLeft -= c; - } - } - - int ol = (int) ( op - op0 ); - - if( LatencyLeft > 0 ) - { - if( LatencyLeft >= ol ) - { - LatencyLeft -= ol; - return( 0 ); - } - - ol -= LatencyLeft; - op0 += LatencyLeft; - LatencyLeft = 0; - } - - return( ol ); - } - -private: - static const int BufLenBits = 8; ///< The length of the ring buffer, - ///< expressed as Nth power of 2. This value can be reduced if it is - ///< known that only short input buffers will be passed to the - ///< interpolator. The minimum value of this parameter is 5, and - ///< 1 << BufLenBits should be at least 3 times larger than the - ///< FilterLen. - ///< - static const int BufLen = 1 << BufLenBits; ///< The length of the ring - ///< buffer. The actual length is twice as long to allow "beyond max - ///< position" positioning. - ///< - static const int BufLenMask = BufLen - 1; ///< Mask used for quick buffer - ///< position wrapping. - ///< - double Buf[ BufLen + 27 ]; ///< The ring buffer, including overrun - ///< protection for the largest filter. - ///< - const double* fltp; ///< Half-band filter taps. - ///< - int fll; ///< Input latency. - ///< - int fl2; ///< Right-side filter length. - ///< - int flo; ///< Overrrun length. - ///< - int Latency; ///< Initial latency that should be removed from the output. - ///< - double LatencyFrac; ///< Fractional latency left on the output. - ///< - int BufLeft; ///< The number of samples left in the buffer to process. - ///< When this value is below FilterLenD2Plus1, the interpolation - ///< cycle ends. - ///< - int WritePos; ///< The current buffer write position. Incremented together - ///< with the BufLeft variable. - ///< - int ReadPos; ///< The current buffer read position. - ///< - int LatencyLeft; ///< Latency left to remove. - ///< - typedef void( *CConvolveFn )( double* op, double* const opend, - const double* const flt, const double* const rp0, int& ReadPos0 ); ///< - ///< Convolution funtion type. - ///< - CConvolveFn convfn; ///< Convolution function in use. - ///< - -#define R8BHBC1( fn ) \ - static void fn( double* op, double* const opend, const double* const flt, \ - const double* const rp0, int& ReadPos0 ) \ - { \ - int rpos = ReadPos0; \ - while( op < opend ) \ - { \ - const double* const rp = rp0 + rpos; \ - op[ 0 ] = rp[ 0 ]; \ - op[ 1 ] = - -#define R8BHBC2 \ - rpos = ( rpos + 1 ) & BufLenMask; \ - op += 2; \ - } \ - ReadPos0 = rpos; \ - } - - R8BHBC1( convolve1 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]); - R8BHBC2 - - R8BHBC1( convolve2 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]); - R8BHBC2 - - R8BHBC1( convolve3 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]); - R8BHBC2 - - R8BHBC1( convolve4 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]); - R8BHBC2 - - R8BHBC1( convolve5 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]); - R8BHBC2 - - R8BHBC1( convolve6 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]) + - flt[ 5 ] * ( rp[ 6 ] + rp[ -5 ]); - R8BHBC2 - - R8BHBC1( convolve7 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]) + - flt[ 5 ] * ( rp[ 6 ] + rp[ -5 ]) + - flt[ 6 ] * ( rp[ 7 ] + rp[ -6 ]); - R8BHBC2 - - R8BHBC1( convolve8 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]) + - flt[ 5 ] * ( rp[ 6 ] + rp[ -5 ]) + - flt[ 6 ] * ( rp[ 7 ] + rp[ -6 ]) + - flt[ 7 ] * ( rp[ 8 ] + rp[ -7 ]); - R8BHBC2 - - R8BHBC1( convolve9 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]) + - flt[ 5 ] * ( rp[ 6 ] + rp[ -5 ]) + - flt[ 6 ] * ( rp[ 7 ] + rp[ -6 ]) + - flt[ 7 ] * ( rp[ 8 ] + rp[ -7 ]) + - flt[ 8 ] * ( rp[ 9 ] + rp[ -8 ]); - R8BHBC2 - - R8BHBC1( convolve10 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]) + - flt[ 5 ] * ( rp[ 6 ] + rp[ -5 ]) + - flt[ 6 ] * ( rp[ 7 ] + rp[ -6 ]) + - flt[ 7 ] * ( rp[ 8 ] + rp[ -7 ]) + - flt[ 8 ] * ( rp[ 9 ] + rp[ -8 ]) + - flt[ 9 ] * ( rp[ 10 ] + rp[ -9 ]); - R8BHBC2 - - R8BHBC1( convolve11 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]) + - flt[ 5 ] * ( rp[ 6 ] + rp[ -5 ]) + - flt[ 6 ] * ( rp[ 7 ] + rp[ -6 ]) + - flt[ 7 ] * ( rp[ 8 ] + rp[ -7 ]) + - flt[ 8 ] * ( rp[ 9 ] + rp[ -8 ]) + - flt[ 9 ] * ( rp[ 10 ] + rp[ -9 ]) + - flt[ 10 ] * ( rp[ 11 ] + rp[ -10 ]); - R8BHBC2 - - R8BHBC1( convolve12 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]) + - flt[ 5 ] * ( rp[ 6 ] + rp[ -5 ]) + - flt[ 6 ] * ( rp[ 7 ] + rp[ -6 ]) + - flt[ 7 ] * ( rp[ 8 ] + rp[ -7 ]) + - flt[ 8 ] * ( rp[ 9 ] + rp[ -8 ]) + - flt[ 9 ] * ( rp[ 10 ] + rp[ -9 ]) + - flt[ 10 ] * ( rp[ 11 ] + rp[ -10 ]) + - flt[ 11 ] * ( rp[ 12 ] + rp[ -11 ]); - R8BHBC2 - - R8BHBC1( convolve13 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]) + - flt[ 5 ] * ( rp[ 6 ] + rp[ -5 ]) + - flt[ 6 ] * ( rp[ 7 ] + rp[ -6 ]) + - flt[ 7 ] * ( rp[ 8 ] + rp[ -7 ]) + - flt[ 8 ] * ( rp[ 9 ] + rp[ -8 ]) + - flt[ 9 ] * ( rp[ 10 ] + rp[ -9 ]) + - flt[ 10 ] * ( rp[ 11 ] + rp[ -10 ]) + - flt[ 11 ] * ( rp[ 12 ] + rp[ -11 ]) + - flt[ 12 ] * ( rp[ 13 ] + rp[ -12 ]); - R8BHBC2 - - R8BHBC1( convolve14 ) - flt[ 0 ] * ( rp[ 1 ] + rp[ 0 ]) + - flt[ 1 ] * ( rp[ 2 ] + rp[ -1 ]) + - flt[ 2 ] * ( rp[ 3 ] + rp[ -2 ]) + - flt[ 3 ] * ( rp[ 4 ] + rp[ -3 ]) + - flt[ 4 ] * ( rp[ 5 ] + rp[ -4 ]) + - flt[ 5 ] * ( rp[ 6 ] + rp[ -5 ]) + - flt[ 6 ] * ( rp[ 7 ] + rp[ -6 ]) + - flt[ 7 ] * ( rp[ 8 ] + rp[ -7 ]) + - flt[ 8 ] * ( rp[ 9 ] + rp[ -8 ]) + - flt[ 9 ] * ( rp[ 10 ] + rp[ -9 ]) + - flt[ 10 ] * ( rp[ 11 ] + rp[ -10 ]) + - flt[ 11 ] * ( rp[ 12 ] + rp[ -11 ]) + - flt[ 12 ] * ( rp[ 13 ] + rp[ -12 ]) + - flt[ 13 ] * ( rp[ 14 ] + rp[ -13 ]); - R8BHBC2 - -#undef R8BHBC1 -#undef R8BHBC2 -}; - -// --------------------------------------------------------------------------- - -} // namespace r8b - -#endif // R8B_CDSPHBUPSAMPLER_INCLUDED diff --git a/src/third_party/r8b/CDSPProcessor.h b/src/third_party/r8b/CDSPProcessor.h deleted file mode 100644 index 1bdcabb..0000000 --- a/src/third_party/r8b/CDSPProcessor.h +++ /dev/null @@ -1,103 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file CDSPProcessor.h - * - * @brief The base virtual class for DSP processing algorithms. - * - * This file includes the base virtual class for DSP processing algorithm - * classes like FIR filtering and interpolation. - * - * r8brain-free-src Copyright (c) 2013-2018 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8B_CDSPPROCESSOR_INCLUDED -#define R8B_CDSPPROCESSOR_INCLUDED - -#include "r8bbase.h" - -namespace r8b { - -/** - * @brief The base virtual class for DSP processing algorithms. - * - * This class can be used as a base class for various DSP processing - * algorithms (processors). DSP processors that are derived from this class - * can be seamlessly integrated into various DSP processing graphs. - */ - -class CDSPProcessor : public R8B_BASECLASS -{ - R8BNOCTOR( CDSPProcessor ); - -public: - CDSPProcessor() - { - } - - virtual ~CDSPProcessor() - { - } - - /** - * @return The latency, in samples, which is present in the output signal. - * This value is usually zero if the DSP processor "consumes" the latency - * automatically. - */ - - virtual int getLatency() const = 0; - - /** - * @return Fractional latency, in samples, which is present in the output - * signal. This value is usually zero if a linear-phase filtering is used. - * With minimum-phase filters in use, this value can be non-zero even if - * the getLatency() function returns zero. - */ - - virtual double getLatencyFrac() const = 0; - - /** - * @param MaxInLen The number of samples planned to process at once, at - * most. - * @return The maximal length of the output buffer required when - * processing the "MaxInLen" number of input samples. - */ - - virtual int getMaxOutLen( const int MaxInLen ) const = 0; - - /** - * Function clears (resets) the state of *this object and returns it to - * the state after construction. All input data accumulated in the - * internal buffer so far will be discarded. - */ - - virtual void clear() = 0; - - /** - * Function performs DSP processing. - * - * @param ip Input data pointer. - * @param l0 How many samples to process. - * @param[out] op0 Output data pointer. The capacity of this buffer should - * be equal to the value returned by the getMaxOutLen() function for the - * given "l0". This buffer can be equal to "ip" only if the - * getMaxOutLen( l0 ) function returned a value lesser than "l0". This - * pointer can be incremented on function's return if latency compensation - * was performed by the processor. Note that on function's return, this - * pointer may point to some internal buffers, including the "ip" buffer, - * ignoring the originally passed value. - * @return The number of output samples written to the "op0" buffer and - * available after processing. This value can be smaller or larger in - * comparison to the original "l0" value due to processing and filter's - * latency compensation that took place, and due to resampling if it was - * performed. - */ - - virtual int process( double* ip, int l0, double*& op0 ) = 0; -}; - -} // namespace r8b - -#endif // R8B_CDSPPROCESSOR_INCLUDED diff --git a/src/third_party/r8b/CDSPRealFFT.h b/src/third_party/r8b/CDSPRealFFT.h deleted file mode 100644 index ae134df..0000000 --- a/src/third_party/r8b/CDSPRealFFT.h +++ /dev/null @@ -1,718 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file CDSPRealFFT.h - * - * @brief Real-valued FFT transform class. - * - * This file includes FFT object implementation. All created FFT objects are - * kept in a global list after use for future reusal. Such approach minimizes - * time necessary to initialize the FFT object of the required length. - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8B_CDSPREALFFT_INCLUDED -#define R8B_CDSPREALFFT_INCLUDED - -#include "r8bbase.h" - -#if !R8B_IPP && !R8B_PFFFT - #include "fft4g.h" -#endif // !R8B_IPP && !R8B_PFFFT - -namespace r8b { - -/** - * @brief Real-valued FFT transform class. - * - * Class implements a wrapper for real-valued discrete fast Fourier transform - * functions. The object of this class can only be obtained via the - * CDSPRealFFTKeeper class. - * - * Uses functions from the FFT package by: Copyright(C) 1996-2001 Takuya OOURA - * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html - * - * Also uses Intel IPP library functions if available (the R8B_IPP=1 macro was - * defined). Note that IPP library's FFT functions are 2-3 times more - * efficient on the modern Intel Core i7-3770K processor than Ooura's - * functions. It may be worthwhile investing in IPP. Note, that FFT functions - * take less than 20% of the overall sample rate conversion time. However, - * when the "power of 2" resampling is used the performance of FFT functions - * becomes "everything". - */ - -class CDSPRealFFT : public R8B_BASECLASS -{ - R8BNOCTOR( CDSPRealFFT ); - - friend class CDSPRealFFTKeeper; - -public: - /** - * @return A multiplication constant that should be used after inverse - * transform to obtain a correct value scale. - */ - - double getInvMulConst() const - { - return( InvMulConst ); - } - - /** - * @return The length (the number of real values in a transform) of *this - * FFT object, expressed as Nth power of 2. - */ - - int getLenBits() const - { - return( LenBits ); - } - - /** - * @return The length (the number of real values in a transform) of *this - * FFT object. - */ - - int getLen() const - { - return( Len ); - } - - /** - * Function performs in-place forward FFT. - * - * @param[in,out] p Pointer to data block to transform, length should be - * equal to *this object's getLen(). - */ - - void forward( double* const p ) const - { - #if R8B_FLOATFFT - - float* const op = (float*) p; - int i; - - for( i = 0; i < Len; i++ ) - { - op[ i ] = (float) p[ i ]; - } - - #endif // R8B_FLOATFFT - - #if R8B_IPP - - ippsFFTFwd_RToPerm_64f( p, p, SPtr, WorkBuffer ); - - #elif R8B_PFFFT - - pffft_transform_ordered( setup, op, op, work, PFFFT_FORWARD ); - - #else // R8B_PFFFT - - ooura_fft :: rdft( Len, 1, p, wi.getPtr(), wd.getPtr() ); - - #endif // R8B_IPP - } - - /** - * Function performs in-place inverse FFT. - * - * @param[in,out] p Pointer to data block to transform, length should be - * equal to *this object's getLen(). - */ - - void inverse( double* const p ) const - { - #if R8B_IPP - - ippsFFTInv_PermToR_64f( p, p, SPtr, WorkBuffer ); - - #elif R8B_PFFFT - - pffft_transform_ordered( setup, (float*) p, (float*) p, work, - PFFFT_BACKWARD ); - - #else // R8B_PFFFT - - ooura_fft :: rdft( Len, -1, p, wi.getPtr(), wd.getPtr() ); - - #endif // R8B_IPP - - #if R8B_FLOATFFT - - const float* const ip = (const float*) p; - int i; - - for( i = Len - 1; i >= 0; i-- ) - { - p[ i ] = ip[ i ]; - } - - #endif // R8B_FLOATFFT - } - - /** - * Function multiplies two complex-valued data blocks and places result in - * a new data block. Length of all data blocks should be equal to *this - * object's block length. Input blocks should have been produced with the - * forward() function of *this object. - * - * @param aip1 Input data block 1. - * @param aip2 Input data block 2. - * @param[out] aop Output data block, should not be equal to aip1 nor - * aip2. - */ - - void multiplyBlocks( const double* const aip1, const double* const aip2, - double* const aop ) const - { - #if R8B_FLOATFFT - - const float* const ip1 = (const float*) aip1; - const float* const ip2 = (const float*) aip2; - float* const op = (float*) aop; - - #else // R8B_FLOATFFT - - const double* const ip1 = aip1; - const double* const ip2 = aip2; - double* const op = aop; - - #endif // R8B_FLOATFFT - - #if R8B_IPP - - ippsMulPerm_64f( (Ipp64f*) ip1, (Ipp64f*) ip2, (Ipp64f*) op, Len ); - - #else // R8B_IPP - - op[ 0 ] = ip1[ 0 ] * ip2[ 0 ]; - op[ 1 ] = ip1[ 1 ] * ip2[ 1 ]; - - int i = 2; - - while( i < Len ) - { - op[ i ] = ip1[ i ] * ip2[ i ] - ip1[ i + 1 ] * ip2[ i + 1 ]; - op[ i + 1 ] = ip1[ i ] * ip2[ i + 1 ] + ip1[ i + 1 ] * ip2[ i ]; - i += 2; - } - - #endif // R8B_IPP - } - - /** - * Function multiplies two complex-valued data blocks in-place. Length of - * both data blocks should be equal to *this object's block length. Blocks - * should have been produced with the forward() function of *this object. - * - * @param aip Input data block 1. - * @param[in,out] aop Output/input data block 2. - */ - - void multiplyBlocks( const double* const aip, double* const aop ) const - { - #if R8B_FLOATFFT - - const float* const ip = (const float*) aip; - float* const op = (float*) aop; - float t; - - #else // R8B_FLOATFFT - - const double* const ip = aip; - double* const op = aop; - double t; - - #endif // R8B_FLOATFFT - - #if R8B_IPP - - ippsMulPerm_64f( (Ipp64f*) op, (Ipp64f*) ip, (Ipp64f*) op, Len ); - - #else // R8B_IPP - - op[ 0 ] *= ip[ 0 ]; - op[ 1 ] *= ip[ 1 ]; - - int i = 2; - - while( i < Len ) - { - t = op[ i ] * ip[ i ] - op[ i + 1 ] * ip[ i + 1 ]; - op[ i + 1 ] = op[ i ] * ip[ i + 1 ] + op[ i + 1 ] * ip[ i ]; - op[ i ] = t; - i += 2; - } - - #endif // R8B_IPP - } - - /** - * Function multiplies two complex-valued data blocks in-place, - * considering that the "ip" block contains "zero-phase" response. Length - * of both data blocks should be equal to *this object's block length. - * Blocks should have been produced with the forward() function of *this - * object. - * - * @param aip Input data block 1, "zero-phase" response. This block should - * be first transformed via the convertToZ() function. - * @param[in,out] aop Output/input data block 2. - */ - - void multiplyBlocksZ( const double* const aip, double* const aop ) const - { - #if R8B_FLOATFFT - - const float* const ip = (const float*) aip; - float* const op = (float*) aop; - - #else // R8B_FLOATFFT - - const double* const ip = aip; - double* const op = aop; - - #endif // R8B_FLOATFFT - - #if R8B_IPP - - ippsMul_64f_I( (const Ipp64f*) ip, (Ipp64f*) op, Len ); - - #else // R8B_IPP - - int i; - - for( i = 0; i < Len; i++ ) - { - op[ i ] *= ip[ i ]; - } - - #endif // R8B_IPP - } - - /** - * Function converts the specified forward-transformed block into - * "zero-phase" form suitable for use with the multiplyBlocksZ() function. - * - * @param[in,out] ap Block to transform. - */ - - void convertToZ( double* const ap ) const - { - #if R8B_FLOATFFT - - float* const p = (float*) ap; - - #else // R8B_FLOATFFT - - double* const p = ap; - - #endif // R8B_FLOATFFT - - int i = 2; - - while( i < Len ) - { - p[ i + 1 ] = p[ i ]; - i += 2; - } - } - -private: - int LenBits; ///< Length of FFT block (expressed as Nth power of 2). - ///< - int Len; ///< Length of FFT block (number of real values). - ///< - double InvMulConst; ///< Inverse FFT multiply constant. - ///< - CDSPRealFFT* Next; ///< Next object in a singly-linked list. - ///< - - #if R8B_IPP - IppsFFTSpec_R_64f* SPtr; ///< Pointer to initialized data buffer - ///< to be passed to IPP's FFT functions. - ///< - CFixedBuffer< unsigned char > SpecBuffer; ///< Working buffer. - ///< - CFixedBuffer< unsigned char > WorkBuffer; ///< Working buffer. - ///< - #elif R8B_PFFFT - PFFFT_Setup* setup; ///< PFFFT setup object. - ///< - CFixedBuffer< float > work; ///< Working buffer. - ///< - #else // R8B_PFFFT - CFixedBuffer< int > wi; ///< Working buffer (ints). - ///< - CFixedBuffer< double > wd; ///< Working buffer (doubles). - ///< - #endif // R8B_IPP - - /** - * A simple class that keeps the pointer to the object and deletes it - * automatically. - */ - - class CObjKeeper - { - R8BNOCTOR( CObjKeeper ); - - public: - CObjKeeper() - : Object( NULL ) - { - } - - ~CObjKeeper() - { - delete Object; - } - - CObjKeeper& operator = ( CDSPRealFFT* const aObject ) - { - Object = aObject; - return( *this ); - } - - operator CDSPRealFFT* () const - { - return( Object ); - } - - private: - CDSPRealFFT* Object; ///< FFT object being kept. - ///< - }; - - CDSPRealFFT() - { - } - - /** - * Constructor initializes FFT object. - * - * @param aLenBits The length of FFT block (Nth power of 2), specifies the - * number of real values in a block. Values from 1 to 30 inclusive are - * supported. - */ - - CDSPRealFFT( const int aLenBits ) - : LenBits( aLenBits ) - , Len( 1 << aLenBits ) - #if R8B_IPP - , InvMulConst( 1.0 / Len ) - #elif R8B_PFFFT - , InvMulConst( 1.0 / Len ) - #else // R8B_PFFFT - , InvMulConst( 2.0 / Len ) - #endif // R8B_IPP - { - #if R8B_IPP - - int SpecSize; - int SpecBufferSize; - int BufferSize; - - ippsFFTGetSize_R_64f( LenBits, IPP_FFT_NODIV_BY_ANY, - ippAlgHintFast, &SpecSize, &SpecBufferSize, &BufferSize ); - - CFixedBuffer< unsigned char > InitBuffer( SpecBufferSize ); - SpecBuffer.alloc( SpecSize ); - WorkBuffer.alloc( BufferSize ); - - ippsFFTInit_R_64f( &SPtr, LenBits, IPP_FFT_NODIV_BY_ANY, - ippAlgHintFast, SpecBuffer, InitBuffer ); - - #elif R8B_PFFFT - - setup = pffft_new_setup( Len, PFFFT_REAL ); - work.alloc( Len ); - - #else // R8B_PFFFT - - wi.alloc( (int) ceil( 2.0 + sqrt( (double) ( Len >> 1 )))); - wi[ 0 ] = 0; - wd.alloc( Len >> 1 ); - - #endif // R8B_IPP - } - - ~CDSPRealFFT() - { - #if R8B_PFFFT - pffft_destroy_setup( setup ); - #endif // R8B_PFFFT - - delete Next; - } -}; - -/** - * @brief A "keeper" class for real-valued FFT transform objects. - * - * Class implements "keeper" functionality for handling CDSPRealFFT objects. - * The allocated FFT objects are placed on the global static list of objects - * for future reuse instead of deallocation. - */ - -class CDSPRealFFTKeeper : public R8B_BASECLASS -{ - R8BNOCTOR( CDSPRealFFTKeeper ); - -public: - CDSPRealFFTKeeper() - : Object( NULL ) - { - } - - /** - * Function acquires FFT object with the specified block length. - * - * @param LenBits The length of FFT block (Nth power of 2), in the range - * [1; 30] inclusive, specifies the number of real values in a FFT block. - */ - - CDSPRealFFTKeeper( const int LenBits ) - { - Object = acquire( LenBits ); - } - - ~CDSPRealFFTKeeper() - { - if( Object != NULL ) - { - release( Object ); - } - } - - /** - * @return Pointer to the acquired FFT object. - */ - - const CDSPRealFFT* operator -> () const - { - R8BASSERT( Object != NULL ); - - return( Object ); - } - - /** - * Function acquires FFT object with the specified block length. This - * function can be called any number of times. - * - * @param LenBits The length of FFT block (Nth power of 2), in the range - * [1; 30] inclusive, specifies the number of real values in a FFT block. - */ - - void init( const int LenBits ) - { - if( Object != NULL ) - { - if( Object -> LenBits == LenBits ) - { - return; - } - - release( Object ); - } - - Object = acquire( LenBits ); - } - - /** - * Function releases a previously acquired FFT object. - */ - - void reset() - { - if( Object != NULL ) - { - release( Object ); - Object = NULL; - } - } - -private: - CDSPRealFFT* Object; ///< FFT object. - ///< - - static CSyncObject StateSync; ///< FFTObjects synchronizer. - ///< - static CDSPRealFFT :: CObjKeeper FFTObjects[]; ///< Pool of FFT objects of - ///< various lengths. - ///< - - /** - * Function acquires FFT object from the global pool. - * - * @param LenBits FFT block length (expressed as Nth power of 2). - */ - - CDSPRealFFT* acquire( const int LenBits ) - { - R8BASSERT( LenBits > 0 && LenBits <= 30 ); - - R8BSYNC( StateSync ); - - if( FFTObjects[ LenBits ] == NULL ) - { - return( new CDSPRealFFT( LenBits )); - } - - CDSPRealFFT* ffto = FFTObjects[ LenBits ]; - FFTObjects[ LenBits ] = ffto -> Next; - - return( ffto ); - } - - /** - * Function releases a previously acquired FFT object. - * - * @param ffto FFT object to release. - */ - - void release( CDSPRealFFT* const ffto ) - { - R8BSYNC( StateSync ); - - ffto -> Next = FFTObjects[ ffto -> LenBits ]; - FFTObjects[ ffto -> LenBits ] = ffto; - } -}; - -/** - * Function calculates the minimum-phase transform of the filter kernel, using - * a discrete Hilbert transform in cepstrum domain. - * - * For more details, see part III.B of - * http://www.hpl.hp.com/personal/Niranjan_Damera-Venkata/files/ComplexMinPhase.pdf - * - * @param[in,out] Kernel Filter kernel buffer. - * @param KernelLen Filter kernel's length, in samples. - * @param LenMult Kernel length multiplier. Used as a coefficient of the - * "oversampling" in the frequency domain. Such oversampling is needed to - * improve the precision of the minimum-phase transform. If the filter's - * attenuation is high, this multiplier should be increased or otherwise the - * required attenuation will not be reached due to "smoothing" effect of this - * transform. - * @param DoFinalMul "True" if the final multiplication after transform should - * be performed or not. Such multiplication returns the gain of the signal to - * its original value. This parameter can be set to "false" if normalization - * of the resulting filter kernel is planned to be used. - * @param[out] DCGroupDelay If not NULL, this variable receives group delay - * at DC offset, in samples (can be a non-integer value). - */ - -inline void calcMinPhaseTransform( double* const Kernel, const int KernelLen, - const int LenMult = 2, const bool DoFinalMul = true, - double* const DCGroupDelay = NULL ) -{ - R8BASSERT( KernelLen > 0 ); - R8BASSERT( LenMult >= 2 ); - - const int LenBits = getBitOccupancy(( KernelLen * LenMult ) - 1 ); - const int Len = 1 << LenBits; - const int Len2 = Len >> 1; - int i; - - CFixedBuffer< double > ip( Len ); - CFixedBuffer< double > ip2( Len2 + 1 ); - - memcpy( &ip[ 0 ], Kernel, KernelLen * sizeof( double )); - memset( &ip[ KernelLen ], 0, ( Len - KernelLen ) * sizeof( double )); - - CDSPRealFFTKeeper ffto( LenBits ); - ffto -> forward( ip ); - - // Create the "log |c|" spectrum while saving the original power spectrum - // in the "ip2" buffer. - - #if R8B_FLOATFFT - float* const aip = (float*) &ip[ 0 ]; - float* const aip2 = (float*) &ip2[ 0 ]; - const float nzbias = 1e-35; - #else // R8B_FLOATFFT - double* const aip = &ip[ 0 ]; - double* const aip2 = &ip2[ 0 ]; - const double nzbias = 1e-300; - #endif // R8B_FLOATFFT - - aip2[ 0 ] = aip[ 0 ]; - aip[ 0 ] = log( fabs( aip[ 0 ]) + nzbias ); - aip2[ Len2 ] = aip[ 1 ]; - aip[ 1 ] = log( fabs( aip[ 1 ]) + nzbias ); - - for( i = 1; i < Len2; i++ ) - { - aip2[ i ] = sqrt( aip[ i * 2 ] * aip[ i * 2 ] + - aip[ i * 2 + 1 ] * aip[ i * 2 + 1 ]); - - aip[ i * 2 ] = log( aip2[ i ] + nzbias ); - aip[ i * 2 + 1 ] = 0.0; - } - - // Convert to cepstrum and apply discrete Hilbert transform. - - ffto -> inverse( ip ); - - const double m1 = ffto -> getInvMulConst(); - const double m2 = -m1; - - ip[ 0 ] = 0.0; - - for( i = 1; i < Len2; i++ ) - { - ip[ i ] *= m1; - } - - ip[ Len2 ] = 0.0; - - for( i = Len2 + 1; i < Len; i++ ) - { - ip[ i ] *= m2; - } - - // Convert Hilbert-transformed cepstrum back to the "log |c|" spectrum and - // perform its exponentiation, multiplied by the power spectrum previously - // saved in the "ip2" buffer. - - ffto -> forward( ip ); - - aip[ 0 ] = aip2[ 0 ]; - aip[ 1 ] = aip2[ Len2 ]; - - for( i = 1; i < Len2; i++ ) - { - aip[ i * 2 + 0 ] = cos( aip[ i * 2 + 1 ]) * aip2[ i ]; - aip[ i * 2 + 1 ] = sin( aip[ i * 2 + 1 ]) * aip2[ i ]; - } - - ffto -> inverse( ip ); - - if( DoFinalMul ) - { - for( i = 0; i < KernelLen; i++ ) - { - Kernel[ i ] = ip[ i ] * m1; - } - } - else - { - memcpy( &Kernel[ 0 ], &ip[ 0 ], KernelLen * sizeof( double )); - } - - if( DCGroupDelay != NULL ) - { - double tmp; - - calcFIRFilterResponseAndGroupDelay( Kernel, KernelLen, 0.0, - tmp, tmp, *DCGroupDelay ); - } -} - -} // namespace r8b - -#endif // VOX_CDSPREALFFT_INCLUDED diff --git a/src/third_party/r8b/CDSPResampler.h b/src/third_party/r8b/CDSPResampler.h deleted file mode 100644 index 76f7bf0..0000000 --- a/src/third_party/r8b/CDSPResampler.h +++ /dev/null @@ -1,753 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file CDSPResampler.h - * - * @brief The master sample rate converter (resampler) class. - * - * This file includes the master sample rate converter (resampler) class that - * combines all elements of this library into a single front-end class. - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8B_CDSPRESAMPLER_INCLUDED -#define R8B_CDSPRESAMPLER_INCLUDED - -#include "CDSPHBDownsampler.h" -#include "CDSPHBUpsampler.h" -#include "CDSPBlockConvolver.h" -#include "CDSPFracInterpolator.h" - -namespace r8b { - -/** - * @brief The master sample rate converter (resampler) class. - * - * This class can be considered the "master" sample rate converter (resampler) - * class since it combines all functionality of this library into a single - * front-end class to perform sample rate conversion to/from any sample rate, - * including non-integer sample rates. - * - * Note that objects of this class can be constructed on the stack as it has a - * small member data size. The default template parameters of this class are - * suited for 27-bit fixed point resampling. - * - * Use the CDSPResampler16 class for 16-bit resampling. - * - * Use the CDSPResampler16IR class for 16-bit impulse response resampling. - * - * Use the CDSPResampler24 class for 24-bit resampling (including 32-bit - * floating point resampling). - */ - -class CDSPResampler : public CDSPProcessor -{ -public: - /** - * Constructor initalizes the resampler object. - * - * Note that increasing the transition band and decreasing attenuation - * reduces the filter length, this in turn reduces the "input before - * output" delay. However, the filter length has only a minor influence on - * the overall resampling speed. - * - * It should be noted that the ReqAtten specifies the minimal difference - * between the loudest input signal component and the produced aliasing - * artifacts during resampling. For example, if ReqAtten=100 was specified - * when performing 2x upsampling, the analysis of the resulting signal may - * display high-frequency components which are quieter than the loudest - * part of the input signal by only 100 decibel meaning the high-frequency - * part did not become "magically" completely silent after resampling. You - * have to specify a higher ReqAtten value if you need a totally clean - * high-frequency content. On the other hand, it may not be reasonable to - * have a high-frequency content cleaner than the input signal itself: if - * the input signal is 16-bit, setting ReqAtten to 150 will make its - * high-frequency content 24-bit, but the original part of the signal will - * remain 16-bit. - * - * @param SrcSampleRate Source signal sample rate. Both sample rates can - * be specified as a ratio, e.g. SrcSampleRate = 1.0, DstSampleRate = 2.0. - * @param DstSampleRate Destination signal sample rate. The "power of 2" - * ratios between the source and destination sample rates force resampler - * to use several fast "power of 2" resampling steps, without using - * fractional interpolation at all. - * @param aMaxInLen The maximal planned length of the input buffer (in - * samples) that will be passed to the resampler. The resampler relies on - * this value as it allocates intermediate buffers. Input buffers longer - * than this value should never be supplied to the resampler. Note that - * upsampling produces more samples than was provided on input, so at - * higher upsampling ratios it is advisable to use smaller MaxInLen - * values to reduce memory footprint. When downsampling, a larger MaxInLen - * is suggested in order to increase downsampling performance. - * @param ReqTransBand Required transition band, in percent of the - * spectral space of the input signal (or the output signal if - * downsampling is performed) between filter's -3 dB point and the Nyquist - * frequency. The range is from CDSPFIRFilter::getLPMinTransBand() to - * CDSPFIRFilter::getLPMaxTransBand(), inclusive. When upsampling 88200 or - * 96000 audio to a higher sample rates the ReqTransBand can be - * considerably increased, up to 30. The selection of ReqTransBand depends - * on the level of desire to preserve the high-frequency content. While - * values 0.5 to 2 are extremely "greedy" settings, not necessary in most - * cases, values 2 to 3 can be used in most cases. Values 3 to 4 are - * relaxed settings, but they still offer a flat frequency response up to - * 21kHz with 44.1k source or destination sample rate. - * @param ReqAtten Required stop-band attenuation in decibel, in the - * range CDSPFIRFilter::getLPMinAtten() to CDSPFIRFilter::getLPMaxAtten(), - * inclusive. The actual attenuation may be 0.40-4.46 dB higher. The - * general formula for selecting the ReqAtten is 6.02 * Bits + 40, where - * "Bits" is the bit resolution (e.g. 16, 24), "40" is an added resolution - * for stationary signals, this value can be decreased to 20 to 10 if the - * signal being resampled is mostly non-stationary (e.g. impulse - * response). - * @param ReqPhase Required filter's phase response. Note that this - * setting does not affect interpolator's phase response which is always - * linear-phase. Also note that if the "power of 2" resampling was engaged - * by the resampler together with the minimum-phase response, the audio - * stream may become fractionally delayed, depending on the minimum-phase - * filter's actual fractional delay. Linear-phase filters do not have - * fractional delay. - * @see CDSPFIRFilterCache::getLPFilter() - */ - - CDSPResampler( const double SrcSampleRate, const double DstSampleRate, - const int aMaxInLen, const double ReqTransBand = 2.0, - const double ReqAtten = 206.91, - const EDSPFilterPhaseResponse ReqPhase = fprLinearPhase ) - : StepCapacity( 0 ) - , StepCount( 0 ) - , MaxInLen( aMaxInLen ) - , CurMaxOutLen( aMaxInLen ) - , LatencyFrac( 0.0 ) - { - R8BASSERT( SrcSampleRate > 0.0 ); - R8BASSERT( DstSampleRate > 0.0 ); - R8BASSERT( MaxInLen > 0 ); - - R8BCONSOLE( "* CDSPResampler: src=%.1f dst=%.1f len=%i tb=%.1f " - "att=%.2f ph=%i\n", SrcSampleRate, DstSampleRate, aMaxInLen, - ReqTransBand, ReqAtten, (int) ReqPhase ); - - if( SrcSampleRate == DstSampleRate ) - { - return; - } - - TmpBufCapacities[ 0 ] = 0; - TmpBufCapacities[ 1 ] = 0; - CurTmpBuf = 0; - - // Try some common efficient ratios requiring only a single step. - - const int CommonRatioCount = 5; - const int CommonRatios[ CommonRatioCount ][ 2 ] = { - { 1, 2 }, - { 1, 3 }, - { 2, 3 }, - { 3, 2 }, - { 3, 4 } - }; - - int i; - - for( i = 0; i < CommonRatioCount; i++ ) - { - const int num = CommonRatios[ i ][ 0 ]; - const int den = CommonRatios[ i ][ 1 ]; - - if( SrcSampleRate * num == DstSampleRate * den ) - { - addProcessor( new CDSPBlockConvolver( - CDSPFIRFilterCache :: getLPFilter( - 1.0 / ( num > den ? num : den ), ReqTransBand, - ReqAtten, ReqPhase, num ), num, den, LatencyFrac )); - - createTmpBuffers(); - return; - } - } - - // Try whole-number power-of-2 or 3*power-of-2 upsampling. - - for( i = 2; i <= 3; i++ ) - { - bool WasFound = false; - int c = 0; - - while( true ) - { - const double NewSR = SrcSampleRate * ( i << c ); - - if( NewSR == DstSampleRate ) - { - WasFound = true; - break; - } - - if( NewSR > DstSampleRate ) - { - break; - } - - c++; - } - - if( WasFound ) - { - addProcessor( new CDSPBlockConvolver( - CDSPFIRFilterCache :: getLPFilter( 1.0 / i, ReqTransBand, - ReqAtten, ReqPhase, i ), i, 1, LatencyFrac )); - - const bool IsThird = ( i == 3 ); - - for( i = 0; i < c; i++ ) - { - addProcessor( new CDSPHBUpsampler( ReqAtten, i, IsThird, - LatencyFrac )); - } - - createTmpBuffers(); - return; - } - } - - if( DstSampleRate * 2 > SrcSampleRate ) - { - // Upsampling or fractional downsampling down to 2X. - - const double NormFreq = ( DstSampleRate > SrcSampleRate ? 0.5 : - 0.5 * DstSampleRate / SrcSampleRate ); - - addProcessor( new CDSPBlockConvolver( - CDSPFIRFilterCache :: getLPFilter( NormFreq, ReqTransBand, - ReqAtten, ReqPhase, 2.0 ), 2, 1, LatencyFrac )); - - // Try intermediate interpolated'd resampling with subsequent 2X - // or 3X upsampling. - - const double ThreshSampleRate = SrcSampleRate * 1.01; - int c = 0; - int div = 1; - - while( true ) - { - const int ndiv = div * 2; - - if( DstSampleRate < ThreshSampleRate * ndiv ) - { - break; - } - - div = ndiv; - c++; - } - - int c2 = 0; - int div2 = 1; - - while( true ) - { - const int ndiv = div * ( c2 == 0 ? 3 : 2 ); - - if( DstSampleRate < ThreshSampleRate * ndiv ) - { - break; - } - - div2 = ndiv; - c2++; - } - - const double SrcSampleRate2 = SrcSampleRate * 2.0; - int tmp1; - int tmp2; - - if( c == 1 && getWholeStepping( SrcSampleRate2, DstSampleRate, - tmp1, tmp2 )) - { - // Do not use intermediate interpolation if whole stepping is - // available as it performs very fast. - - c = 0; - } - - if( c > 0 ) - { - // Add steps using intermediate interpolation. - - int num; - - if( c2 > 0 && div2 > div ) - { - div = div2; - c = c2; - num = 3; - } - else - { - num = 2; - } - - addProcessor( new CDSPFracInterpolator( SrcSampleRate2 * div, - DstSampleRate, ReqAtten, false, LatencyFrac )); - - const double tb = 100.0 * ( 1.0 - SrcSampleRate * div / - DstSampleRate ) / 1.75; // Divide TransBand by a constant - // that assures a linear response in the pass-band. - - addProcessor( new CDSPBlockConvolver( - CDSPFIRFilterCache :: getLPFilter( 1.0 / num, tb, - ReqAtten, ReqPhase, num ), num, 1, LatencyFrac )); - - for( i = 1; i < c; i++ ) - { - addProcessor( new CDSPHBUpsampler( ReqAtten, i - 1, - ( num == 3 ), LatencyFrac )); - } - } - else - { - addProcessor( new CDSPFracInterpolator( SrcSampleRate2, - DstSampleRate, ReqAtten, false, LatencyFrac )); - } - - createTmpBuffers(); - return; - } - - // Use downsampling steps, including power-of-2 downsampling. - - double CheckSR = DstSampleRate * 4.0; - int c = 0; - double FinGain = 1.0; - - while( CheckSR <= SrcSampleRate ) - { - c++; - CheckSR *= 2.0; - FinGain *= 0.5; - } - - const int SrcSRDiv = ( 1 << c ); - int downf; - double NormFreq = 0.5; - bool UseInterp = true; - bool IsThird = false; - - for( downf = 2; downf <= 3; downf++ ) - { - if( DstSampleRate * SrcSRDiv * downf == SrcSampleRate ) - { - NormFreq = 1.0 / downf; - UseInterp = false; - IsThird = ( downf == 3 ); - break; - } - } - - if( UseInterp ) - { - downf = 1; - NormFreq = DstSampleRate * SrcSRDiv / SrcSampleRate; - IsThird = ( NormFreq * 3.0 <= 1.0 ); - } - - for( i = 0; i < c; i++ ) - { - // Use a fixed very relaxed 2X downsampling filters, that at - // the final stage only guarantees stop-band between 0.75 and - // pi. 0.5-0.75 range will be aliased to 0.25-0.5 range which - // will then be filtered out by the final filter. - - addProcessor( new CDSPHBDownsampler( ReqAtten, c - 1 - i, IsThird, - LatencyFrac )); - } - - addProcessor( new CDSPBlockConvolver( - CDSPFIRFilterCache :: getLPFilter( NormFreq, ReqTransBand, - ReqAtten, ReqPhase, FinGain ), 1, downf, LatencyFrac )); - - if( UseInterp ) - { - addProcessor( new CDSPFracInterpolator( SrcSampleRate, - DstSampleRate * SrcSRDiv, ReqAtten, IsThird, LatencyFrac )); - } - - createTmpBuffers(); - } - - virtual ~CDSPResampler() - { - int i; - - for( i = 0; i < StepCount; i++ ) - { - delete Steps[ i ]; - } - } - - virtual int getLatency() const - { - return( 0 ); - } - - virtual double getLatencyFrac() const - { - return( LatencyFrac ); - } - - /** - * This function ignores the supplied parameter and returns the maximal - * output buffer length that depends on the MaxInLen supplied to the - * constructor. - */ - - virtual int getMaxOutLen( const int/* MaxInLen */ ) const - { - return( CurMaxOutLen ); - } - - /** - * Function clears (resets) the state of *this object and returns it to - * the state after construction. All input data accumulated in the - * internal buffer so far will be discarded. - * - * This function makes it possible to use *this object for converting - * separate streams from the same source sample rate to the same - * destination sample rate without reconstructing the object. It is more - * efficient to clear the state of the resampler object than to destroy it - * and create a new object. - */ - - virtual void clear() - { - int i; - - for( i = 0; i < StepCount; i++ ) - { - Steps[ i ] -> clear(); - } - } - - /** - * Function performs sample rate conversion. - * - * If the source and destination sample rates are equal, the resampler - * will do nothing and will simply return the input buffer unchanged. - * - * You do not need to allocate an intermediate output buffer for use with - * this function. If required, the resampler will allocate a suitable - * intermediate output buffer itself. - * - * @param ip0 Input buffer. This buffer is never used as output buffer by - * this function. This pointer may be returned in "op0" if no resampling - * is happening (source sample rate equals destination sample rate). - * @param l The number of samples available in the input buffer. Should - * not exceed the MaxInLen supplied in the constructor. - * @param[out] op0 This variable receives the pointer to the resampled - * data. On function's return, this pointer points to *this object's - * internal buffer. In real-time applications it is suggested to pass this - * pointer to the next output audio block and consume any data left from - * the previous output audio block first before calling the process() - * function again. The buffer pointed to by the "op0" on return is owned - * by the resampler, so it should not be freed by the caller. - * @return The number of samples available in the "op0" output buffer. If - * the data from the output buffer "op0" is going to be written to a - * bigger output buffer, it is suggested to check the returned number of - * samples so that no overflow of the bigger output buffer happens. - */ - - virtual int process( double* ip0, int l, double*& op0 ) - { - R8BASSERT( l >= 0 ); - - double* ip = ip0; - int i; - - for( i = 0; i < StepCount; i++ ) - { - double* op = TmpBufs[ i & 1 ]; - l = Steps[ i ] -> process( ip, l, op ); - ip = op; - } - - op0 = ip; - return( l ); - } - - /** - * Function performs resampling of an input sample buffer of the specified - * length in the "one-shot" mode. This function can be useful when impulse - * response resampling is required. - * - * @param ip Input buffer pointer. - * @param iplen Length of the input buffer in samples. - * @param[out] op Output buffer pointer. - * @param oplen Length of the output buffer in samples. - * @tparam Tin Input buffer type. - * @tparam Tout Output buffer type. - */ - - template< class Tin, class Tout > - void oneshot( const Tin* ip, int iplen, Tout* op, int oplen ) - { - CFixedBuffer< double > Buf( MaxInLen ); - bool IsZero = false; - - while( oplen > 0 ) - { - int rc; - double* p; - int i; - - if( iplen == 0 ) - { - rc = MaxInLen; - p = &Buf[ 0 ]; - - if( !IsZero ) - { - IsZero = true; - memset( p, 0, MaxInLen * sizeof( double )); - } - } - else - { - rc = min( iplen, MaxInLen ); - - if( sizeof( Tin ) == sizeof( double )) - { - p = (double*) ip; - } - else - { - p = &Buf[ 0 ]; - - for( i = 0; i < rc; i++ ) - { - p[ i ] = ip[ i ]; - } - } - - ip += rc; - iplen -= rc; - } - - double* op0; - int wc = process( p, rc, op0 ); - wc = min( oplen, wc ); - - for( i = 0; i < wc; i++ ) - { - op[ i ] = (Tout) op0[ i ]; - } - - op += wc; - oplen -= wc; - } - - clear(); - } - - /** - * Function obtains overall input sample count required to produce first - * output sample. Function works by iteratively passing 1 sample at a time - * until output begins. This is a relatively CPU-consuming operation. This - * function should be called after the clear() function call or after - * object's construction. The function itself calls the clear() function - * before return. - */ - - int getInLenBeforeOutStart() - { - int inc = 0; - - while( true ) - { - double ins = 0.0; - double* op; - - if( process( &ins, 1, op ) > 0 ) - { - clear(); - return( inc ); - } - - inc++; - } - } - -private: - CFixedBuffer< CDSPProcessor* > Steps; ///< Array of processing steps. - ///< - int StepCapacity; ///< The capacity of the Steps array. - ///< - int StepCount; ///< The number of created processing steps. - ///< - int MaxInLen; ///< Maximal input length. - ///< - CFixedBuffer< double > TmpBufAll; ///< Buffer containing both temporary - ///< buffers. - ///< - double* TmpBufs[ 2 ]; ///< Temporary output buffers. - ///< - int TmpBufCapacities[ 2 ]; ///< Capacities of temporary buffers, updated - ///< during processing steps building. - ///< - int CurTmpBuf; ///< Current temporary buffer. - ///< - int CurMaxOutLen; ///< Current maximal output length. - ///< - double LatencyFrac; ///< Current fractional latency. After object's - ///< construction, equals to the remaining fractional latency in the - ///< output. - ///< - - /** - * Function adds processor, updates MaxOutLen variable and adjusts length - * of temporary internal buffers. - * - * @param Proc Processor to add. This pointer is inherited and will be - * destroyed on *this object's destruction. - */ - - void addProcessor( CDSPProcessor* const Proc ) - { - if( StepCount == StepCapacity ) - { - // Reallocate and increase Steps array's capacity. - - const int NewCapacity = StepCapacity + 8; - Steps.realloc( StepCapacity, NewCapacity ); - StepCapacity = NewCapacity; - } - - LatencyFrac = Proc -> getLatencyFrac(); - CurMaxOutLen = Proc -> getMaxOutLen( CurMaxOutLen ); - - if( CurMaxOutLen > TmpBufCapacities[ CurTmpBuf ]) - { - TmpBufCapacities[ CurTmpBuf ] = CurMaxOutLen; - } - - CurTmpBuf ^= 1; - - Steps[ StepCount ] = Proc; - StepCount++; - } - - /** - * Function creates temporary buffers. - */ - - void createTmpBuffers() - { - const int ol = TmpBufCapacities[ 0 ] + TmpBufCapacities[ 1 ]; - - if( ol > 0 ) - { - TmpBufAll.alloc( ol ); - TmpBufs[ 0 ] = &TmpBufAll[ 0 ]; - TmpBufs[ 1 ] = &TmpBufAll[ TmpBufCapacities[ 0 ]]; - } - - R8BCONSOLE( "* CDSPResampler: init done\n" ); - } -}; - -/** - * @brief The resampler class for 16-bit resampling. - * - * This class defines resampling parameters suitable for 16-bit resampling, - * using linear-phase low-pass filter. See the r8b::CDSPResampler class for - * details. - */ - -class CDSPResampler16 : public CDSPResampler -{ -public: - /** - * Constructor initializes the 16-bit resampler. See the - * r8b::CDSPResampler class for details. - * - * @param SrcSampleRate Source signal sample rate. - * @param DstSampleRate Destination signal sample rate. - * @param aMaxInLen The maximal planned length of the input buffer (in - * samples) that will be passed to the resampler. - * @param ReqTransBand Required transition band, in percent. - */ - - CDSPResampler16( const double SrcSampleRate, const double DstSampleRate, - const int aMaxInLen, const double ReqTransBand = 2.0 ) - : CDSPResampler( SrcSampleRate, DstSampleRate, aMaxInLen, ReqTransBand, - 136.45, fprLinearPhase ) - { - } -}; - -/** - * @brief The resampler class for 16-bit impulse response resampling. - * - * This class defines resampling parameters suitable for 16-bit impulse - * response resampling, using linear-phase low-pass filter. Impulse responses - * usually do not feature stationary signal components and thus need resampler - * with a less SNR. See the r8b::CDSPResampler class for details. - */ - -class CDSPResampler16IR : public CDSPResampler -{ -public: - /** - * Constructor initializes the 16-bit impulse response resampler. See the - * r8b::CDSPResampler class for details. - * - * @param SrcSampleRate Source signal sample rate. - * @param DstSampleRate Destination signal sample rate. - * @param aMaxInLen The maximal planned length of the input buffer (in - * samples) that will be passed to the resampler. - * @param ReqTransBand Required transition band, in percent. - */ - - CDSPResampler16IR( const double SrcSampleRate, const double DstSampleRate, - const int aMaxInLen, const double ReqTransBand = 2.0 ) - : CDSPResampler( SrcSampleRate, DstSampleRate, aMaxInLen, ReqTransBand, - 109.56, fprLinearPhase ) - { - } -}; - -/** - * @brief The resampler class for 24-bit resampling. - * - * This class defines resampling parameters suitable for 24-bit resampling - * (including 32-bit floating point resampling), using linear-phase low-pass - * filter. See the r8b::CDSPResampler class for details. - */ - -class CDSPResampler24 : public CDSPResampler -{ -public: - /** - * Constructor initializes the 24-bit resampler (including 32-bit floating - * point). See the r8b::CDSPResampler class for details. - * - * @param SrcSampleRate Source signal sample rate. - * @param DstSampleRate Destination signal sample rate. - * @param aMaxInLen The maximal planned length of the input buffer (in - * samples) that will be passed to the resampler. - * @param ReqTransBand Required transition band, in percent. - */ - - CDSPResampler24( const double SrcSampleRate, const double DstSampleRate, - const int aMaxInLen, const double ReqTransBand = 2.0 ) - : CDSPResampler( SrcSampleRate, DstSampleRate, aMaxInLen, ReqTransBand, - 180.15, fprLinearPhase ) - { - } -}; - -} // namespace r8b - -#endif // R8B_CDSPRESAMPLER_INCLUDED diff --git a/src/third_party/r8b/CDSPSincFilterGen.h b/src/third_party/r8b/CDSPSincFilterGen.h deleted file mode 100644 index 2df82fc..0000000 --- a/src/third_party/r8b/CDSPSincFilterGen.h +++ /dev/null @@ -1,687 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file CDSPSincFilterGen.h - * - * @brief Sinc function-based FIR filter generator class. - * - * This file includes the CDSPSincFilterGen class implementation that - * generates FIR filters. - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8B_CDSPSINCFILTERGEN_INCLUDED -#define R8B_CDSPSINCFILTERGEN_INCLUDED - -#include "r8bbase.h" - -namespace r8b { - -/** - * @brief Sinc function-based FIR filter generator class. - * - * Structure that holds state used to perform generation of sinc functions of - * various types, windowed by the Blackman window by default (but the window - * function can be changed if necessary). - */ - -class CDSPSincFilterGen -{ -public: - double Len2; ///< Required half filter kernel's length in samples (can be - ///< a fractional value). Final physical kernel length will be - ///< provided in the KernelLen variable. Len2 should be >= 2. - ///< - int KernelLen; ///< Resulting length of the filter kernel, this variable - ///< is set after the call to one of the "init" functions. - ///< - int fl2; ///< Internal "half kernel length" value. This value can be used - ///< as filter's latency in samples (taps), this variable is set after - ///< the call to one of the "init" functions. - ///< - - union - { - struct - { - double Freq1; ///< Required corner circular frequency 1 [0; pi]. - ///< Used only in the generateBand() function. - ///< - double Freq2; ///< Required corner circular frequency 2 [0; pi]. - ///< Used only in the generateBand() function. The range - ///< [Freq1; Freq2] defines a pass band for the generateBand() - ///< function. - ///< - }; - - struct - { - double FracDelay; ///< Fractional delay in the range [0; 1], used - ///< only in the generateFrac() function. Note that the - ///< FracDelay parameter is actually inversed. At 0.0 value it - ///< produces 1 sample delay (with the latency equal to fl2), - ///< at 1.0 value it produces 0 sample delay (with the latency - ///< equal to fl2 - 1). - ///< - }; - }; - - /** - * Window function type. - */ - - enum EWindowFunctionType - { - wftCosine, ///< Generalized cosine window function. No parameters - ///< required. The "Power" parameter is optional. - ///< - wftKaiser, ///< Kaiser window function. Requires the "Beta" parameter. - ///< The "Power" parameter is optional. - ///< - wftGaussian ///< Gaussian window function. Requires the "Sigma" - ///< parameter. The "Power" parameter is optional. - ///< - }; - - typedef double( CDSPSincFilterGen :: *CWindowFunc )(); ///< Window - ///< calculation function pointer type. - ///< - - /** - * Function initializes *this structure for generation of a window - * function, odd-sized. - * - * @param WinType Window function type. - * @param Params Window function's parameters. If NULL, the table values - * may be used. - * @param UsePower "True" if the power factor should be used to raise the - * window function. If "true", the power factor should be specified as the - * last value in the Params array. If Params is NULL, the table or default - * value of -1.0 (off) will be used. - */ - - void initWindow( const EWindowFunctionType WinType = wftCosine, - const double* const Params = NULL, const bool UsePower = false ) - { - R8BASSERT( Len2 >= 2.0 ); - - fl2 = (int) floor( Len2 ); - KernelLen = fl2 + fl2 + 1; - - setWindow( WinType, Params, UsePower, true ); - } - - /** - * Function initializes *this structure for generation of band-limited - * sinc filter kernel. The generateBand() or generateBandPow() functions - * should be used to calculate the filter. - * - * @param WinType Window function type. - * @param Params Window function's parameters. If NULL, the table values - * may be used. - * @param UsePower "True" if the power factor should be used to raise the - * window function. If "true", the power factor should be specified as the - * last value in the Params array. If Params is NULL, the table or default - * value of -1.0 (off) will be used. - */ - - void initBand( const EWindowFunctionType WinType = wftCosine, - const double* const Params = NULL, const bool UsePower = false ) - { - R8BASSERT( Len2 >= 2.0 ); - - fl2 = (int) floor( Len2 ); - KernelLen = fl2 + fl2 + 1; - f1.init( Freq1, 0.0 ); - f2.init( Freq2, 0.0 ); - - setWindow( WinType, Params, UsePower, true ); - } - - /** - * Function initializes *this structure for Hilbert transformation filter - * calculation. Freq1 and Freq2 variables are not used. - * The generateHilbert() function should be used to calculate the filter. - * - * @param WinType Window function type. - * @param Params Window function's parameters. If NULL, the table values - * may be used. - * @param UsePower "True" if the power factor should be used to raise the - * window function. If "true", the power factor should be specified as the - * last value in the Params array. If Params is NULL, the table or default - * value of -1.0 (off) will be used. - */ - - void initHilbert( const EWindowFunctionType WinType = wftCosine, - const double* const Params = NULL, const bool UsePower = false ) - { - R8BASSERT( Len2 >= 2.0 ); - - fl2 = (int) floor( Len2 ); - KernelLen = fl2 + fl2 + 1; - - setWindow( WinType, Params, UsePower, true ); - } - - /** - * Function initializes *this structure for generation of full-bandwidth - * fractional delay sinc filter kernel. Freq1 and Freq2 variables are not - * used. The generateFrac() function should be used to calculate the - * filter. - * - * @param WinType Window function type. - * @param Params Window function's parameters. If NULL, the table values - * may be used. - * @param UsePower "True" if the power factor should be used to raise the - * window function. If "true", the power factor should be specified as the - * last value in the Params array. If Params is NULL, the table or default - * value of -1.0 (off) will be used. - */ - - void initFrac( const EWindowFunctionType WinType = wftCosine, - const double* const Params = NULL, const bool UsePower = false ) - { - R8BASSERT( Len2 >= 2.0 ); - - fl2 = (int) ceil( Len2 ); - KernelLen = fl2 + fl2; - - setWindow( WinType, Params, UsePower, false, FracDelay ); - } - - /** - * @return The next "Hann" window function coefficient. - */ - - double calcWindowHann() - { - return( 0.5 + 0.5 * w1.generate() ); - } - - /** - * @return The next "Hamming" window function coefficient. - */ - - double calcWindowHamming() - { - return( 0.54 + 0.46 * w1.generate() ); - } - - /** - * @return The next "Blackman" window function coefficient. - */ - - double calcWindowBlackman() - { - return( 0.42 + 0.5 * w1.generate() + 0.08 * w2.generate() ); - } - - /** - * @return The next "Nuttall" window function coefficient. - */ - - double calcWindowNuttall() - { - return( 0.355768 + 0.487396 * w1.generate() + - 0.144232 * w2.generate() + 0.012604 * w3.generate() ); - } - - /** - * @return The next "Blackman-Nuttall" window function coefficient. - */ - - double calcWindowBlackmanNuttall() - { - return( 0.3635819 + 0.4891775 * w1.generate() + - 0.1365995 * w2.generate() + 0.0106411 * w3.generate() ); - } - - /** - * @return The next "Kaiser" window function coefficient. - */ - - double calcWindowKaiser() - { - const double n = 1.0 - sqr( wn / Len2 + KaiserLen2Frac ); - wn++; - - if( n < 0.0 ) - { - return( 0.0 ); - } - - return( besselI0( KaiserBeta * sqrt( n )) / KaiserDiv ); - } - - /** - * @return The next "Gaussian" window function coefficient. - */ - - double calcWindowGaussian() - { - const double f = exp( -0.5 * sqr( wn / GaussianSigma + - GaussianSigmaFrac )); - - wn++; - - return( f ); - } - - /** - * Function calculates window function only. - * - * @param[out] op Output buffer, length = KernelLen. - * @param wfunc Window calculation function to use. - */ - - template< class T > - void generateWindow( T* op, - CWindowFunc wfunc = &CDSPSincFilterGen :: calcWindowBlackman ) - { - op += fl2; - T* op2 = op; - - int l = fl2; - - if( Power < 0.0 ) - { - *op = ( *this.*wfunc )(); - - while( l > 0 ) - { - const double v = ( *this.*wfunc )(); - - op++; - op2--; - *op = v; - *op2 = v; - l--; - } - } - else - { - *op = pows(( *this.*wfunc )(), Power ); - - while( l > 0 ) - { - const double v = pows(( *this.*wfunc )(), Power ); - - op++; - op2--; - *op = v; - *op2 = v; - l--; - } - } - } - - /** - * Function calculates band-limited windowed sinc function-based filter - * kernel. - * - * @param[out] op Output buffer, length = KernelLen. - * @param wfunc Window calculation function to use. - */ - - template< class T > - void generateBand( T* op, - CWindowFunc wfunc = &CDSPSincFilterGen :: calcWindowBlackman ) - { - op += fl2; - T* op2 = op; - f1.generate(); - f2.generate(); - int t = 1; - - if( Power < 0.0 ) - { - *op = ( Freq2 - Freq1 ) * ( *this.*wfunc )() / M_PI; - - while( t <= fl2 ) - { - const double v = ( f2.generate() - f1.generate() ) * - ( *this.*wfunc )() / t / M_PI; - - op++; - op2--; - *op = v; - *op2 = v; - t++; - } - } - else - { - *op = ( Freq2 - Freq1 ) * pows(( *this.*wfunc )(), Power ) / M_PI; - - while( t <= fl2 ) - { - const double v = ( f2.generate() - f1.generate() ) * - pows(( *this.*wfunc )(), Power ) / t / M_PI; - - op++; - op2--; - *op = v; - *op2 = v; - t++; - } - } - } - - /** - * Function calculates windowed Hilbert transformer filter kernel. - * - * @param[out] op Output buffer, length = KernelLen. - * @param wfunc Window calculation function to use. - */ - - template< class T > - void generateHilbert( T* op, - CWindowFunc wfunc = &CDSPSincFilterGen :: calcWindowBlackman ) - { - static const double fvalues[ 2 ] = { 0.0, 2.0 }; - op += fl2; - T* op2 = op; - - ( *this.*wfunc )(); - *op = 0.0; - - int t = 1; - - if( Power < 0.0 ) - { - while( t <= fl2 ) - { - const double v = fvalues[ t & 1 ] * - ( *this.*wfunc )() / t / M_PI; - - op++; - op2--; - *op = v; - *op2 = -v; - t++; - } - } - else - { - while( t <= fl2 ) - { - const double v = fvalues[ t & 1 ] * - pows( ( *this.*wfunc )(), Power ) / t / M_PI; - - op++; - op2--; - *op = v; - *op2 = -v; - t++; - } - } - } - - /** - * Function calculates windowed fractional delay filter kernel. - * - * @param[out] op Output buffer, length = KernelLen. - * @param wfunc Window calculation function to use. - * @param opinc Output buffer increment, in "op" elements. - */ - - template< class T > - void generateFrac( T* op, - CWindowFunc wfunc = &CDSPSincFilterGen :: calcWindowBlackman, - const int opinc = 1 ) - { - R8BASSERT( opinc != 0 ); - - double f[ 2 ]; - f[ 0 ] = sin( FracDelay * M_PI ); - f[ 1 ] = -f[ 0 ]; - - int t = -fl2; - - if( t + FracDelay < -Len2 ) - { - ( *this.*wfunc )(); - *op = 0.0; - op += opinc; - t++; - } - - int mt = ( FracDelay >= 1.0 - 1e-13 && FracDelay <= 1.0 + 1e-13 ? - -1 : 0 ); - - if( Power < 0.0 ) - { - while( t < mt ) - { - *op = f[ t & 1 ] * ( *this.*wfunc )() / ( t + FracDelay ) / - M_PI; - - op += opinc; - t++; - } - - double ut = t + FracDelay; - *op = ( fabs( ut ) <= 1e-13 ? ( *this.*wfunc )() : - f[ t & 1 ] * ( *this.*wfunc )() / ut / M_PI ); - - mt = fl2 - 2; - - while( t < mt ) - { - op += opinc; - t++; - *op = f[ t & 1 ] * ( *this.*wfunc )() / ( t + FracDelay ) / - M_PI; - } - - op += opinc; - t++; - ut = t + FracDelay; - *op = ( ut > Len2 ? 0.0 : - f[ t & 1 ] * ( *this.*wfunc )() / ut / M_PI ); - } - else - { - while( t < mt ) - { - *op = f[ t & 1 ] * pows( ( *this.*wfunc )(), Power ) / - ( t + FracDelay ) / M_PI; - - op += opinc; - t++; - } - - double ut = t + FracDelay; - *op = ( fabs( ut ) <= 1e-13 ? pows( ( *this.*wfunc )(), Power ) : - f[ t & 1 ] * pows( ( *this.*wfunc )(), Power ) / ut / M_PI ); - - mt = fl2 - 2; - - while( t < mt ) - { - op += opinc; - t++; - *op = f[ t & 1 ] * pows( ( *this.*wfunc )(), Power ) / - ( t + FracDelay ) / M_PI; - } - - op += opinc; - t++; - ut = t + FracDelay; - *op = ( ut > Len2 ? 0.0 : - f[ t & 1 ] * pows( ( *this.*wfunc )(), Power ) / ut / M_PI ); - } - } - -private: - double Power; ///< The power factor used to raise the window function. - ///< Equals a negative value if the power factor should not be used. - ///< - CSineGen f1; ///< Sine function 1. Used in the generateBand() function. - ///< - CSineGen f2; ///< Sine function 2. Used in the generateBand() function. - ///< - int wn; ///< Window function integer position. 0 - center of the window - ///< function. This variable may not be used by some window functions. - ///< - CSineGen w1; ///< Cosine wave 1 for window function. - ///< - CSineGen w2; ///< Cosine wave 2 for window function. - ///< - CSineGen w3; ///< Cosine wave 3 for window function. - ///< - - union - { - struct - { - double KaiserBeta; ///< Kaiser window function's "Beta" - ///< coefficient. - ///< - double KaiserDiv; ///< Kaiser window function's divisor. - ///< - double KaiserLen2Frac; ///< Equals FracDelay / Len2. - ///< - }; - - struct - { - double GaussianSigma; ///< Gaussian window function's "Sigma" - ///< coefficient. - ///< - double GaussianSigmaFrac; ///< Equals FracDelay / GaussianSigma. - ///< - }; - }; - - /** - * Function initializes Kaiser window function calculation. The FracDelay - * variable should be initialized when using this window function. - * - * @param Params Function parameters. If NULL, the default values will be - * used. If not NULL, the first parameter should specify the "Beta" value. - * @param UsePower "True" if the power factor should be used to raise the - * window function. - * @param IsCentered "True" if centered window should be used. This - * parameter usually equals to "false" for fractional delay filters only. - */ - - void setWindowKaiser( const double* Params, const bool UsePower, - const bool IsCentered ) - { - wn = ( IsCentered ? 0 : -fl2 ); - - if( Params == NULL ) - { - KaiserBeta = 9.5945013206755156; - Power = ( UsePower ? 1.9718457932433306 : -1.0 ); - } - else - { - KaiserBeta = clampr( Params[ 0 ], 1.0, 350.0 ); - Power = ( UsePower ? fabs( Params[ 1 ]) : -1.0 ); - } - - KaiserDiv = besselI0( KaiserBeta ); - KaiserLen2Frac = FracDelay / Len2; - } - - /** - * Function initializes Gaussian window function calculation. The FracDelay - * variable should be initialized when using this window function. - * - * @param Params Function parameters. If NULL, the table values will be - * used. If not NULL, the first parameter should specify the "Sigma" - * value. - * @param UsePower "True" if the power factor should be used to raise the - * window function. - * @param IsCentered "True" if centered window should be used. This - * parameter usually equals to "false" for fractional delay filters only. - */ - - void setWindowGaussian( const double* Params, const bool UsePower, - const bool IsCentered ) - { - wn = ( IsCentered ? 0 : -fl2 ); - - if( Params == NULL ) - { - GaussianSigma = 1.0; - Power = -1.0; - } - else - { - GaussianSigma = clampr( fabs( Params[ 0 ]), 1e-1, 100.0 ); - Power = ( UsePower ? fabs( Params[ 1 ]) : -1.0 ); - } - - GaussianSigma *= Len2; - GaussianSigmaFrac = FracDelay / GaussianSigma; - } - - /** - * Function initializes calculation of window function of the specified - * type. - * - * @param WinType Window function type. - * @param Params Window function's parameters. If NULL, the table values - * may be used. - * @param UsePower "True" if the power factor should be used to raise the - * window function. If "true", the power factor should be specified as the - * last value in the Params array. If Params is NULL, the table or default - * value of -1.0 (off) will be used. - * @param IsCentered "True" if centered window should be used. This - * parameter usually equals to "false" for fractional delay filters only. - * @param UseFracDelay Fractional delay to use. - */ - - void setWindow( const EWindowFunctionType WinType, - const double* const Params, const bool UsePower, - const bool IsCentered, const double UseFracDelay = 0.0 ) - { - FracDelay = UseFracDelay; - - if( WinType == wftCosine ) - { - if( IsCentered ) - { - w1.init( M_PI / Len2, M_PI * 0.5 ); - w2.init( M_2PI / Len2, M_PI * 0.5 ); - w3.init( M_3PI / Len2, M_PI * 0.5 ); - } - else - { - const double step1 = M_PI / Len2; - w1.init( step1, M_PI * 0.5 - step1 * fl2 + - step1 * FracDelay ); - - const double step2 = M_2PI / Len2; - w2.init( step2, M_PI * 0.5 - step2 * fl2 + - step2 * FracDelay ); - - const double step3 = M_3PI / Len2; - w3.init( step3, M_PI * 0.5 - step3 * fl2 + - step3 * FracDelay ); - } - - Power = ( UsePower && Params != NULL ? Params[ 0 ] : -1.0 ); - } - else - if( WinType == wftKaiser ) - { - setWindowKaiser( Params, UsePower, IsCentered ); - } - else - if( WinType == wftGaussian ) - { - setWindowGaussian( Params, UsePower, IsCentered ); - } - } -}; - -} // namespace r8b - -#endif // R8B_CDSPSINCFILTERGEN_INCLUDED diff --git a/src/third_party/r8b/pffft.cpp b/src/third_party/r8b/pffft.cpp deleted file mode 100644 index 5f43e43..0000000 --- a/src/third_party/r8b/pffft.cpp +++ /dev/null @@ -1,1891 +0,0 @@ -//$ nobt - -/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) - - Based on original fortran 77 code from FFTPACKv4 from NETLIB - (http://www.netlib.org/fftpack), authored by Dr Paul Swarztrauber - of NCAR, in 1985. - - As confirmed by the NCAR fftpack software curators, the following - FFTPACKv5 license applies to FFTPACKv4 sources. My changes are - released under the same terms. - - FFTPACK license: - - http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html - - Copyright (c) 2004 the University Corporation for Atmospheric - Research ("UCAR"). All rights reserved. Developed by NCAR's - Computational and Information Systems Laboratory, UCAR, - www.cisl.ucar.edu. - - Redistribution and use of the Software in source and binary forms, - with or without modification, is permitted provided that the - following conditions are met: - - - Neither the names of NCAR's Computational and Information Systems - Laboratory, the University Corporation for Atmospheric Research, - nor the names of its sponsors or contributors may be used to - endorse or promote products derived from this Software without - specific prior written permission. - - - Redistributions of source code must retain the above copyright - notices, this list of conditions, and the disclaimer below. - - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions, and the disclaimer below in the - documentation and/or other materials provided with the - distribution. - - THIS 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 CONTRIBUTORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL 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 WITH THE - SOFTWARE. - - - PFFFT : a Pretty Fast FFT. - - This file is largerly based on the original FFTPACK implementation, modified in - order to take advantage of SIMD instructions of modern CPUs. -*/ - -/* - ChangeLog: - - 2011/10/02, version 1: This is the very first release of this file. -*/ - -/* detect compiler flavour */ -#if defined(_MSC_VER) -# define COMPILER_MSVC -#elif defined(__GNUC__) -# define COMPILER_GCC -#endif - -#ifdef COMPILER_MSVC -# define _USE_MATH_DEFINES -#endif - -#include "pffft.h" -#include -#include -#include -#include - -#if defined(COMPILER_GCC) -# define ALWAYS_INLINE(return_type) inline return_type __attribute__ ((always_inline)) -# define NEVER_INLINE(return_type) return_type __attribute__ ((noinline)) -# define RESTRICT __restrict -# define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ varname__[size__]; -#elif defined(COMPILER_MSVC) -# define ALWAYS_INLINE(return_type) __forceinline return_type -# define NEVER_INLINE(return_type) __declspec(noinline) return_type -# define RESTRICT __restrict -# define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ *varname__ = (type__*)_alloca(size__ * sizeof(type__)) -#endif - - -#ifdef COMPILER_MSVC -#pragma warning( disable : 4244 4305 4204 4456 ) -#endif - -/* - vector support macros: the rest of the code is independant of - SSE/Altivec/NEON -- adding support for other platforms with 4-element - vectors should be limited to these macros -*/ - - -// define PFFFT_SIMD_DISABLE if you want to use scalar code instead of simd code -//#define PFFFT_SIMD_DISABLE - -/* - Altivec support macros -*/ -#if !defined(PFFFT_SIMD_DISABLE) && (defined(__ppc__) || defined(__ppc64__)) -typedef vector float v4sf; -# define SIMD_SZ 4 -# define VZERO() ((vector float) vec_splat_u8(0)) -# define VMUL(a,b) vec_madd(a,b, VZERO()) -# define VADD(a,b) vec_add(a,b) -# define VMADD(a,b,c) vec_madd(a,b,c) -# define VSUB(a,b) vec_sub(a,b) -inline v4sf ld_ps1(const float *p) { v4sf v=vec_lde(0,p); return vec_splat(vec_perm(v, v, vec_lvsl(0, p)), 0); } -# define LD_PS1(p) ld_ps1(&p) -# define INTERLEAVE2(in1, in2, out1, out2) { v4sf tmp__ = vec_mergeh(in1, in2); out2 = vec_mergel(in1, in2); out1 = tmp__; } -# define UNINTERLEAVE2(in1, in2, out1, out2) { \ - vector unsigned char vperm1 = (vector unsigned char)(0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27); \ - vector unsigned char vperm2 = (vector unsigned char)(4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31); \ - v4sf tmp__ = vec_perm(in1, in2, vperm1); out2 = vec_perm(in1, in2, vperm2); out1 = tmp__; \ - } -# define VTRANSPOSE4(x0,x1,x2,x3) { \ - v4sf y0 = vec_mergeh(x0, x2); \ - v4sf y1 = vec_mergel(x0, x2); \ - v4sf y2 = vec_mergeh(x1, x3); \ - v4sf y3 = vec_mergel(x1, x3); \ - x0 = vec_mergeh(y0, y2); \ - x1 = vec_mergel(y0, y2); \ - x2 = vec_mergeh(y1, y3); \ - x3 = vec_mergel(y1, y3); \ - } -# define VSWAPHL(a,b) vec_perm(a,b, (vector unsigned char)(16,17,18,19,20,21,22,23,8,9,10,11,12,13,14,15)) -# define VALIGNED(ptr) ((((uintptr_t)(ptr)) & 0xF) == 0) - -/* - SSE1 support macros -*/ -#elif !defined(PFFFT_SIMD_DISABLE) && (defined(__x86_64__) || defined(__i386__) || defined(_M_X64) || defined(i386) || defined(_M_IX86)) - -#include -typedef __m128 v4sf; -# define SIMD_SZ 4 // 4 floats by simd vector -- this is pretty much hardcoded in the preprocess/finalize functions anyway so you will have to work if you want to enable AVX with its 256-bit vectors. -# define VZERO() _mm_setzero_ps() -# define VMUL(a,b) _mm_mul_ps(a,b) -# define VADD(a,b) _mm_add_ps(a,b) -# define VMADD(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c) -# define VSUB(a,b) _mm_sub_ps(a,b) -# define LD_PS1(p) _mm_set1_ps(p) -# define INTERLEAVE2(in1, in2, out1, out2) { v4sf tmp__ = _mm_unpacklo_ps(in1, in2); out2 = _mm_unpackhi_ps(in1, in2); out1 = tmp__; } -# define UNINTERLEAVE2(in1, in2, out1, out2) { v4sf tmp__ = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(2,0,2,0)); out2 = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(3,1,3,1)); out1 = tmp__; } -# define VTRANSPOSE4(x0,x1,x2,x3) _MM_TRANSPOSE4_PS(x0,x1,x2,x3) -# define VSWAPHL(a,b) _mm_shuffle_ps(b, a, _MM_SHUFFLE(3,2,1,0)) -# define VALIGNED(ptr) ((((uintptr_t)(ptr)) & 0xF) == 0) - -/* - ARM NEON support macros -*/ -#elif !defined(PFFFT_SIMD_DISABLE) && (defined(__arm__) || defined(__aarch64__)) -# include -typedef float32x4_t v4sf; -# define SIMD_SZ 4 -# define VZERO() vdupq_n_f32(0) -# define VMUL(a,b) vmulq_f32(a,b) -# define VADD(a,b) vaddq_f32(a,b) -# define VMADD(a,b,c) vmlaq_f32(c,a,b) -# define VSUB(a,b) vsubq_f32(a,b) -# define LD_PS1(p) vld1q_dup_f32(&(p)) -# define INTERLEAVE2(in1, in2, out1, out2) { float32x4x2_t tmp__ = vzipq_f32(in1,in2); out1=tmp__.val[0]; out2=tmp__.val[1]; } -# define UNINTERLEAVE2(in1, in2, out1, out2) { float32x4x2_t tmp__ = vuzpq_f32(in1,in2); out1=tmp__.val[0]; out2=tmp__.val[1]; } -# define VTRANSPOSE4(x0,x1,x2,x3) { \ - float32x4x2_t t0_ = vzipq_f32(x0, x2); \ - float32x4x2_t t1_ = vzipq_f32(x1, x3); \ - float32x4x2_t u0_ = vzipq_f32(t0_.val[0], t1_.val[0]); \ - float32x4x2_t u1_ = vzipq_f32(t0_.val[1], t1_.val[1]); \ - x0 = u0_.val[0]; x1 = u0_.val[1]; x2 = u1_.val[0]; x3 = u1_.val[1]; \ - } -// marginally faster version -//# define VTRANSPOSE4(x0,x1,x2,x3) { asm("vtrn.32 %q0, %q1;\n vtrn.32 %q2,%q3\n vswp %f0,%e2\n vswp %f1,%e3" : "+w"(x0), "+w"(x1), "+w"(x2), "+w"(x3)::); } -# define VSWAPHL(a,b) vcombine_f32(vget_low_f32(b), vget_high_f32(a)) -# define VALIGNED(ptr) ((((uintptr_t)(ptr)) & 0x3) == 0) -#else -# if !defined(PFFFT_SIMD_DISABLE) -# warning "building with simd disabled !\n"; -# define PFFFT_SIMD_DISABLE // fallback to scalar code -# endif -#endif - -// fallback mode for situations where SSE/Altivec are not available, use scalar mode instead -#ifdef PFFFT_SIMD_DISABLE -typedef float v4sf; -# define SIMD_SZ 1 -# define VZERO() 0.f -# define VMUL(a,b) ((a)*(b)) -# define VADD(a,b) ((a)+(b)) -# define VMADD(a,b,c) ((a)*(b)+(c)) -# define VSUB(a,b) ((a)-(b)) -# define LD_PS1(p) (p) -# define VALIGNED(ptr) ((((uintptr_t)(ptr)) & 0x3) == 0) -#endif - -// shortcuts for complex multiplcations -#define VCPLXMUL(ar,ai,br,bi) { v4sf tmp; tmp=VMUL(ar,bi); ar=VMUL(ar,br); ar=VSUB(ar,VMUL(ai,bi)); ai=VMUL(ai,br); ai=VADD(ai,tmp); } -#define VCPLXMULCONJ(ar,ai,br,bi) { v4sf tmp; tmp=VMUL(ar,bi); ar=VMUL(ar,br); ar=VADD(ar,VMUL(ai,bi)); ai=VMUL(ai,br); ai=VSUB(ai,tmp); } -#ifndef SVMUL -// multiply a scalar with a vector -#define SVMUL(f,v) VMUL(LD_PS1(f),v) -#endif - -#if !defined(PFFFT_SIMD_DISABLE) -typedef union v4sf_union { - v4sf v; - float f[4]; -} v4sf_union; - -#include - -#define assertv4(v,f0,f1,f2,f3) assert(v.f[0] == (f0) && v.f[1] == (f1) && v.f[2] == (f2) && v.f[3] == (f3)) - -/* detect bugs with the vector support macros */ -void validate_pffft_simd() { - float f[16] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }; - v4sf_union a0, a1, a2, a3, t, u; - memcpy(a0.f, f, 4*sizeof(float)); - memcpy(a1.f, f+4, 4*sizeof(float)); - memcpy(a2.f, f+8, 4*sizeof(float)); - memcpy(a3.f, f+12, 4*sizeof(float)); - - t = a0; u = a1; t.v = VZERO(); - printf("VZERO=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 0, 0, 0, 0); - t.v = VADD(a1.v, a2.v); - printf("VADD(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 12, 14, 16, 18); - t.v = VMUL(a1.v, a2.v); - printf("VMUL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 32, 45, 60, 77); - t.v = VMADD(a1.v, a2.v,a0.v); - printf("VMADD(4:7,8:11,0:3)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 32, 46, 62, 80); - - INTERLEAVE2(a1.v,a2.v,t.v,u.v); - printf("INTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]); - assertv4(t, 4, 8, 5, 9); assertv4(u, 6, 10, 7, 11); - UNINTERLEAVE2(a1.v,a2.v,t.v,u.v); - printf("UNINTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]); - assertv4(t, 4, 6, 8, 10); assertv4(u, 5, 7, 9, 11); - - t.v=LD_PS1(f[15]); - printf("LD_PS1(15)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); - assertv4(t, 15, 15, 15, 15); - t.v = VSWAPHL(a1.v, a2.v); - printf("VSWAPHL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); - assertv4(t, 8, 9, 6, 7); - VTRANSPOSE4(a0.v, a1.v, a2.v, a3.v); - printf("VTRANSPOSE4(0:3,4:7,8:11,12:15)=[%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", - a0.f[0], a0.f[1], a0.f[2], a0.f[3], a1.f[0], a1.f[1], a1.f[2], a1.f[3], - a2.f[0], a2.f[1], a2.f[2], a2.f[3], a3.f[0], a3.f[1], a3.f[2], a3.f[3]); - assertv4(a0, 0, 4, 8, 12); assertv4(a1, 1, 5, 9, 13); assertv4(a2, 2, 6, 10, 14); assertv4(a3, 3, 7, 11, 15); -} -#endif //!PFFFT_SIMD_DISABLE - -/* SSE and co like 16-bytes aligned pointers */ -#define MALLOC_V4SF_ALIGNMENT 64 // with a 64-byte alignment, we are even aligned on L2 cache lines... -void *pffft_aligned_malloc(size_t nb_bytes) { - void *p, *p0 = malloc(nb_bytes + MALLOC_V4SF_ALIGNMENT); - if (!p0) return (void *) 0; - p = (void *) (((size_t) p0 + MALLOC_V4SF_ALIGNMENT) & (~((size_t) (MALLOC_V4SF_ALIGNMENT-1)))); - *((void **) p - 1) = p0; - return p; -} - -void pffft_aligned_free(void *p) { - if (p) free(*((void **) p - 1)); -} - -int pffft_simd_size() { return SIMD_SZ; } - -/* - passf2 and passb2 has been merged here, fsign = -1 for passf2, +1 for passb2 -*/ -static NEVER_INLINE(void) passf2_ps(int ido, int l1, const v4sf *cc, v4sf *ch, const float *wa1, float fsign) { - int k, i; - int l1ido = l1*ido; - if (ido <= 2) { - for (k=0; k < l1ido; k += ido, ch += ido, cc+= 2*ido) { - ch[0] = VADD(cc[0], cc[ido+0]); - ch[l1ido] = VSUB(cc[0], cc[ido+0]); - ch[1] = VADD(cc[1], cc[ido+1]); - ch[l1ido + 1] = VSUB(cc[1], cc[ido+1]); - } - } else { - for (k=0; k < l1ido; k += ido, ch += ido, cc += 2*ido) { - for (i=0; i 2); - for (k=0; k< l1ido; k += ido, cc+= 3*ido, ch +=ido) { - for (i=0; i 2); - for (k = 0; k < l1; ++k, cc += 5*ido, ch += ido) { - for (i = 0; i < ido-1; i += 2) { - ti5 = VSUB(cc_ref(i , 2), cc_ref(i , 5)); - ti2 = VADD(cc_ref(i , 2), cc_ref(i , 5)); - ti4 = VSUB(cc_ref(i , 3), cc_ref(i , 4)); - ti3 = VADD(cc_ref(i , 3), cc_ref(i , 4)); - tr5 = VSUB(cc_ref(i-1, 2), cc_ref(i-1, 5)); - tr2 = VADD(cc_ref(i-1, 2), cc_ref(i-1, 5)); - tr4 = VSUB(cc_ref(i-1, 3), cc_ref(i-1, 4)); - tr3 = VADD(cc_ref(i-1, 3), cc_ref(i-1, 4)); - ch_ref(i-1, 1) = VADD(cc_ref(i-1, 1), VADD(tr2, tr3)); - ch_ref(i , 1) = VADD(cc_ref(i , 1), VADD(ti2, ti3)); - cr2 = VADD(cc_ref(i-1, 1), VADD(SVMUL(tr11, tr2),SVMUL(tr12, tr3))); - ci2 = VADD(cc_ref(i , 1), VADD(SVMUL(tr11, ti2),SVMUL(tr12, ti3))); - cr3 = VADD(cc_ref(i-1, 1), VADD(SVMUL(tr12, tr2),SVMUL(tr11, tr3))); - ci3 = VADD(cc_ref(i , 1), VADD(SVMUL(tr12, ti2),SVMUL(tr11, ti3))); - cr5 = VADD(SVMUL(ti11, tr5), SVMUL(ti12, tr4)); - ci5 = VADD(SVMUL(ti11, ti5), SVMUL(ti12, ti4)); - cr4 = VSUB(SVMUL(ti12, tr5), SVMUL(ti11, tr4)); - ci4 = VSUB(SVMUL(ti12, ti5), SVMUL(ti11, ti4)); - dr3 = VSUB(cr3, ci4); - dr4 = VADD(cr3, ci4); - di3 = VADD(ci3, cr4); - di4 = VSUB(ci3, cr4); - dr5 = VADD(cr2, ci5); - dr2 = VSUB(cr2, ci5); - di5 = VSUB(ci2, cr5); - di2 = VADD(ci2, cr5); - wr1=wa1[i], wi1=fsign*wa1[i+1], wr2=wa2[i], wi2=fsign*wa2[i+1]; - wr3=wa3[i], wi3=fsign*wa3[i+1], wr4=wa4[i], wi4=fsign*wa4[i+1]; - VCPLXMUL(dr2, di2, LD_PS1(wr1), LD_PS1(wi1)); - ch_ref(i - 1, 2) = dr2; - ch_ref(i, 2) = di2; - VCPLXMUL(dr3, di3, LD_PS1(wr2), LD_PS1(wi2)); - ch_ref(i - 1, 3) = dr3; - ch_ref(i, 3) = di3; - VCPLXMUL(dr4, di4, LD_PS1(wr3), LD_PS1(wi3)); - ch_ref(i - 1, 4) = dr4; - ch_ref(i, 4) = di4; - VCPLXMUL(dr5, di5, LD_PS1(wr4), LD_PS1(wi4)); - ch_ref(i - 1, 5) = dr5; - ch_ref(i, 5) = di5; - } - } -#undef ch_ref -#undef cc_ref -} - -static NEVER_INLINE(void) radf2_ps(int ido, int l1, const v4sf * RESTRICT cc, v4sf * RESTRICT ch, const float *wa1) { - static const float minus_one = -1.f; - int i, k, l1ido = l1*ido; - for (k=0; k < l1ido; k += ido) { - v4sf a = cc[k], b = cc[k + l1ido]; - ch[2*k] = VADD(a, b); - ch[2*(k+ido)-1] = VSUB(a, b); - } - if (ido < 2) return; - if (ido != 2) { - for (k=0; k < l1ido; k += ido) { - for (i=2; i 5) { - wa[i1-1] = wa[i-1]; - wa[i1] = wa[i]; - } - } - l1 = l2; - } -} /* cffti1 */ - - -v4sf *cfftf1_ps(int n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, const float *wa, const int *ifac, int isign) { - v4sf *in = (v4sf*)input_readonly; - v4sf *out = (in == work2 ? work1 : work2); - int nf = ifac[1], k1; - int l1 = 1; - int iw = 0; - assert(in != out && work1 != work2); - for (k1=2; k1<=nf+1; k1++) { - int ip = ifac[k1]; - int l2 = ip*l1; - int ido = n / l2; - int idot = ido + ido; - switch (ip) { - case 5: { - int ix2 = iw + idot; - int ix3 = ix2 + idot; - int ix4 = ix3 + idot; - passf5_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], isign); - } break; - case 4: { - int ix2 = iw + idot; - int ix3 = ix2 + idot; - passf4_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], isign); - } break; - case 2: { - passf2_ps(idot, l1, in, out, &wa[iw], isign); - } break; - case 3: { - int ix2 = iw + idot; - passf3_ps(idot, l1, in, out, &wa[iw], &wa[ix2], isign); - } break; - default: - assert(0); - } - l1 = l2; - iw += (ip - 1)*idot; - if (out == work2) { - out = work1; in = work2; - } else { - out = work2; in = work1; - } - } - - return in; /* this is in fact the output .. */ -} - - -struct PFFFT_Setup { - int N; - int Ncvec; // nb of complex simd vectors (N/4 if PFFFT_COMPLEX, N/8 if PFFFT_REAL) - int ifac[15]; - pffft_transform_t transform; - v4sf *data; // allocated room for twiddle coefs - float *e; // points into 'data' , N/4*3 elements - float *twiddle; // points into 'data', N/4 elements -}; - -PFFFT_Setup *pffft_new_setup(int N, pffft_transform_t transform) { - PFFFT_Setup *s = (PFFFT_Setup*)malloc(sizeof(PFFFT_Setup)); - int k, m; - /* unfortunately, the fft size must be a multiple of 16 for complex FFTs - and 32 for real FFTs -- a lot of stuff would need to be rewritten to - handle other cases (or maybe just switch to a scalar fft, I don't know..) */ - if (transform == PFFFT_REAL) { assert((N%(2*SIMD_SZ*SIMD_SZ))==0 && N>0); } - if (transform == PFFFT_COMPLEX) { assert((N%(SIMD_SZ*SIMD_SZ))==0 && N>0); } - //assert((N % 32) == 0); - s->N = N; - s->transform = transform; - /* nb of complex simd vectors */ - s->Ncvec = (transform == PFFFT_REAL ? N/2 : N)/SIMD_SZ; - s->data = (v4sf*)pffft_aligned_malloc(2*s->Ncvec * sizeof(v4sf)); - s->e = (float*)s->data; - s->twiddle = (float*)(s->data + (2*s->Ncvec*(SIMD_SZ-1))/SIMD_SZ); - - if (transform == PFFFT_REAL) { - for (k=0; k < s->Ncvec; ++k) { - int i = k/SIMD_SZ; - int j = k%SIMD_SZ; - for (m=0; m < SIMD_SZ-1; ++m) { - float A = -2*(float)M_PI*(m+1)*k / N; - s->e[(2*(i*3 + m) + 0) * SIMD_SZ + j] = cosf(A); - s->e[(2*(i*3 + m) + 1) * SIMD_SZ + j] = sinf(A); - } - } - rffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac); - } else { - for (k=0; k < s->Ncvec; ++k) { - int i = k/SIMD_SZ; - int j = k%SIMD_SZ; - for (m=0; m < SIMD_SZ-1; ++m) { - float A = -2*(float)M_PI*(m+1)*k / N; - s->e[(2*(i*3 + m) + 0)*SIMD_SZ + j] = cosf(A); - s->e[(2*(i*3 + m) + 1)*SIMD_SZ + j] = sinf(A); - } - } - cffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac); - } - - /* check that N is decomposable with allowed prime factors */ - for (k=0, m=1; k < s->ifac[1]; ++k) { m *= s->ifac[2+k]; } - if (m != N/SIMD_SZ) { - pffft_destroy_setup(s); s = 0; - } - - return s; -} - - -void pffft_destroy_setup(PFFFT_Setup *s) { - pffft_aligned_free(s->data); - free(s); -} - -#if !defined(PFFFT_SIMD_DISABLE) - -/* [0 0 1 2 3 4 5 6 7 8] -> [0 8 7 6 5 4 3 2 1] */ -static void reversed_copy(int N, const v4sf *in, int in_stride, v4sf *out) { - v4sf g0, g1; - int k; - INTERLEAVE2(in[0], in[1], g0, g1); in += in_stride; - - *--out = VSWAPHL(g0, g1); // [g0l, g0h], [g1l g1h] -> [g1l, g0h] - for (k=1; k < N; ++k) { - v4sf h0, h1; - INTERLEAVE2(in[0], in[1], h0, h1); in += in_stride; - *--out = VSWAPHL(g1, h0); - *--out = VSWAPHL(h0, h1); - g1 = h1; - } - *--out = VSWAPHL(g1, g0); -} - -static void unreversed_copy(int N, const v4sf *in, v4sf *out, int out_stride) { - v4sf g0, g1, h0, h1; - int k; - g0 = g1 = in[0]; ++in; - for (k=1; k < N; ++k) { - h0 = *in++; h1 = *in++; - g1 = VSWAPHL(g1, h0); - h0 = VSWAPHL(h0, h1); - UNINTERLEAVE2(h0, g1, out[0], out[1]); out += out_stride; - g1 = h1; - } - h0 = *in++; h1 = g0; - g1 = VSWAPHL(g1, h0); - h0 = VSWAPHL(h0, h1); - UNINTERLEAVE2(h0, g1, out[0], out[1]); -} - -void pffft_zreorder(PFFFT_Setup *setup, const float *in, float *out, pffft_direction_t direction) { - int k, N = setup->N, Ncvec = setup->Ncvec; - const v4sf *vin = (const v4sf*)in; - v4sf *vout = (v4sf*)out; - assert(in != out); - if (setup->transform == PFFFT_REAL) { - int k, dk = N/32; - if (direction == PFFFT_FORWARD) { - for (k=0; k < dk; ++k) { - INTERLEAVE2(vin[k*8 + 0], vin[k*8 + 1], vout[2*(0*dk + k) + 0], vout[2*(0*dk + k) + 1]); - INTERLEAVE2(vin[k*8 + 4], vin[k*8 + 5], vout[2*(2*dk + k) + 0], vout[2*(2*dk + k) + 1]); - } - reversed_copy(dk, vin+2, 8, (v4sf*)(out + N/2)); - reversed_copy(dk, vin+6, 8, (v4sf*)(out + N)); - } else { - for (k=0; k < dk; ++k) { - UNINTERLEAVE2(vin[2*(0*dk + k) + 0], vin[2*(0*dk + k) + 1], vout[k*8 + 0], vout[k*8 + 1]); - UNINTERLEAVE2(vin[2*(2*dk + k) + 0], vin[2*(2*dk + k) + 1], vout[k*8 + 4], vout[k*8 + 5]); - } - unreversed_copy(dk, (v4sf*)(in + N/4), (v4sf*)(out + N - 6*SIMD_SZ), -8); - unreversed_copy(dk, (v4sf*)(in + 3*N/4), (v4sf*)(out + N - 2*SIMD_SZ), -8); - } - } else { - if (direction == PFFFT_FORWARD) { - for (k=0; k < Ncvec; ++k) { - int kk = (k/4) + (k%4)*(Ncvec/4); - INTERLEAVE2(vin[k*2], vin[k*2+1], vout[kk*2], vout[kk*2+1]); - } - } else { - for (k=0; k < Ncvec; ++k) { - int kk = (k/4) + (k%4)*(Ncvec/4); - UNINTERLEAVE2(vin[kk*2], vin[kk*2+1], vout[k*2], vout[k*2+1]); - } - } - } -} - -void pffft_cplx_finalize(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) { - int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks - v4sf r0, i0, r1, i1, r2, i2, r3, i3; - v4sf sr0, dr0, sr1, dr1, si0, di0, si1, di1; - assert(in != out); - for (k=0; k < dk; ++k) { - r0 = in[8*k+0]; i0 = in[8*k+1]; - r1 = in[8*k+2]; i1 = in[8*k+3]; - r2 = in[8*k+4]; i2 = in[8*k+5]; - r3 = in[8*k+6]; i3 = in[8*k+7]; - VTRANSPOSE4(r0,r1,r2,r3); - VTRANSPOSE4(i0,i1,i2,i3); - VCPLXMUL(r1,i1,e[k*6+0],e[k*6+1]); - VCPLXMUL(r2,i2,e[k*6+2],e[k*6+3]); - VCPLXMUL(r3,i3,e[k*6+4],e[k*6+5]); - - sr0 = VADD(r0,r2); dr0 = VSUB(r0, r2); - sr1 = VADD(r1,r3); dr1 = VSUB(r1, r3); - si0 = VADD(i0,i2); di0 = VSUB(i0, i2); - si1 = VADD(i1,i3); di1 = VSUB(i1, i3); - - /* - transformation for each column is: - - [1 1 1 1 0 0 0 0] [r0] - [1 0 -1 0 0 -1 0 1] [r1] - [1 -1 1 -1 0 0 0 0] [r2] - [1 0 -1 0 0 1 0 -1] [r3] - [0 0 0 0 1 1 1 1] * [i0] - [0 1 0 -1 1 0 -1 0] [i1] - [0 0 0 0 1 -1 1 -1] [i2] - [0 -1 0 1 1 0 -1 0] [i3] - */ - - r0 = VADD(sr0, sr1); i0 = VADD(si0, si1); - r1 = VADD(dr0, di1); i1 = VSUB(di0, dr1); - r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1); - r3 = VSUB(dr0, di1); i3 = VADD(di0, dr1); - - *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1; - *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3; - } -} - -void pffft_cplx_preprocess(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) { - int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks - v4sf r0, i0, r1, i1, r2, i2, r3, i3; - v4sf sr0, dr0, sr1, dr1, si0, di0, si1, di1; - assert(in != out); - for (k=0; k < dk; ++k) { - r0 = in[8*k+0]; i0 = in[8*k+1]; - r1 = in[8*k+2]; i1 = in[8*k+3]; - r2 = in[8*k+4]; i2 = in[8*k+5]; - r3 = in[8*k+6]; i3 = in[8*k+7]; - - sr0 = VADD(r0,r2); dr0 = VSUB(r0, r2); - sr1 = VADD(r1,r3); dr1 = VSUB(r1, r3); - si0 = VADD(i0,i2); di0 = VSUB(i0, i2); - si1 = VADD(i1,i3); di1 = VSUB(i1, i3); - - r0 = VADD(sr0, sr1); i0 = VADD(si0, si1); - r1 = VSUB(dr0, di1); i1 = VADD(di0, dr1); - r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1); - r3 = VADD(dr0, di1); i3 = VSUB(di0, dr1); - - VCPLXMULCONJ(r1,i1,e[k*6+0],e[k*6+1]); - VCPLXMULCONJ(r2,i2,e[k*6+2],e[k*6+3]); - VCPLXMULCONJ(r3,i3,e[k*6+4],e[k*6+5]); - - VTRANSPOSE4(r0,r1,r2,r3); - VTRANSPOSE4(i0,i1,i2,i3); - - *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1; - *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3; - } -} - - -static ALWAYS_INLINE(void) pffft_real_finalize_4x4(const v4sf *in0, const v4sf *in1, const v4sf *in, - const v4sf *e, v4sf *out) { - v4sf r0, i0, r1, i1, r2, i2, r3, i3; - v4sf sr0, dr0, sr1, dr1, si0, di0, si1, di1; - r0 = *in0; i0 = *in1; - r1 = *in++; i1 = *in++; r2 = *in++; i2 = *in++; r3 = *in++; i3 = *in++; - VTRANSPOSE4(r0,r1,r2,r3); - VTRANSPOSE4(i0,i1,i2,i3); - - /* - transformation for each column is: - - [1 1 1 1 0 0 0 0] [r0] - [1 0 -1 0 0 -1 0 1] [r1] - [1 0 -1 0 0 1 0 -1] [r2] - [1 -1 1 -1 0 0 0 0] [r3] - [0 0 0 0 1 1 1 1] * [i0] - [0 -1 0 1 -1 0 1 0] [i1] - [0 -1 0 1 1 0 -1 0] [i2] - [0 0 0 0 -1 1 -1 1] [i3] - */ - - //cerr << "matrix initial, before e , REAL:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n"; - //cerr << "matrix initial, before e, IMAG :\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n"; - - VCPLXMUL(r1,i1,e[0],e[1]); - VCPLXMUL(r2,i2,e[2],e[3]); - VCPLXMUL(r3,i3,e[4],e[5]); - - //cerr << "matrix initial, real part:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n"; - //cerr << "matrix initial, imag part:\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n"; - - sr0 = VADD(r0,r2); dr0 = VSUB(r0,r2); - sr1 = VADD(r1,r3); dr1 = VSUB(r3,r1); - si0 = VADD(i0,i2); di0 = VSUB(i0,i2); - si1 = VADD(i1,i3); di1 = VSUB(i3,i1); - - r0 = VADD(sr0, sr1); - r3 = VSUB(sr0, sr1); - i0 = VADD(si0, si1); - i3 = VSUB(si1, si0); - r1 = VADD(dr0, di1); - r2 = VSUB(dr0, di1); - i1 = VSUB(dr1, di0); - i2 = VADD(dr1, di0); - - *out++ = r0; - *out++ = i0; - *out++ = r1; - *out++ = i1; - *out++ = r2; - *out++ = i2; - *out++ = r3; - *out++ = i3; - -} - -static NEVER_INLINE(void) pffft_real_finalize(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) { - int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks - /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ - - v4sf_union cr, ci, *uout = (v4sf_union*)out; - v4sf save = in[7], zero=VZERO(); - float xr0, xi0, xr1, xi1, xr2, xi2, xr3, xi3; - static const float s = (float)M_SQRT2/2; - - cr.v = in[0]; ci.v = in[Ncvec*2-1]; - assert(in != out); - pffft_real_finalize_4x4(&zero, &zero, in+1, e, out); - - /* - [cr0 cr1 cr2 cr3 ci0 ci1 ci2 ci3] - - [Xr(1)] ] [1 1 1 1 0 0 0 0] - [Xr(N/4) ] [0 0 0 0 1 s 0 -s] - [Xr(N/2) ] [1 0 -1 0 0 0 0 0] - [Xr(3N/4)] [0 0 0 0 1 -s 0 s] - [Xi(1) ] [1 -1 1 -1 0 0 0 0] - [Xi(N/4) ] [0 0 0 0 0 -s -1 -s] - [Xi(N/2) ] [0 -1 0 1 0 0 0 0] - [Xi(3N/4)] [0 0 0 0 0 -s 1 -s] - */ - - xr0=(cr.f[0]+cr.f[2]) + (cr.f[1]+cr.f[3]); uout[0].f[0] = xr0; - xi0=(cr.f[0]+cr.f[2]) - (cr.f[1]+cr.f[3]); uout[1].f[0] = xi0; - xr2=(cr.f[0]-cr.f[2]); uout[4].f[0] = xr2; - xi2=(cr.f[3]-cr.f[1]); uout[5].f[0] = xi2; - xr1= ci.f[0] + s*(ci.f[1]-ci.f[3]); uout[2].f[0] = xr1; - xi1=-ci.f[2] - s*(ci.f[1]+ci.f[3]); uout[3].f[0] = xi1; - xr3= ci.f[0] - s*(ci.f[1]-ci.f[3]); uout[6].f[0] = xr3; - xi3= ci.f[2] - s*(ci.f[1]+ci.f[3]); uout[7].f[0] = xi3; - - for (k=1; k < dk; ++k) { - v4sf save_next = in[8*k+7]; - pffft_real_finalize_4x4(&save, &in[8*k+0], in + 8*k+1, - e + k*6, out + k*8); - save = save_next; - } - -} - -static ALWAYS_INLINE(void) pffft_real_preprocess_4x4(const v4sf *in, - const v4sf *e, v4sf *out, int first) { - v4sf r0=in[0], i0=in[1], r1=in[2], i1=in[3], r2=in[4], i2=in[5], r3=in[6], i3=in[7]; - /* - transformation for each column is: - - [1 1 1 1 0 0 0 0] [r0] - [1 0 0 -1 0 -1 -1 0] [r1] - [1 -1 -1 1 0 0 0 0] [r2] - [1 0 0 -1 0 1 1 0] [r3] - [0 0 0 0 1 -1 1 -1] * [i0] - [0 -1 1 0 1 0 0 1] [i1] - [0 0 0 0 1 1 -1 -1] [i2] - [0 1 -1 0 1 0 0 1] [i3] - */ - - v4sf sr0 = VADD(r0,r3), dr0 = VSUB(r0,r3); - v4sf sr1 = VADD(r1,r2), dr1 = VSUB(r1,r2); - v4sf si0 = VADD(i0,i3), di0 = VSUB(i0,i3); - v4sf si1 = VADD(i1,i2), di1 = VSUB(i1,i2); - - r0 = VADD(sr0, sr1); - r2 = VSUB(sr0, sr1); - r1 = VSUB(dr0, si1); - r3 = VADD(dr0, si1); - i0 = VSUB(di0, di1); - i2 = VADD(di0, di1); - i1 = VSUB(si0, dr1); - i3 = VADD(si0, dr1); - - VCPLXMULCONJ(r1,i1,e[0],e[1]); - VCPLXMULCONJ(r2,i2,e[2],e[3]); - VCPLXMULCONJ(r3,i3,e[4],e[5]); - - VTRANSPOSE4(r0,r1,r2,r3); - VTRANSPOSE4(i0,i1,i2,i3); - - if (!first) { - *out++ = r0; - *out++ = i0; - } - *out++ = r1; - *out++ = i1; - *out++ = r2; - *out++ = i2; - *out++ = r3; - *out++ = i3; -} - -static NEVER_INLINE(void) pffft_real_preprocess(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) { - int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks - /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ - - v4sf_union Xr, Xi, *uout = (v4sf_union*)out; - float cr0, ci0, cr1, ci1, cr2, ci2, cr3, ci3; - static const float s = (float)M_SQRT2; - assert(in != out); - for (k=0; k < 4; ++k) { - Xr.f[k] = ((float*)in)[8*k]; - Xi.f[k] = ((float*)in)[8*k+4]; - } - - pffft_real_preprocess_4x4(in, e, out+1, 1); // will write only 6 values - - /* - [Xr0 Xr1 Xr2 Xr3 Xi0 Xi1 Xi2 Xi3] - - [cr0] [1 0 2 0 1 0 0 0] - [cr1] [1 0 0 0 -1 0 -2 0] - [cr2] [1 0 -2 0 1 0 0 0] - [cr3] [1 0 0 0 -1 0 2 0] - [ci0] [0 2 0 2 0 0 0 0] - [ci1] [0 s 0 -s 0 -s 0 -s] - [ci2] [0 0 0 0 0 -2 0 2] - [ci3] [0 -s 0 s 0 -s 0 -s] - */ - for (k=1; k < dk; ++k) { - pffft_real_preprocess_4x4(in+8*k, e + k*6, out-1+k*8, 0); - } - - cr0=(Xr.f[0]+Xi.f[0]) + 2*Xr.f[2]; uout[0].f[0] = cr0; - cr1=(Xr.f[0]-Xi.f[0]) - 2*Xi.f[2]; uout[0].f[1] = cr1; - cr2=(Xr.f[0]+Xi.f[0]) - 2*Xr.f[2]; uout[0].f[2] = cr2; - cr3=(Xr.f[0]-Xi.f[0]) + 2*Xi.f[2]; uout[0].f[3] = cr3; - ci0= 2*(Xr.f[1]+Xr.f[3]); uout[2*Ncvec-1].f[0] = ci0; - ci1= s*(Xr.f[1]-Xr.f[3]) - s*(Xi.f[1]+Xi.f[3]); uout[2*Ncvec-1].f[1] = ci1; - ci2= 2*(Xi.f[3]-Xi.f[1]); uout[2*Ncvec-1].f[2] = ci2; - ci3=-s*(Xr.f[1]-Xr.f[3]) - s*(Xi.f[1]+Xi.f[3]); uout[2*Ncvec-1].f[3] = ci3; -} - - -void pffft_transform_internal(PFFFT_Setup *setup, const float *finput, float *foutput, v4sf *scratch, - pffft_direction_t direction, int ordered) { - int k, Ncvec = setup->Ncvec; - int nf_odd = (setup->ifac[1] & 1); - - // temporary buffer is allocated on the stack if the scratch pointer is NULL - int stack_allocate = (scratch == 0 ? Ncvec*2 : 1); - VLA_ARRAY_ON_STACK(v4sf, scratch_on_stack, stack_allocate); - - const v4sf *vinput = (const v4sf*)finput; - v4sf *voutput = (v4sf*)foutput; - v4sf *buff[2] = { voutput, scratch ? scratch : scratch_on_stack }; - int ib = (nf_odd ^ ordered ? 1 : 0); - - assert(VALIGNED(finput) && VALIGNED(foutput)); - - //assert(finput != foutput); - if (direction == PFFFT_FORWARD) { - ib = !ib; - if (setup->transform == PFFFT_REAL) { - ib = (rfftf1_ps(Ncvec*2, vinput, buff[ib], buff[!ib], - setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1); - pffft_real_finalize(Ncvec, buff[ib], buff[!ib], (v4sf*)setup->e); - } else { - v4sf *tmp = buff[ib]; - for (k=0; k < Ncvec; ++k) { - UNINTERLEAVE2(vinput[k*2], vinput[k*2+1], tmp[k*2], tmp[k*2+1]); - } - ib = (cfftf1_ps(Ncvec, buff[ib], buff[!ib], buff[ib], - setup->twiddle, &setup->ifac[0], -1) == buff[0] ? 0 : 1); - pffft_cplx_finalize(Ncvec, buff[ib], buff[!ib], (v4sf*)setup->e); - } - if (ordered) { - pffft_zreorder(setup, (float*)buff[!ib], (float*)buff[ib], PFFFT_FORWARD); - } else ib = !ib; - } else { - if (vinput == buff[ib]) { - ib = !ib; // may happen when finput == foutput - } - if (ordered) { - pffft_zreorder(setup, (float*)vinput, (float*)buff[ib], PFFFT_BACKWARD); - vinput = buff[ib]; ib = !ib; - } - if (setup->transform == PFFFT_REAL) { - pffft_real_preprocess(Ncvec, vinput, buff[ib], (v4sf*)setup->e); - ib = (rfftb1_ps(Ncvec*2, buff[ib], buff[0], buff[1], - setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1); - } else { - pffft_cplx_preprocess(Ncvec, vinput, buff[ib], (v4sf*)setup->e); - ib = (cfftf1_ps(Ncvec, buff[ib], buff[0], buff[1], - setup->twiddle, &setup->ifac[0], +1) == buff[0] ? 0 : 1); - for (k=0; k < Ncvec; ++k) { - INTERLEAVE2(buff[ib][k*2], buff[ib][k*2+1], buff[ib][k*2], buff[ib][k*2+1]); - } - } - } - - if (buff[ib] != voutput) { - /* extra copy required -- this situation should only happen when finput == foutput */ - assert(finput==foutput); - for (k=0; k < Ncvec; ++k) { - v4sf a = buff[ib][2*k], b = buff[ib][2*k+1]; - voutput[2*k] = a; voutput[2*k+1] = b; - } - ib = !ib; - } - assert(buff[ib] == voutput); -} - -void pffft_zconvolve_accumulate(PFFFT_Setup *s, const float *a, const float *b, float *ab, float scaling) { - int Ncvec = s->Ncvec; - const v4sf * RESTRICT va = (const v4sf*)a; - const v4sf * RESTRICT vb = (const v4sf*)b; - v4sf * RESTRICT vab = (v4sf*)ab; - -#if defined(__arm__) || defined(__aarch64__) - __builtin_prefetch(va); - __builtin_prefetch(vb); - __builtin_prefetch(vab); - __builtin_prefetch(va+2); - __builtin_prefetch(vb+2); - __builtin_prefetch(vab+2); - __builtin_prefetch(va+4); - __builtin_prefetch(vb+4); - __builtin_prefetch(vab+4); - __builtin_prefetch(va+6); - __builtin_prefetch(vb+6); - __builtin_prefetch(vab+6); -# ifndef __clang__ -# define ZCONVOLVE_USING_INLINE_NEON_ASM -# endif -#endif - - float ar, ai, br, bi, abr, abi; -#ifndef ZCONVOLVE_USING_INLINE_ASM - v4sf vscal = LD_PS1(scaling); - int i; -#endif - - assert(VALIGNED(a) && VALIGNED(b) && VALIGNED(ab)); - ar = ((v4sf_union*)va)[0].f[0]; - ai = ((v4sf_union*)va)[1].f[0]; - br = ((v4sf_union*)vb)[0].f[0]; - bi = ((v4sf_union*)vb)[1].f[0]; - abr = ((v4sf_union*)vab)[0].f[0]; - abi = ((v4sf_union*)vab)[1].f[0]; - -#ifdef ZCONVOLVE_USING_INLINE_ASM // inline asm version, unfortunately miscompiled by clang 3.2, at least on ubuntu.. so this will be restricted to gcc - const float *a_ = a, *b_ = b; float *ab_ = ab; - int N = Ncvec; - asm volatile("mov r8, %2 \n" - "vdup.f32 q15, %4 \n" - "1: \n" - "pld [%0,#64] \n" - "pld [%1,#64] \n" - "pld [%2,#64] \n" - "pld [%0,#96] \n" - "pld [%1,#96] \n" - "pld [%2,#96] \n" - "vld1.f32 {q0,q1}, [%0,:128]! \n" - "vld1.f32 {q4,q5}, [%1,:128]! \n" - "vld1.f32 {q2,q3}, [%0,:128]! \n" - "vld1.f32 {q6,q7}, [%1,:128]! \n" - "vld1.f32 {q8,q9}, [r8,:128]! \n" - - "vmul.f32 q10, q0, q4 \n" - "vmul.f32 q11, q0, q5 \n" - "vmul.f32 q12, q2, q6 \n" - "vmul.f32 q13, q2, q7 \n" - "vmls.f32 q10, q1, q5 \n" - "vmla.f32 q11, q1, q4 \n" - "vld1.f32 {q0,q1}, [r8,:128]! \n" - "vmls.f32 q12, q3, q7 \n" - "vmla.f32 q13, q3, q6 \n" - "vmla.f32 q8, q10, q15 \n" - "vmla.f32 q9, q11, q15 \n" - "vmla.f32 q0, q12, q15 \n" - "vmla.f32 q1, q13, q15 \n" - "vst1.f32 {q8,q9},[%2,:128]! \n" - "vst1.f32 {q0,q1},[%2,:128]! \n" - "subs %3, #2 \n" - "bne 1b \n" - : "+r"(a_), "+r"(b_), "+r"(ab_), "+r"(N) : "r"(scaling) : "r8", "q0","q1","q2","q3","q4","q5","q6","q7","q8","q9", "q10","q11","q12","q13","q15","memory"); -#else // default routine, works fine for non-arm cpus with current compilers - for (i=0; i < Ncvec; i += 2) { - v4sf ar, ai, br, bi; - ar = va[2*i+0]; ai = va[2*i+1]; - br = vb[2*i+0]; bi = vb[2*i+1]; - VCPLXMUL(ar, ai, br, bi); - vab[2*i+0] = VMADD(ar, vscal, vab[2*i+0]); - vab[2*i+1] = VMADD(ai, vscal, vab[2*i+1]); - ar = va[2*i+2]; ai = va[2*i+3]; - br = vb[2*i+2]; bi = vb[2*i+3]; - VCPLXMUL(ar, ai, br, bi); - vab[2*i+2] = VMADD(ar, vscal, vab[2*i+2]); - vab[2*i+3] = VMADD(ai, vscal, vab[2*i+3]); - } -#endif - if (s->transform == PFFFT_REAL) { - ((v4sf_union*)vab)[0].f[0] = abr + ar*br*scaling; - ((v4sf_union*)vab)[1].f[0] = abi + ai*bi*scaling; - } -} - - -#else // defined(PFFFT_SIMD_DISABLE) - -// standard routine using scalar floats, without SIMD stuff. - -#define pffft_zreorder_nosimd pffft_zreorder -void pffft_zreorder_nosimd(PFFFT_Setup *setup, const float *in, float *out, pffft_direction_t direction) { - int k, N = setup->N; - if (setup->transform == PFFFT_COMPLEX) { - for (k=0; k < 2*N; ++k) out[k] = in[k]; - return; - } - else if (direction == PFFFT_FORWARD) { - float x_N = in[N-1]; - for (k=N-1; k > 1; --k) out[k] = in[k-1]; - out[0] = in[0]; - out[1] = x_N; - } else { - float x_N = in[1]; - for (k=1; k < N-1; ++k) out[k] = in[k+1]; - out[0] = in[0]; - out[N-1] = x_N; - } -} - -#define pffft_transform_internal_nosimd pffft_transform_internal -void pffft_transform_internal_nosimd(PFFFT_Setup *setup, const float *input, float *output, float *scratch, - pffft_direction_t direction, int ordered) { - int Ncvec = setup->Ncvec; - int nf_odd = (setup->ifac[1] & 1); - - // temporary buffer is allocated on the stack if the scratch pointer is NULL - int stack_allocate = (scratch == 0 ? Ncvec*2 : 1); - VLA_ARRAY_ON_STACK(v4sf, scratch_on_stack, stack_allocate); - float *buff[2]; - int ib; - if (scratch == 0) scratch = scratch_on_stack; - buff[0] = output; buff[1] = scratch; - - if (setup->transform == PFFFT_COMPLEX) ordered = 0; // it is always ordered. - ib = (nf_odd ^ ordered ? 1 : 0); - - if (direction == PFFFT_FORWARD) { - if (setup->transform == PFFFT_REAL) { - ib = (rfftf1_ps(Ncvec*2, input, buff[ib], buff[!ib], - setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1); - } else { - ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], - setup->twiddle, &setup->ifac[0], -1) == buff[0] ? 0 : 1); - } - if (ordered) { - pffft_zreorder(setup, buff[ib], buff[!ib], PFFFT_FORWARD); ib = !ib; - } - } else { - if (input == buff[ib]) { - ib = !ib; // may happen when finput == foutput - } - if (ordered) { - pffft_zreorder(setup, input, buff[!ib], PFFFT_BACKWARD); - input = buff[!ib]; - } - if (setup->transform == PFFFT_REAL) { - ib = (rfftb1_ps(Ncvec*2, input, buff[ib], buff[!ib], - setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1); - } else { - ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], - setup->twiddle, &setup->ifac[0], +1) == buff[0] ? 0 : 1); - } - } - if (buff[ib] != output) { - int k; - // extra copy required -- this situation should happens only when finput == foutput - assert(input==output); - for (k=0; k < Ncvec; ++k) { - float a = buff[ib][2*k], b = buff[ib][2*k+1]; - output[2*k] = a; output[2*k+1] = b; - } - ib = !ib; - } - assert(buff[ib] == output); -} - -#define pffft_zconvolve_accumulate_nosimd pffft_zconvolve_accumulate -void pffft_zconvolve_accumulate_nosimd(PFFFT_Setup *s, const float *a, const float *b, - float *ab, float scaling) { - int i, Ncvec = s->Ncvec; - - if (s->transform == PFFFT_REAL) { - // take care of the fftpack ordering - ab[0] += a[0]*b[0]*scaling; - ab[2*Ncvec-1] += a[2*Ncvec-1]*b[2*Ncvec-1]*scaling; - ++ab; ++a; ++b; --Ncvec; - } - for (i=0; i < Ncvec; ++i) { - float ar, ai, br, bi; - ar = a[2*i+0]; ai = a[2*i+1]; - br = b[2*i+0]; bi = b[2*i+1]; - VCPLXMUL(ar, ai, br, bi); - ab[2*i+0] += ar*scaling; - ab[2*i+1] += ai*scaling; - } -} - -#endif // defined(PFFFT_SIMD_DISABLE) - -void pffft_transform(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction) { - pffft_transform_internal(setup, input, output, (v4sf*)work, direction, 0); -} - -void pffft_transform_ordered(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction) { - pffft_transform_internal(setup, input, output, (v4sf*)work, direction, 1); -} diff --git a/src/third_party/r8b/pffft.h b/src/third_party/r8b/pffft.h deleted file mode 100644 index d9fbbaf..0000000 --- a/src/third_party/r8b/pffft.h +++ /dev/null @@ -1,178 +0,0 @@ -/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) - - Based on original fortran 77 code from FFTPACKv4 from NETLIB, - authored by Dr Paul Swarztrauber of NCAR, in 1985. - - As confirmed by the NCAR fftpack software curators, the following - FFTPACKv5 license applies to FFTPACKv4 sources. My changes are - released under the same terms. - - FFTPACK license: - - http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html - - Copyright (c) 2004 the University Corporation for Atmospheric - Research ("UCAR"). All rights reserved. Developed by NCAR's - Computational and Information Systems Laboratory, UCAR, - www.cisl.ucar.edu. - - Redistribution and use of the Software in source and binary forms, - with or without modification, is permitted provided that the - following conditions are met: - - - Neither the names of NCAR's Computational and Information Systems - Laboratory, the University Corporation for Atmospheric Research, - nor the names of its sponsors or contributors may be used to - endorse or promote products derived from this Software without - specific prior written permission. - - - Redistributions of source code must retain the above copyright - notices, this list of conditions, and the disclaimer below. - - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions, and the disclaimer below in the - documentation and/or other materials provided with the - distribution. - - THIS 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 CONTRIBUTORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL 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 WITH THE - SOFTWARE. -*/ - -/* - PFFFT : a Pretty Fast FFT. - - This is basically an adaptation of the single precision fftpack - (v4) as found on netlib taking advantage of SIMD instruction found - on cpus such as intel x86 (SSE1), powerpc (Altivec), and arm (NEON). - - For architectures where no SIMD instruction is available, the code - falls back to a scalar version. - - Restrictions: - - - 1D transforms only, with 32-bit single precision. - - - supports only transforms for inputs of length N of the form - N=(2^a)*(3^b)*(5^c), a >= 5, b >=0, c >= 0 (32, 48, 64, 96, 128, - 144, 160, etc are all acceptable lengths). Performance is best for - 128<=N<=8192. - - - all (float*) pointers in the functions below are expected to - have an "simd-compatible" alignment, that is 16 bytes on x86 and - powerpc CPUs. - - You can allocate such buffers with the functions - pffft_aligned_malloc / pffft_aligned_free (or with stuff like - posix_memalign..) - -*/ - -#ifndef PFFFT_H -#define PFFFT_H - -#include // for size_t -#include // Addition by AV. - -#ifdef __cplusplus -extern "C" { -#endif - - /* opaque struct holding internal stuff (precomputed twiddle factors) - this struct can be shared by many threads as it contains only - read-only data. - */ - typedef struct PFFFT_Setup PFFFT_Setup; - - /* direction of the transform */ - typedef enum { PFFFT_FORWARD, PFFFT_BACKWARD } pffft_direction_t; - - /* type of transform */ - typedef enum { PFFFT_REAL, PFFFT_COMPLEX } pffft_transform_t; - - /* - prepare for performing transforms of size N -- the returned - PFFFT_Setup structure is read-only so it can safely be shared by - multiple concurrent threads. - */ - PFFFT_Setup *pffft_new_setup(int N, pffft_transform_t transform); - void pffft_destroy_setup(PFFFT_Setup *); - /* - Perform a Fourier transform , The z-domain data is stored in the - most efficient order for transforming it back, or using it for - convolution. If you need to have its content sorted in the - "usual" way, that is as an array of interleaved complex numbers, - either use pffft_transform_ordered , or call pffft_zreorder after - the forward fft, and before the backward fft. - - Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x. - Typically you will want to scale the backward transform by 1/N. - - The 'work' pointer should point to an area of N (2*N for complex - fft) floats, properly aligned. If 'work' is NULL, then stack will - be used instead (this is probably the best strategy for small - FFTs, say for N < 16384). - - input and output may alias. - */ - void pffft_transform(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction); - - /* - Similar to pffft_transform, but makes sure that the output is - ordered as expected (interleaved complex numbers). This is - similar to calling pffft_transform and then pffft_zreorder. - - input and output may alias. - */ - void pffft_transform_ordered(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction); - - /* - call pffft_zreorder(.., PFFFT_FORWARD) after pffft_transform(..., - PFFFT_FORWARD) if you want to have the frequency components in - the correct "canonical" order, as interleaved complex numbers. - - (for real transforms, both 0-frequency and half frequency - components, which are real, are assembled in the first entry as - F(0)+i*F(n/2+1). Note that the original fftpack did place - F(n/2+1) at the end of the arrays). - - input and output should not alias. - */ - void pffft_zreorder(PFFFT_Setup *setup, const float *input, float *output, pffft_direction_t direction); - - /* - Perform a multiplication of the frequency components of dft_a and - dft_b and accumulate them into dft_ab. The arrays should have - been obtained with pffft_transform(.., PFFFT_FORWARD) and should - *not* have been reordered with pffft_zreorder (otherwise just - perform the operation yourself as the dft coefs are stored as - interleaved complex numbers). - - the operation performed is: dft_ab += (dft_a * fdt_b)*scaling - - The dft_a, dft_b and dft_ab pointers may alias. - */ - void pffft_zconvolve_accumulate(PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab, float scaling); - - /* - the float buffers must have the correct alignment (16-byte boundary - on intel and powerpc). This function may be used to obtain such - correctly aligned buffers. - */ - void *pffft_aligned_malloc(size_t nb_bytes); - void pffft_aligned_free(void *); - - /* return 4 or 1 wether support SSE/Altivec instructions was enable when building pffft.c */ - int pffft_simd_size(); - -#ifdef __cplusplus -} -#endif - -#endif // PFFFT_H diff --git a/src/third_party/r8b/r8bbase.cpp b/src/third_party/r8b/r8bbase.cpp deleted file mode 100644 index 7c27d1c..0000000 --- a/src/third_party/r8b/r8bbase.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file r8bbase.cpp - * - * @brief C++ file that should be compiled and included into your application. - * - * This is a single library file that should be compiled and included into the - * project that uses the "r8brain-free-src" sample rate converter. This file - * defines several global static objects used by the library. - * - * You may also need to include to your project: the "Kernel32" library - * (on Windows) and the "pthread" library on Mac OS X and Linux. - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#include "CDSPFIRFilter.h" -#include "CDSPFracInterpolator.h" - -namespace r8b { - -#if R8B_FLTTEST - int InterpFilterFracs = -1; - int InterpFilterFracsThird = -1; -#endif // R8B_FLTTEST - -CSyncObject CDSPRealFFTKeeper :: StateSync; -CDSPRealFFT :: CObjKeeper CDSPRealFFTKeeper :: FFTObjects[ 31 ]; - -CSyncObject CDSPFIRFilterCache :: StateSync; -CPtrKeeper< CDSPFIRFilter* > CDSPFIRFilterCache :: Objects; -int CDSPFIRFilterCache :: ObjCount = 0; - -CSyncObject CDSPFracDelayFilterBankCache :: StateSync; -CPtrKeeper< CDSPFracDelayFilterBank* > CDSPFracDelayFilterBankCache :: Objects; -CPtrKeeper< CDSPFracDelayFilterBank* > CDSPFracDelayFilterBankCache :: StaticObjects; -int CDSPFracDelayFilterBankCache :: ObjCount = 0; - -} // namespace r8b diff --git a/src/third_party/r8b/r8bbase.h b/src/third_party/r8b/r8bbase.h deleted file mode 100644 index f1c0b7e..0000000 --- a/src/third_party/r8b/r8bbase.h +++ /dev/null @@ -1,1240 +0,0 @@ -//$ nobt - -/** - * @file r8bbase.h - * - * @brief The "base" inclusion file with basic classes and functions. - * - * This is the "base" inclusion file for the "r8brain-free-src" sample rate - * converter. This inclusion file contains implementations of several small - * utility classes and functions used by the library. - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * See the "License.txt" file for license. - * - * @mainpage - * - * @section intro_sec Introduction - * - * Open source (under the MIT license) high-quality professional audio sample - * rate converter (SRC) (resampling) library. Features routines for SRC, both - * up- and downsampling, to/from any sample rate, including non-integer sample - * rates: it can be also used for conversion to/from SACD sample rate and even - * go beyond that. SRC routines were implemented in multi-platform C++ code, - * and have a high level of optimality. - * - * For more information, please visit - * https://github.com/avaneev/r8brain-free-src - * - * @section license License - * - * The MIT License (MIT) - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * - * 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. - * - * Please credit the creator of this library in your documentation in the - * following way: "Sample rate converter designed by Aleksey Vaneev of - * Voxengo" - * - * @version 4.6 - */ - -#ifndef R8BBASE_INCLUDED -#define R8BBASE_INCLUDED - -#include -#include -#include -#include -#include "r8bconf.h" - -#if defined( R8B_WIN ) - #include -#else // R8B_WIN - #include -#endif // R8B_WIN - -/** - * @brief The "r8brain-free-src" library namespace. - * - * The "r8brain-free-src" sample rate converter library namespace. - */ - -namespace r8b { - -/** - * Macro defines r8brain-free-src version string. - */ - -#define R8B_VERSION "4.6" - -#if !defined( M_PI ) - /** - * The macro equals to "pi" constant, fits 53-bit floating point mantissa. - */ - - #define M_PI 3.14159265358979324 -#endif // M_PI - -#if !defined( M_2PI ) - /** - * The M_2PI macro equals to "2 * pi" constant, fits 53-bit floating point - * mantissa. - */ - - #define M_2PI 6.28318530717958648 -#endif // M_2PI - -#if !defined( M_3PI ) - /** - * The M_3PI macro equals to "3 * pi" constant, fits 53-bit floating point - * mantissa. - */ - - #define M_3PI 9.42477796076937972 -#endif // M_3PI - -#if !defined( M_4PI ) - /** - * The M_4PI macro equals to "4 * pi" constant, fits 53-bit floating point - * mantissa. - */ - - #define M_4PI 12.56637061435917295 -#endif // M_4PI - -#if !defined( M_PId2 ) - /** - * The macro equals to "pi divided by 2" constant, fits 53-bit floating - * point mantissa. - */ - - #define M_PId2 1.57079632679489662 -#endif // M_PId2 - -/** - * A special macro that defines empty copy-constructor and copy operator with - * the "private:" prefix. This macro should be used in classes that cannot be - * copied in a standard C++ way. - * - * This macro does not need to be defined in classes derived from a class - * where such macro was already used. - * - * @param ClassName The name of the class which uses this macro. - */ - -#define R8BNOCTOR( ClassName ) \ - private: \ - ClassName( const ClassName& ) { } \ - ClassName& operator = ( const ClassName& ) { return( *this ); } - -/** - * @brief The default base class for objects created on heap. - * - * Class that implements "new" and "delete" operators that use standard - * malloc() and free() functions. - */ - -class CStdClassAllocator -{ -public: - /** - * @param n The size of the object, in bytes. - * @param p Pointer to object's pre-allocated memory block. - * @return Pointer to object. - */ - - void* operator new( size_t, void* p ) - { - return( p ); - } - - /** - * @param n The size of the object, in bytes. - * @return Pointer to the allocated memory block for the object. - */ - - void* operator new( size_t n ) - { - return( :: malloc( n )); - } - - /** - * @param n The size of the object, in bytes. - * @return Pointer to the allocated memory block for the object. - */ - - void* operator new[]( size_t n ) - { - return( :: malloc( n )); - } - - /** - * Operator frees a previously allocated memory block for the object. - * - * @param p Pointer to the allocated memory block for the object. - */ - - void operator delete( void* p ) - { - :: free( p ); - } - - /** - * Operator frees a previously allocated memory block for the object. - * - * @param p Pointer to the allocated memory block for the object. - */ - - void operator delete[]( void* p ) - { - :: free( p ); - } -}; - -/** - * @brief The default base class for objects that allocate blocks of memory. - * - * Memory buffer allocator that uses "stdlib" standard memory functions. - */ - -class CStdMemAllocator : public CStdClassAllocator -{ -public: - /** - * Function allocates memory block. - * - * @param Size The size of the block, in bytes. - * @result The pointer to the allocated block. - */ - - static void* allocmem( const size_t Size ) - { - return( :: malloc( Size )); - } - - /** - * Function reallocates a previously allocated memory block. - * - * @param p Pointer to the allocated block, can be NULL. - * @param Size The new size of the block, in bytes. - * @result The pointer to the (re)allocated block. - */ - - static void* reallocmem( void* p, const size_t Size ) - { - return( :: realloc( p, Size )); - } - - /** - * Function frees a previously allocated memory block. - * - * @param p Pointer to the allocated block, can be NULL. - */ - - static void freemem( void* p ) - { - :: free( p ); - } -}; - -/** - * @brief Templated memory buffer class for element buffers of fixed capacity. - * - * Fixed memory buffer object. Supports allocation of a fixed amount of - * memory. Does not store buffer's capacity - the user should know the actual - * capacity of the buffer. Does not feature "internal" storage, memory is - * always allocated via the R8B_MEMALLOCCLASS class's functions. Thus the - * object of this class can be moved in memory. - * - * This class manages memory space only - it does not perform element class - * construction nor destruction operations. - * - * This class applies 256-bit memory address alignment to the allocated data - * block. - * - * @param T The class of the stored elements (e.g. "double"). - */ - -template< class T > -class CFixedBuffer : public R8B_MEMALLOCCLASS -{ - R8BNOCTOR( CFixedBuffer ); - -public: - CFixedBuffer() - : Data0( NULL ) - , Data( NULL ) - { - } - - /** - * Constructor allocates memory so that the specified number of elements - * of type T can be stored in *this buffer object. - * - * @param Capacity Storage for this number of elements to allocate. - */ - - CFixedBuffer( const int Capacity ) - { - R8BASSERT( Capacity > 0 || Capacity == 0 ); - - Data0 = allocmem( Capacity * sizeof( T ) + Alignment ); - Data = (T*) alignptr( Data0, Alignment ); - - R8BASSERT( Data0 != NULL || Capacity == 0 ); - } - - ~CFixedBuffer() - { - freemem( Data0 ); - } - - /** - * Function allocates memory so that the specified number of elements of - * type T can be stored in *this buffer object. - * - * @param Capacity Storage for this number of elements to allocate. - */ - - void alloc( const int Capacity ) - { - R8BASSERT( Capacity > 0 || Capacity == 0 ); - - freemem( Data0 ); - Data0 = allocmem( Capacity * sizeof( T ) + Alignment ); - Data = (T*) alignptr( Data0, Alignment ); - - R8BASSERT( Data0 != NULL || Capacity == 0 ); - } - - /** - * Function reallocates memory so that the specified number of elements of - * type T can be stored in *this buffer object. Previously allocated data - * is copied to the new memory buffer. - * - * @param PrevCapacity Previous capacity of *this buffer. - * @param NewCapacity Storage for this number of elements to allocate. - */ - - void realloc( const int PrevCapacity, const int NewCapacity ) - { - R8BASSERT( PrevCapacity >= 0 ); - R8BASSERT( NewCapacity >= 0 ); - - void* const NewData0 = allocmem( NewCapacity * sizeof( T ) + - Alignment ); - - T* const NewData = (T*) alignptr( NewData0, Alignment ); - const size_t CopySize = ( PrevCapacity > NewCapacity ? - NewCapacity : PrevCapacity ) * sizeof( T ); - - if( CopySize > 0 ) - { - memcpy( NewData, Data, CopySize ); - } - - freemem( Data0 ); - Data0 = NewData0; - Data = NewData; - - R8BASSERT( Data0 != NULL || NewCapacity == 0 ); - } - - /** - * Function deallocates a previously allocated buffer. - */ - - void free() - { - freemem( Data0 ); - Data0 = NULL; - Data = NULL; - } - - /** - * @return Pointer to the first element of the allocated buffer, NULL if - * not allocated. - */ - - T* getPtr() const - { - return( Data ); - } - - /** - * @return Pointer to the first element of the allocated buffer, NULL if - * not allocated. - */ - - operator T* () const - { - return( Data ); - } - -private: - static const size_t Alignment = 32; ///< Data buffer alignment, in bytes. - ///< - void* Data0; ///< Buffer pointer, original unaligned. - ///< - T* Data; ///< Element buffer pointer, aligned. - ///< - - /** - * This macro forces provided pointer ptr to be aligned to align bytes. - * Works with power-of-2 alignments only. If no alignment is necessary, - * "align" bytes will be added to the pointer value. - * - * @tparam Tp Pointer type. - */ - - template< class Tp > - inline Tp alignptr( const Tp ptr, const uintptr_t align ) - { - return( (Tp) ( (uintptr_t) ptr + align - - ( (uintptr_t) ptr & ( align - 1 ))) ); - } -}; - -/** - * @brief Pointer-to-object "keeper" class with automatic deletion. - * - * An auxiliary class that can be used for keeping a pointer to object that - * should be deleted together with the "keeper" by calling object's "delete" - * operator. - * - * @param T Pointer type to operate with, must include the asterisk (e.g. - * "CDSPFIRFilter*"). - */ - -template< class T > -class CPtrKeeper -{ - R8BNOCTOR( CPtrKeeper ); - -public: - CPtrKeeper() - : Object( NULL ) - { - } - - /** - * Constructor assigns a pointer to object to *this keeper. - * - * @param aObject Pointer to object to keep, can be NULL. - */ - - template< class T2 > - CPtrKeeper( T2 const aObject ) - : Object( aObject ) - { - } - - ~CPtrKeeper() - { - delete Object; - } - - /** - * Function assigns a pointer to object to *this keeper. A previously - * keeped pointer will be reset and object deleted. - * - * @param aObject Pointer to object to keep, can be NULL. - */ - - template< class T2 > - void operator = ( T2 const aObject ) - { - reset(); - Object = aObject; - } - - /** - * @return Pointer to keeped object, NULL if no object is being kept. - */ - - T operator -> () const - { - return( Object ); - } - - /** - * @return Pointer to keeped object, NULL if no object is being kept. - */ - - operator T () const - { - return( Object ); - } - - /** - * Function resets the keeped pointer and deletes the keeped object. - */ - - void reset() - { - T DelObj = Object; - Object = NULL; - delete DelObj; - } - - /** - * @return Function returns the keeped pointer and resets it in *this - * keeper without object deletion. - */ - - T unkeep() - { - T ResObject = Object; - Object = NULL; - return( ResObject ); - } - -private: - T Object; ///< Pointer to keeped object. - ///< -}; - -/** - * @brief Multi-threaded synchronization object class. - * - * This class uses standard OS thread-locking (mutex) mechanism which is - * fairly efficient in most cases. - * - * The acquire() function can be called recursively, in the same thread, for - * this kind of thread-locking mechanism. This will not produce a dead-lock. - */ - -class CSyncObject -{ - R8BNOCTOR( CSyncObject ); - -public: - CSyncObject() - { - #if defined( R8B_WIN ) - InitializeCriticalSectionAndSpinCount( &CritSec, 4000 ); - #else // R8B_WIN - pthread_mutexattr_t MutexAttrs; - pthread_mutexattr_init( &MutexAttrs ); - pthread_mutexattr_settype( &MutexAttrs, PTHREAD_MUTEX_RECURSIVE ); - pthread_mutex_init( &Mutex, &MutexAttrs ); - pthread_mutexattr_destroy( &MutexAttrs ); - #endif // R8B_WIN - } - - ~CSyncObject() - { - #if defined( R8B_WIN ) - DeleteCriticalSection( &CritSec ); - #else // R8B_WIN - pthread_mutex_destroy( &Mutex ); - #endif // R8B_WIN - } - - /** - * Function "acquires" *this thread synchronizer object immediately or - * waits until another thread releases it. - */ - - void acquire() - { - #if defined( R8B_WIN ) - EnterCriticalSection( &CritSec ); - #else // R8B_WIN - pthread_mutex_lock( &Mutex ); - #endif // R8B_WIN - } - - /** - * Function "releases" *this previously acquired thread synchronizer - * object. - */ - - void release() - { - #if defined( R8B_WIN ) - LeaveCriticalSection( &CritSec ); - #else // R8B_WIN - pthread_mutex_unlock( &Mutex ); - #endif // R8B_WIN - } - -private: - #if defined( R8B_WIN ) - CRITICAL_SECTION CritSec; ///< Standard Windows critical section - ///< structure. - ///< - #else // R8B_WIN - pthread_mutex_t Mutex; ///< pthread.h mutex object. - ///< - #endif // R8B_WIN -}; - -/** - * @brief A "keeper" class for CSyncObject-based synchronization. - * - * Sync keeper class. The object of this class can be used as auto-init and - * auto-deinit object for calling the acquire() and release() functions of an - * object of the CSyncObject class. This "keeper" object is best used in - * functions as an "automatic" object allocated on the stack, possibly via the - * R8BSYNC() macro. - */ - -class CSyncKeeper -{ - R8BNOCTOR( CSyncKeeper ); - -public: - CSyncKeeper() - : SyncObj( NULL ) - { - } - - /** - * @param aSyncObj Pointer to the sync object which should be used for - * sync'ing, can be NULL. - */ - - CSyncKeeper( CSyncObject* const aSyncObj ) - : SyncObj( aSyncObj ) - { - if( SyncObj != NULL ) - { - SyncObj -> acquire(); - } - } - - /** - * @param aSyncObj Reference to the sync object which should be used for - * sync'ing. - */ - - CSyncKeeper( CSyncObject& aSyncObj ) - : SyncObj( &aSyncObj ) - { - SyncObj -> acquire(); - } - - ~CSyncKeeper() - { - if( SyncObj != NULL ) - { - SyncObj -> release(); - } - } - -protected: - CSyncObject* SyncObj; ///< Sync object in use (can be NULL). - ///< -}; - -/** - * The synchronization macro. The R8BSYNC( obj ) macro, which creates and - * object of the r8b::CSyncKeeper class on stack, should be put before - * sections of the code that may potentially change data asynchronously with - * other threads at the same time. The R8BSYNC( obj ) macro "acquires" the - * synchronization object thus blocking execution of other threads that also - * use the same R8BSYNC( obj ) macro. The blocked section begins with the - * R8BSYNC( obj ) macro and finishes at the end of the current C++ code block. - * Multiple R8BSYNC() macros may be defined from within the same code block. - * - * @param SyncObject An object of the CSyncObject type that is used for - * synchronization. - */ - -#define R8BSYNC( SyncObject ) R8BSYNC_( SyncObject, __LINE__ ) -#define R8BSYNC_( SyncObject, id ) R8BSYNC__( SyncObject, id ) -#define R8BSYNC__( SyncObject, id ) CSyncKeeper SyncKeeper##id( SyncObject ) - -/** - * @brief Sine signal generator class. - * - * Class implements sine signal generator without biasing. - */ - -class CSineGen -{ -public: - CSineGen() - { - } - - /** - * Constructor initializes *this sine signal generator. - * - * @param si Sine function increment, in radians. - * @param ph Starting phase, in radians. Add 0.5 * M_PI for cosine - * function. - */ - - CSineGen( const double si, const double ph ) - : svalue1( sin( ph )) - , svalue2( sin( ph - si )) - , sincr( 2.0 * cos( si )) - { - } - - /** - * Constructor initializes *this sine signal generator. - * - * @param si Sine function increment, in radians. - * @param ph Starting phase, in radians. Add 0.5 * M_PI for cosine - * function. - * @param g The overall gain factor, 1.0 for unity gain (-1.0 to 1.0 - * amplitude). - */ - - CSineGen( const double si, const double ph, const double g ) - : svalue1( sin( ph ) * g ) - , svalue2( sin( ph - si ) * g ) - , sincr( 2.0 * cos( si )) - { - } - - /** - * Function initializes *this sine signal generator. - * - * @param si Sine function increment, in radians. - * @param ph Starting phase, in radians. Add 0.5 * M_PI for cosine - * function. - */ - - void init( const double si, const double ph ) - { - svalue1 = sin( ph ); - svalue2 = sin( ph - si ); - sincr = 2.0 * cos( si ); - } - - /** - * Function initializes *this sine signal generator. - * - * @param si Sine function increment, in radians. - * @param ph Starting phase, in radians. Add 0.5 * M_PI for cosine - * function. - * @param g The overall gain factor, 1.0 for unity gain (-1.0 to 1.0 - * amplitude). - */ - - void init( const double si, const double ph, const double g ) - { - svalue1 = sin( ph ) * g; - svalue2 = sin( ph - si ) * g; - sincr = 2.0 * cos( si ); - } - - /** - * @return Next value of the sine function, without biasing. - */ - - double generate() - { - const double res = svalue1; - - svalue1 = sincr * res - svalue2; - svalue2 = res; - - return( res ); - } - -private: - double svalue1; ///< Current sine value. - ///< - double svalue2; ///< Previous sine value. - ///< - double sincr; ///< Sine value increment. - ///< -}; - -/** - * @param v Input value. - * @return Calculated bit occupancy of the specified input value. Bit - * occupancy means how many significant lower bits are necessary to store a - * specified value. Function treats the input value as unsigned. - */ - -inline int getBitOccupancy( const int v ) -{ - static const char OccupancyTable[] = - { - 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 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, 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, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 - }; - - const int tt = v >> 16; - - if( tt != 0 ) - { - const int t = v >> 24; - return( t != 0 ? 24 + OccupancyTable[ t & 0xFF ] : - 16 + OccupancyTable[ tt ]); - } - else - { - const int t = v >> 8; - return( t != 0 ? 8 + OccupancyTable[ t ] : OccupancyTable[ v ]); - } -} - -/** - * Function calculates frequency response of the specified FIR filter at the - * specified circular frequency. Phase can be calculated as atan2( im, re ). - * - * @param flt FIR filter's coefficients. - * @param fltlen Number of coefficients (taps) in the filter. - * @param th Circular frequency [0; pi]. - * @param[out] re0 Resulting real part of the complex frequency response. - * @param[out] im0 Resulting imaginary part of the complex frequency response. - * @param fltlat Filter's latency in samples. - */ - -inline void calcFIRFilterResponse( const double* flt, int fltlen, - const double th, double& re0, double& im0, const int fltlat = 0 ) -{ - const double sincr = 2.0 * cos( th ); - double cvalue1; - double svalue1; - - if( fltlat == 0 ) - { - cvalue1 = 1.0; - svalue1 = 0.0; - } - else - { - cvalue1 = cos( -fltlat * th ); - svalue1 = sin( -fltlat * th ); - } - - double cvalue2 = cos( -( fltlat + 1 ) * th ); - double svalue2 = sin( -( fltlat + 1 ) * th ); - - double re = 0.0; - double im = 0.0; - - while( fltlen > 0 ) - { - re += cvalue1 * flt[ 0 ]; - im += svalue1 * flt[ 0 ]; - flt++; - fltlen--; - - double tmp = cvalue1; - cvalue1 = sincr * cvalue1 - cvalue2; - cvalue2 = tmp; - - tmp = svalue1; - svalue1 = sincr * svalue1 - svalue2; - svalue2 = tmp; - } - - re0 = re; - im0 = im; -} - -/** - * Function calculates frequency response and group delay of the specified FIR - * filter at the specified circular frequency. The group delay is calculated - * by evaluating the filter's response at close side-band frequencies of "th". - * - * @param flt FIR filter's coefficients. - * @param fltlen Number of coefficients (taps) in the filter. - * @param th Circular frequency [0; pi]. - * @param[out] re Resulting real part of the complex frequency response. - * @param[out] im Resulting imaginary part of the complex frequency response. - * @param[out] gd Resulting group delay at the specified frequency, in - * samples. - */ - -inline void calcFIRFilterResponseAndGroupDelay( const double* const flt, - const int fltlen, const double th, double& re, double& im, double& gd ) -{ - // Calculate response at "th". - - calcFIRFilterResponse( flt, fltlen, th, re, im ); - - // Calculate response at close sideband frequencies. - - const int Count = 2; - const double thd2 = 1e-9; - double ths[ Count ] = { th - thd2, th + thd2 }; - - if( ths[ 0 ] < 0.0 ) - { - ths[ 0 ] = 0.0; - } - - if( ths[ 1 ] > M_PI ) - { - ths[ 1 ] = M_PI; - } - - double ph1[ Count ]; - int i; - - for( i = 0; i < Count; i++ ) - { - double re1; - double im1; - - calcFIRFilterResponse( flt, fltlen, ths[ i ], re1, im1 ); - ph1[ i ] = atan2( im1, re1 ); - } - - if( fabs( ph1[ 1 ] - ph1[ 0 ]) > M_PI ) - { - if( ph1[ 1 ] > ph1[ 0 ]) - { - ph1[ 1 ] -= M_2PI; - } - else - { - ph1[ 1 ] += M_2PI; - } - } - - const double thd = ths[ 1 ] - ths[ 0 ]; - gd = ( ph1[ 1 ] - ph1[ 0 ]) / thd; -} - -/** - * Function normalizes FIR filter so that its frequency response at DC is - * equal to DCGain. - * - * @param[in,out] p Filter coefficients. - * @param l Filter length. - * @param DCGain Filter's gain at DC (linear, non-decibel value). - * @param pstep "p" array step. - */ - -inline void normalizeFIRFilter( double* const p, const int l, - const double DCGain, const int pstep = 1 ) -{ - R8BASSERT( l > 0 ); - R8BASSERT( pstep != 0 ); - - double s = 0.0; - double* pp = p; - int i = l; - - while( i > 0 ) - { - s += *pp; - pp += pstep; - i--; - } - - s = DCGain / s; - pp = p; - i = l; - - while( i > 0 ) - { - *pp *= s; - pp += pstep; - i--; - } -} - -/** - * Function calculates coefficients used to calculate 3rd order spline - * (polynomial) on the equidistant lattice, using 8 points. - * - * @param[out] c Output coefficients buffer, length = 4. - * @param xm3 Point at x-3 position. - * @param xm2 Point at x-2 position. - * @param xm1 Point at x-1 position. - * @param x0 Point at x position. - * @param x1 Point at x+1 position. - * @param x2 Point at x+2 position. - * @param x3 Point at x+3 position. - * @param x4 Point at x+4 position. - */ - -inline void calcSpline3p8Coeffs( double* c, const double xm3, - const double xm2, const double xm1, const double x0, const double x1, - const double x2, const double x3, const double x4 ) -{ - c[ 0 ] = x0; - c[ 1 ] = ( 61.0 * ( x1 - xm1 ) + 16.0 * ( xm2 - x2 ) + - 3.0 * ( x3 - xm3 )) / 76.0; - - c[ 2 ] = ( 106.0 * ( xm1 + x1 ) + 10.0 * x3 + 6.0 * xm3 - 3.0 * x4 - - 29.0 * ( xm2 + x2 ) - 167.0 * x0 ) / 76.0; - - c[ 3 ] = ( 91.0 * ( x0 - x1 ) + 45.0 * ( x2 - xm1 ) + - 13.0 * ( xm2 - x3 ) + 3.0 * ( x4 - xm3 )) / 76.0; -} - -/** - * Function calculates coefficients used to calculate 2rd order spline - * (polynomial) on the equidistant lattice, using 8 points. This function is - * based on the calcSpline3Coeffs8() function, but without the 3rd order - * coefficient. - * - * @param[out] c Output coefficients buffer, length = 3. - * @param xm3 Point at x-3 position. - * @param xm2 Point at x-2 position. - * @param xm1 Point at x-1 position. - * @param x0 Point at x position. - * @param x1 Point at x+1 position. - * @param x2 Point at x+2 position. - * @param x3 Point at x+3 position. - * @param x4 Point at x+4 position. - */ - -inline void calcSpline2p8Coeffs( double* c, const double xm3, - const double xm2, const double xm1, const double x0, const double x1, - const double x2, const double x3, const double x4 ) -{ - c[ 0 ] = x0; - c[ 1 ] = ( 61.0 * ( x1 - xm1 ) + 16.0 * ( xm2 - x2 ) + - 3.0 * ( x3 - xm3 )) / 76.0; - - c[ 2 ] = ( 106.0 * ( xm1 + x1 ) + 10.0 * x3 + 6.0 * xm3 - 3.0 * x4 - - 29.0 * ( xm2 + x2 ) - 167.0 * x0 ) / 76.0; -} - -/** - * Function calculates coefficients used to calculate 3rd order segment - * interpolation polynomial on the equidistant lattice, using 4 points. - * - * @param[out] c Output coefficients buffer, length = 4. - * @param[in] y Equidistant point values. Value at offset 1 corresponds to - * x=0 point. - */ - -inline void calcInterpCoeffs3p4( double* const c, const double* const y ) -{ - c[ 0 ] = y[ 1 ]; - c[ 1 ] = 0.5 * ( y[ 2 ] - y[ 0 ]); - c[ 2 ] = y[ 0 ] - 2.5 * y[ 1 ] + y[ 2 ] + y[ 2 ] - 0.5 * y[ 3 ]; - c[ 3 ] = 0.5 * ( y[ 3 ] - y[ 0 ] ) + 1.5 * ( y[ 1 ] - y[ 2 ]); -} - -/** - * Function calculates coefficients used to calculate 3rd order segment - * interpolation polynomial on the equidistant lattice, using 6 points. - * - * @param[out] c Output coefficients buffer, length = 4. - * @param[in] y Equidistant point values. Value at offset 2 corresponds to - * x=0 point. - */ - -inline void calcInterpCoeffs3p6( double* const c, const double* const y ) -{ - c[ 0 ] = y[ 2 ]; - c[ 1 ] = ( 11.0 * ( y[ 3 ] - y[ 1 ]) + 2.0 * ( y[ 0 ] - y[ 4 ])) / 14.0; - c[ 2 ] = ( 20.0 * ( y[ 1 ] + y[ 3 ]) + 2.0 * y[ 5 ] - 4.0 * y[ 0 ] - - 7.0 * y[ 4 ] - 31.0 * y[ 2 ]) / 14.0; - - c[ 3 ] = ( 17.0 * ( y[ 2 ] - y[ 3 ]) + 9.0 * ( y[ 4 ] - y[ 1 ]) + - 2.0 * ( y[ 0 ] - y[ 5 ])) / 14.0; -} - -/** - * Function calculates coefficients used to calculate 3rd order segment - * interpolation polynomial on the equidistant lattice, using 8 points. - * - * @param[out] c Output coefficients buffer, length = 4. - * @param[in] y Equidistant point values. Value at offset 3 corresponds to - * x=0 point. - */ - -inline void calcInterpCoeffs3p8( double* const c, const double* const y ) -{ - c[ 0 ] = y[ 3 ]; - c[ 1 ] = ( 61.0 * ( y[ 4 ] - y[ 2 ]) + 16.0 * ( y[ 1 ] - y[ 5 ]) + - 3.0 * ( y[ 6 ] - y[ 0 ])) / 76.0; - - c[ 2 ] = ( 106.0 * ( y[ 2 ] + y[ 4 ]) + 10.0 * y[ 6 ] + 6.0 * y[ 0 ] - - 3.0 * y[ 7 ] - 29.0 * ( y[ 1 ] + y[ 5 ]) - 167.0 * y[ 3 ]) / 76.0; - - c[ 3 ] = ( 91.0 * ( y[ 3 ] - y[ 4 ]) + 45.0 * ( y[ 5 ] - y[ 2 ]) + - 13.0 * ( y[ 1 ] - y[ 6 ]) + 3.0 * ( y[ 7 ] - y[ 0 ])) / 76.0; -} - -/** - * Function calculates coefficients used to calculate 3rd order segment - * interpolation polynomial on the equidistant lattice, using 8 points. - * - * @param[out] c Output coefficients buffer, length = 3. - * @param[in] y Equidistant point values. Value at offset 3 corresponds to - * x=0 point. - */ - -inline void calcInterpCoeffs2p8( double* const c, const double* const y ) -{ - c[ 0 ] = y[ 3 ]; - c[ 1 ] = ( 61.0 * ( y[ 4 ] - y[ 2 ]) + 16.0 * ( y[ 1 ] - y[ 5 ]) + - 3.0 * ( y[ 6 ] - y[ 0 ])) / 76.0; - - c[ 2 ] = ( 106.0 * ( y[ 2 ] + y[ 4 ]) + 10.0 * y[ 6 ] + 6.0 * y[ 0 ] - - 3.0 * y[ 7 ] - 29.0 * ( y[ 1 ] + y[ 5 ]) - 167.0 * y[ 3 ]) / 76.0; -} - -#if !defined( min ) - -/** - * @param v1 Value 1. - * @param v2 Value 2. - * @return The minimum of 2 values. - */ - -template< class T > -inline T min( const T& v1, const T& v2 ) -{ - return( v1 < v2 ? v1 : v2 ); -} - -#endif // min - -#if !defined( max ) - -/** - * @param v1 Value 1. - * @param v2 Value 2. - * @return The maximum of 2 values. - */ - -template< class T > -inline T max( const T& v1, const T& v2 ) -{ - return( v1 > v2 ? v1 : v2 ); -} - -#endif // max - -/** - * Function "clamps" (clips) the specified value so that it is not lesser than - * "minv", and not greater than "maxv". - * - * @param Value Value to clamp. - * @param minv Minimal allowed value. - * @param maxv Maximal allowed value. - * @return "Clamped" value. - */ - -inline double clampr( const double Value, const double minv, - const double maxv ) -{ - if( Value < minv ) - { - return( minv ); - } - else - if( Value > maxv ) - { - return( maxv ); - } - else - { - return( Value ); - } -} - -/** - * @param x Value to square. - * @return Squared value of the argument. - */ - -inline double sqr( const double x ) -{ - return( x * x ); -} - -/** - * @param v Input value. - * @param p Power factor. - * @return Returns pow() function's value with input value's sign check. - */ - -inline double pows( const double v, const double p ) -{ - return( v < 0.0 ? -pow( -v, p ) : pow( v, p )); -} - -/** - * @param v Input value. - * @return Calculated single-argument Gaussian function of the input value. - */ - -inline double gauss( const double v ) -{ - return( exp( -( v * v ))); -} - -/** - * @param v Input value. - * @return Calculated inverse hyperbolic sine of the input value. - */ - -inline double asinh( const double v ) -{ - return( log( v + sqrt( v * v + 1.0 ))); -} - -/** - * @param x Input value. - * @return Calculated zero-th order modified Bessel function of the first kind - * of the input value. Approximate value. - */ - -inline double besselI0( const double x ) -{ - const double ax = fabs( x ); - double y; - - if( ax < 3.75 ) - { - y = x / 3.75; - y *= y; - - return( 1.0 + y * ( 3.5156229 + y * ( 3.0899424 + y * ( 1.2067492 + - y * ( 0.2659732 + y * ( 0.360768e-1 + y * 0.45813e-2 )))))); - } - - y = 3.75 / ax; - - return( exp( ax ) / sqrt( ax ) * ( 0.39894228 + y * ( 0.1328592e-1 + - y * ( 0.225319e-2 + y * ( -0.157565e-2 + y * ( 0.916281e-2 + - y * ( -0.2057706e-1 + y * ( 0.2635537e-1 + y * ( -0.1647633e-1 + - y * 0.392377e-2 ))))))))); -} - -} // namespace r8b - -#endif // R8BBASE_INCLUDED diff --git a/src/third_party/r8b/r8bconf.h b/src/third_party/r8b/r8bconf.h deleted file mode 100644 index 1018805..0000000 --- a/src/third_party/r8b/r8bconf.h +++ /dev/null @@ -1,198 +0,0 @@ -//$ nobt -//$ nocpp - -/** - * @file r8bconf.h - * - * @brief The "configuration" inclusion file you can modify. - * - * This is the "configuration" inclusion file for the "r8brain-free-src" - * sample rate converter. You may redefine the macros here as you see fit. - * - * r8brain-free-src Copyright (c) 2013-2019 Aleksey Vaneev - * See the "License.txt" file for license. - */ - -#ifndef R8BCONF_INCLUDED -#define R8BCONF_INCLUDED - -#define R8B_PFFFT 1 -#define R8B_FASTTIMING 1 -#define R8B_EXTFFT 1 - -#if defined( _WIN32 ) || defined( _WIN64 ) - #define R8B_WIN 1 -#elif defined( __APPLE__ ) - #define R8B_MAC 1 -#else // defined( __APPLE__ ) - #define R8B_LNX 1 // Assume Linux (Unix) platform by default. -#endif // defined( __APPLE__ ) - -#if !defined( R8B_FLTLEN ) - /** - * This macro defines the default fractional delay filter length. Macro is - * used by the r8b::CDSPResampler class. - */ - - #define R8B_FLTLEN 28 -#endif // !defined( R8B_FLTLEN ) - -#if !defined( R8B_FLTFRACS ) - /** - * This macro defines the default number of fractional delay filters that - * are sampled by the filter bank. Macro is used by the r8b::CDSPResampler - * class. In order to get consistent results when resampling to/from - * different sample rates, it is suggested to set this macro to a suitable - * prime number. - */ - - #define R8B_FLTFRACS 1733 -#endif // !defined( R8B_FLTFRACS ) - -#if !defined( R8B_IPP ) - /** - * Set the R8B_IPP macro definition to 1 to enable the use of Intel IPP's - * fast Fourier transform functions. Also uncomment and correct the IPP - * header inclusion macros. - * - * Do not forget to call the ippInit() function at the start of the - * application, before using this library's functions. - */ - - #define R8B_IPP 0 - -// #include -// #include -#endif // !defined( R8B_IPP ) - -#if !defined( R8BASSERT ) - /** - * Assertion macro used to check for certain run-time conditions. By - * default no action is taken if assertion fails. - * - * @param e Expression to check. - */ - - #define R8BASSERT( e ) -#endif // !defined( R8BASSERT ) - -#if !defined( R8BCONSOLE ) - /** - * Console output macro, used to output various resampler status strings, - * including filter design parameters, convolver parameters. - * - * @param e Expression to send to the console, usually consists of a - * standard "printf" format string followed by several parameters - * (__VA_ARGS__). - */ - - #define R8BCONSOLE( ... ) -#endif // !defined( R8BCONSOLE ) - -#if !defined( R8B_BASECLASS ) - /** - * Macro defines the name of the class from which all classes that are - * designed to be created on heap are derived. The default - * r8b::CStdClassAllocator class uses "stdlib" memory allocation - * functions. - * - * The classes that are best placed on stack or as class members are not - * derived from any class. - */ - - #define R8B_BASECLASS :: r8b :: CStdClassAllocator -#endif // !defined( R8B_BASECLASS ) - -#if !defined( R8B_MEMALLOCCLASS ) - /** - * Macro defines the name of the class that implements raw memory - * allocation functions, see the r8b::CStdMemAllocator class for details. - */ - - #define R8B_MEMALLOCCLASS :: r8b :: CStdMemAllocator -#endif // !defined( R8B_MEMALLOCCLASS ) - -#if !defined( R8B_FILTER_CACHE_MAX ) - /** - * This macro specifies the number of filters kept in the cache at most. - * The actual number can be higher if many different filters are in use at - * the same time. - */ - - #define R8B_FILTER_CACHE_MAX 96 -#endif // !defined( R8B_FILTER_CACHE_MAX ) - -#if !defined( R8B_FRACBANK_CACHE_MAX ) - /** - * This macro specifies the number of whole-number stepping fractional - * delay filter banks kept in the cache at most. The actual number can be - * higher if many different filter banks are in use at the same time. As - * filter banks are usually big objects, it is advisable to keep this - * cache size small. - */ - - #define R8B_FRACBANK_CACHE_MAX 12 -#endif // !defined( R8B_FRACBANK_CACHE_MAX ) - -#if !defined( R8B_FLTTEST ) - /** - * This macro, when equal to 1, enables fractional delay filter bank - * testing: in this mode the filter bank becomes dynamic member of the - * CDSPFracInterpolator object instead of being a global static object. - */ - - #define R8B_FLTTEST 0 -#endif // !defined( R8B_FLTTEST ) - -#if !defined( R8B_FASTTIMING ) - /** - * This macro, when equal to 1, enables fast approach to interpolation - * sample timing. This approach improves interpolation performance - * (by around 15%) at the expense of a minor sample timing drift which is - * on the order of 1e-6 samples per 10 billion output samples. This - * setting does not apply to whole-number stepping if it is in use as this - * stepping provides zero timing error without performance impact. Also - * does not apply to the cases when whole-numbered resampling is in actual - * use. - */ - - #define R8B_FASTTIMING 0 -#endif // !defined( R8B_FASTTIMING ) - -#if !defined( R8B_EXTFFT ) - /** - * This macro, when equal to 1, extends length of low-pass filters' FFT - * block by a factor of 2 by zero-padding them. This usually improves the - * overall time performance of the resampler at the expense of higher - * overall latency (initial processing delay). If such delay is not an - * issue, setting this macro to 1 is preferrable. This macro can only have - * a value of 0 or 1. - */ - - #define R8B_EXTFFT 0 -#endif // !defined( R8B_EXTFFT ) - -#if !defined( R8B_PFFFT ) - /** - * When defined as 1, enables PFFFT routines which are fast, but limited - * to 24-bit precision. - */ - - #define R8B_PFFFT 0 -#endif // !defined( R8B_PFFFT ) - -#if R8B_PFFFT - #include "pffft.h" - #define R8B_FLOATFFT 1 -#endif // R8B_PFFFT - -#if !defined( R8B_FLOATFFT ) - /** - * The R8B_FLOATFFT definition enables double-to-float buffer conversion - * for FFT operations for algorithms that work with "float" values. - */ - - #define R8B_FLOATFFT 0 -#endif // !defined( R8B_FLOATFFT ) - -#endif // R8BCONF_INCLUDED diff --git a/src/third_party/texture_compressor/README.md b/src/third_party/texture_compressor/README.md index 05ae4a8..087d3f3 100644 --- a/src/third_party/texture_compressor/README.md +++ b/src/third_party/texture_compressor/README.md @@ -1,6 +1,2 @@ Real-time texture compression code salvaged from chromium project repository. Implements ATC, DXT and ETC1 compression, optimized for NEON. - -It was used in chromium project for compositor tiles to reduce the GPU memory -usage for low to mid-end mobile devices. It got obsolete with the GPU rasterizer -introduced in chromium 37 and removed.