react-native-simple-note-pitch-detector 0.7.1 → 0.7.2

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.
@@ -63,6 +63,14 @@ class PitchAnalyzer {
63
63
  private var hpsProduct: FloatArray? = null
64
64
  private var hannWindow: FloatArray? = null
65
65
 
66
+ // Adaptive noise gate: tracks background noise floor per-dispatcher
67
+ // A note must be this many dB above the noise floor to be emitted
68
+ private val noiseGateMarginDb = 10f
69
+ // Exponential moving average decay for noise floor (0.01 = slow adaptation)
70
+ private val noiseFloorAlpha = 0.01f
71
+ @Volatile private var hpsNoiseFloor = -60f
72
+ @Volatile private var yinNoiseFloor = -60f
73
+
66
74
  // HPS searches low range only: A0 (27.5Hz) to ~G3 (196Hz)
67
75
  private val hpsMinHz = 26f
68
76
  private val hpsMaxHz = 200f
@@ -72,12 +80,26 @@ class PitchAnalyzer {
72
80
  // MPM max: reject garbage above piano range (C8 = 4186Hz)
73
81
  private val mpmMaxHz = 4200f
74
82
 
83
+ private fun updateNoiseFloor(currentFloor: Float, dB: Float): Float {
84
+ // Only update noise floor when signal is quiet (close to current floor)
85
+ // This prevents played notes from raising the floor
86
+ return if (dB < currentFloor + noiseGateMarginDb) {
87
+ currentFloor + noiseFloorAlpha * (dB - currentFloor)
88
+ } else {
89
+ currentFloor
90
+ }
91
+ }
92
+
75
93
  private val hpsProcessor = object : AudioProcessor {
76
94
  override fun process(audioEvent: AudioEvent): Boolean {
77
95
  val buffer = audioEvent.floatBuffer
78
96
  val dB = audioEvent.getdBSPL().toFloat()
79
97
 
80
- if (dB > levelThreshold) {
98
+ // Update noise floor estimate
99
+ hpsNoiseFloor = updateNoiseFloor(hpsNoiseFloor, dB)
100
+
101
+ // Adaptive gate: only process if dB is above noise floor + margin
102
+ if (dB > hpsNoiseFloor + noiseGateMarginDb) {
81
103
  try {
82
104
  val pitch = detectPitchHPS(buffer)
83
105
  if (pitch > 0f) {
@@ -187,8 +209,13 @@ class PitchAnalyzer {
187
209
 
188
210
  val pitchHandler = PitchDetectionHandler { result: PitchDetectionResult, event: AudioEvent ->
189
211
  val pitch = result.pitch
190
- if (pitch > 0 && pitch >= mpmMinHz && pitch <= mpmMaxHz && result.isPitched) {
191
- val dB = event.getdBSPL().toFloat()
212
+ val dB = event.getdBSPL().toFloat()
213
+
214
+ // Update YIN noise floor
215
+ yinNoiseFloor = updateNoiseFloor(yinNoiseFloor, dB)
216
+
217
+ if (pitch > 0 && pitch >= mpmMinHz && pitch <= mpmMaxHz && result.isPitched
218
+ && dB > yinNoiseFloor + noiseGateMarginDb) {
192
219
  emitPitch(pitch, dB)
193
220
  }
194
221
  }
@@ -225,10 +252,6 @@ class PitchAnalyzer {
225
252
  val octave = (roundedMidiNote / 12) - 1
226
253
  val centsOff = (midiNote - roundedMidiNote) * 100
227
254
 
228
- // Pre-filter: JS discards offset > 25% anyway, so skip detections > 40%
229
- // to reduce noise in the JS majority voting window
230
- if (kotlin.math.abs(centsOff) > 40f) return
231
-
232
255
  onPitchDetected(PitchData(
233
256
  note = notes[noteIndex],
234
257
  octave = octave,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-simple-note-pitch-detector",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "a simple react native library to detect the pitch of the input recording",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",