第一章 琴类作品
介绍安卓作品开发,先从我们日常熟悉的琴棋书画开始吧,本章节就介绍乐器的开发,想想乐器爱好者,音乐爱好者可以把世界上几乎所有的乐器装进手机,用便携的方式来实现一定是件开心的事情吧。
世界乐坛三大乐器为:
钢琴,号称乐器之王,它的音域宽广,音色清澈,演奏技术成熟,表现力丰富。
小提琴:号称乐器皇后,她的音色高贵,富幻想色彩,多把小提琴的齐奏音色更为迷人,表现力非常丰富。
古典吉他号称乐器王子,他的音色忧郁迷人,可控性极强,表现力非常丰富。
这三件乐器唯一出现在管弦乐队的是小提琴,其他两件由于本身即可作出完善的和声而都没有
被应用于管弦乐队。特别适合独奏,那我们就从手机上实现这三大乐器开始吧。
1.1钢琴开发
钢琴是音乐中的乐器之王, 钢琴拥有最广的音域,最丰富的表现力,最华丽而高难度的技巧,又能和任何乐器有机而合理的结合演奏出优美而漂亮的音乐,适合任何场合任何环境的表现,自然深受大众的喜爱,自然成为最流行的乐器之首。
而用手机或平板模拟出钢琴效果从易到难的思路,单指按下发志,到多指弹奏来一步一步实现。
1.1.1手机利用MediaPlayer发声
在Android系统中,对计算机中大部分图像和音视频格式支持,如支持的格式有H.264、VC-1、MPEG-2、MPEG4、XviD/DivX、MP4、M4V、AVI、VOB、WMV、3GP、MKV等所有主流格式,而对钢琴发声,我采用了最通用的的MP3格式作音效。
播放工具则用Android中很重要也最为复杂的媒体播放器---MediaPlayer.
Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的。
MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制。通过读取RES文件夹中的音效文件来实现弹钢琴。
步骤1.新建一个空白HELLOWORLD工程,
步骤2.在res文件夹中新建raw文件夹,并将音效mp3文件拷入
步骤3. onCreate中添加两行代码,定义mMediaPlayer01,并指定raw.m11中的mp3音效文件,按下F11运行程序,就能播放声音。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MediaPlayer mMediaPlayer01;
mMediaPlayer01 =MediaPlayer.create(MainActivity.this, R.raw.m11);mMediaPlayer01.start();
}
1.1.2钢琴画面的绘制
钢琴上有数十个弹奏键,我们可以添加按钮控件来实现按下弹起功能,也可以通过触屏onTouchEvent事件计算按下屏幕上画的键盘对应哪个位置,来判断是按下哪个琴键,来程序就是通过后一种方法来实现的。
1.那么首先就计算屏幕分辨率及onTouchEvent事件所响应的位置
DPI:每英寸像素数
简单的屏幕分辨率计算方法:
DisplayMetrics metrics = new DisplayMetrics();
Display display = this.getWindowManager().getDefaultDisplay();
display.getMetrics(metrics);
Log.e("display", "高:"+display.getHeight()+"宽:"+display.getWidth()+"屏幕密度比:"+metrics.density);
dp与px计算图(mdpi 1dp=1px):
Android手机屏幕标准 对应图标尺寸标准 屏幕密度 比例
xhdpi 1280*720 96*96 320 8
hdpi 480*800 72*72 240 6
mdpi 480*320 48*48 160 4
ldpi 320*240 36*36 120 3
我在这里把背景图放在hdpihdpi目录下,实现的分辨率为
1.定义一个全屏显示窗口,并强制横屏,setContentView(mAnimView);
public class MainActivity extends Activity {
MyView mAnimView = null;
MediaPlayer mMediaPlayer01;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 全屏显示窗口
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
//强制横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 显示自定义的游戏View
mAnimView = new MyView(this);
setContentView(mAnimView);
2绘制MyView
a. 在mdpi中添加背景图片与按钮图片,注意分辨率。
b. 建立class MyViewextends SurfaceView并设置每30帧刷新一次屏幕。
c. 在draw()中显示背景图片与按钮图片,并在屏幕上显示文本drawText("X值:", 0, 20, mPaint);用于察看调试参数。
public class MyView extends SurfaceViewimplements Callback,Runnable {
/**每30帧刷新一次屏幕**/
public static final int TIME_IN_FRAME = 30;
/** 游戏画笔 **/
Paint mPaint = null;
PaintmTextPaint = null;
SurfaceHolder mSurfaceHolder =null;
booleanmRunning =false;
/** 游戏画布 **/
Canvas mCanvas = null;
/**控制游戏循环**/
booleanmIsRunning =false;
/**游戏背景文件**/
private BitmapmbitmapBg;
private BitmapmbitmapKey;
public MyView(Context context) {
super(context);
/** 设置当前View拥有控制焦点 **/
this.setFocusable(true);
/** 设置当前View拥有触摸事件 **/
this.setFocusableInTouchMode(true);
/** 拿到SurfaceHolder对象 **/
mSurfaceHolder = this.getHolder();
/** 将mSurfaceHolder添加到Callback回调函数中 **/
mSurfaceHolder.addCallback(this);
/** 创建画布 **/
mCanvas = new Canvas();
/** 创建曲线画笔 **/
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mbitmapKey = BitmapFactory.decodeResource(this.getResources(), R.drawable.key);
/**加载游戏背景**/
mbitmapBg = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg);
}
private void Draw() {
intip=0;
/**绘制游戏背景**/
Shader mShader=newLinearGradient(0,0,100,100,
newint[]{Color.RED,Color.GREEN,Color.BLUE,Color.YELLOW},
null,Shader.TileMode.REPEAT);
mPaint.setShader(mShader);
mCanvas.drawBitmap(mbitmapBg,0,0,mPaint);
mCanvas.drawBitmap(mbitmapKey,110,110,mPaint);
mPaint.setTextSize(30);//设置字体大小
mCanvas.drawText("X值:", 0, 20,mPaint);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format,int width,
int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
/**开始游戏主循环线程**/
mIsRunning = true;
new Thread(this).start();
/**得到当前屏幕宽高**/
}
public void run() {
while (mIsRunning) {
/** 取得更新游戏之前的时间 **/
long startTime = System.currentTimeMillis();
/** 在这里加上线程安全锁 **/
synchronized (mSurfaceHolder) {
/** 拿到当前画布然后锁定 **/
mCanvas = mSurfaceHolder.lockCanvas();
Draw();
/** 绘制结束后解锁显示在屏幕上 **/
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
/** 取得更新游戏结束的时间 **/
long endTime = System.currentTimeMillis();
/** 计算出游戏一次更新的毫秒数 **/
int diffTime = (int) (endTime - startTime);
/** 确保每次更新时间为30帧 **/
while (diffTime <=TIME_IN_FRAME) {
diffTime = (int) (System.currentTimeMillis()- startTime);
/** 线程等待 **/
Thread.yield();
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}}
1.1.3弹钢琴的实现
可以发声后,接着就是让钢琴弹起来,一种方法通过多个按钮实现,一种是判断按下屏幕位置来判断具体播放哪个mp3音效文件来实现,我在设计程序的时候考虑到,钢琴的按键有很多,还利用计算按下屏幕位置来实现,可以少多控件,节省布局等因素,所以这里是利用第二种方法。
介绍一下钢琴的弹奏方法,演奏者一般是右手弹主旋侓,左手弹伴奏,这样就可以发出两个声部以上的乐曲。
我们先来实现乐曲的主旋侓弹奏,也就是单手指弹琴。
1. 在touch事件中取得各手指的触屏位置,及手指全抬起时设为较大负值,让界面上不显示琴键按下:
public boolean onTouchEvent(MotionEvent event)
{
p = event.getPointerCount();
for (i=0;i<p;i++) {
ry[i]=event.getX(i);
rx[i]=event.getY(i);
}
if (event.getAction()==MotionEvent.ACTION_UP)
{ for (i=0;i<5;i++){
ry[i]=-1100;rx[i]=-1100;} }
return true;
}
2. draw()中绘制按下的位置,并判断是否是按钢琴键有没变化
for (i=0;i<5;i++){
rk[i]=(int)(ry[i])/107+1;
if (rx[i]<100 &&rx[i]>10) {rk[i]=15;}
mCanvas.drawBitmap(mbitmapKey,ry[i]-22,rx[i]-22, mPaint);
if (rk[i]==rk2[i]){ rk3[i]++;}else {rk3[i]=0;}
rk2[i]=rk[i];
}
mCanvas.drawText("Key值:"+rk[0]+"屏幕y值:"+ry[0], 0, 20, mPaint);
3. 根据屏幕宽与按键比rk[i]=(int)(ry[i])/107+1; 计算出按了哪个钢琴键,让mMediaPlayer01发出相应的音效。
if ( rk3[0]==1){
mMediaPlayer01.release(); // mMediaPlayer02.release();
switch (rk[0])
{
case 1:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m05);mMediaPlayer01.start();break;
case 2:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m06);mMediaPlayer01.start();break;
case 3:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m07);mMediaPlayer01.start();break;
case 4:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m11);mMediaPlayer01.start();break;
case 5:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m12);mMediaPlayer01.start();break;
case 6:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m13);mMediaPlayer01.start();break;
case 7:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m14);mMediaPlayer01.start();break;
case 8:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m15);mMediaPlayer01.start();break;
case 9:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m16);mMediaPlayer01.start();break;
case 10:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m17);mMediaPlayer01.start();break;
case 11:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m21);mMediaPlayer01.start();break;
case 12:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m22);mMediaPlayer01.start();break;
case 13:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m23);mMediaPlayer01.start();break;
case 15:mMediaPlayer01 = MediaPlayer.create(MainActivity.this, R.raw.m225);mMediaPlayer01.start();break;
default:break;//System.out.println("Other Condition");
}
}
4. 多指多声部弹奏,定义多个mMediaPlayer,变让相应的触屏位置计算发声。
Myview()中定义
mMediaPlayer01 = new MediaPlayer();
mMediaPlayer02 = new MediaPlayer();
mMediaPlayer03 = new MediaPlayer();
mMediaPlayer04 = new MediaPlayer();
mMediaPlayer05 = new MediaPlayer();
draw()中 多个 if ( rk3[0]==1){
mMediaPlayer01.release(); // mMediaPlayer02.release();
switch (rk[0])
1.1.4运行分析及扩展思考
通过以上步骤,我们实现了下可以左右手齐奏,实现一个两声部的钢琴APP.
关于扩展思考,我提出是否可以滑动屏幕,让钢琴出现更多的声部。
最后给出完整的代码
视频介绍,声音有问题,有重录//www.le.com/ptv/vplay/25864621.html
-
顶
- 0
-
踩