如何在JDA v5 Discord机器人中启用语音模式并推送音频流
问题解决思路
你的核心问题是startStream方法里的死循环while(true)阻塞了JDA的事件线程。SlashCommandEvent的处理逻辑运行在JDA的事件分发线程中,这个线程被死循环占住后,JDA无法执行后续的音频调度逻辑,自然不会调用AudioSendHandler的canProvide()方法。
具体修复步骤
1. 移除阻塞事件线程的死循环
直接删掉while(true),JDA的音频连接会由内部线程自动维护,不需要手动用死循环保持。修改后的startStream方法:
public void startStream(SlashCommandEvent event) { AudioManager manager = Objects.requireNonNull(event.getGuild()).getAudioManager(); AudioFormatter stream = new AudioFormatter(); manager.setSendingHandler(stream); manager.setSpeakingMode(SpeakingMode.VOICE); if (event.getMember().getVoiceState().inAudioChannel()) { manager.openAudioConnection(event.getMember().getVoiceState().getChannel()); } // 移除死循环,不要阻塞事件线程 }
2. 修正音频格式匹配Discord要求
Discord的语音服务要求音频为48kHz、16位、立体声、小端(little-endian)的PCM格式,你的当前格式设置了大端(最后一个参数true),需要改为false:
this.format = new AudioFormat((float)48000, 16, 2, true, false); // 最后一个参数改为false
3. 优化AudioFormatter的实现
- 避免每次
canProvide()都新建buffer,减少内存分配开销 - 读取音频数据时检查实际读取字节数,确保传递有效数据
- 确保TargetDataLine正确打开并启动采集
修改后的AudioFormatter:
public class AudioFormatter implements AudioSendHandler { private final AudioFormat format; private final TargetDataLine targetDataLine; private final byte[] buffer; private ByteBuffer returnedAudio; public AudioFormatter() { this.format = new AudioFormat(48000.0f, 16, 2, true, false); // 修正小端格式 this.targetDataLine = AudioDevice.getAudioDevice(); // 计算20ms音频所需字节数:48000采样率 * 2声道 * 2字节/采样 / 50(1秒含50个20ms片段) this.buffer = new byte[48000 * 2 * 2 / 50]; try { targetDataLine.open(format); targetDataLine.start(); // 启动音频采集 } catch (LineUnavailableException e) { e.printStackTrace(); } } @Override public boolean canProvide() { int bytesRead = targetDataLine.read(buffer, 0, buffer.length); if (bytesRead <= 0) { return false; // 无有效数据时返回false } returnedAudio = ByteBuffer.wrap(buffer, 0, bytesRead); // 仅传递实际读取的有效字节 return true; } @Nullable @Override public ByteBuffer provide20MsAudio() { return returnedAudio; } @Override public boolean isOpus() { return false; } }
4. 验证音频设备获取逻辑
检查AudioDevice.getAudioDevice()的实现,确保它正确获取了系统的音频输入设备,没有抛出未捕获的异常。如果需要指定特定输入设备,可以通过AudioSystem.getMixerInfo()遍历设备后选择合适的目标线路。
内容的提问来源于stack exchange,提问作者kabaniery
相关产品推荐
相关产品推荐

