TarsosDSP 音符释放后突发低音降调问题排查求助
TarsosDSP 音符释放后突发低音降调问题排查求助
各位好,我最近在用Kotlin/Android结合TarsosDSP开发一个实时音高检测APP,核心功能是捕获音频输入、通过FFT_YIN算法检测基频,然后在垂直钢琴卷帘视图上实时追踪音高的变化轨迹。
目前功能的正常部分:
- 按住或持续演奏音符时(不管是人声还是电子键盘),音高检测完全准确
- 音高值和钢琴卷帘Y轴位置的映射完全匹配,没有偏差
但现在遇到了一个非常棘手的问题:
当音符突然释放后(尤其是人声或者原声乐器的音符),音高会猛地跌到极低的频率区间(大概50-60Hz),然后又快速恢复到静音状态或者之前的水平。反映在钢琴卷帘上,就是轨迹会出现一个陡直的“悬崖”状下落。而且这种情况只发生在音符释放后,持续演奏时完全不会出现,看起来问题根源在TarsosDSP的音高检测逻辑里,不是我这边的绘图或者数值转换问题。
我已经排查确认的细节:
- 音高转Y轴逻辑无bug:每一次出现“骤降”的帧,计算出的Y轴位置都和TarsosDSP返回的音高值完全对应,
drawPitchGraph()方法里没有问题。 - 多种算法均存在类似问题:我先后试了YIN、FFT_YIN、FFT_PITCH、DYNAMIC_WAVELET这几种算法,都有释放后音高骤降的情况,其中FFT_YIN是相对最稳定的。
- 调整采样率和缓冲区大小无效:试了22050采样率+1024缓冲区+0重叠,结果稍好一些;换成44100采样率的话,释放时的音高跳变会更频繁。
- 排除背景噪音影响:我在完全静音的环境下测试过,单独检测背景噪音时TarsosDSP根本不会返回音高值,所以可以排除背景噪音的干扰。
实际测试场景的表现:
我用电子键盘只弹了两个音:
- 第一个是A3,对应正常显示的状态(音高219.63Hz,Y值985.7)
- 在时间点20:70前一瞬间松开A3,立刻就出现了音高骤降到54.95Hz的情况(Y值1599.4,轨迹陡直下落)
- 紧接着我立刻弹了G3(大概21:30),音高又恢复到正常的196.43Hz(Y值1035.1)
这种诡异的骤降和回跳,就发生在两个音符之间的静音间隙里。
相关代码片段
音高检测初始化代码(播放按钮点击时执行)
val sampleRate = 22050 val bufferSize = 1024 val overlap = 0 val audioRecord = AudioRecord( MediaRecorder.AudioSource.MIC, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize ) val tarsosFormat = TarsosDSPAudioFormat( sampleRate.toFloat(), 16, // 采样位数 1, // 声道数 true, // 有符号 false // 大端字节序 ) val inputStream = AndroidAudioInputStream(audioRecord, tarsosFormat) dispatcher = AudioDispatcher(inputStream, bufferSize, overlap) val pdh = PitchDetectionHandler { result, _ -> val pitchInHz = result.pitch if (pitchInHz > 0) { val note = NoteConverter.hzToNote(pitchInHz) runOnUiThread { findViewById<TextView>(R.id.textView1).text = note } } else { runOnUiThread { findViewById<TextView>(R.id.textView1).text = "未检测到音符" } } pianoRollView.pitchInHz2 = pitchInHz pianoRollView.invalidate() } val p: AudioProcessor = PitchProcessor( PitchProcessor.PitchEstimationAlgorithm.FFT_YIN, 22050f, 1024, pdh ) dispatcher?.addAudioProcessor(p) Thread(dispatcher, "Audio Dispatcher").start()
PianoRollView类中的绘图方法(onDraw中调用)
private fun drawPitchGraph(canvas: Canvas) { if (baseKeyHeight == 0f || listOfNotes.isEmpty()) return val a4Index = listOfNotes.indexOf("A4") val yRef = baseKeyHeight * (totalKeys - 1 - a4Index) + baseKeyHeight / 2f val y: Float? = if (pitchInHz2 > 0.0) { val semitoneOffset = log2(pitchInHz2 / 440.0) * 12 yRef - baseKeyHeight * semitoneOffset.toFloat() } else { null } currentYValue = y if (y != null) { canvas.drawCircle(width * 0.7f, y, 8f, pitchPaint) } } fun drawTrace(canvas: Canvas) { currentYValue?.let { y -> tracePoints.add(TracePoint(width.toFloat() * 0.7f, y)) } ?: tracePoints.add(null) for (i in tracePoints.indices) { tracePoints[i]?.let { tracePoints[i] = it.copy(x = it.x - 2f) } } for (i in 0 until tracePoints.size - 1) { val p1 = tracePoints[i] val p2 = tracePoints[i + 1] if (p1 != null && p2 != null) { canvas.drawLine(p1.x, p1.y, p2.x, p2.y, tracePaint) } } }
想请教各位有没有遇到过类似的问题?或者有没有办法在TarsosDSP的音高检测环节,过滤掉这种音符释放后的异常低频率值?
内容来源于stack exchange
相关产品推荐
相关产品推荐

