当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]在MIDP应用程序中播放声音

■前言

在上一讲中我们讲述的是如何制作JAVA手机多媒体功能中的动画,本讲中我们将介绍如何制作JAVA手机的另一个多媒体功能——声音,即N800的音乐播放功能。若是在动画上再配以音乐播放功能,就能制作出具有丰富表现力的应用程序了。

■音乐播放

现在我们利用MIDP应用程序播放音乐。但是,MIDP标准API不支持声音播放功能。机种不同,则声音播放方法也不同,所以每个厂商都会使用他们各自扩展的API。N800使用NEC扩展的API,所以能用MIDP应用程序播放声音。

■N800的音乐播放功能

N800只能播放SMF(format0)格式的音乐数据,最大文件尺寸为10Kbyte。SMF格式即StandardMIDIFile的缩略语,就是为了能实现互换性而设定的文件形式,互换性是指在不同的应用程序中也具有能处理数据的特性。SMF分为format0和format1两种格式。N800所采用的是format0格式。这两种格式具有以下不同特点。(表1)

表1

■播放音乐

接下来我们实际操作读取文件播放音乐的功能。

●音乐数据的读取

利用扩展API上的Media类的static方法读取音乐数据。自变量中记述了音乐数据文件的通过。AudioClipaudio=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)

表2

■音频事件

音频演奏过程中,演奏开始时、停止时、结束时都会发生音频事件,能定义此时的处理。要定义音频事件发生时的处理,有必要安装AudioListener接口和记述audioAction方法内的处理。

然后,使用AudioClip对象的addAudioListener方法进入AudioListener。

publicclassAudioTestimplementsAudioListener{

/**

*构造函数

**/

publicAudioTest(){

AudioClipaudio=Media.getAudioClip("/test.mid");//读取音乐数据

audio.addAudioListener(this);//注册audio事务监听器

}

/**

*音频事件的处理

**/

publicvoidaudioAction(AudioClipsound,intevent,intparam){

//记述处理

}

}

ex.1

记述处理的audioAction方法的自变量如下所示。

表3

此外,事件的种类(audioAction方法的自变量、事件)在AudioListener接口文件夹中定义如下。(表4)

表4

以下展示的是只播放音频数据的简单范例。

importjavax.microedition.lcdui.Display;

importjavax.microedition.midlet.MIDlet;

importjavax.microedition.midlet.MIDletStateChangeException;

/**

*音乐播放的简单范例

*/

publicclassAudioextendsMIDlet{

Displaydisplay;

AudioCanvascanvas;

/**

*构造函数

*/

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;//音乐数据

Threadth;

/**

*构造函数

**/

publicAudioCanvas(){

a=Media.getAudioClip("/_test.mid");//读取音乐数据

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

接受表示音乐播放结束的事件后,根据明确的开始播放音乐菜单可以无限循环地播放音乐。下面的演示详细记述了上述例子中的audioAction方法,能够实现无限循环播放。(ex.3)

/**

*音频事件的处理

*/

publicvoidaudioAction(AudioClipsound,intevent,intparam){

if(sound==a){

if(event==AudioListener.AUDIO_COMPLETE){

sound.play();

}

}

}

ex.3

制作应用程序

接下来制作实际的发声应用程序。

本讲中制作的是简单的“泡泡龙”游戏。

■游戏方法

移动小棒接住反弹的球使其不掉下去,使上方的彩球逐渐消失的游戏。彩球完全消失并清除后,球再落下则此游戏通过。

■准备工作

准备游戏必备的图片和音效。

准备以下图片。

图1球的图片

图2小棒的图片

图3彩球的图片

·背景音乐(bgm.mid)

·球反弹时的音效(ball.mid)

·彩球破碎时的音效(block.mid)

■设计

以下是状态连接图(図4)

本讲中为了简单化,在启动应用程序的同时立刻就启动游戏。形成游戏开始、球落下后游戏结束、全部清除彩球后游戏过关。

Figure4

■制作应用程序

现在我们按照以下顺序制作应用程序。

1.类结构

2.变量、常量的定义

3.查看图片和音效

4.使图片运动

5.球的反弹

6.音乐的播放

1.类结构

下表内容是类结构。(表5)

表5

2.变量、常量的定义

将下面的应用变量、定量作为BlockCanvas例子的属性并定义。(ex.4)

//状态设定

privateintstate;//状态

privatefinalintACTIVE=1;

privatefinalintGAME_OVER=2;

privatefinalintCLEAR=3;

//彩球的设定

privatefinalintBLOCK_H=7;//彩球横向的个数

privatefinalintBLOCK_V=5;//彩球纵向的个数

privatefinalintBLOCK_WIDTH=getWidth()/BLOCK_H;

privatefinalintBLOCK_HEIGHT=BLOCK_WIDTH/2;

privatebooleanblock[][]=newboolean[BLOCK_H][BLOCK_V];

privateintblockCount;//彩球个数

//小棒的设定

privatefinalintBAR_HEIGHT=11;

privatefinalintBAR_WIDTH=23;

privateintbarX=0;

privateintbarY=getHeight()-BAR_HEIGHT;

privateintbarMovCodeExample=0;

//球的设定

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的构造函数内分别读取小棒、球、彩球的图片。(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,读取彩球图片并描画在画面中。用旗表示彩球的状态。保持原来的排列。将一个一个的彩球使用原来的排列并计算出坐标,安排在画面中。(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

图片安装完成后出现如下画面。

4.使图片运动

接下来使用线程和按键事件移动球和小棒。为了使用线程就得在AudioCanvas类中安装Runnable接口、记述run方法。使用球和小棒定义的移动速度分别变化球和小棒的坐标。此外,小棒的移动速度根据按键处理而变化。以下记述了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--;

彩球立刻就破碎。彩球破碎后余下的彩球数量blockCount将有所减少。

另外,球掉落时,改变游戏状态后游戏结束。

/**

//使球运动

*/

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()){//球落下后

//游戏结束

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

·清除检查

至此安装完毕游戏就有雏形了。但是,在现在的程序中即使彩球全部消失,游戏也不能清除。那么,球与彩球相撞时,数出彩球的剩余数。当该数值为0时,则游戏清除。以下记述的是该处理。(ex.12)

//清除彩球

block[i][j]=false;

blockCount--;

ballMoveY*=-1;

//播放音效

blockSound.play();

//检查游戏清除

if(blockCount==0){

state=CLEAR;

}

ex.12

6.音乐播放

在本讲中的泡泡龙游戏的应用程序中最好使用BGM和音效。游戏开始的同时演奏BGM,音效则是球在碰壁、碰小棒反弹时,以及彩球破碎时才播放的。

·读取

用BlockApplication构造函数读取音乐数据。而且,这里的BGM能够循环播放,所以可以使用音频事件处理。(ex.13)

[!--empirenews.page--]

//声音数据的读取

bgm=Media.getAudioClip("/bgm.mid");//背景音乐

ballSound=Media.getAudioClip("/ball.mid");//球反弹后的音效

blockSound=Media.getAudioClip("/block.mid");//球破碎的音效

bgm.addAudioListener(this);//增加AudioListener

ex.13

·播放

读取音乐数据后,接下来进行播放。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()){//球落下后

//游戏结束

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)

运行结果如下所示。

游戏进行中游戏结束

游戏结束

总结

在本讲的讲解中能够自由播放音乐数据了。因此,能够制作成创造性的应用程序。但是,扩展应用程序时,不能保存高分、数据等。在下讲我们将学习如何使用固定存储器保存数据的方法。

查看png格式的画面文件

播放smf格式的音乐文件

http通信,socket通信

逆光、双感光板控制

Sprite功能

ImageMap功能

各种各样的制图扩展功能

3D引擎

由于N820具有256Kbyte的较大存储空间,所以能制作容量稍大、自由度较高的手机应用程序。另外,也能制作对应http、socket通信的自由度较高网络应用程序。因此,也能够搭载3D引擎、3D描画。而且还能安装N800对应的Sprite功能、ImageMap功能的描画功能。

■与N800的比较

下表是N800和N820的比较。(表6)

项目N800N820

显示屏尺寸180x162(纵x横)255x240(纵x横)

JAD文件尺寸最大2KB最大2KB

JAR文件尺寸最大50KB最大1MB

RMS尺寸最大10KB最大10KB

记录存储数量最大3records最大3records

通信协议只有httpHTTPandsocket

画像文件PNGPNG

音乐文件SMF(format0)最大10KbyteSMF(format0)最大10Kbyte

表6

■NECN820Application模拟器

下面是模拟N820工作的模拟器,称为「NEC820ApplicationEmulator」。与以前我们所介绍的「NECN800ApplicationEmulator」在外观上没有什么区别。(図5)

图5

打开模拟器,就是现在的手机画面表示。与N800相比,手机设计多少有些变化,手机的内显示屏变大了。下图是用N820ApplicationEmulator制作的“泡泡龙”游戏画面。“泡泡龙”游戏由于是假定在N800的屏幕上应用的。因此画面尺寸要比N820中的内屏尺寸稍小。因此,彩球之间存有空隙。(図6)

図6

■总结

N820的优点是具有256Kbyte的大容量存储空间,而且使用3D图表引擎、3D图表应用程序、能够制作成对应socket通信的TCP/IP网络应用程序。对于应用开发者而言,N820是一部制作JAVA应用程序非常有价值的终端。对于寻求高级机种的用户而言,应该是一部高精细画面、高功能的极大满足用户需要的终端。今后N820的用户应该会大幅度增加的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭