react-native-tuner-engine 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +188 -0
  3. package/TunerEngine.podspec +32 -0
  4. package/android/build.gradle +86 -0
  5. package/android/src/main/AndroidManifest.xml +3 -0
  6. package/android/src/main/cpp/CMakeLists.txt +31 -0
  7. package/android/src/main/cpp/OboeAudioSource.cpp +101 -0
  8. package/android/src/main/cpp/OboeAudioSource.h +41 -0
  9. package/android/src/main/cpp/TunerEngineJni.cpp +220 -0
  10. package/android/src/main/java/com/tunerengine/TunerEngineModule.kt +183 -0
  11. package/android/src/main/java/com/tunerengine/TunerEnginePackage.kt +31 -0
  12. package/cpp/CMakeLists.txt +25 -0
  13. package/cpp/build/CMakeCache.txt +347 -0
  14. package/cpp/build/CMakeFiles/4.3.2/CMakeCXXCompiler.cmake +102 -0
  15. package/cpp/build/CMakeFiles/4.3.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
  16. package/cpp/build/CMakeFiles/4.3.2/CMakeSystem.cmake +15 -0
  17. package/cpp/build/CMakeFiles/4.3.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +949 -0
  18. package/cpp/build/CMakeFiles/4.3.2/CompilerIdCXX/a.out +0 -0
  19. package/cpp/build/CMakeFiles/4.3.2/CompilerIdCXX/apple-sdk.cpp +1 -0
  20. package/cpp/build/CMakeFiles/CMakeConfigureLog.yaml +1619 -0
  21. package/cpp/build/CMakeFiles/CMakeDirectoryInformation.cmake +16 -0
  22. package/cpp/build/CMakeFiles/InstallScripts.json +7 -0
  23. package/cpp/build/CMakeFiles/Makefile.cmake +118 -0
  24. package/cpp/build/CMakeFiles/Makefile2 +122 -0
  25. package/cpp/build/CMakeFiles/TargetDirectories.txt +3 -0
  26. package/cpp/build/CMakeFiles/cmake.check_cache +1 -0
  27. package/cpp/build/CMakeFiles/progress.marks +1 -0
  28. package/cpp/build/CMakeFiles/tuner_engine_core.dir/DependInfo.cmake +36 -0
  29. package/cpp/build/CMakeFiles/tuner_engine_core.dir/build.make +322 -0
  30. package/cpp/build/CMakeFiles/tuner_engine_core.dir/cmake_clean.cmake +37 -0
  31. package/cpp/build/CMakeFiles/tuner_engine_core.dir/cmake_clean_target.cmake +3 -0
  32. package/cpp/build/CMakeFiles/tuner_engine_core.dir/compiler_depend.make +2 -0
  33. package/cpp/build/CMakeFiles/tuner_engine_core.dir/compiler_depend.ts +2 -0
  34. package/cpp/build/CMakeFiles/tuner_engine_core.dir/depend.make +2 -0
  35. package/cpp/build/CMakeFiles/tuner_engine_core.dir/flags.make +12 -0
  36. package/cpp/build/CMakeFiles/tuner_engine_core.dir/link.txt +2 -0
  37. package/cpp/build/CMakeFiles/tuner_engine_core.dir/progress.make +16 -0
  38. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/AudioFrameDispatcher.cpp.o +0 -0
  39. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/AudioFrameDispatcher.cpp.o.d +814 -0
  40. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/BiquadHpf.cpp.o +0 -0
  41. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/BiquadHpf.cpp.o.d +206 -0
  42. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/CepstrumPitchDetector.cpp.o +0 -0
  43. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/CepstrumPitchDetector.cpp.o.d +797 -0
  44. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/EnsembleSelector.cpp.o +0 -0
  45. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/EnsembleSelector.cpp.o.d +755 -0
  46. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/NoteMapper.cpp.o +0 -0
  47. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/NoteMapper.cpp.o.d +669 -0
  48. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/OnsetDetector.cpp.o +0 -0
  49. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/OnsetDetector.cpp.o.d +648 -0
  50. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/Pipeline.cpp.o +0 -0
  51. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/Pipeline.cpp.o.d +765 -0
  52. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/PostProcessor.cpp.o +0 -0
  53. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/PostProcessor.cpp.o.d +648 -0
  54. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/PyinPitchDetector.cpp.o +0 -0
  55. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/PyinPitchDetector.cpp.o.d +755 -0
  56. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/SnrEstimator.cpp.o +0 -0
  57. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/SnrEstimator.cpp.o.d +648 -0
  58. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/StringMatcher.cpp.o +0 -0
  59. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/StringMatcher.cpp.o.d +665 -0
  60. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/TunerEngine.cpp.o +0 -0
  61. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/TunerEngine.cpp.o.d +811 -0
  62. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/Window.cpp.o +0 -0
  63. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/Window.cpp.o.d +754 -0
  64. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/YinPitchDetector.cpp.o +0 -0
  65. package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/YinPitchDetector.cpp.o.d +755 -0
  66. package/cpp/build/Makefile +532 -0
  67. package/cpp/build/cmake_install.cmake +61 -0
  68. package/cpp/build/libtuner_engine_core.a +0 -0
  69. package/cpp/include/AudioFrameDispatcher.hpp +87 -0
  70. package/cpp/include/BiquadHpf.hpp +22 -0
  71. package/cpp/include/CepstrumPitchDetector.hpp +33 -0
  72. package/cpp/include/EnsembleSelector.hpp +25 -0
  73. package/cpp/include/Fft.hpp +44 -0
  74. package/cpp/include/IPitchDetector.hpp +21 -0
  75. package/cpp/include/InstrumentPresets.hpp +33 -0
  76. package/cpp/include/NoteMapper.hpp +15 -0
  77. package/cpp/include/OnsetDetector.hpp +37 -0
  78. package/cpp/include/Pipeline.hpp +55 -0
  79. package/cpp/include/PitchResult.hpp +24 -0
  80. package/cpp/include/PostProcessor.hpp +51 -0
  81. package/cpp/include/PyinPitchDetector.hpp +35 -0
  82. package/cpp/include/RingBuffer.hpp +72 -0
  83. package/cpp/include/SnrEstimator.hpp +21 -0
  84. package/cpp/include/StringMatcher.hpp +27 -0
  85. package/cpp/include/TunerEngine.hpp +32 -0
  86. package/cpp/include/TuningPresets.hpp +103 -0
  87. package/cpp/include/Window.hpp +13 -0
  88. package/cpp/include/YinPitchDetector.hpp +37 -0
  89. package/cpp/src/AudioFrameDispatcher.cpp +180 -0
  90. package/cpp/src/BiquadHpf.cpp +35 -0
  91. package/cpp/src/CepstrumPitchDetector.cpp +116 -0
  92. package/cpp/src/EnsembleSelector.cpp +91 -0
  93. package/cpp/src/NoteMapper.cpp +47 -0
  94. package/cpp/src/OnsetDetector.cpp +39 -0
  95. package/cpp/src/Pipeline.cpp +133 -0
  96. package/cpp/src/PostProcessor.cpp +111 -0
  97. package/cpp/src/PyinPitchDetector.cpp +134 -0
  98. package/cpp/src/SnrEstimator.cpp +33 -0
  99. package/cpp/src/StringMatcher.cpp +37 -0
  100. package/cpp/src/TunerEngine.cpp +67 -0
  101. package/cpp/src/Window.cpp +21 -0
  102. package/cpp/src/YinPitchDetector.cpp +118 -0
  103. package/cpp/tests/CMakeLists.txt +23 -0
  104. package/cpp/tests/bench.cpp +160 -0
  105. package/cpp/tests/build/CMakeCache.txt +356 -0
  106. package/cpp/tests/build/CMakeFiles/4.3.2/CMakeCXXCompiler.cmake +102 -0
  107. package/cpp/tests/build/CMakeFiles/4.3.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
  108. package/cpp/tests/build/CMakeFiles/4.3.2/CMakeSystem.cmake +15 -0
  109. package/cpp/tests/build/CMakeFiles/4.3.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +949 -0
  110. package/cpp/tests/build/CMakeFiles/4.3.2/CompilerIdCXX/a.out +0 -0
  111. package/cpp/tests/build/CMakeFiles/4.3.2/CompilerIdCXX/apple-sdk.cpp +1 -0
  112. package/cpp/tests/build/CMakeFiles/CMakeConfigureLog.yaml +1619 -0
  113. package/cpp/tests/build/CMakeFiles/CMakeDirectoryInformation.cmake +16 -0
  114. package/cpp/tests/build/CMakeFiles/InstallScripts.json +8 -0
  115. package/cpp/tests/build/CMakeFiles/Makefile.cmake +122 -0
  116. package/cpp/tests/build/CMakeFiles/Makefile2 +211 -0
  117. package/cpp/tests/build/CMakeFiles/TargetDirectories.txt +9 -0
  118. package/cpp/tests/build/CMakeFiles/cmake.check_cache +1 -0
  119. package/cpp/tests/build/CMakeFiles/progress.marks +1 -0
  120. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/DependInfo.cmake +23 -0
  121. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/bench.cpp.o +0 -0
  122. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/bench.cpp.o.d +813 -0
  123. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/build.make +114 -0
  124. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/cmake_clean.cmake +11 -0
  125. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/compiler_depend.make +2 -0
  126. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/compiler_depend.ts +2 -0
  127. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/depend.make +2 -0
  128. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/flags.make +12 -0
  129. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/link.txt +1 -0
  130. package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/progress.make +3 -0
  131. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/DependInfo.cmake +23 -0
  132. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/build.make +114 -0
  133. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/cmake_clean.cmake +11 -0
  134. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/compiler_depend.make +2 -0
  135. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/compiler_depend.ts +2 -0
  136. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/depend.make +2 -0
  137. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/flags.make +12 -0
  138. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/link.txt +1 -0
  139. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/main.cpp.o +0 -0
  140. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/main.cpp.o.d +823 -0
  141. package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/progress.make +3 -0
  142. package/cpp/tests/build/CTestTestfile.cmake +9 -0
  143. package/cpp/tests/build/Makefile +247 -0
  144. package/cpp/tests/build/cmake_install.cmake +66 -0
  145. package/cpp/tests/build/tuner_engine_bench +0 -0
  146. package/cpp/tests/build/tuner_engine_core/CMakeFiles/CMakeDirectoryInformation.cmake +16 -0
  147. package/cpp/tests/build/tuner_engine_core/CMakeFiles/progress.marks +1 -0
  148. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/DependInfo.cmake +36 -0
  149. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/build.make +322 -0
  150. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/cmake_clean.cmake +37 -0
  151. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/cmake_clean_target.cmake +3 -0
  152. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/compiler_depend.make +2 -0
  153. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/compiler_depend.ts +2 -0
  154. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/depend.make +2 -0
  155. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/flags.make +12 -0
  156. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/link.txt +2 -0
  157. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/progress.make +16 -0
  158. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/AudioFrameDispatcher.cpp.o +0 -0
  159. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/AudioFrameDispatcher.cpp.o.d +814 -0
  160. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/BiquadHpf.cpp.o +0 -0
  161. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/BiquadHpf.cpp.o.d +206 -0
  162. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/CepstrumPitchDetector.cpp.o +0 -0
  163. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/CepstrumPitchDetector.cpp.o.d +797 -0
  164. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/EnsembleSelector.cpp.o +0 -0
  165. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/EnsembleSelector.cpp.o.d +755 -0
  166. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/NoteMapper.cpp.o +0 -0
  167. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/NoteMapper.cpp.o.d +669 -0
  168. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/OnsetDetector.cpp.o +0 -0
  169. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/OnsetDetector.cpp.o.d +648 -0
  170. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/Pipeline.cpp.o +0 -0
  171. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/Pipeline.cpp.o.d +765 -0
  172. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/PostProcessor.cpp.o +0 -0
  173. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/PostProcessor.cpp.o.d +648 -0
  174. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/PyinPitchDetector.cpp.o +0 -0
  175. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/PyinPitchDetector.cpp.o.d +755 -0
  176. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/SnrEstimator.cpp.o +0 -0
  177. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/SnrEstimator.cpp.o.d +648 -0
  178. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/StringMatcher.cpp.o +0 -0
  179. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/StringMatcher.cpp.o.d +665 -0
  180. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/TunerEngine.cpp.o +0 -0
  181. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/TunerEngine.cpp.o.d +811 -0
  182. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/Window.cpp.o +0 -0
  183. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/Window.cpp.o.d +754 -0
  184. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/YinPitchDetector.cpp.o +0 -0
  185. package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/YinPitchDetector.cpp.o.d +755 -0
  186. package/cpp/tests/build/tuner_engine_core/Makefile +544 -0
  187. package/cpp/tests/build/tuner_engine_core/cmake_install.cmake +45 -0
  188. package/cpp/tests/build/tuner_engine_core/libtuner_engine_core.a +0 -0
  189. package/cpp/tests/build/tuner_engine_tests +0 -0
  190. package/cpp/tests/main.cpp +624 -0
  191. package/ios/IosAudioSource.h +23 -0
  192. package/ios/IosAudioSource.mm +174 -0
  193. package/ios/TunerBridge.h +24 -0
  194. package/ios/TunerBridge.mm +136 -0
  195. package/ios/TunerEngine.h +6 -0
  196. package/ios/TunerEngine.mm +144 -0
  197. package/lib/module/NativeTunerEngine.js +5 -0
  198. package/lib/module/NativeTunerEngine.js.map +1 -0
  199. package/lib/module/TunerEngine.js +43 -0
  200. package/lib/module/TunerEngine.js.map +1 -0
  201. package/lib/module/index.js +5 -0
  202. package/lib/module/index.js.map +1 -0
  203. package/lib/module/package.json +1 -0
  204. package/lib/module/types.js +2 -0
  205. package/lib/module/types.js.map +1 -0
  206. package/lib/module/useTuner.js +55 -0
  207. package/lib/module/useTuner.js.map +1 -0
  208. package/lib/typescript/package.json +1 -0
  209. package/lib/typescript/src/NativeTunerEngine.d.ts +17 -0
  210. package/lib/typescript/src/NativeTunerEngine.d.ts.map +1 -0
  211. package/lib/typescript/src/TunerEngine.d.ts +18 -0
  212. package/lib/typescript/src/TunerEngine.d.ts.map +1 -0
  213. package/lib/typescript/src/index.d.ts +4 -0
  214. package/lib/typescript/src/index.d.ts.map +1 -0
  215. package/lib/typescript/src/types.d.ts +86 -0
  216. package/lib/typescript/src/types.d.ts.map +1 -0
  217. package/lib/typescript/src/useTuner.d.ts +15 -0
  218. package/lib/typescript/src/useTuner.d.ts.map +1 -0
  219. package/package.json +141 -0
  220. package/src/NativeTunerEngine.ts +17 -0
  221. package/src/TunerEngine.ts +62 -0
  222. package/src/index.tsx +11 -0
  223. package/src/types.ts +109 -0
  224. package/src/useTuner.ts +67 -0
@@ -0,0 +1,37 @@
1
+ #pragma once
2
+
3
+ #include "IPitchDetector.hpp"
4
+ #include <vector>
5
+
6
+ struct YinResult {
7
+ bool hasPitch = false;
8
+ float frequency = 0.0f;
9
+ float confidence = 0.0f;
10
+ };
11
+
12
+ class YinPitchDetector : public IPitchDetector {
13
+ public:
14
+ YinPitchDetector(float sampleRate, int frameSize);
15
+
16
+ // Legacy interface used by existing tests
17
+ YinResult detect(const float* input, int frameCount);
18
+
19
+ // IPitchDetector — delegates to the above
20
+ DetectorResult detect(const float* frame, int n, float sampleRate) override;
21
+
22
+ void setFrequencyRange(float minFrequency, float maxFrequency) override;
23
+ void setThreshold(float threshold) override;
24
+
25
+ private:
26
+ float sampleRate_;
27
+ int frameSize_;
28
+
29
+ float minFrequency_ = 60.0f;
30
+ float maxFrequency_ = 1200.0f;
31
+ float threshold_ = 0.15f;
32
+
33
+ std::vector<float> difference_;
34
+ std::vector<float> cmnd_;
35
+
36
+ float parabolicInterpolation(int tau) const;
37
+ };
@@ -0,0 +1,180 @@
1
+ #include "AudioFrameDispatcher.hpp"
2
+ #include "InstrumentPresets.hpp"
3
+
4
+ #include <algorithm>
5
+ #include <chrono>
6
+ #include <cstring>
7
+
8
+ AudioFrameDispatcher::AudioFrameDispatcher(
9
+ int frameSize, float sampleRate, PitchCallback callback, float overlapRatio
10
+ )
11
+ : frameSize_(frameSize)
12
+ , hopSize_(frameSize) // will be recomputed below
13
+ , overlapRatio_(std::clamp(overlapRatio, 0.0f, 0.75f))
14
+ , sampleRate_(sampleRate)
15
+ , callback_(std::move(callback))
16
+ , frameBuffer_(static_cast<size_t>(frameSize), 0.0f)
17
+ , engine_(std::make_unique<TunerEngine>(sampleRate, frameSize))
18
+ {
19
+ recomputeHopSize();
20
+ }
21
+
22
+ AudioFrameDispatcher::~AudioFrameDispatcher() {
23
+ stop();
24
+ }
25
+
26
+ void AudioFrameDispatcher::recomputeHopSize() {
27
+ hopSize_ = std::max(1, static_cast<int>(
28
+ std::round(frameSize_ * (1.0f - overlapRatio_))
29
+ ));
30
+ }
31
+
32
+ void AudioFrameDispatcher::start() {
33
+ if (running_.exchange(true)) return; // already running
34
+ firstFrame_ = true;
35
+ workerThread_ = std::thread(&AudioFrameDispatcher::workerLoop, this);
36
+ }
37
+
38
+ void AudioFrameDispatcher::stop() {
39
+ if (!running_.exchange(false)) return; // already stopped
40
+ if (workerThread_.joinable()) {
41
+ workerThread_.join();
42
+ }
43
+ }
44
+
45
+ void AudioFrameDispatcher::push(const float* samples, int count) {
46
+ ring_.push(samples, count);
47
+ }
48
+
49
+ void AudioFrameDispatcher::setSampleRate(float sampleRate) {
50
+ if (sampleRate <= 0.0f) return;
51
+ std::lock_guard<std::mutex> lock(engineMutex_);
52
+ sampleRate_ = sampleRate;
53
+ engine_ = std::make_unique<TunerEngine>(sampleRate, frameSize_);
54
+ }
55
+
56
+ void AudioFrameDispatcher::setA4(float hz) {
57
+ std::lock_guard<std::mutex> lock(engineMutex_);
58
+ if (engine_) engine_->setA4(hz);
59
+ }
60
+
61
+ void AudioFrameDispatcher::setNoiseGateDb(float db) {
62
+ std::lock_guard<std::mutex> lock(engineMutex_);
63
+ if (engine_) engine_->setNoiseGateDb(db);
64
+ }
65
+
66
+ void AudioFrameDispatcher::setConfidenceThreshold(float value) {
67
+ std::lock_guard<std::mutex> lock(engineMutex_);
68
+ if (engine_) engine_->setConfidenceThreshold(value);
69
+ }
70
+
71
+ void AudioFrameDispatcher::setFrequencyRange(float minHz, float maxHz) {
72
+ std::lock_guard<std::mutex> lock(engineMutex_);
73
+ if (engine_) engine_->setFrequencyRange(minHz, maxHz);
74
+ }
75
+
76
+ void AudioFrameDispatcher::setInstrument(const std::string& name) {
77
+ // Check if the instrument's recommended frame size differs
78
+ const int recommended = instrumentRecommendedFrameSize(name);
79
+ if (recommended != frameSize_) {
80
+ reconfigure(recommended, sampleRate_);
81
+ }
82
+
83
+ std::lock_guard<std::mutex> lock(engineMutex_);
84
+ if (engine_) engine_->setInstrument(name);
85
+ }
86
+
87
+ void AudioFrameDispatcher::setTuning(const std::string& name) {
88
+ std::lock_guard<std::mutex> lock(engineMutex_);
89
+ if (engine_) engine_->setTuning(name);
90
+ }
91
+
92
+ void AudioFrameDispatcher::setPostProcessorConfig(PostProcessor::Config cfg) {
93
+ std::lock_guard<std::mutex> lock(engineMutex_);
94
+ if (engine_) engine_->setPostProcessorConfig(cfg);
95
+ }
96
+
97
+ void AudioFrameDispatcher::setHpfCutoff(float hz) {
98
+ std::lock_guard<std::mutex> lock(engineMutex_);
99
+ if (engine_) engine_->setHpfCutoff(hz);
100
+ }
101
+
102
+ void AudioFrameDispatcher::setOnsetDetectionEnabled(bool enabled) {
103
+ std::lock_guard<std::mutex> lock(engineMutex_);
104
+ if (engine_) engine_->setOnsetDetectionEnabled(enabled);
105
+ }
106
+
107
+ void AudioFrameDispatcher::setOnsetConfig(OnsetDetector::Config cfg) {
108
+ std::lock_guard<std::mutex> lock(engineMutex_);
109
+ if (engine_) engine_->setOnsetConfig(cfg);
110
+ }
111
+
112
+ void AudioFrameDispatcher::setOverlapRatio(float ratio) {
113
+ std::lock_guard<std::mutex> lock(engineMutex_);
114
+ overlapRatio_ = std::clamp(ratio, 0.0f, 0.75f);
115
+ recomputeHopSize();
116
+ firstFrame_ = true; // reset sliding window state
117
+ }
118
+
119
+ void AudioFrameDispatcher::reconfigure(int newFrameSize, float sampleRate) {
120
+ const bool wasRunning = running_.load();
121
+ if (wasRunning) stop();
122
+
123
+ {
124
+ std::lock_guard<std::mutex> lock(engineMutex_);
125
+ frameSize_ = newFrameSize;
126
+ sampleRate_ = sampleRate;
127
+ recomputeHopSize();
128
+ frameBuffer_.assign(static_cast<size_t>(frameSize_), 0.0f);
129
+ firstFrame_ = true;
130
+ engine_ = std::make_unique<TunerEngine>(sampleRate_, frameSize_);
131
+ }
132
+
133
+ if (wasRunning) start();
134
+ }
135
+
136
+ void AudioFrameDispatcher::workerLoop() {
137
+ while (running_.load(std::memory_order_relaxed)) {
138
+ if (firstFrame_) {
139
+ // Cold start: wait for a full frame before first processing
140
+ if (ring_.available() >= frameSize_) {
141
+ ring_.pop(frameBuffer_.data(), frameSize_);
142
+ firstFrame_ = false;
143
+
144
+ PitchResult result;
145
+ {
146
+ std::lock_guard<std::mutex> lock(engineMutex_);
147
+ result = engine_->process(frameBuffer_.data(), frameSize_);
148
+ }
149
+ if (callback_) {
150
+ callback_(result);
151
+ }
152
+ } else {
153
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
154
+ }
155
+ } else {
156
+ // Sliding window: shift old data left, pop hopSize_ new samples at the end
157
+ if (ring_.available() >= hopSize_) {
158
+ // Shift existing samples left by hopSize_
159
+ const int retain = frameSize_ - hopSize_;
160
+ std::memmove(frameBuffer_.data(),
161
+ frameBuffer_.data() + hopSize_,
162
+ static_cast<size_t>(retain) * sizeof(float));
163
+
164
+ // Pop new samples into the tail
165
+ ring_.pop(frameBuffer_.data() + retain, hopSize_);
166
+
167
+ PitchResult result;
168
+ {
169
+ std::lock_guard<std::mutex> lock(engineMutex_);
170
+ result = engine_->process(frameBuffer_.data(), frameSize_);
171
+ }
172
+ if (callback_) {
173
+ callback_(result);
174
+ }
175
+ } else {
176
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
177
+ }
178
+ }
179
+ }
180
+ }
@@ -0,0 +1,35 @@
1
+ #include "BiquadHpf.hpp"
2
+
3
+ #include <cmath>
4
+
5
+ static constexpr float kPi = 3.14159265358979323846f;
6
+
7
+ BiquadHpf::BiquadHpf(float sampleRate, float cutoffHz, float q) {
8
+ // Audio EQ Cookbook — High Pass Filter
9
+ const float w0 = 2.0f * kPi * cutoffHz / sampleRate;
10
+ const float cosW0 = std::cos(w0);
11
+ const float alpha = std::sin(w0) / (2.0f * q);
12
+ const float a0 = 1.0f + alpha;
13
+
14
+ b0_ = (1.0f + cosW0) / 2.0f / a0;
15
+ b1_ = -(1.0f + cosW0) / a0;
16
+ b2_ = (1.0f + cosW0) / 2.0f / a0;
17
+ a1_ = -2.0f * cosW0 / a0;
18
+ a2_ = (1.0f - alpha) / a0;
19
+ }
20
+
21
+ void BiquadHpf::process(float* frame, int n) {
22
+ if (!frame || n <= 0) return;
23
+ for (int i = 0; i < n; ++i) {
24
+ const float x = frame[i];
25
+ const float y = b0_ * x + w1_;
26
+ w1_ = b1_ * x - a1_ * y + w2_;
27
+ w2_ = b2_ * x - a2_ * y;
28
+ frame[i] = y;
29
+ }
30
+ }
31
+
32
+ void BiquadHpf::reset() {
33
+ w1_ = 0.0f;
34
+ w2_ = 0.0f;
35
+ }
@@ -0,0 +1,116 @@
1
+ #include "CepstrumPitchDetector.hpp"
2
+ #include "Fft.hpp"
3
+
4
+ #include <algorithm>
5
+ #include <cmath>
6
+
7
+ static constexpr float kEps = 1e-10f;
8
+ static constexpr float kPi = 3.14159265358979323846f;
9
+
10
+ CepstrumPitchDetector::CepstrumPitchDetector(float sampleRate, int frameSize)
11
+ : sampleRate_(sampleRate)
12
+ , frameSize_(frameSize)
13
+ , hann_(static_cast<size_t>(frameSize))
14
+ , fftBuf_(static_cast<size_t>(frameSize))
15
+ , logPow_(static_cast<size_t>(frameSize))
16
+ {
17
+ for (int i = 0; i < frameSize_; ++i) {
18
+ hann_[i] = 0.5f * (1.0f - std::cos(2.0f * kPi * i / (frameSize_ - 1)));
19
+ }
20
+ }
21
+
22
+ void CepstrumPitchDetector::setFrequencyRange(float minHz, float maxHz) {
23
+ minHz_ = minHz;
24
+ maxHz_ = maxHz;
25
+ }
26
+
27
+ void CepstrumPitchDetector::setThreshold(float threshold) {
28
+ threshold_ = threshold;
29
+ }
30
+
31
+ DetectorResult CepstrumPitchDetector::detect(const float* frame, int n, float sampleRate) {
32
+ const float sr = sampleRate > 0.0f ? sampleRate : sampleRate_;
33
+ const int len = std::min(n, frameSize_);
34
+
35
+ // Hann-window the frame into the FFT buffer
36
+ for (int i = 0; i < len; ++i) {
37
+ fftBuf_[i] = {frame[i] * hann_[i], 0.0f};
38
+ }
39
+ for (int i = len; i < frameSize_; ++i) {
40
+ fftBuf_[i] = {};
41
+ }
42
+
43
+ tuner::fft(fftBuf_);
44
+
45
+ // Log power spectrum — build full two-sided symmetric array in logPow_
46
+ for (int k = 0; k <= frameSize_ / 2; ++k) {
47
+ const float re = fftBuf_[k].real();
48
+ const float im = fftBuf_[k].imag();
49
+ logPow_[k] = std::log(re * re + im * im + kEps);
50
+ }
51
+ for (int k = 1; k < frameSize_ / 2; ++k) {
52
+ logPow_[frameSize_ - k] = logPow_[k];
53
+ }
54
+
55
+ // Load log-power into fftBuf_ (real) and IFFT → real cepstrum
56
+ for (int k = 0; k < frameSize_; ++k) {
57
+ fftBuf_[k] = {logPow_[k], 0.0f};
58
+ }
59
+ tuner::ifft(fftBuf_);
60
+
61
+ // Quefrency search range
62
+ const int tauMin = std::max(1, static_cast<int>(sr / maxHz_));
63
+ const int tauMax = std::min(frameSize_ / 2 - 1, static_cast<int>(sr / minHz_));
64
+ if (tauMin >= tauMax) return DetectorResult{};
65
+
66
+ // Find the quefrency peak
67
+ float maxVal = -1e30f;
68
+ int bestTau = -1;
69
+ for (int q = tauMin; q <= tauMax; ++q) {
70
+ const float v = fftBuf_[q].real();
71
+ if (v > maxVal) { maxVal = v; bestTau = q; }
72
+ }
73
+ if (bestTau < 0) return DetectorResult{};
74
+
75
+ const float prominence = peakProminence(tauMin, tauMax, bestTau);
76
+ if (prominence < threshold_) return DetectorResult{};
77
+
78
+ // Sub-sample refinement via parabolic interpolation
79
+ float peakTau = static_cast<float>(bestTau);
80
+ if (bestTau > tauMin && bestTau < tauMax) {
81
+ const float L = fftBuf_[bestTau - 1].real();
82
+ const float C = fftBuf_[bestTau].real();
83
+ const float R = fftBuf_[bestTau + 1].real();
84
+ const float d = L - 2.0f * C + R;
85
+ if (std::fabs(d) > 1e-6f) {
86
+ peakTau += 0.5f * (L - R) / d;
87
+ }
88
+ }
89
+ if (peakTau <= 0.0f) return DetectorResult{};
90
+
91
+ return DetectorResult{true, sr / peakTau, prominence};
92
+ }
93
+
94
+ float CepstrumPitchDetector::peakProminence(int tauMin, int tauMax, int peakTau) const {
95
+ const float peak = fftBuf_[peakTau].real();
96
+
97
+ // RMS and mean of the quefrency range (excluding the peak itself)
98
+ float sum = 0.0f;
99
+ float sumSq = 0.0f;
100
+ const int count = tauMax - tauMin + 1;
101
+ for (int q = tauMin; q <= tauMax; ++q) {
102
+ const float v = fftBuf_[q].real();
103
+ sum += v;
104
+ sumSq += v * v;
105
+ }
106
+ const float mean = sum / static_cast<float>(count);
107
+ const float rms = std::sqrt(sumSq / static_cast<float>(count));
108
+
109
+ // Confidence: how much the peak exceeds the RMS level.
110
+ // A cepstrum with no clear periodicity (pure sine) has peak ≈ RMS → conf ≈ 0.
111
+ // A strong harmonic signal has peak >> RMS → conf → 1.
112
+ if (rms < kEps) return 0.0f;
113
+ const float snr = (peak - mean) / rms;
114
+ // Map snr range [0, 5] → [0, 1]; cap both sides
115
+ return std::max(0.0f, std::min(1.0f, snr / 5.0f));
116
+ }
@@ -0,0 +1,91 @@
1
+ #include "EnsembleSelector.hpp"
2
+
3
+ #include <cmath>
4
+
5
+ EnsembleSelector::EnsembleSelector(std::vector<std::unique_ptr<IPitchDetector>> detectors)
6
+ : detectors_(std::move(detectors))
7
+ {
8
+ resultsBuf_.resize(detectors_.size());
9
+ }
10
+
11
+ void EnsembleSelector::reset() {
12
+ for (auto& d : detectors_) d->reset();
13
+ }
14
+
15
+ void EnsembleSelector::setFrequencyRange(float minHz, float maxHz) {
16
+ for (auto& d : detectors_) d->setFrequencyRange(minHz, maxHz);
17
+ }
18
+
19
+ void EnsembleSelector::setThreshold(float threshold) {
20
+ for (auto& d : detectors_) d->setThreshold(threshold);
21
+ }
22
+
23
+ bool EnsembleSelector::withinSemitones(float f1, float f2, float tolerance) {
24
+ if (f1 <= 0.0f || f2 <= 0.0f) return false;
25
+ return std::fabs(12.0f * std::log2(f1 / f2)) <= tolerance;
26
+ }
27
+
28
+ DetectorResult EnsembleSelector::detect(const float* frame, int n, float sampleRate) {
29
+ // Run all detectors into the pre-allocated buffer
30
+ for (int i = 0; i < static_cast<int>(detectors_.size()); ++i) {
31
+ resultsBuf_[i] = detectors_[i]->detect(frame, n, sampleRate);
32
+ }
33
+
34
+ // Work with a small stack-local view of voiced results to avoid heap allocation.
35
+ // Maximum 8 detectors is more than enough for any realistic ensemble.
36
+ struct Entry { int idx; float freq; float conf; int votes; };
37
+ Entry voiced[8];
38
+ int voicedCount = 0;
39
+
40
+ for (int i = 0; i < static_cast<int>(resultsBuf_.size()) && voicedCount < 8; ++i) {
41
+ const auto& r = resultsBuf_[i];
42
+ if (r.voiced && r.confidence > 0.0f) {
43
+ voiced[voicedCount++] = {i, r.frequency, r.confidence, 0};
44
+ }
45
+ }
46
+
47
+ if (voicedCount == 0) return DetectorResult{};
48
+
49
+ // Tally agreement votes between voiced entries
50
+ for (int i = 0; i < voicedCount; ++i) {
51
+ for (int j = i + 1; j < voicedCount; ++j) {
52
+ if (withinSemitones(voiced[i].freq, voiced[j].freq)) {
53
+ ++voiced[i].votes;
54
+ ++voiced[j].votes;
55
+ }
56
+ }
57
+ }
58
+
59
+ // Pick winner: most votes first, then highest confidence
60
+ const Entry* best = &voiced[0];
61
+ for (int i = 1; i < voicedCount; ++i) {
62
+ const Entry& c = voiced[i];
63
+ if (c.votes > best->votes
64
+ || (c.votes == best->votes && c.conf > best->conf)) {
65
+ best = &c;
66
+ }
67
+ }
68
+
69
+ // Average the frequency (and confidence) of all detectors that agree with winner
70
+ float freqSum = best->freq;
71
+ float confSum = best->conf;
72
+ int agreeing = 1;
73
+
74
+ for (int i = 0; i < voicedCount; ++i) {
75
+ if (&voiced[i] != best && withinSemitones(voiced[i].freq, best->freq)) {
76
+ freqSum += voiced[i].freq;
77
+ confSum += voiced[i].conf;
78
+ ++agreeing;
79
+ }
80
+ }
81
+
82
+ const float avgFreq = freqSum / static_cast<float>(agreeing);
83
+ float avgConf = confSum / static_cast<float>(agreeing);
84
+
85
+ // Confidence bonus for agreement, penalty for a lone detector
86
+ avgConf = (agreeing > 1)
87
+ ? std::min(1.0f, avgConf * 1.1f)
88
+ : avgConf * 0.85f;
89
+
90
+ return DetectorResult{true, avgFreq, avgConf};
91
+ }
@@ -0,0 +1,47 @@
1
+ #include "NoteMapper.hpp"
2
+
3
+ #include <array>
4
+ #include <cmath>
5
+ #include <stdexcept>
6
+
7
+ NoteMapper::NoteMapper(float a4) : a4_(a4) {}
8
+
9
+ void NoteMapper::setA4(float value) {
10
+ if (value <= 0.0f) {
11
+ throw std::invalid_argument("A4 must be positive");
12
+ }
13
+
14
+ a4_ = value;
15
+ }
16
+
17
+ PitchResult NoteMapper::map(float frequency, float confidence, float rmsDb) const {
18
+ PitchResult result;
19
+
20
+ if (frequency <= 0.0f) {
21
+ return result;
22
+ }
23
+
24
+ static const std::array<const char*, 12> names = {
25
+ "C", "C#", "D", "D#", "E", "F",
26
+ "F#", "G", "G#", "A", "A#", "B"
27
+ };
28
+
29
+ const int midi = static_cast<int>(
30
+ std::round(69.0f + 12.0f * std::log2(frequency / a4_))
31
+ );
32
+
33
+ const float target = a4_ * std::pow(2.0f, (midi - 69) / 12.0f);
34
+ const float cents = 1200.0f * std::log2(frequency / target);
35
+
36
+ result.hasPitch = true;
37
+ result.frequency = frequency;
38
+ result.confidence = confidence;
39
+ result.rmsDb = rmsDb;
40
+ result.midiNote = midi;
41
+ result.noteName = names[midi % 12];
42
+ result.octave = midi / 12 - 1;
43
+ result.targetFrequency = target;
44
+ result.cents = cents;
45
+
46
+ return result;
47
+ }
@@ -0,0 +1,39 @@
1
+ #include "OnsetDetector.hpp"
2
+
3
+ #include <algorithm>
4
+
5
+ OnsetDetector::OnsetDetector(Config cfg) : cfg_(cfg) {}
6
+
7
+ bool OnsetDetector::detect(float rmsDb) {
8
+ // Fast path: disabled → no work at all.
9
+ if (!enabled_) return false;
10
+
11
+ // Decrement cooldown counter
12
+ if (cooldown_ > 0) {
13
+ --cooldown_;
14
+ // Still update envelope during cooldown so we don't miss the next onset
15
+ envelopeDb_ += cfg_.envelopeAlpha * (rmsDb - envelopeDb_);
16
+ return false;
17
+ }
18
+
19
+ const float rise = rmsDb - envelopeDb_;
20
+
21
+ // Update envelope (exponential follower — tracks slow changes)
22
+ envelopeDb_ += cfg_.envelopeAlpha * (rmsDb - envelopeDb_);
23
+
24
+ if (rise > cfg_.thresholdDb) {
25
+ cooldown_ = cfg_.cooldownFrames;
26
+ return true;
27
+ }
28
+
29
+ return false;
30
+ }
31
+
32
+ void OnsetDetector::setConfig(Config cfg) {
33
+ cfg_ = cfg;
34
+ }
35
+
36
+ void OnsetDetector::reset() {
37
+ envelopeDb_ = -100.0f;
38
+ cooldown_ = 0;
39
+ }
@@ -0,0 +1,133 @@
1
+ #include "Pipeline.hpp"
2
+
3
+ #include <algorithm>
4
+ #include <cmath>
5
+
6
+ static constexpr float kMinLinear = 1e-7f;
7
+
8
+ Pipeline::Pipeline(int frameSize, float sampleRate, std::unique_ptr<IPitchDetector> detector)
9
+ : frameSize_(frameSize)
10
+ , sampleRate_(sampleRate)
11
+ , hpf_(sampleRate, 70.0f) // 70 Hz HPF — removes DC and sub-bass rumble
12
+ , window_(frameSize)
13
+ , detector_(std::move(detector))
14
+ , workBuffer_(static_cast<size_t>(frameSize))
15
+ {}
16
+
17
+ PitchResult Pipeline::process(const float* input, int frameCount) {
18
+ if (!input || frameCount < frameSize_) return PitchResult{};
19
+
20
+ // --- RMS gate ---
21
+ const float rmsLinear = calculateRmsLinear(input, frameSize_);
22
+ const float rmsDb = 20.0f * std::log10(std::max(rmsLinear, kMinLinear));
23
+
24
+ if (rmsDb < noiseGateDb_) {
25
+ PitchResult silent;
26
+ silent.rmsDb = rmsDb;
27
+ return silent;
28
+ }
29
+
30
+ // --- Onset detection (no-op when disabled — single branch) ---
31
+ if (onsetDetector_.detect(rmsDb)) {
32
+ postProcessor_.reset();
33
+ }
34
+
35
+ // --- Working copy: HPF only.
36
+ // Hann windowing is reserved for FFT-based detectors (M3 cepstrum/PYIN).
37
+ // YIN works in the time domain — windowing distorts its difference function.
38
+ std::copy(input, input + frameSize_, workBuffer_.begin());
39
+ hpf_.process(workBuffer_.data(), frameSize_);
40
+
41
+ // --- Pitch detection ---
42
+ DetectorResult det = detector_->detect(workBuffer_.data(), frameSize_, sampleRate_);
43
+
44
+ // --- SNR-weighted confidence ---
45
+ const float snrDb = snr_.update(rmsLinear);
46
+ const float snrWeight = SnrEstimator::snrToWeight(snrDb);
47
+ const float weightedConf = det.confidence * snrWeight;
48
+
49
+ if (!det.voiced || weightedConf < confidenceThreshold_) {
50
+ PitchResult nopit;
51
+ nopit.rmsDb = rmsDb;
52
+ return nopit;
53
+ }
54
+
55
+ // --- Post-process: median + EMA + hysteresis ---
56
+ PostProcessor::Result pp = postProcessor_.process(det.frequency, weightedConf);
57
+
58
+ if (!pp.isStable || pp.frequency <= 0.0f) {
59
+ PitchResult nopit;
60
+ nopit.rmsDb = rmsDb;
61
+ return nopit;
62
+ }
63
+
64
+ // --- Note mapping ---
65
+ PitchResult result = noteMapper_.map(pp.frequency, weightedConf, rmsDb);
66
+ // Override cents with the hysteresis-stabilised value from PostProcessor
67
+ result.cents = pp.cents;
68
+
69
+ // --- String matching (optional, only when a TuningProfile is active) ---
70
+ if (stringMatcher_.hasTuning()) {
71
+ auto m = stringMatcher_.match(pp.frequency);
72
+ if (m) {
73
+ result.nearestString = m->name;
74
+ result.stringDeviation = m->deviationCents;
75
+ }
76
+ }
77
+
78
+ return result;
79
+ }
80
+
81
+ void Pipeline::setA4(float hz) {
82
+ noteMapper_.setA4(hz);
83
+ }
84
+
85
+ void Pipeline::setNoiseGateDb(float db) {
86
+ noiseGateDb_ = db;
87
+ }
88
+
89
+ void Pipeline::setConfidenceThreshold(float threshold) {
90
+ confidenceThreshold_ = threshold;
91
+ }
92
+
93
+ void Pipeline::setFrequencyRange(float minHz, float maxHz) {
94
+ detector_->setFrequencyRange(minHz, maxHz);
95
+ }
96
+
97
+ void Pipeline::setInstrument(const std::string& name) {
98
+ FrequencyRange r = instrumentPreset(name);
99
+ detector_->setFrequencyRange(r.minHz, r.maxHz);
100
+ }
101
+
102
+ void Pipeline::setTuning(const std::string& name) {
103
+ stringMatcher_.setTuning(name.empty() ? nullptr : tuningPreset(name));
104
+ }
105
+
106
+ void Pipeline::setPostProcessorConfig(PostProcessor::Config cfg) {
107
+ postProcessor_.setConfig(cfg);
108
+ }
109
+
110
+ void Pipeline::setHpfCutoff(float hz) {
111
+ hpf_ = BiquadHpf(sampleRate_, hz);
112
+ }
113
+
114
+ void Pipeline::setOnsetDetectionEnabled(bool enabled) {
115
+ onsetDetector_.setEnabled(enabled);
116
+ if (!enabled) onsetDetector_.reset();
117
+ }
118
+
119
+ void Pipeline::setOnsetConfig(OnsetDetector::Config cfg) {
120
+ onsetDetector_.setConfig(cfg);
121
+ }
122
+
123
+ float Pipeline::calculateRmsDb(const float* input, int n) const {
124
+ return 20.0f * std::log10(std::max(calculateRmsLinear(input, n), kMinLinear));
125
+ }
126
+
127
+ float Pipeline::calculateRmsLinear(const float* input, int n) const {
128
+ float sum = 0.0f;
129
+ for (int i = 0; i < n; ++i) {
130
+ sum += input[i] * input[i];
131
+ }
132
+ return std::sqrt(sum / static_cast<float>(n));
133
+ }