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

Nokia J2ME 音頻播放實戰:S40/S60 專有 API 與 MMAPI (MIDP 2.0) 雙軌並行方案

     編輯:J2ME

Nokia 音頻 API 兩代演進

平台/系列代表機型支援 API特點
S40 (早期)7210, 3300, 6230, 6600com.nokia.mid.sound.Sound (專有)僅支援音調序列 (Tone)、簡短音效、無 MIDI 流式播放
S60 (早期)3650, 7650, 6600com.nokia.mid.sound.Sound (專有)同 S40,部分機型支援較長音調
S40/S60 (後期)7610, 6280, N70, N95MMAPI (JSR 135) + 專有 API 並存完整支援 MIDI、WAV、AMR、MP3 (視編解碼器)

方案一:Nokia 專有 Sound API (S40/S60 早期機型)

核心類別

import com.nokia.mid.sound.Sound;
// 建構子
Sound(byte[] data, int format) // format: Sound.FORMAT_TONE
// 方法
void play(int loopCount)     // loopCount=-1 循環
void stop()
void setGain(int level)      // 0-255 音量

音調資料獲取流程

  1. 準備 MIDI 檔 (.mid)。
  2. 使用 Nokia Audio Converter (Nokia Developer's Suite 內建) 轉換為位元組陣列。
  3. 複製生成的十六進位陣列至原始碼。

完整實作範例

public class NokiaSoundPlayer {
    private Sound sound;
    private static final byte[] EXPLOSION_TONE = {
        (byte)0x02, (byte)0x4a, (byte)0x3a, (byte)0x40,
        (byte)0x04, (byte)0x00, (byte)0x0b, (byte)0x28,
        (byte)0x83, (byte)0x28, (byte)0x28, (byte)0xd2,
        (byte)0x28, (byte)0x00
    };

    public void playExplosion() {
        stop(); // 避免重疊
        try {
            sound = new Sound(EXPLOSION_TONE, Sound.FORMAT_TONE);
            sound.setGain(200); // 音量
            sound.play(1);      // 播放一次
        } catch (Exception e) {
            // 靜默失敗,不影響遊戲主流程
        }
    }

    public void stop() {
        if (sound != null) {
            sound.stop();
            sound = null;
        }
    }
}

WTK 整合 Nokia 庫步驟

  1. 找到 SDK 目錄 Devices/Nokia_XXXX/lib/class.zip (或 classes.zip)。
  2. 解壓取出 com/nokia/mid/sound/Sound.class。
  3. 打包進專案 lib/nokia-sound.jar,加入 Build Path。
  4. WTK 打包時:產生 JAR → 解壓 JAR → 刪除 com/nokia/mid/sound/ 目錄 (避免與手機內建類衝突) → 重新打包 JAR → 更新 JAD 大小。

方案二:MMAPI (JSR 135) 標準 API (MIDP 2.0 / 後期機型)

Player 生命週期管理

import javax.microedition.media.*;

public class MmaPlayer {
    private Player player;

    public void playMidi(String resourcePath) { // e.g. "/audio/bgm.mid"
        stop();
        try {
            InputStream is = getClass().getResourceAsStream(resourcePath);
            if (is == null) throw new IllegalArgumentException("Resource not found: " + resourcePath);
            
            player = Manager.createPlayer(is, "audio/midi");
            player.addPlayerListener(this);
            player.setLoopCount(1);
            player.realize();   // 預載資源
            player.prefetch();  // 緩衝數據
            player.start();     // 開始播放
        } catch (MediaException | IOException e) {
            // 錯誤處理
        }
    }

    public void playerUpdate(Player p, String event, Object data) {
        if (PlayerListener.END_OF_MEDIA.equals(event)) {
            closePlayer();
        } else if (PlayerListener.ERROR.equals(event)) {
            // 處理錯誤
        }
    }

    public void stop() {
        closePlayer();
    }

    private void closePlayer() {
        if (player != null) {
            try { player.close(); } catch (Exception ignored) {}
            player = null;
        }
    }
}

關鍵問題:應用切換至背景時音樂持續播放

J2ME 生命週期:來電/按紅鍵/切換應用 → MIDlet.pauseApp() 被調用。若不處理,Player 繼續播放消耗電量並干擾用戶。

標準解法:pauseApp() 中停止音頻

public class GameMidlet extends MIDlet {
    private GameCanvas gameCanvas; // 含音頻播放器
    private boolean soundEnabled = true;

    protected void pauseApp() {
        if (gameCanvas != null && soundEnabled) {
            gameCanvas.stopAllSounds(); // 停止 MMAPI Player / Nokia Sound
        }
    }

    protected void startApp() {
        if (gameCanvas != null && soundEnabled) {
            gameCanvas.resumeSounds(); // 可選:恢復背景音樂
        }
    }
}

GameCanvas 實作停止介面


public class GameCanvas extends Canvas {
    private MmaPlayer bgmPlayer;
    private NokiaSoundPlayer sfxPlayer;

    public void stopAllSounds() {
        if (bgmPlayer != null) bgmPlayer.stop();
        if (sfxPlayer != null) sfxPlayer.stop();
    }

    public void resumeSounds() {
        // 可選:恢復 BGM
        if (bgmPlayer != null) bgmPlayer.playMidi("/audio/bgm.mid");
    }
}

雙軌並行架構:運行期自動選擇最佳 API


public interface SoundPlayer {
    void playEffect(String name);
    void playMusic(String name);
    void stopAll();
}

public class SoundManager {
    private static SoundPlayer instance;

    public static SoundPlayer getInstance() {
        if (instance == null) {
            // 檢測 MMAPI 可用性
            boolean hasMMAPI = false;
            try {
                Class.forName("javax.microedition.media.Manager");
                hasMMAPI = true;
            } catch (ClassNotFoundException ignored) {}

            String platform = System.getProperty("microedition.platform").toLowerCase();
            boolean isNokia = platform.contains("nokia");

            if (hasMMAPI) {
                instance = new MmaSoundPlayer(); // 完整功能
            } else if (isNokia) {
                instance = new NokiaSoundPlayerAdapter(); // 僅音效
            } else {
                instance = new NullSoundPlayer(); // 靜默實作
            }
        }
        return instance;
    }
}

總結建議

  • 新專案:以 MMAPI 為主,專有 API 為輔,封裝統一介面。
  • 音效:短促、低延遲優先用 Nokia Sound (S40) 或 MMAPI 预取模式。
  • 背景音樂:MIDI 循環播放用 MMAPI setLoopCount(-1)。
  • 生命週期:務必在 pauseApp() 停止所有 Player,destroyApp() 釋放資源。
  • 真機測試:模擬器音頻行為常與真機不同,重點測試目標機型。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved