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

Android中MP3转Int16格式PCM及播放问题求助

解决MP3转Int16 PCM后播放噪音的问题

你的问题核心是对Int16(16位)PCM格式的理解偏差:Int16 PCM是用两个连续字节组合成一个16位整数来表示一个音频采样值,而不是把单个byte单独转成Int后写入文本。你当前的代码既破坏了16位数据的结构,又把二进制音频转成了文本格式,播放器自然无法识别,只能输出噪音。

错误原因拆解

  • Int16是16位数据,需要两个8位byte才能组成一个有效采样值,单个byte转Int完全丢失了另一半数据
  • 直接写入i.toString()+" "是把数值转成了文本字符,不是播放器需要的二进制PCM数据

正确的Int16 PCM处理与写入方法

下面针对两种常见场景给出正确实现:

场景1:获取Int16数值用于处理,同时保存可播放的PCM文件

先把解码后的byte数组转成ShortArray(Kotlin中short对应16位整数),处理后再转回byte数组写入文件:

override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo) {
    Log.d(TAG, "onOutputBufferAvailable")
    val buffer = mediaCodec.getOutputBuffer(index) ?: return
    val byteArray = ByteArray(info.size - info.offset)
    val bufferPosition = buffer.position()
    
    // 读取解码后的原始byte数据
    buffer.get(byteArray)
    buffer.position(bufferPosition)
    mediaCodec.releaseOutputBuffer(index, true)

    // 将byte数组转成Int16(ShortArray):MP3解码后的PCM通常是小端字节序(低字节在前)
    val shortArray = ShortArray(byteArray.size / 2)
    for (i in shortArray.indices) {
        // 两个byte拼接成16位short:高字节左移8位,和低字节做或运算
        shortArray[i] = ((byteArray[2*i+1].toInt() shl 8) or (byteArray[2*i].toInt() and 0xFF)).toShort()
    }

    // ------------------------------
    // 这里可以对shortArray进行自定义数据处理
    // ------------------------------

    // 将处理后的ShortArray转回byte数组,写入文件(可直接用播放器播放)
    val outputBytes = ByteArray(shortArray.size * 2)
    for (i in shortArray.indices) {
        outputBytes[2*i] = (shortArray[i].toInt() and 0xFF).toByte()
        outputBytes[2*i+1] = (shortArray[i].toInt() shr 8 and 0xFF).toByte()
    }
    fos.write(outputBytes)
}

场景2:直接播放Int16格式的PCM数据

如果不需要保存文件,而是要直接播放ShortArray格式的PCM,可以用AudioTrack实现:

// 先初始化AudioTrack,参数必须和解码后的PCM完全匹配
val sampleRate = 44100 // 替换为你实际解码得到的采样率
val channelConfig = AudioFormat.CHANNEL_OUT_STEREO // 单声道用CHANNEL_OUT_MONO
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
val bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat)
val audioTrack = AudioTrack(
    AudioManager.STREAM_MUSIC,
    sampleRate,
    channelConfig,
    audioFormat,
    bufferSize,
    AudioTrack.MODE_STREAM
)

// 在得到shortArray后,直接写入AudioTrack播放
audioTrack.play()
audioTrack.write(shortArray, 0, shortArray.size)

关键注意事项

  • 字节序:MP3解码后的PCM基本都是小端字节序,拼接byte时要保证低字节在前、高字节在后
  • 参数一致性:播放时的采样率、声道数必须和解码输出的PCM参数完全一致,否则会出现速度异常或噪音
  • 数据类型:Int16对应Kotlin的short类型,不要用int直接存储单个byte,避免高位数据丢失

内容的提问来源于stack exchange,提问作者hushed_voice

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:42:45