《智能设备艺术、科技、文化作品实例开发与设计》android开发系列介绍---1.1琴类作品:钢琴

第一章    琴类作品

介绍安卓作品开发,先从我们日常熟悉的琴棋书画开始吧,本章节就介绍乐器的开发,想想乐器爱好者,音乐爱好者可以把世界上几乎所有的乐器装进手机,用便携的方式来实现一定是件开心的事情吧。

世界乐坛三大乐器为:

钢琴,号称乐器之王,它的音域宽广,音色清澈,演奏技术成熟,表现力丰富。

小提琴:号称乐器皇后,她的音色高贵,富幻想色彩,多把小提琴的齐奏音色更为迷人,表现力非常丰富。

古典吉他号称乐器王子,他的音色忧郁迷人,可控性极强,表现力非常丰富。

这三件乐器唯一出现在管弦乐队的是小提琴,其他两件由于本身即可作出完善的和声而都没有

被应用于管弦乐队。特别适合独奏,那我们就从手机上实现这三大乐器开始吧。

 

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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值