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  宁静致远,一览众山小  阅读(309)  评论(0编辑  收藏  举报

导航