You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TarsosDSP 音符释放后突发低音降调问题排查求助

TarsosDSP 音符释放后突发低音降调问题排查求助

各位好,我最近在用Kotlin/Android结合TarsosDSP开发一个实时音高检测APP,核心功能是捕获音频输入、通过FFT_YIN算法检测基频,然后在垂直钢琴卷帘视图上实时追踪音高的变化轨迹。

目前功能的正常部分:

  • 按住或持续演奏音符时(不管是人声还是电子键盘),音高检测完全准确
  • 音高值和钢琴卷帘Y轴位置的映射完全匹配,没有偏差

但现在遇到了一个非常棘手的问题:
当音符突然释放后(尤其是人声或者原声乐器的音符),音高会猛地跌到极低的频率区间(大概50-60Hz),然后又快速恢复到静音状态或者之前的水平。反映在钢琴卷帘上,就是轨迹会出现一个陡直的“悬崖”状下落。而且这种情况只发生在音符释放后,持续演奏时完全不会出现,看起来问题根源在TarsosDSP的音高检测逻辑里,不是我这边的绘图或者数值转换问题。

我已经排查确认的细节:

  1. 音高转Y轴逻辑无bug:每一次出现“骤降”的帧,计算出的Y轴位置都和TarsosDSP返回的音高值完全对应,drawPitchGraph()方法里没有问题。
  2. 多种算法均存在类似问题:我先后试了YIN、FFT_YIN、FFT_PITCH、DYNAMIC_WAVELET这几种算法,都有释放后音高骤降的情况,其中FFT_YIN是相对最稳定的。
  3. 调整采样率和缓冲区大小无效:试了22050采样率+1024缓冲区+0重叠,结果稍好一些;换成44100采样率的话,释放时的音高跳变会更频繁。
  4. 排除背景噪音影响:我在完全静音的环境下测试过,单独检测背景噪音时TarsosDSP根本不会返回音高值,所以可以排除背景噪音的干扰。

实际测试场景的表现:

我用电子键盘只弹了两个音:

  1. 第一个是A3,对应正常显示的状态(音高219.63Hz,Y值985.7)
  2. 在时间点20:70前一瞬间松开A3,立刻就出现了音高骤降到54.95Hz的情况(Y值1599.4,轨迹陡直下落)
  3. 紧接着我立刻弹了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 13:28:07