|
|
副标题:
作者:未知 文章来源:nec
■ 前言
在上一讲中我们讲述的是如何制作JAVA手机多媒体功能中的动画,本讲中我们将介绍如何制作JAVA手机的另一个多媒体功能——声音,即N800的音乐播放功能。若是在动画上再配以音乐播放功能,就能制作出具有丰富表现力的应用程序了。
■ 音乐播放
现在我们利用MIDP应用程序播放音乐。但是, MIDP标准API不支持声音播放功能。机种不同,则声音播放方法也不同,所以每个厂商都会使用他们各自扩展的API 。N800使用NEC扩展的API,所以能用MIDP应用程序播放声音。
■ N800的音乐播放功能
N800只能播放SMF(format 0)格式的音乐数据,最大文件尺寸为10Kbyte。SMF格式即Standard MIDI File 的缩略语,就是为了能实现互换性而设定的文件形式,互换性是指在不同的应用程序中也具有能处理数据的特性。SMF分为format0和format1两种格式。N800所采用的是format0格式。这两种格式具有以下不同特点。(表 1)
format0 Midi的16频声音收录在1个磁道上的形式
format1
不限制磁道数量的形式
表 1
■ 播放音乐
接下来我们实际操作读取文件播放音乐的功能。
● 音乐数据的读取
利用扩展API上的Media类的static方法读取音乐数据。自变量中记述了音乐数据文件的通过。AudioClip audio = Media.getAudioClip(“/test.mid”);
另外,能够从web上获得音乐数,并且能够从RMS中得到音乐数据。但,由于形式相同,这里就不特别讲述了。详细情况请参考扩展API文档。
● 播放
播放读取的音乐数据。使用AudioClip例子(在这称为audio)play方法播放。
audio.play();
而且也能同时播放两个以上的音乐数据。此时,使用AudioClip例子(在这称为audio1、audio2)play方法播放。
audio1.play();
audio2.play();
上述情况下能够同时播放audio1,audio2。
● 停止
能够暂停音乐的播放。使用AudioClip例子的stop方法能够停止。
audio.stop();
● 其他功能
AudioClip定义了读取其他音乐数据信息的方法和决定反复播放次数的方法。(表 2)
getChannel() 取得音乐数据的频数
getLapsedTime()
以MS为单位取得所演奏的音乐数据的播放时间
getTempo()
取得音乐数据的速度
getTime()
取得音乐数据的播放时间
setLoopCount(int count)
设定演奏的音乐数据的反复播放次数
表 2
■ 音频事件
音频演奏过程中,演奏开始时、停止时、结束时都会发生音频事件,能定义此时的处理。要定义音频事件发生时的处理,有必要安装AudioListener接口和记述audioAction方法内的处理。
然后,使用AudioClip对象的addAudioListener方法进入AudioListener。
public class AudioTest implements AudioListener {
/**
* 构造函数
**/
public AudioTest() {
AudioClip audio = Media.getAudioClip("/test.mid");// 读取音乐数据
audio.addAudioListener(this);// 注册audio事务监听器
}
/**
* 音频事件的处理
**/
public void audioAction(AudioClip sound, int event, int param) {
//记述处理
•
•
}
}
ex. 1
记述处理的audioAction方法的自变量如下所示。
AudioClip sound 传递事件发生来源的对象
int event
传递事件的种类
int param
传递事件的参数。由于事件不同,则参数的意思也不同。不包含参数的事件的情况下,只传递0
表 3
此外,事件的种类(audioAction方法的自变量、事件)在AudioListener接口文件夹中定义如下。(表 4)
static int AUDIO_COMPLETE 表示音乐播放结束
static int AUDIO_STARTED 表示音乐播放开始
static int AUDIO_COMPLETE 表示音乐播放停止
表 4
以下展示的是只播放音频数据的简单范例。
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
/**
* 音乐播放的简单范例
*/
public class Audio extends MIDlet {
Display display;
AudioCanvas canvas;
/**
* 构造函数
*/
public Audio() {
canvas = new AudioCanvas();
display = Display.getDisplay(this);
}
protected void startApp() throws MIDletStateChangeException {
display.setCurrent(canvas);
}
protected void pauseApp() {}
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {}
}
import javax.microedition.lcdui.*;
import com.nec.media.*;
/**
* 音频 canvas
**/
public class AudioCanvas
extends Canvas
implements Runnable, CommandListener, AudioListener {
Command START = new Command("play", Command.OK, 0);
Command STOP = new Command("stop", Command.OK, 0);
AudioClip a;// 音乐数据
Thread th;
/**
* 构造函数
**/
public AudioCanvas() {
a = Media.getAudioClip("/_test.mid");// 读取音乐数据
a.addAudioListener(this);// 注册 AudioListener
this.addCommand(START);
this.addCommand(STOP);
this.setCommandListener(this);
th = new Thread(this);
th.start();
}
/**
* 音频事件的处理
*/
public void audioAction(AudioClip sound, int event, int param) {
if (sound == a) {
if (event == AudioListener.AUDIO_COMPLETE) {
sound.play();
}
}
}
/**
* 描绘处理
*/
protected void paint(Graphics g) {
g.setColor(255, 255, 255);
g.fillRect(0, 0, getHeight(), getWidth());
g.setColor(0, 0, 0);
g.drawString("Music Play?", 50, 52, Graphics.TOP | Graphics.LEFT);
g.drawString(
"channel=" + a.getChannel(),
30,
64,
Graphics.TOP | Graphics.LEFT);
g.drawString(
"lapsed time=" + a.getLapsedTime(),
30,
76,
Graphics.TOP | Graphics.LEFT);
g.drawString(
"tempo=" + a.getTempo(),
30,
88,
Graphics.TOP | Graphics.LEFT);
g.drawString(
"time=" + a.getTime(),
30,
100,
Graphics.TOP | Graphics.LEFT);
}
/**
* 命令事件的处理
*/
public void commandAction(Command c, Displayable d) {
System.out.println("test");
if (c.equals(START)) {
a.play();
} else if (c.equals(STOP)) {
a.stop();
}
}
/**
* 线程的处理
* 刷新查看
*/
public void run() {
while (true) {
repaint();
try{
Thread.sleep(500);
}catch(Exception e){
}
}
}
}
ex. 2
接受表示音乐播放结束的事件后,根据明确的开始播放音乐菜单可以无限循环地播放音乐。下面的演示详细记述了上述例子中的audioAction方法,能够实现无限循环播放。(ex. 3)
/**
* 音频事件的处理
*/
public void audioAction(AudioClip sound, int event, int param) {
if (sound == a) {
if (event == AudioListener.AUDIO_COMPLETE) {
sound.play();
}
}
}
ex. 3
制作应用程序
接下来制作实际的发声应用程序。
本讲中制作的是简单的“泡泡龙”游戏。
■ 游戏方法
移动小棒接住反弹的球使其不掉下去,使上方的彩球逐渐消失的游戏。彩球完全消失并清除后,球再落下则此游戏通过。
■ 准备工作
准备游戏必备的图片和音效。
准备以下图片。
· 背景音乐 ( bgm.mid )
· 球反弹时的音效 ( ball.mid )
· 彩球破碎时的音效 ( block.mid )
■ 设计
以下是状态连接图
本讲中为了简单化,在启动应用程序的同时立刻就启动游戏。形成游戏开始、球落下后游戏结束、全部清除彩球后游戏过关。
Figure 4
■ 制作应用程序
现在我们按照以下顺序制作应用程序。
1.类结构
2.变量、常量的定义
3.查看图片和音效
4.使图片运动
5.球的反弹
6.音乐的播放
1.类结构
下表内容是类结构。(表 5)
BlockApplication 泡泡龙游戏的MIDlet
BlockCanvas 泡泡龙游戏的Canvas
表 5
2. 变量、常量的定义
将下面的应用变量、定量作为BlockCanvas例子的属性并定义。(ex. 4)
// 状态设定
private int state; // 状态
private final int ACTIVE = 1;
private final int GAME_OVER = 2;
private final int CLEAR = 3;
// 彩球的设定
private final int BLOCK_H = 7; // 彩球横向的个数
private final int BLOCK_V = 5; // 彩球纵向的个数
private final int BLOCK_WIDTH = getWidth() / BLOCK_H;
private final int BLOCK_HEIGHT = BLOCK_WIDTH / 2;
private boolean block[][] = new boolean[BLOCK_H][BLOCK_V];
private int blockCount; // 彩球个数
// 小棒的设定
private final int BAR_HEIGHT = 11;
private final int BAR_WIDTH = 23;
private int barX = 0;
private int barY = getHeight() - BAR_HEIGHT;
private int barMovCode Example = 0;
// 球的设定
private final int BALL_HEIGHT = 10;
private final int BALL_WIDTH = 10;
private int ballX;
private int ballY;
private int ballMovCode Example = 5;
private int ballMoveY = 5;
private Thread th;
// 画面类
private Image barImg = null;
private Image ballImg = null;
private Image blockImg = null;
// 音效类
private AudioClip bgm; // Background music
private AudioClip ballSound; // Sound of bouncing ball
private AudioClip blockSound; // Sound of destroying blocks
ex. 4
3. 查看图片和音效
查看准备好的图片和音效。BlockCanvas的构造函数内分别读取小棒、球、彩球的图片。(ex. 5)
// 读取图片
try {
barImg = Image.createImage("/bar.png");
ballImg = Image.createImage("/ball.png");
blockImg = Image.createImage("/block.png");
} catch (Code Exampleception e) {
e.printStackTrace();
}
|
|