首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2ME

J2ME 遊戲音效跨平台適配實戰:Nokia S40/S60、Moto V300、MMAPI 統一封裝

     編輯:J2ME

挑戰

早期 J2ME 手機音效 API 混亂:Nokia 專有 com.nokia.mid.sound.Sound(S40)、com.nokia.mid.ui.DirectGraphics 系列、Moto V300 只支援 MMAPI javax.microedition.media.Player、部份機型僅支援 MIDI、部份支援 OTT(Nokia 鈴聲格式)。本文展示統一封裝策略,以單一介面支援多平台。

統一介面設計

interface SoundPlayer {
    void load(String resPath, int resId);   // 載入資源
    void play(int loopCount);               // 播放
    void stop();                            // 停止
    boolean isPlaying();
    void setEnabled(boolean on);            // 靜音開關
}

平台一:Nokia S40 (Sound API + OTT)

class NokiaS40Sound implements SoundPlayer {
    private com.nokia.mid.sound.Sound sound;
    private com.nokia.mid.sound.SoundListener listener = new SoundListener() {
        public void soundStateChanged(Sound s, int event) {
            playing = (event == Sound.SOUND_PLAYING);
        }
    };
    private boolean playing = false, enabled = true;

    public void load(String base, int id) {
        try {
            InputStream is = getClass().getResourceAsStream(base + "/" + id + ".ott");
            byte[] data = readAll(is);
            sound = new Sound(data, Sound.FORMAT_TONE);
            sound.setSoundListener(listener);
        } catch (Exception e) { sound = null; }
    }

    public void play(int loop) {
        if (!enabled || sound == null) return;
        try { if (playing) stop(); sound.play(loop); }
        catch (Exception e) { playing = false; }
    }
    public void stop() { if (sound != null) sound.stop(); playing = false; }
    public boolean isPlaying() { return playing; }
    public void setEnabled(boolean on) { enabled = on; if (!on) stop(); }
}

平台二:Nokia S60 / 通用 MMAPI (Player + MIDI)

class MmaSound implements SoundPlayer {
    private javax.microedition.media.Player player;
    private javax.microedition.media.PlayerListener listener = new PlayerListener() {
        public void playerUpdate(Player p, String event, Object data) {
            if (PlayerListener.STARTED.equals(event)) playing = true;
            else if (PlayerListener.STOPPED.equals(event)) playing = false;
        }
    };
    private boolean playing = false, enabled = true;

    public void load(String base, int id) {
        try {
            InputStream is = getClass().getResourceAsStream(base + "/" + id + ".mid");
            byte[] data = readAll(is);
            player = Manager.createPlayer(new ByteArrayInputStream(data), "audio/midi");
            player.addPlayerListener(listener);
            player.realize();
            player.prefetch();
        } catch (Exception e) { player = null; }
    }

    public void play(int loop) {
        if (!enabled || player == null) return;
        try { if (playing) stop(); player.setLoopCount(loop); player.start(); }
        catch (Exception e) { playing = false; }
    }
    public void stop() { try { if (player != null) player.stop(); } catch (Exception e) {} playing = false; }
    public boolean isPlaying() { return playing; }
    public void setEnabled(boolean on) { enabled = on; if (!on) stop(); }
}

平台三:Motorola V300 (MMAPI + 自定義 MIME)

V300 需指定 MIME audio/midi 或 audio/sp-midi,載入流程同 MMAPI,但 EMSound 類別中 type 欄位改為 String。

工廠模式自動選擇實作

class SoundFactory {
    public static SoundPlayer create() {
        String platform = System.getProperty("microedition.platform");
        if (platform != null && platform.toLowerCase().indexOf("nokia") >= 0) {
            // 進一步判斷 S40/S60:嘗試載入 Sound 類別
            try {
                Class.forName("com.nokia.mid.sound.Sound");
                return new NokiaS40Sound(); // S40
            } catch (ClassNotFoundException e) {
                return new MmaSound();      // S60 或其他支援 MMAPI 的 Nokia
            }
        } else if (platform != null && platform.toLowerCase().indexOf("motorola") >= 0) {
            return new MmaSound(); // V300 等
        }
        // 預設回退
        return new MmaSound();
    }
}

資源讀取工具

private byte[] readAll(InputStream is) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buf = new byte[1024];
    int len;
    while ((len = is.read(buf)) != -1) baos.write(buf, 0, len);
    is.close();
    return baos.toByteArray();
}

遊戲中使用範例

SoundPlayer bgm = SoundFactory.create();
bgm.load("/res/snd", 1); // 載入 /res/snd/1.ott 或 1.mid
bgm.play(-1); // 循環播放

// 選單切換音效
SoundPlayer sfx = SoundFactory.create();
sfx.load("/res/snd", 2);
sfx.play(1);

關鍵注意事項

  • 資源命名統一:同一音效提供 .ott (S40)、.mid (S60/MMAPI) 兩版,打包時依平台剔除或全量打包由工廠選擇。
  • 記憶體管理:stop() 後應 player.deallocate() 或 sound.release() 釋放原生資源。
  • 並發限制:多數手機同一時間僅支援 1 個 Player/Sound。BGM 與 SFX 互斥時,需自行管理優先級(如 BGM 暫停播 SFX,SFX 結束恢復 BGM)。
  • 靜音開關:持久化至 RMS,啟動時套用 setEnabled()。

總結

以介面隔離 + 工廠模式 + 平台偵測封裝碎片化音效 API,使遊戲邏輯零感知地跨平台運行。此模式同樣適用於震動、全螢幕、鍵盤燈等差異化功能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved