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.
- package/LICENSE +20 -0
- package/README.md +188 -0
- package/TunerEngine.podspec +32 -0
- package/android/build.gradle +86 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/cpp/CMakeLists.txt +31 -0
- package/android/src/main/cpp/OboeAudioSource.cpp +101 -0
- package/android/src/main/cpp/OboeAudioSource.h +41 -0
- package/android/src/main/cpp/TunerEngineJni.cpp +220 -0
- package/android/src/main/java/com/tunerengine/TunerEngineModule.kt +183 -0
- package/android/src/main/java/com/tunerengine/TunerEnginePackage.kt +31 -0
- package/cpp/CMakeLists.txt +25 -0
- package/cpp/build/CMakeCache.txt +347 -0
- package/cpp/build/CMakeFiles/4.3.2/CMakeCXXCompiler.cmake +102 -0
- package/cpp/build/CMakeFiles/4.3.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
- package/cpp/build/CMakeFiles/4.3.2/CMakeSystem.cmake +15 -0
- package/cpp/build/CMakeFiles/4.3.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +949 -0
- package/cpp/build/CMakeFiles/4.3.2/CompilerIdCXX/a.out +0 -0
- package/cpp/build/CMakeFiles/4.3.2/CompilerIdCXX/apple-sdk.cpp +1 -0
- package/cpp/build/CMakeFiles/CMakeConfigureLog.yaml +1619 -0
- package/cpp/build/CMakeFiles/CMakeDirectoryInformation.cmake +16 -0
- package/cpp/build/CMakeFiles/InstallScripts.json +7 -0
- package/cpp/build/CMakeFiles/Makefile.cmake +118 -0
- package/cpp/build/CMakeFiles/Makefile2 +122 -0
- package/cpp/build/CMakeFiles/TargetDirectories.txt +3 -0
- package/cpp/build/CMakeFiles/cmake.check_cache +1 -0
- package/cpp/build/CMakeFiles/progress.marks +1 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/DependInfo.cmake +36 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/build.make +322 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/cmake_clean.cmake +37 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/cmake_clean_target.cmake +3 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/compiler_depend.make +2 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/compiler_depend.ts +2 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/depend.make +2 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/flags.make +12 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/link.txt +2 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/progress.make +16 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/AudioFrameDispatcher.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/AudioFrameDispatcher.cpp.o.d +814 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/BiquadHpf.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/BiquadHpf.cpp.o.d +206 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/CepstrumPitchDetector.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/CepstrumPitchDetector.cpp.o.d +797 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/EnsembleSelector.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/EnsembleSelector.cpp.o.d +755 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/NoteMapper.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/NoteMapper.cpp.o.d +669 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/OnsetDetector.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/OnsetDetector.cpp.o.d +648 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/Pipeline.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/Pipeline.cpp.o.d +765 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/PostProcessor.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/PostProcessor.cpp.o.d +648 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/PyinPitchDetector.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/PyinPitchDetector.cpp.o.d +755 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/SnrEstimator.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/SnrEstimator.cpp.o.d +648 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/StringMatcher.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/StringMatcher.cpp.o.d +665 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/TunerEngine.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/TunerEngine.cpp.o.d +811 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/Window.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/Window.cpp.o.d +754 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/YinPitchDetector.cpp.o +0 -0
- package/cpp/build/CMakeFiles/tuner_engine_core.dir/src/YinPitchDetector.cpp.o.d +755 -0
- package/cpp/build/Makefile +532 -0
- package/cpp/build/cmake_install.cmake +61 -0
- package/cpp/build/libtuner_engine_core.a +0 -0
- package/cpp/include/AudioFrameDispatcher.hpp +87 -0
- package/cpp/include/BiquadHpf.hpp +22 -0
- package/cpp/include/CepstrumPitchDetector.hpp +33 -0
- package/cpp/include/EnsembleSelector.hpp +25 -0
- package/cpp/include/Fft.hpp +44 -0
- package/cpp/include/IPitchDetector.hpp +21 -0
- package/cpp/include/InstrumentPresets.hpp +33 -0
- package/cpp/include/NoteMapper.hpp +15 -0
- package/cpp/include/OnsetDetector.hpp +37 -0
- package/cpp/include/Pipeline.hpp +55 -0
- package/cpp/include/PitchResult.hpp +24 -0
- package/cpp/include/PostProcessor.hpp +51 -0
- package/cpp/include/PyinPitchDetector.hpp +35 -0
- package/cpp/include/RingBuffer.hpp +72 -0
- package/cpp/include/SnrEstimator.hpp +21 -0
- package/cpp/include/StringMatcher.hpp +27 -0
- package/cpp/include/TunerEngine.hpp +32 -0
- package/cpp/include/TuningPresets.hpp +103 -0
- package/cpp/include/Window.hpp +13 -0
- package/cpp/include/YinPitchDetector.hpp +37 -0
- package/cpp/src/AudioFrameDispatcher.cpp +180 -0
- package/cpp/src/BiquadHpf.cpp +35 -0
- package/cpp/src/CepstrumPitchDetector.cpp +116 -0
- package/cpp/src/EnsembleSelector.cpp +91 -0
- package/cpp/src/NoteMapper.cpp +47 -0
- package/cpp/src/OnsetDetector.cpp +39 -0
- package/cpp/src/Pipeline.cpp +133 -0
- package/cpp/src/PostProcessor.cpp +111 -0
- package/cpp/src/PyinPitchDetector.cpp +134 -0
- package/cpp/src/SnrEstimator.cpp +33 -0
- package/cpp/src/StringMatcher.cpp +37 -0
- package/cpp/src/TunerEngine.cpp +67 -0
- package/cpp/src/Window.cpp +21 -0
- package/cpp/src/YinPitchDetector.cpp +118 -0
- package/cpp/tests/CMakeLists.txt +23 -0
- package/cpp/tests/bench.cpp +160 -0
- package/cpp/tests/build/CMakeCache.txt +356 -0
- package/cpp/tests/build/CMakeFiles/4.3.2/CMakeCXXCompiler.cmake +102 -0
- package/cpp/tests/build/CMakeFiles/4.3.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
- package/cpp/tests/build/CMakeFiles/4.3.2/CMakeSystem.cmake +15 -0
- package/cpp/tests/build/CMakeFiles/4.3.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +949 -0
- package/cpp/tests/build/CMakeFiles/4.3.2/CompilerIdCXX/a.out +0 -0
- package/cpp/tests/build/CMakeFiles/4.3.2/CompilerIdCXX/apple-sdk.cpp +1 -0
- package/cpp/tests/build/CMakeFiles/CMakeConfigureLog.yaml +1619 -0
- package/cpp/tests/build/CMakeFiles/CMakeDirectoryInformation.cmake +16 -0
- package/cpp/tests/build/CMakeFiles/InstallScripts.json +8 -0
- package/cpp/tests/build/CMakeFiles/Makefile.cmake +122 -0
- package/cpp/tests/build/CMakeFiles/Makefile2 +211 -0
- package/cpp/tests/build/CMakeFiles/TargetDirectories.txt +9 -0
- package/cpp/tests/build/CMakeFiles/cmake.check_cache +1 -0
- package/cpp/tests/build/CMakeFiles/progress.marks +1 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/DependInfo.cmake +23 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/bench.cpp.o +0 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/bench.cpp.o.d +813 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/build.make +114 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/cmake_clean.cmake +11 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/compiler_depend.make +2 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/compiler_depend.ts +2 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/depend.make +2 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/flags.make +12 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/link.txt +1 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_bench.dir/progress.make +3 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/DependInfo.cmake +23 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/build.make +114 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/cmake_clean.cmake +11 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/compiler_depend.make +2 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/compiler_depend.ts +2 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/depend.make +2 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/flags.make +12 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/link.txt +1 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/main.cpp.o +0 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/main.cpp.o.d +823 -0
- package/cpp/tests/build/CMakeFiles/tuner_engine_tests.dir/progress.make +3 -0
- package/cpp/tests/build/CTestTestfile.cmake +9 -0
- package/cpp/tests/build/Makefile +247 -0
- package/cpp/tests/build/cmake_install.cmake +66 -0
- package/cpp/tests/build/tuner_engine_bench +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/CMakeDirectoryInformation.cmake +16 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/progress.marks +1 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/DependInfo.cmake +36 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/build.make +322 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/cmake_clean.cmake +37 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/cmake_clean_target.cmake +3 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/compiler_depend.make +2 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/compiler_depend.ts +2 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/depend.make +2 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/flags.make +12 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/link.txt +2 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/progress.make +16 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/AudioFrameDispatcher.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/AudioFrameDispatcher.cpp.o.d +814 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/BiquadHpf.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/BiquadHpf.cpp.o.d +206 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/CepstrumPitchDetector.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/CepstrumPitchDetector.cpp.o.d +797 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/EnsembleSelector.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/EnsembleSelector.cpp.o.d +755 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/NoteMapper.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/NoteMapper.cpp.o.d +669 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/OnsetDetector.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/OnsetDetector.cpp.o.d +648 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/Pipeline.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/Pipeline.cpp.o.d +765 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/PostProcessor.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/PostProcessor.cpp.o.d +648 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/PyinPitchDetector.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/PyinPitchDetector.cpp.o.d +755 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/SnrEstimator.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/SnrEstimator.cpp.o.d +648 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/StringMatcher.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/StringMatcher.cpp.o.d +665 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/TunerEngine.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/TunerEngine.cpp.o.d +811 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/Window.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/Window.cpp.o.d +754 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/YinPitchDetector.cpp.o +0 -0
- package/cpp/tests/build/tuner_engine_core/CMakeFiles/tuner_engine_core.dir/src/YinPitchDetector.cpp.o.d +755 -0
- package/cpp/tests/build/tuner_engine_core/Makefile +544 -0
- package/cpp/tests/build/tuner_engine_core/cmake_install.cmake +45 -0
- package/cpp/tests/build/tuner_engine_core/libtuner_engine_core.a +0 -0
- package/cpp/tests/build/tuner_engine_tests +0 -0
- package/cpp/tests/main.cpp +624 -0
- package/ios/IosAudioSource.h +23 -0
- package/ios/IosAudioSource.mm +174 -0
- package/ios/TunerBridge.h +24 -0
- package/ios/TunerBridge.mm +136 -0
- package/ios/TunerEngine.h +6 -0
- package/ios/TunerEngine.mm +144 -0
- package/lib/module/NativeTunerEngine.js +5 -0
- package/lib/module/NativeTunerEngine.js.map +1 -0
- package/lib/module/TunerEngine.js +43 -0
- package/lib/module/TunerEngine.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/useTuner.js +55 -0
- package/lib/module/useTuner.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeTunerEngine.d.ts +17 -0
- package/lib/typescript/src/NativeTunerEngine.d.ts.map +1 -0
- package/lib/typescript/src/TunerEngine.d.ts +18 -0
- package/lib/typescript/src/TunerEngine.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +86 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/useTuner.d.ts +15 -0
- package/lib/typescript/src/useTuner.d.ts.map +1 -0
- package/package.json +141 -0
- package/src/NativeTunerEngine.ts +17 -0
- package/src/TunerEngine.ts +62 -0
- package/src/index.tsx +11 -0
- package/src/types.ts +109 -0
- package/src/useTuner.ts +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 denizyesilirmak
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# react-native-tuner-engine
|
|
2
|
+
|
|
3
|
+
A React Native Turbo Module for real-time instrument pitch detection. The detection pipeline runs entirely in C++ on a dedicated audio thread and delivers per-frame results to JavaScript via the New Architecture event system.
|
|
4
|
+
|
|
5
|
+
Requires React Native **0.75 or later** with the New Architecture enabled.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
Audio is captured through platform-native APIs (AVAudioEngine on iOS, Oboe on Android) and fed into a lock-free SPSC ring buffer. A C++ worker thread drains the buffer in fixed-size frames, runs the signal through a preprocessing pipeline, then through an ensemble of three pitch detectors (YIN, PYIN, cepstrum). The ensemble votes for the best estimate and fires an `onPitch` event on the JS thread.
|
|
10
|
+
|
|
11
|
+
The audio callback allocates nothing at runtime — all working buffers are pre-allocated during initialization.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install react-native-tuner-engine
|
|
17
|
+
# or
|
|
18
|
+
yarn add react-native-tuner-engine
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### iOS
|
|
22
|
+
|
|
23
|
+
Add a microphone usage description to your app's `Info.plist`:
|
|
24
|
+
|
|
25
|
+
```xml
|
|
26
|
+
<key>NSMicrophoneUsageDescription</key>
|
|
27
|
+
<string>Microphone access is required for pitch detection.</string>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then run `pod install`.
|
|
31
|
+
|
|
32
|
+
### Android
|
|
33
|
+
|
|
34
|
+
The library's `AndroidManifest.xml` already declares `RECORD_AUDIO`. You still need to request the permission at runtime — use `requestPermission()` described below, or handle it yourself before calling `start()`.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { useTuner } from 'react-native-tuner-engine';
|
|
40
|
+
|
|
41
|
+
export function TunerScreen() {
|
|
42
|
+
const { start, stop, latest, isRunning, error } = useTuner({
|
|
43
|
+
noiseGateDb: -50,
|
|
44
|
+
confidenceThreshold: 0.75,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
<Text>{latest?.noteName}{latest?.octave} {latest?.cents.toFixed(1)} ¢</Text>
|
|
50
|
+
<Button title={isRunning ? 'Stop' : 'Start'} onPress={isRunning ? stop : start} />
|
|
51
|
+
</>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`useTuner` requests the microphone permission, configures the engine, and subscribes to events — all in one call.
|
|
57
|
+
|
|
58
|
+
## API
|
|
59
|
+
|
|
60
|
+
### `useTuner(options?)`
|
|
61
|
+
|
|
62
|
+
React hook. Returns `{ start, stop, latest, isRunning, error }`.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { useTuner } from 'react-native-tuner-engine';
|
|
66
|
+
|
|
67
|
+
const { start, stop, latest, isRunning, error } = useTuner({
|
|
68
|
+
// All fields optional — defaults shown
|
|
69
|
+
sampleRate?: number; // 48000
|
|
70
|
+
frameSize?: number; // 2048
|
|
71
|
+
noiseGateDb?: number; // -55 dBFS
|
|
72
|
+
confidenceThreshold?: number; // 0.75 (0–1)
|
|
73
|
+
minFrequency?: number; // 60 Hz
|
|
74
|
+
maxFrequency?: number; // 1200 Hz
|
|
75
|
+
instrument?: Instrument; // 'chromatic'
|
|
76
|
+
a4?: number; // 440 Hz
|
|
77
|
+
|
|
78
|
+
// DSP tuning
|
|
79
|
+
emaAlpha?: number; // 0.35 — smoothing (0.05 = slow/stable, 1.0 = instant)
|
|
80
|
+
hysteresisFrames?: number; // 3 — frames before a note change is confirmed (1–10)
|
|
81
|
+
hpfCutoffHz?: number; // 70 Hz — high-pass filter cutoff (30 for bass, 100 for violin)
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`start()` — requests mic permission, configures the engine, then begins capture. Throws if permission is denied.
|
|
86
|
+
|
|
87
|
+
`stop()` — stops capture. Safe to call when already stopped.
|
|
88
|
+
|
|
89
|
+
`latest` — the most recent `PitchEvent`, or `null` before the first frame.
|
|
90
|
+
|
|
91
|
+
`error` — set if `start()` throws; `null` otherwise.
|
|
92
|
+
|
|
93
|
+
### `TunerEngine`
|
|
94
|
+
|
|
95
|
+
Low-level imperative API, useful outside of React components.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { TunerEngine } from 'react-native-tuner-engine';
|
|
99
|
+
|
|
100
|
+
await TunerEngine.requestPermission(); // → boolean
|
|
101
|
+
await TunerEngine.configure({ noiseGateDb: -50 });
|
|
102
|
+
await TunerEngine.start();
|
|
103
|
+
|
|
104
|
+
const unsub = TunerEngine.onPitch((event) => {
|
|
105
|
+
if (event.hasPitch) console.log(event.noteName, event.cents);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// later:
|
|
109
|
+
unsub();
|
|
110
|
+
await TunerEngine.stop();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Types
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
type PitchEvent = {
|
|
117
|
+
hasPitch: boolean;
|
|
118
|
+
frequency: number; // Hz
|
|
119
|
+
confidence: number; // 0–1
|
|
120
|
+
rmsDb: number; // dBFS
|
|
121
|
+
noteName: string; // e.g. "A"
|
|
122
|
+
octave: number;
|
|
123
|
+
cents: number; // −50 to +50 relative to equal temperament
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
type TunerConfig = {
|
|
127
|
+
sampleRate?: number; // default 48000
|
|
128
|
+
frameSize?: number; // default 2048
|
|
129
|
+
noiseGateDb?: number; // default -55 dBFS
|
|
130
|
+
confidenceThreshold?: number; // default 0.75
|
|
131
|
+
minFrequency?: number; // default 60 Hz
|
|
132
|
+
maxFrequency?: number; // default 1200 Hz
|
|
133
|
+
a4?: number; // default 440 Hz
|
|
134
|
+
emaAlpha?: number; // default 0.35 — PostProcessor smoothing (0.05–1.0)
|
|
135
|
+
hysteresisFrames?: number; // default 3 — frames to confirm a note change (1–10)
|
|
136
|
+
hpfCutoffHz?: number; // default 70 — high-pass filter cutoff in Hz (20–300)
|
|
137
|
+
onsetDetection?: boolean; // default false — resets smoothing on note attacks
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
type Instrument =
|
|
141
|
+
| 'guitar' | 'bass' | 'violin' | 'viola' | 'cello'
|
|
142
|
+
| 'ukulele' | 'mandolin' | 'banjo' | 'chromatic';
|
|
143
|
+
|
|
144
|
+
type Temperament = 'equal' | 'just';
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Additional methods on `TunerEngine`
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
TunerEngine.setA4(hz: number): void // default 440
|
|
151
|
+
TunerEngine.setInstrument(name: Instrument): void
|
|
152
|
+
TunerEngine.setTemperament(name: Temperament): void
|
|
153
|
+
TunerEngine.getStatus(): { isRunning: boolean; engineReady: boolean }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
For the full API reference (all config options, quality presets, adaptive frame size, overlap ratio), see **[documents/API.md](documents/API.md)**.
|
|
157
|
+
|
|
158
|
+
## C++ pipeline
|
|
159
|
+
|
|
160
|
+
The shared C++ core (`cpp/`) compiles as a static library on both platforms.
|
|
161
|
+
|
|
162
|
+
| Stage | Class | Notes |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| High-pass filter | `BiquadHpf` | Direct-Form II Transposed, 70 Hz cutoff (configurable), Q 0.707 |
|
|
165
|
+
| Windowing | `Window` | Hann window, precomputed coefficients |
|
|
166
|
+
| Pitch detection | `EnsembleSelector` | Runs YIN, PYIN, and cepstrum; votes by agreement within 1 semitone |
|
|
167
|
+
| — detector 1 | `YinPitchDetector` | YIN with parabolic interpolation |
|
|
168
|
+
| — detector 2 | `PyinPitchDetector` | Probabilistic YIN; prunes harmonic aliases |
|
|
169
|
+
| — detector 3 | `CepstrumPitchDetector` | Real cepstrum via radix-2 FFT; SNR-based confidence |
|
|
170
|
+
| Note mapping | `NoteMapper` | Hz → MIDI, note name, octave, cents deviation |
|
|
171
|
+
| SNR estimation | `SnrEstimator` | Signal RMS vs. noise-floor EMA |
|
|
172
|
+
| Post-processing | `PostProcessor` | Median-5 filter, EMA smoothing (configurable), note-transition hysteresis (configurable) |
|
|
173
|
+
| Dispatch | `AudioFrameDispatcher` | SPSC lock-free queue, dedicated worker thread |
|
|
174
|
+
|
|
175
|
+
## Performance targets
|
|
176
|
+
|
|
177
|
+
| Metric | Target |
|
|
178
|
+
|---|---|
|
|
179
|
+
| Time to first pitch | < 300 ms |
|
|
180
|
+
| Per-frame CPU (midrange Android) | < 5 ms |
|
|
181
|
+
| Per-frame CPU (iPhone 12+) | < 3 ms |
|
|
182
|
+
| Audio-thread allocations | 0 |
|
|
183
|
+
|
|
184
|
+
## Requirements
|
|
185
|
+
|
|
186
|
+
- React Native 0.75+ (New Architecture / Bridgeless)
|
|
187
|
+
- iOS 13+
|
|
188
|
+
- Android API 24+, NDK r26+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "TunerEngine"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => '13.4' }
|
|
14
|
+
s.source = { :git => "https://github.com/denizyesilirmak/react-native-tuner-engine.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = [
|
|
17
|
+
"ios/**/*.{h,m,mm}",
|
|
18
|
+
"cpp/src/**/*.cpp",
|
|
19
|
+
"cpp/include/**/*.hpp"
|
|
20
|
+
]
|
|
21
|
+
s.private_header_files = "ios/**/*.h"
|
|
22
|
+
|
|
23
|
+
s.frameworks = ["AVFoundation", "AVFAudio"]
|
|
24
|
+
|
|
25
|
+
s.pod_target_xcconfig = {
|
|
26
|
+
"HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/cpp/include\"",
|
|
27
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
|
|
28
|
+
"CLANG_CXX_LIBRARY" => "libc++"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
install_modules_dependencies(s)
|
|
32
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.TunerEngine = [
|
|
3
|
+
kotlinVersion: "2.0.21",
|
|
4
|
+
minSdkVersion: 24,
|
|
5
|
+
compileSdkVersion: 36,
|
|
6
|
+
targetSdkVersion: 36,
|
|
7
|
+
ndkVersion: "26.3.11579264"
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
ext.getExtOrDefault = { prop ->
|
|
11
|
+
if (rootProject.ext.has(prop)) {
|
|
12
|
+
return rootProject.ext.get(prop)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return TunerEngine[prop]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
repositories {
|
|
19
|
+
google()
|
|
20
|
+
mavenCentral()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
dependencies {
|
|
24
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
25
|
+
// noinspection DifferentKotlinGradleVersion
|
|
26
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
apply plugin: "com.android.library"
|
|
32
|
+
apply plugin: "kotlin-android"
|
|
33
|
+
|
|
34
|
+
apply plugin: "com.facebook.react"
|
|
35
|
+
|
|
36
|
+
android {
|
|
37
|
+
namespace "com.tunerengine"
|
|
38
|
+
|
|
39
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion")
|
|
40
|
+
ndkVersion getExtOrDefault("ndkVersion")
|
|
41
|
+
|
|
42
|
+
defaultConfig {
|
|
43
|
+
minSdkVersion getExtOrDefault("minSdkVersion")
|
|
44
|
+
targetSdkVersion getExtOrDefault("targetSdkVersion")
|
|
45
|
+
|
|
46
|
+
externalNativeBuild {
|
|
47
|
+
cmake {
|
|
48
|
+
cppFlags "-std=c++17"
|
|
49
|
+
abiFilters "arm64-v8a", "armeabi-v7a", "x86_64"
|
|
50
|
+
arguments "-DANDROID_STL=c++_shared"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
externalNativeBuild {
|
|
56
|
+
cmake {
|
|
57
|
+
path "src/main/cpp/CMakeLists.txt"
|
|
58
|
+
version "3.22.1"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
buildFeatures {
|
|
63
|
+
buildConfig true
|
|
64
|
+
prefab true
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
buildTypes {
|
|
68
|
+
release {
|
|
69
|
+
minifyEnabled false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
lint {
|
|
74
|
+
disable "GradleCompatible"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
compileOptions {
|
|
78
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
79
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
dependencies {
|
|
84
|
+
implementation "com.facebook.react:react-android"
|
|
85
|
+
implementation "com.google.oboe:oboe:1.9.3"
|
|
86
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.22.1)
|
|
2
|
+
|
|
3
|
+
project(tunerengine LANGUAGES CXX)
|
|
4
|
+
|
|
5
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
6
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
7
|
+
|
|
8
|
+
# Pull in the shared C++ core
|
|
9
|
+
add_subdirectory(
|
|
10
|
+
${CMAKE_CURRENT_SOURCE_DIR}/../../../../cpp
|
|
11
|
+
${CMAKE_BINARY_DIR}/tuner_engine_core
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
find_package(oboe REQUIRED CONFIG)
|
|
15
|
+
|
|
16
|
+
add_library(tunerengine SHARED
|
|
17
|
+
TunerEngineJni.cpp
|
|
18
|
+
OboeAudioSource.cpp
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
target_include_directories(tunerengine PRIVATE
|
|
22
|
+
${CMAKE_CURRENT_SOURCE_DIR}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
target_link_libraries(tunerengine
|
|
26
|
+
PRIVATE
|
|
27
|
+
tuner_engine_core
|
|
28
|
+
oboe::oboe
|
|
29
|
+
android
|
|
30
|
+
log
|
|
31
|
+
)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#include "OboeAudioSource.h"
|
|
2
|
+
#include <android/log.h>
|
|
3
|
+
|
|
4
|
+
#define LOG_TAG "OboeAudioSource"
|
|
5
|
+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
|
6
|
+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
|
7
|
+
|
|
8
|
+
OboeAudioSource::OboeAudioSource(SamplesCallback callback)
|
|
9
|
+
: callback_(std::move(callback)) {}
|
|
10
|
+
|
|
11
|
+
OboeAudioSource::~OboeAudioSource() {
|
|
12
|
+
stop();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
bool OboeAudioSource::openStream() {
|
|
17
|
+
oboe::AudioStreamBuilder builder;
|
|
18
|
+
builder.setDirection(oboe::Direction::Input);
|
|
19
|
+
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
|
20
|
+
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
|
21
|
+
builder.setFormat(oboe::AudioFormat::Float);
|
|
22
|
+
builder.setChannelCount(oboe::ChannelCount::Mono);
|
|
23
|
+
builder.setSampleRate(48000);
|
|
24
|
+
builder.setCallback(this);
|
|
25
|
+
|
|
26
|
+
oboe::Result result = builder.openStream(stream_);
|
|
27
|
+
|
|
28
|
+
if (result != oboe::Result::OK) {
|
|
29
|
+
LOGE("Failed to open Exclusive stream: %s — retrying Shared", oboe::convertToText(result));
|
|
30
|
+
|
|
31
|
+
// Fall back to Shared mode
|
|
32
|
+
builder.setSharingMode(oboe::SharingMode::Shared);
|
|
33
|
+
result = builder.openStream(stream_);
|
|
34
|
+
|
|
35
|
+
if (result != oboe::Result::OK) {
|
|
36
|
+
LOGE("Failed to open Shared stream: %s", oboe::convertToText(result));
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
sampleRate_ = static_cast<float>(stream_->getSampleRate());
|
|
42
|
+
LOGI("Oboe stream opened: sampleRate=%.0f sharingMode=%s",
|
|
43
|
+
sampleRate_,
|
|
44
|
+
oboe::convertToText(stream_->getSharingMode()));
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
bool OboeAudioSource::start() {
|
|
49
|
+
if (!openStream()) return false;
|
|
50
|
+
|
|
51
|
+
oboe::Result result = stream_->requestStart();
|
|
52
|
+
if (result != oboe::Result::OK) {
|
|
53
|
+
LOGE("Failed to start stream: %s", oboe::convertToText(result));
|
|
54
|
+
stream_->close();
|
|
55
|
+
stream_.reset();
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
LOGI("Oboe stream started");
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void OboeAudioSource::stop() {
|
|
64
|
+
stopped_.store(true, std::memory_order_relaxed);
|
|
65
|
+
if (restartThread_.joinable()) {
|
|
66
|
+
restartThread_.join();
|
|
67
|
+
}
|
|
68
|
+
if (stream_) {
|
|
69
|
+
stream_->requestStop();
|
|
70
|
+
stream_->close();
|
|
71
|
+
stream_.reset();
|
|
72
|
+
LOGI("Oboe stream stopped");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
float OboeAudioSource::sampleRate() const {
|
|
77
|
+
return sampleRate_;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
oboe::DataCallbackResult OboeAudioSource::onAudioReady(
|
|
81
|
+
oboe::AudioStream* /*stream*/,
|
|
82
|
+
void* audioData,
|
|
83
|
+
int32_t numFrames
|
|
84
|
+
) {
|
|
85
|
+
if (callback_) {
|
|
86
|
+
callback_(static_cast<const float*>(audioData), numFrames, sampleRate_);
|
|
87
|
+
}
|
|
88
|
+
return oboe::DataCallbackResult::Continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
void OboeAudioSource::onErrorAfterClose(oboe::AudioStream* /*stream*/, oboe::Result error) {
|
|
92
|
+
LOGE("Oboe stream error after close: %s — attempting restart", oboe::convertToText(error));
|
|
93
|
+
if (stopped_.load(std::memory_order_relaxed)) return;
|
|
94
|
+
// Restart on a separate thread to avoid deadlock; join in stop()/destructor
|
|
95
|
+
if (restartThread_.joinable()) restartThread_.join();
|
|
96
|
+
restartThread_ = std::thread([this]() {
|
|
97
|
+
if (!stopped_.load(std::memory_order_relaxed)) {
|
|
98
|
+
start();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <oboe/Oboe.h>
|
|
4
|
+
#include <functional>
|
|
5
|
+
#include <memory>
|
|
6
|
+
#include <atomic>
|
|
7
|
+
#include <thread>
|
|
8
|
+
|
|
9
|
+
// Oboe-based microphone capture.
|
|
10
|
+
// The onSamples callback is invoked on the Oboe real-time audio thread —
|
|
11
|
+
// must not block, lock, or allocate.
|
|
12
|
+
class OboeAudioSource : public oboe::AudioStreamCallback,
|
|
13
|
+
public std::enable_shared_from_this<OboeAudioSource> {
|
|
14
|
+
public:
|
|
15
|
+
using SamplesCallback = std::function<void(const float* samples, int count, float sampleRate)>;
|
|
16
|
+
|
|
17
|
+
explicit OboeAudioSource(SamplesCallback callback);
|
|
18
|
+
~OboeAudioSource();
|
|
19
|
+
|
|
20
|
+
bool start();
|
|
21
|
+
void stop();
|
|
22
|
+
float sampleRate() const;
|
|
23
|
+
|
|
24
|
+
// oboe::AudioStreamCallback
|
|
25
|
+
oboe::DataCallbackResult onAudioReady(
|
|
26
|
+
oboe::AudioStream* stream,
|
|
27
|
+
void* audioData,
|
|
28
|
+
int32_t numFrames
|
|
29
|
+
) override;
|
|
30
|
+
|
|
31
|
+
void onErrorAfterClose(oboe::AudioStream* stream, oboe::Result error) override;
|
|
32
|
+
|
|
33
|
+
private:
|
|
34
|
+
bool openStream();
|
|
35
|
+
|
|
36
|
+
SamplesCallback callback_;
|
|
37
|
+
std::shared_ptr<oboe::AudioStream> stream_;
|
|
38
|
+
float sampleRate_{48000.0f};
|
|
39
|
+
std::atomic<bool> stopped_{false};
|
|
40
|
+
std::thread restartThread_;
|
|
41
|
+
};
|