Exoplayer 将音乐传输到不和谐机器人时出现白噪音

问题描述 投票:0回答:1

我正在尝试将音频字节流式传输到不和谐机器人,但没有音乐,而是有很多噪音。此外,机器人连接成功,编解码器工作。我首先想到的是我要么将数据放在错误的位置,要么错误地将其传输到机器人。此外,机器人本身工作正常,它正确连接到语音通道,甚至可以识别声音,但不是正确的声音。我使用 Exoplayer 在设备本身和 Discord 中播放本地 mp3 文件。在手机上播放没有问题

Discord 机器人要求

为了使用不和谐机器人,我使用 Kord。文档中描述了其对传输数据的要求:

一帧 20ms Opus 编码的 48k 立体声音频数据。

编解码器设置

基于此,我根据要求设置了以下编解码器设置:

@OptIn(UnstableApi::class)
class BotAudioProcessor(
    private val audioDataListener: AudioDataListener
): BaseAudioProcessor(){
    val codec = Opus()
    val SAMPLE_RATE = Constants.SampleRate._48000()
    val CHANNELS = Constants.Channels.stereo()
    val APPLICATION = Constants.Application.audio()
    val FRAME_SIZE = Constants.FrameSize._960()
    val COMPLEXITY = Constants.Complexity.instance(10)
    val BITRATE = Constants.Bitrate.max()

    init {
        codec.encoderInit(SAMPLE_RATE, CHANNELS, APPLICATION)
        codec.encoderSetComplexity(COMPLEXITY)
        codec.encoderSetBitrate(BITRATE)
    }

    override fun onConfigure(inputFormat: AudioProcessor.AudioFormat): AudioProcessor.AudioFormat {
        val outputFormat = AudioProcessor.AudioFormat(
            SAMPLE_RATE.v,
            CHANNELS.v,
            C.ENCODING_PCM_16BIT
        )
        return outputFormat
    }
    //... queueInput()
}

这里我按照文档中的要求设置了所有参数。所有这些值都是通过编解码器设置的,唯一的是我花了一个小时计算帧大小 帧大小 = SAMPLE_RATE * 0.02 (20ms) = 960

获取音频流

如果我理解正确的话,可以使用 AudioProcessor 获得未压缩的 PCM 流,为此,我继承自 BaseAudioProcessor 并重写 queueInput 方法,在该方法中我以 Opus 格式对缓冲区进行编码,然后将其传递给侦听器,这将将流发送到不和谐机器人。

override fun queueInput(inputBuffer: ByteBuffer) {
    if (inputBuffer.remaining() < FRAME_SIZE.v / 2) {
        Log.e("BotAudioProcessor", "Input buffer too small")
        return
    }
    val frame = ShortArray( FRAME_SIZE.v / 2)
    inputBuffer.asShortBuffer().get(frame)
    val encoded = codec.encode(frame, FRAME_SIZE)

    audioDataListener.onAudioData(encoded!!.toByteArray())
}

将数据传输至机器人

接下来,接收到的音频流保存在一个变量中,然后由机器人广播。

@UnstableApi
class MusicBot(...) : AudioDataListener {
    private var audioData: ByteArray? = null
    //...

    @OptIn(KordVoice::class)
    suspend fun start() {
        kord!!.on<ReadyEvent> {
            //...
            voiceChannel.connect {
                audioProvider {
                    println(audioBuffer?.joinToString(" "))
                    println(audioBuffer?.size)
                    audioBuffer?.let { AudioFrame(it) }
                }
            }
        }
    }

    override fun onAudioData(data: ByteArray) {
        audioBuffer = data
    }
}

链接和附加

  1. kord 库:https://github.com/kordlib/kord/tree/main/voice
  2. Opuc 编解码器:https://github.com/theeasiestway/android-opus-codec
  3. 使用音频处理器:https://github.com/google/ExoPlayer/issues/8342

这就是我将其连接到播放器的方式

@OptIn(UnstableApi::class)
class DiscordRendersFactory
    (
    context: Context,
    private val audioDataListener: AudioDataListener
) : DefaultRenderersFactory(context) {

    override fun buildAudioSink(
        context: Context,
        enableFloatOutput: Boolean,
        enableAudioTrackPlaybackParams: Boolean
    ): AudioSink {

        val defaultAudioSink = DefaultAudioSink.Builder()
            .setAudioProcessors(arrayOf(BotAudioProcessor(audioDataListener)))
            .setEnableFloatOutput(true)
            .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)
            .build()
        return defaultAudioSink
    }
}
android kotlin discord audio-streaming exoplayer
1个回答
0
投票

queueInput
方法中,您会收到
inputBuffer
充满音频样本。您对其进行编码并发送前
FRAME_SIZE
字节。其余的样品都被扔掉了。

AFAIK Opus 默认使用 20 毫秒长帧。所以你可能根本不需要对缓冲区进行切片。

但是如果这样做,您应该确保处理每个缓冲区中的所有样本。您还需要处理剩菜(长度<

FRAME_SIZE
的切片)。

© www.soinside.com 2019 - 2024. All rights reserved.