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类的能力和灵活性。由于可以通过算法生成音频,因此能够使用任何想要的方法来决定音频的特征(此示例利用了音频的声调或者频率)。

你可能感兴趣的:(生成)