在MIDP應(yīng)用程序中播放聲音
■前言
在上一講中我們講述的是如何制作JAVA手機多媒體功能中的動畫,本講中我們將介紹如何制作JAVA手機的另一個多媒體功能——聲音,即N800的音樂播放功能。若是在動畫上再配以音樂播放功能,就能制作出具有豐富表現(xiàn)力的應(yīng)用程序了。
■音樂播放
現(xiàn)在我們利用MIDP應(yīng)用程序播放音樂。但是,MIDP標(biāo)準API不支持聲音播放功能。機種不同,則聲音播放方法也不同,所以每個廠商都會使用他們各自擴展的API。N800使用NEC擴展的API,所以能用MIDP應(yīng)用程序播放聲音。
■N800的音樂播放功能
N800只能播放SMF(format0)格式的音樂數(shù)據(jù),最大文件尺寸為10Kbyte。SMF格式即StandardMIDIFile的縮略語,就是為了能實現(xiàn)互換性而設(shè)定的文件形式,互換性是指在不同的應(yīng)用程序中也具有能處理數(shù)據(jù)的特性。SMF分為format0和format1兩種格式。N800所采用的是format0格式。這兩種格式具有以下不同特點。(表1)
表1
■播放音樂
接下來我們實際操作讀取文件播放音樂的功能。
●音樂數(shù)據(jù)的讀取
利用擴展API上的Media類的static方法讀取音樂數(shù)據(jù)。自變量中記述了音樂數(shù)據(jù)文件的通過。AudioClipaudio=Media.getAudioClip(“/test.mid”);
另外,能夠從web上獲得音樂數(shù),并且能夠從RMS中得到音樂數(shù)據(jù)。但,由于形式相同,這里就不特別講述了。詳細情況請參考擴展API文檔。
●播放
播放讀取的音樂數(shù)據(jù)。使用AudioClip例子(在這稱為audio)play方法播放。
audio.play();
而且也能同時播放兩個以上的音樂數(shù)據(jù)。此時,使用AudioClip例子(在這稱為audio1、audio2)play方法播放。
audio1.play();
audio2.play();
上述情況下能夠同時播放audio1,audio2。
●停止
能夠暫停音樂的播放。使用AudioClip例子的stop方法能夠停止。
audio.stop();
●其他功能
AudioClip定義了讀取其他音樂數(shù)據(jù)信息的方法和決定反復(fù)播放次數(shù)的方法。(表2)
表2
■音頻事件
音頻演奏過程中,演奏開始時、停止時、結(jié)束時都會發(fā)生音頻事件,能定義此時的處理。要定義音頻事件發(fā)生時的處理,有必要安裝AudioListener接口和記述audioAction方法內(nèi)的處理。
然后,使用AudioClip對象的addAudioListener方法進入AudioListener。
publicclassAudioTestimplementsAudioListener{
/**
*構(gòu)造函數(shù)
**/
publicAudioTest(){
AudioClipaudio=Media.getAudioClip("/test.mid");//讀取音樂數(shù)據(jù)
audio.addAudioListener(this);//注冊audio事務(wù)監(jiān)聽器
}
/**
*音頻事件的處理
**/
publicvoidaudioAction(AudioClipsound,intevent,intparam){
//記述處理
•
•
}
}
ex.1
記述處理的audioAction方法的自變量如下所示。
表3
此外,事件的種類(audioAction方法的自變量、事件)在AudioListener接口文件夾中定義如下。(表4)
表4
以下展示的是只播放音頻數(shù)據(jù)的簡單范例。
importjavax.microedition.lcdui.Display;
importjavax.microedition.midlet.MIDlet;
importjavax.microedition.midlet.MIDletStateChangeException;
/**
*音樂播放的簡單范例
*/
publicclassAudioextendsMIDlet{
Displaydisplay;
AudioCanvascanvas;
/**
*構(gòu)造函數(shù)
*/
publicAudio(){
canvas=newAudioCanvas();
display=Display.getDisplay(this);
}
protectedvoidstartApp()throwsMIDletStateChangeException{
display.setCurrent(canvas);
}
protectedvoidpauseApp(){}
protectedvoiddestroyApp(booleanarg0)throwsMIDletStateChangeException{}
}
importjavax.microedition.lcdui.*;
importcom.nec.media.*;
/**
*音頻canvas
**/
publicclassAudioCanvas
extendsCanvas
implementsRunnable,CommandListener,AudioListener{
CommandSTART=newCommand("play",Command.OK,0);
CommandSTOP=newCommand("stop",Command.OK,0);
AudioClipa;//音樂數(shù)據(jù)
Threadth;
/**
*構(gòu)造函數(shù)
**/
publicAudioCanvas(){
a=Media.getAudioClip("/_test.mid");//讀取音樂數(shù)據(jù)
a.addAudioListener(this);//注冊AudioListener
this.addCommand(START);
this.addCommand(STOP);
this.setCommandListener(this);
th=newThread(this);
th.start();
}
/**
*音頻事件的處理
*/
publicvoidaudioAction(AudioClipsound,intevent,intparam){
if(sound==a){
if(event==AudioListener.AUDIO_COMPLETE){
sound.play();
}
}[!--empirenews.page--]
}
/**
*描繪處理
*/
protectedvoidpaint(Graphicsg){
g.setColor(255,255,255);
g.fillRect(0,0,getHeight(),getWidth());
g.setColor(0,0,0);
g.drawString("MusicPlay?",50,52,Graphics.TOP|Graphics.LEFT);
g.drawString(
"channel="+a.getChannel(),
30,
64,
Graphics.TOP|Graphics.LEFT);
g.drawString(
"lapsedtime="+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);
}
/**
*命令事件的處理
*/
publicvoidcommandAction(Commandc,Displayabled){
System.out.println("test");
if(c.equals(START)){
a.play();
}elseif(c.equals(STOP)){
a.stop();
}
}
/**
*線程的處理
*刷新查看
*/
publicvoidrun(){
while(true){
repaint();
try{
Thread.sleep(500);
}catch(Exceptione){
}
}
}
}
ex.2
接受表示音樂播放結(jié)束的事件后,根據(jù)明確的開始播放音樂菜單可以無限循環(huán)地播放音樂。下面的演示詳細記述了上述例子中的audioAction方法,能夠?qū)崿F(xiàn)無限循環(huán)播放。(ex.3)
/**
*音頻事件的處理
*/
publicvoidaudioAction(AudioClipsound,intevent,intparam){
if(sound==a){
if(event==AudioListener.AUDIO_COMPLETE){
sound.play();
}
}
}
ex.3
制作應(yīng)用程序
接下來制作實際的發(fā)聲應(yīng)用程序。
本講中制作的是簡單的“泡泡龍”游戲。
■游戲方法
移動小棒接住反彈的球使其不掉下去,使上方的彩球逐漸消失的游戲。彩球完全消失并清除后,球再落下則此游戲通過。
■準備工作
準備游戲必備的圖片和音效。
準備以下圖片。
圖1球的圖片
圖2小棒的圖片
圖3彩球的圖片
·背景音樂(bgm.mid)
·球反彈時的音效(ball.mid)
·彩球破碎時的音效(block.mid)
■設(shè)計
以下是狀態(tài)連接圖(図4)
本講中為了簡單化,在啟動應(yīng)用程序的同時立刻就啟動游戲。形成游戲開始、球落下后游戲結(jié)束、全部清除彩球后游戲過關(guān)。
Figure4
■制作應(yīng)用程序
現(xiàn)在我們按照以下順序制作應(yīng)用程序。
1.類結(jié)構(gòu)
2.變量、常量的定義
3.查看圖片和音效
4.使圖片運動
5.球的反彈
6.音樂的播放
1.類結(jié)構(gòu)
下表內(nèi)容是類結(jié)構(gòu)。(表5)
表5
2.變量、常量的定義
將下面的應(yīng)用變量、定量作為BlockCanvas例子的屬性并定義。(ex.4)
//狀態(tài)設(shè)定
privateintstate;//狀態(tài)
privatefinalintACTIVE=1;
privatefinalintGAME_OVER=2;
privatefinalintCLEAR=3;
//彩球的設(shè)定
privatefinalintBLOCK_H=7;//彩球橫向的個數(shù)
privatefinalintBLOCK_V=5;//彩球縱向的個數(shù)
privatefinalintBLOCK_WIDTH=getWidth()/BLOCK_H;
privatefinalintBLOCK_HEIGHT=BLOCK_WIDTH/2;
privatebooleanblock[][]=newboolean[BLOCK_H][BLOCK_V];
privateintblockCount;//彩球個數(shù)
//小棒的設(shè)定
privatefinalintBAR_HEIGHT=11;
privatefinalintBAR_WIDTH=23;
privateintbarX=0;
privateintbarY=getHeight()-BAR_HEIGHT;
privateintbarMovCodeExample=0;
//球的設(shè)定
privatefinalintBALL_HEIGHT=10;
privatefinalintBALL_WIDTH=10;
privateintballX;
privateintballY;
privateintballMovCodeExample=5;
privateintballMoveY=5;
privateThreadth;
//畫面類
privateImagebarImg=null;
privateImageballImg=null;
privateImageblockImg=null;
//音效類
privateAudioClipbgm;//Backgroundmusic
privateAudioClipballSound;//Soundofbouncingball
privateAudioClipblockSound;//Soundofdestroyingblocks
ex.4
3.查看圖片和音效
查看準備好的圖片和音效。BlockCanvas的構(gòu)造函數(shù)內(nèi)分別讀取小棒、球、彩球的圖片。(ex.5)
//讀取圖片
try{
barImg=Image.createImage("/bar.png");
ballImg=Image.createImage("/ball.png");
blockImg=Image.createImage("/block.png");
}catch(CodeExampleceptione){
e.printStackTrace();
}
ex.5
能查看讀取后的圖片。
彩球在圖中的分配為橫7縱5,讀取彩球圖片并描畫在畫面中。用旗表示彩球的狀態(tài)。保持原來的排列。將一個一個的彩球使用原來的排列并計算出坐標(biāo),安排在畫面中。(ex.6)
//查看彩球
g.setColor(0,0,255);
for(inti=0;i
for(intj=0;j[!--empirenews.page--]
if(block[i][j]){
g.drawImage(
blockImg,
i*BLOCK_WIDTH,
(j+1)*BLOCK_HEIGHT,
Graphics.LEFT|Graphics.TOP);
}
}
}
ex.6
接著查看球和小棒。在paint方法中有以下敘述。(ex.7)
//查看球
g.drawImage(ballImg,ballX,ballY,Graphics.LEFT|Graphics.TOP);
//查看小棒
g.drawImage(barImg,barX,barY,Graphics.LEFT|Graphics.TOP);
ex.7
圖片安裝完成后出現(xiàn)如下畫面。
4.使圖片運動
接下來使用線程和按鍵事件移動球和小棒。為了使用線程就得在AudioCanvas類中安裝Runnable接口、記述run方法。使用球和小棒定義的移動速度分別變化球和小棒的坐標(biāo)。此外,小棒的移動速度根據(jù)按鍵處理而變化。以下記述了run方法。(ex.8)
/**
*線程的運行處理
*/
publicvoidrun(){
while(state==ACTIVE){
moveBall();//使球運動
moveBar();//移動小棒
repaint();//再次描畫
try{
Thread.sleep(50);
}catch(InterruptedCodeExampleceptione){
e.printStackTrace();
break;
}
}
}
/**
//使球運動
*/
publicvoidmoveBall(){
ballX+=ballMovCodeExample;
ballY+=ballMoveY;
}
/**
*移動小棒
*/
publicvoidmoveBar(){
barX+=barMovCodeExample;
//不能向畫面外移動
if(barX<0){
barX=0;
}elseif(barX+BAR_WIDTH>getWidth()){
barX=getWidth()-BAR_WIDTH;
}
}
ex.8
以下表示的是按鍵處理。(ex.9)
/*****************************************
*按鍵處理
*****************************************/
/**
*按按鍵時
*/
protectedvoidkeyPressed(intkey){
if(state==ACTIVE){//正在運動
if(getGameAction(key)==Canvas.RIGHT){
barMovCodeExample=6;
}elseif(getGameAction(key)==Canvas.LEFT){
barMovCodeExample=-6;
}
repaint();
}else{//停止運動后
//再次啟動
this.initialize();
}
}
/**
*釋放按鍵時
*/
protectedvoidkeyReleased(intkey){
barMovCodeExample=0;
}
ex.9
5.球的反彈
下面是球的反彈。
球的反彈形式有以下3種。
·碰邊壁后反彈
·碰小棒后反彈
·碰彩球后反彈
記述了每個moveBall方法。(ex.10)
碰彩球的反彈時
block[i][j]=false;
blockCount--;
彩球立刻就破碎。彩球破碎后余下的彩球數(shù)量blockCount將有所減少。
另外,球掉落時,改變游戲狀態(tài)后游戲結(jié)束。
/**
//使球運動
*/
publicvoidmoveBall(){
ballX+=ballMovCodeExample;
ballY+=ballMoveY;
//反彈
//碰邊壁后反彈
if(ballX<0){
ballMovCodeExample*=-1;
ballX=0;
}elseif(getWidth()
ballX=getWidth()-BALL_HEIGHT;
ballMovCodeExample*=-1;
}
if(ballY<0){
ballMoveY*=-1;
ballY=0;
}elseif(ballY>getHeight()){//球落下后
//游戲結(jié)束
state=GAME_OVER;
}
//碰上小棒后反彈
if(ballY+BALL_HEIGHT>barY
&&ballX+BALL_WIDTH>barX
&&ballX
ballMoveY*=-1;
ballY=barY-BALL_HEIGHT;
if(barMovCodeExample<0){
ballMovCodeExample-=2;
}elseif(barMovCodeExample>0){
ballMovCodeExample+=2;
}
}
//碰上彩球后反彈
for(inti=0;i
for(intj=0;j
if(block[i][j]){
if(ballX+BALL_WIDTH>i*BLOCK_WIDTH
&&ballX<(i+1)*BLOCK_WIDTH){
if(ballY+BALL_HEIGHT>(j+1)*BLOCK_HEIGHT
&&ballY<(j+2)*BLOCK_HEIGHT){
//清除彩球
block[i][j]=false;
blockCount--;
ballMoveY*=-1;}
}
}
}
}
}
}
ex.10
碰小棒后反彈情況如下所示:向右按鍵時,球就會讓右方快速移動,反之,向左按鍵時,球則向左方快速移動。(ex.11)
if(barMoveX<0){
ballMoveX-=2;
}elseif(barMoveX>0){
ballMoveX+=2;
}
ex.11
·清除檢查
至此安裝完畢游戲就有雛形了。但是,在現(xiàn)在的程序中即使彩球全部消失,游戲也不能清除。那么,球與彩球相撞時,數(shù)出彩球的剩余數(shù)。當(dāng)該數(shù)值為0時,則游戲清除。以下記述的是該處理。(ex.12)
//清除彩球
block[i][j]=false;
blockCount--;
ballMoveY*=-1;
//播放音效
blockSound.play();
//檢查游戲清除
if(blockCount==0){
state=CLEAR;
}
ex.12
6.音樂播放
在本講中的泡泡龍游戲的應(yīng)用程序中最好使用BGM和音效。游戲開始的同時演奏BGM,音效則是球在碰壁、碰小棒反彈時,以及彩球破碎時才播放的。
·讀取
用BlockApplication構(gòu)造函數(shù)讀取音樂數(shù)據(jù)。而且,這里的BGM能夠循環(huán)播放,所以可以使用音頻事件處理。(ex.13)
[!--empirenews.page--]//聲音數(shù)據(jù)的讀取
bgm=Media.getAudioClip("/bgm.mid");//背景音樂
ballSound=Media.getAudioClip("/ball.mid");//球反彈后的音效
blockSound=Media.getAudioClip("/block.mid");//球破碎的音效
bgm.addAudioListener(this);//增加AudioListener
ex.13
·播放
讀取音樂數(shù)據(jù)后,接下來進行播放。BGM在游戲開始的同時能夠播放,所以在AudioCanvas類的start方法中記述播放處理并能夠播放出來。
球的音效:用moveBall方法進行下面反彈判斷時,能夠播放音效。(ex.14)
//反彈
//碰邊壁后反彈
if(ballX<0){
ballMoveX*=-1;
ballX=0;
//播放音效
ballSound.play();
}elseif(getWidth()
ballX=getWidth()-BALL_HEIGHT;
ballMoveX*=-1;
//播放音效
ballSound.play();
}
if(ballY<0){
ballMoveY*=-1;
ballY=0;
//播放音效
ballSound.play();
}elseif(ballY>getHeight()){//球落下后
//游戲結(jié)束
state=GAME_OVER;
}
//碰上小棒反彈
if(ballY+BALL_HEIGHT>barY
&&ballX+BALL_WIDTH>barX
&&ballX
ballMoveY*=-1;
ballY=barY-BALL_HEIGHT;
if(barMoveX<0){
ballMoveX-=2;
}elseif(barMoveX>0){
ballMoveX+=2;
}
//播放音效
ballSound.play();
}
ex.14
彩球的音效:用moveBall方法判定彩球的碰撞時,如下記述并能夠播放。(ex.15)
//碰上彩球后反彈
for(inti=0;i
for(intj=0;j
if(block[i][j]){
if(ballX+BALL_WIDTH>i*BLOCK_WIDTH
&&ballX<(i+1)*BLOCK_WIDTH){
if(ballY+BALL_HEIGHT>(j+1)*BLOCK_HEIGHT
&&ballY<(j+2)*BLOCK_HEIGHT){
block[i][j]=false;
ballMoveY*=-1;
//播放音效
blockSound.play();
}
}
}
}
}
ex.15
■完成
下面是實際制作的程序一式。(BlockApplication.zip)
運行結(jié)果如下所示。
游戲進行中游戲結(jié)束
游戲結(jié)束
總結(jié)
在本講的講解中能夠自由播放音樂數(shù)據(jù)了。因此,能夠制作成創(chuàng)造性的應(yīng)用程序。但是,擴展應(yīng)用程序時,不能保存高分、數(shù)據(jù)等。在下講我們將學(xué)習(xí)如何使用固定存儲器保存數(shù)據(jù)的方法。
查看png格式的畫面文件
播放smf格式的音樂文件
http通信,socket通信
逆光、雙感光板控制
Sprite功能
ImageMap功能
各種各樣的制圖擴展功能
3D引擎
由于N820具有256Kbyte的較大存儲空間,所以能制作容量稍大、自由度較高的手機應(yīng)用程序。另外,也能制作對應(yīng)http、socket通信的自由度較高網(wǎng)絡(luò)應(yīng)用程序。因此,也能夠搭載3D引擎、3D描畫。而且還能安裝N800對應(yīng)的Sprite功能、ImageMap功能的描畫功能。
■與N800的比較
下表是N800和N820的比較。(表6)
項目N800N820
顯示屏尺寸180x162(縱x橫)255x240(縱x橫)
JAD文件尺寸最大2KB最大2KB
JAR文件尺寸最大50KB最大1MB
RMS尺寸最大10KB最大10KB
記錄存儲數(shù)量最大3records最大3records
通信協(xié)議只有httpHTTPandsocket
畫像文件PNGPNG
音樂文件SMF(format0)最大10KbyteSMF(format0)最大10Kbyte
表6
■NECN820Application模擬器
下面是模擬N820工作的模擬器,稱為「NEC820ApplicationEmulator」。與以前我們所介紹的「NECN800ApplicationEmulator」在外觀上沒有什么區(qū)別。(図5)
圖5
打開模擬器,就是現(xiàn)在的手機畫面表示。與N800相比,手機設(shè)計多少有些變化,手機的內(nèi)顯示屏變大了。下圖是用N820ApplicationEmulator制作的“泡泡龍”游戲畫面。“泡泡龍”游戲由于是假定在N800的屏幕上應(yīng)用的。因此畫面尺寸要比N820中的內(nèi)屏尺寸稍小。因此,彩球之間存有空隙。(図6)
図6
■總結(jié)
N820的優(yōu)點是具有256Kbyte的大容量存儲空間,而且使用3D圖表引擎、3D圖表應(yīng)用程序、能夠制作成對應(yīng)socket通信的TCP/IP網(wǎng)絡(luò)應(yīng)用程序。對于應(yīng)用開發(fā)者而言,N820是一部制作JAVA應(yīng)用程序非常有價值的終端。對于尋求高級機種的用戶而言,應(yīng)該是一部高精細畫面、高功能的極大滿足用戶需要的終端。今后N820的用戶應(yīng)該會大幅度增加的。