Java游戏声音引擎报错ClassCastException:DirectClip无法转为SourceDataLine
声音引擎开发中的ClassCastException问题分析
我正在为游戏开发新的声音引擎以替换性能不佳的旧引擎,采用SourceDataLine进行开发,但运行时抛出如下异常:
java.lang.ClassCastException: com.sun.media.sound.DirectAudioDevice$DirectClip cannot be cast to javax.sound.sampled.SourceDataLine at com.glowiak.aws.SoundLibrary.play(Main.java:1360) at com.glowiak.aws.UIButton.loop(Main.java:475) at com.glowiak.aws.Renderer.paintComponent(Main.java:174) at javax.swing.JComponent.paint(JComponent.java:1056) at javax.swing.JComponent.paintToOffscreen(JComponent.java:5210) at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:290) at javax.swing.RepaintManager.paint(RepaintManager.java:1272) at javax.swing.JComponent._paintImmediately(JComponent.java:5158) at javax.swing.JComponent.paintImmediately(JComponent.java:4969) at javax.swing.RepaintManager$4.run(RepaintManager.java:831) at javax.swing.RepaintManager$4.run(RepaintManager.java:814) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74) at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814) at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789) at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738) at javax.swing.RepaintManager.access$1200(RepaintManager.java:64) at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758) at java.awt.EventQueue.access$500(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:709) at java.awt.EventQueue$3.run(EventQueue.java:703) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74) at java.awt.EventQueue.dispatchEvent(EventQueue.java:728) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
对应的声音引擎实现代码如下:
// the second option from https://www.baeldung.com/java-play-sound modified to fit my engine idea class SoundLibrary { public static final int BUFFER_SIZE = 4096; public ArrayList<String> urls; public void registerSound(String fp) { urls.add(fp); } public boolean play(int i) { try { InputStream is = getClass().getResourceAsStream(urls.get(urls.size() - 1)); AudioInputStream ais = AudioSystem.getAudioInputStream(is); SourceDataLine.Info dli = new DataLine.Info(Clip.class, ais.getFormat()); SourceDataLine sdl = (SourceDataLine)AudioSystem.getLine(dli); sdl.open(ais.getFormat()); sdl.start(); byte[] buffer = new byte[BUFFER_SIZE]; int rb = -1; while ((rb = ais.read(buffer)) != -1) { sdl.write(buffer, 0, rb); } sdl.drain(); sdl.close(); ais.close(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } public static void cleanup() { } public SoundLibrary() { this.urls = new ArrayList<String>(); this.registerSound("/resources/sound/bow.wav"); this.registerSound("NULL"); this.registerSound("/resources/sound/buttonpress.wav"); } }
代码中的错误分析
类型转换异常的直接原因:创建
DataLine.Info时,错误传入了Clip.class作为线路类型,但你需要的是SourceDataLine。AudioSystem.getLine()会返回符合指定类型的线路实例,这里返回的是Clip对象,强转成SourceDataLine必然抛出ClassCastException。play方法逻辑错误:方法接收参数
i用于指定播放的声音索引,但实际代码中调用的是urls.get(urls.size() - 1),始终获取最后一个注册的声音,完全忽略了传入的索引参数,导致无法按需求播放指定声音。无效路径注册问题:构造方法中注册了
"NULL"这个无效的资源路径,当后续尝试访问该索引时,getResourceAsStream()会返回null,进而导致AudioSystem.getAudioInputStream()抛出空指针或非法参数异常。
修正后的关键代码示例
public boolean play(int i) { // 先判断索引是否合法 if (i < 0 || i >= urls.size()) { return false; } try { InputStream is = getClass().getResourceAsStream(urls.get(i)); if (is == null) { System.err.println("Invalid sound resource path: " + urls.get(i)); return false; } AudioInputStream ais = AudioSystem.getAudioInputStream(is); // 修正DataLine.Info的类型为SourceDataLine.class DataLine.Info dli = new DataLine.Info(SourceDataLine.class, ais.getFormat()); SourceDataLine sdl = (SourceDataLine) AudioSystem.getLine(dli); sdl.open(ais.getFormat()); sdl.start(); byte[] buffer = new byte[BUFFER_SIZE]; int rb = -1; while ((rb = ais.read(buffer)) != -1) { sdl.write(buffer, 0, rb); } sdl.drain(); sdl.close(); ais.close(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } public SoundLibrary() { this.urls = new ArrayList<String>(); this.registerSound("/resources/sound/bow.wav"); // 移除无效的"NULL"注册,或替换为有效路径 // this.registerSound("NULL"); this.registerSound("/resources/sound/buttonpress.wav"); }
内容的提问来源于stack exchange,提问作者glowiak
相关产品推荐
相关产品推荐

