8.1.2 生成样本
通过使用一点算术,就能够利用算法来创建这些样本,例如可以重新生成经典的正弦波,以下示例产生了一个440Hz的正弦波。
1 package com.nthm.androidtestActivity; 2 3 import com.nthm.androidtest.R; 4 import android.app.Activity; 5 import android.media.AudioFormat; 6 import android.media.AudioManager; 7 import android.media.AudioTrack; 8 import android.os.AsyncTask; 9 import android.os.Bundle; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.widget.Button; 13 14 public class AudioSynthesis extends Activity implements OnClickListener { 15 private Button startSound; 16 private Button endSound; 17 private AudioSynthesisTask audioSynth; 18 private boolean keepGoing=false; 19 private float synth_frequency=440;//440Hz , Middle A 20 @Override 21 protected void onCreate(Bundle savedInstanceState) { 22 super.onCreate(savedInstanceState); 23 setContentView(R.layout.audiosynthesis); 24 startSound=(Button) findViewById(R.id.StartSound); 25 startSound.setOnClickListener(this); 26 endSound=(Button) findViewById(R.id.EndSound); 27 endSound.setOnClickListener(this); 28 29 endSound.setEnabled(false); 30 } 31 32 @Override 33 protected void onPause() { 34 super.onPause(); 35 keepGoing=false; 36 endSound.setEnabled(false); 37 startSound.setEnabled(true); 38 } 39 40 @Override 41 public void onClick(View v) { 42 if(v==startSound){ 43 keepGoing=true; 44 audioSynth=new AudioSynthesisTask(); 45 audioSynth.execute(); 46 endSound.setEnabled(true); 47 startSound.setEnabled(false); 48 }else if(v==endSound){ 49 keepGoing=false; 50 endSound.setEnabled(false); 51 startSound.setEnabled(true); 52 } 53 } 54 private class AudioSynthesisTask extends AsyncTask<Void, Void, Void>{ 55 56 @Override 57 protected Void doInBackground(Void... params) { 58 final int SAMPLE_RATE=11025; 59 int minSize=AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); 60 AudioTrack audioTrack=new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM); 61 audioTrack.play(); 62 short[] buffer=new short[minSize]; 63 float angular_frequency=(float)(2*Math.PI)*synth_frequency/SAMPLE_RATE; 64 float angle=0; 65 while(keepGoing){ 66 for(int i=0;i<buffer.length;i++){ 67 buffer[i]=(short)(Short.MAX_VALUE*((float) Math.sin(angle))); 68 angle+=angular_frequency; 69 } 70 audioTrack.write(buffer, 0, buffer.length); 71 } 72 return null; 73 } 74 } 75 }
下面是用于活动的布局XML文件:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" 5 > 6 7 <Button 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:id="@+id/StartSound" 11 android:text="Start Sound"/> 12 <Button 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:id="@+id/EndSound" 16 android:text="End Sound"/> 17 18 </LinearLayout>
通过更改synth_frequency,我们可以重新生成任何其他想要的频率。当然,更改用于生成值的函数同样也将改变声音。我们可能希望尝试将样本固定位Short.MAX_VALUE或Short.MIN_VALUE,以实现一个快速而平稳的方波示例。
当然,这仅仅是在Android上处理音频合成的浅显内容。由于使用AudioTrack能够播放原始的PCM样本,因此在Android上几乎可以使用任何可用来生成数字音频的技术,不过这需要考虑到处理器的速度和内存的限制。
下面是一个示例应用程序,其采用了一些第4章中介绍的技术来跟踪手指在触摸屏上的位置,同时采用上述示例代码生成音频。此应用程序将生成音频,同时根据用户手指在触摸屏的x轴上的位置选择频率。
1 package com.nthm.androidtestActivity; 2 3 import com.nthm.androidtest.R; 4 import android.app.Activity; 5 import android.media.AudioFormat; 6 import android.media.AudioManager; 7 import android.media.AudioTrack; 8 import android.os.AsyncTask; 9 import android.os.Bundle; 10 import android.view.MotionEvent; 11 import android.view.View; 12 import android.view.View.OnTouchListener;
活动将实现OnTouchListener,从而可以跟踪触摸的位置。
1 public class FingerSynthesis extends Activity implements OnTouchListener {
就像之前的示例一样,本示例将使用AsyncTask,以提供一个生成和播放音频样本的线程。
1 private AudioSynthesisTask AudioSynth;
需要一个基准音频频率,当手指放在x轴上的0位置时播放该频率。这将是播放的最低频率。
1 private static final float BASE_FREQUENCY=440;
随着手指的移动会变浮点数synth_frequency。在应用启动应用程序时,将它设置为BASE_FREQUENCY。
1 private float synth_frequency=BASE_FREQUENCY;
使用play布尔值来确定何时应该实际的播放音频。该布尔值将由触摸事件控制。
1 private boolean play=false; 2 @Override 3 protected void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 setContentView(R.layout.fingersynthesis); 6 View mainView=findViewById(R.id.MainView);
在布局中只有一个条目,即包含ID MainView的LinearLayout。获得该条目的一个引用,同时将OnTouchListener注册为当前活动。这样,当用户触摸屏时将调用活动的onTouch方法。
1 View mainView=findViewById(R.id.MainView); 2 mainView.setOnTouchListener(this); 3 4 AudioSynth=new AudioSynthesisTask(); 5 AudioSynth.execute(); 6 } 7 8 @Override 9 protected void onPause() { 10 super.onPause(); 11 play=false; 12 finish(); 13 }
当用户开始触摸,停止触摸或在屏幕上移动手指时,都将调用onTouch方法,它根据用户的操作将play布尔值设置为true或false。这将控制是否生成音频样本。该方法还将跟踪用户手指在触摸屏x轴上的位置,从而相应的调整synth_frequency变量。
1 @Override 2 public boolean onTouch(View v, MotionEvent event) { 3 int action=event.getAction(); 4 switch (action) { 5 case MotionEvent.ACTION_DOWN: 6 play=true; 7 synth_frequency=event.getX()+BASE_FREQUENCY; 8 break; 9 case MotionEvent.ACTION_MOVE: 10 play=true; 11 synth_frequency=event.getX()+BASE_FREQUENCY; 12 break; 13 case MotionEvent.ACTION_UP: 14 play=false; 15 break; 16 case MotionEvent.ACTION_CANCEL: 17 18 break; 19 default: 20 break; 21 } 22 return true; 23 } 24 private class AudioSynthesisTask extends AsyncTask<Void, Void, Void>{ 25 26 @Override 27 protected Void doInBackground(Void... params) { 28 final int SAMPLE_RATE=11025; 29 int minSize=AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); 30 AudioTrack audioTrack=new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM); 31 audioTrack.play(); 32 short[]buffer=new short[minSize]; 33 float angle=0;
最后,在AudioSynthesisTask生成音频的循环中,检查play布尔值,同时进行计算以根据synth_frequency变量(可以根据用户的手指位置对这个变量进行更改)生成音频样本。
1 while(true){ 2 if(play){ 3 for(int i=0;i<buffer.length;i++){ 4 float angular_frequency=(float)(2*Math.PI)*synth_frequency/SAMPLE_RATE; 5 buffer[i]=(short)(Short.MAX_VALUE*((float) Math.sin(angle))); 6 angle+=angular_frequency; 7 } 8 audioTrack.write(buffer, 0, buffer.length); 9 }else{ 10 try { 11 Thread.sleep(50); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 } 17 } 18 } 19 }
下面是布局XML
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" 5 android:id="@+id/MainView" 6 > 7 8 </LinearLayout>
这个示例部分演示了AudioTrack类的能力和灵活性。由于可以通过算法生成音频,因此能够使用任何想要的方法来决定音频的特征(此示例利用了音频的声调或者频率)。
posted on 2014-09-02 16:17 宁静致远,一览众山小 阅读(311) 评论(0) 编辑 收藏 举报