MainActivity.java :
1. 主类,播放方波(提供byteDate)和正弦波。
2. 提供三个按钮分别为 短方波(btnPlayFS)、长方波(btnPlayFL)、正弦波(btnPlayZ),其功能如下:
2.1. btnPlayFS /btnPlayFL 单击事件 :实例化WaveOutF(方波类),调用sendByteDate(byte[],length)方法 传送byteDate和有效长度,
如果已经实例化则直接调用sendByteDate方法;
2.2. btnPlayZ 单击事件 :实例化WaveOutZ(正弦波类),调用palyWaveZ()方法,播放正弦波;
2.3. btnStopZ 单击事件:调用WaveOutZ下的colseWaveZ()方法 ,停止正弦波的播放。
3. 实现onKeyDown构造方法:当用户点击返回按钮时注销已经打开的WaveOutF和WaveOutZ占用的资源,结束程序。
WaveOutF.java :
1.方波类,实现播放方波。
2. 实例化 该类时,构造AudioTrack对象, 设置 类型:系统声音,采样率:44100,声道:单声道(右),
采样精度:16bit,缓冲区大小:4096,模式:流媒体。
3.方法:
3.1. sendByteDate(byte[] byteDate,int len)将byteDate通过getShortDate()方法转换为音频源,
开一个线程(audioTrackThread)播放。
3.2. getShortDate()先 将byteDate通过getstrBinary(byte[] date, int lenght)方法转为转为2进制字符串,
定义一个short[58*字符串长度]数组 ,再将字符串通过String.charAt(int i)检索出“1”则连续赋58个32767给数组,
否则连续赋58个-32768给数组,返回数组(音频数据)。
3.3. getstrBinary(byte[] date, int lenght) 将字节编码转为2进制字符串并补0,返回2进制字符串。
3.4. playWaveF(short[] m_bitDateF) 写入音频数据给音频硬件进行播放,该方法是阻塞的。
3.5. closeAudioTrack() 停止播放音频数据。
4. 线程:AudioTrackThread 调用playWaveF方法。
WaveOutZ.java :
1. 正弦波类,实现播放正弦波。
2. 实例化 该类时,构造AudioTrack对象 ,设置 类型:系统声音,采样率:44100,声道:单声道(左),
采样精度:16bit,缓冲区大小:2*4096,模式:流媒体。
3.方法:
3.1. palyWaveZ() 开一个线程audioTrackZThread 播放正弦波;
3.2. colseWaveZ() 通过标志位isRunning 关闭线程
4.线程:AudioTrackZThread 定义一个short[44100]数组 通过算法:32767*Sin((2*3.1415926*8250.0/44100.0)*i)给数组赋值,
写入音频数据到音频硬件进行播放
package scl.wavedome2; import scl.waveout.WaveOutF; import scl.waveout.WaveOutZ; import android.app.Activity; import android.os.Bundle; import android.view.KeyEvent; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { private Button btnPlayFS;// 方波按钮 private Button btnPlayFL; private Button btnStopZ;// 停止正弦波 private Button btnPlayZ;// 正弦波按钮 private WaveOutF woF;// 方波类 private WaveOutZ woZ;// 正弦波类 private byte[] byteDate; private int len; private byte[] byteDate1; private int len1; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); byteDate = new byte[] { 0x71, 0x22, 0x42 }; len = byteDate.length; byteDate1 = new byte[] { 0x11, 0x22, 0x42, 0x1A, 0x24, 0x13, 0x0a }; len1 = byteDate1.length; } private void init() { btnPlayFS = (Button) findViewById(R.id.btn_play); btnPlayFL = (Button) findViewById(R.id.btn_play1); btnPlayZ = (Button) findViewById(R.id.btn_play2); btnStopZ = (Button) findViewById(R.id.btn_stop); btnStopZ.setEnabled(false); btnPlayFS.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (woF == null) { woF = new WaveOutF(); } woF.sendByteDate(byteDate, len); } }); btnPlayFL.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (woF == null) { woF = new WaveOutF(); } woF.sendByteDate(byteDate1, len1); } }); btnPlayZ.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { btnStopZ.setEnabled(true); btnPlayZ.setEnabled(false); if (woZ == null) { woZ = new WaveOutZ(); } woZ.palyWaveZ(); } }); btnStopZ.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (woZ != null) { woZ.colseWaveZ(); btnPlayZ.setEnabled(true); btnStopZ.setEnabled(false); } } }); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (woZ != null) { woZ.colseWaveZ(); woZ = null; } if (woF != null) { woF.closeAudioTrack(); woF = null; } this.finish(); } return super.onKeyDown(keyCode, event); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override protected void onStop() { super.onStop(); } }
package scl.waveout; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; import android.util.Log; public class WaveOutF { // private ExecutorService pool = Executors.newSingleThreadExecutor(); private short max_iAmp = Short.MAX_VALUE;// 方波幅度<-32767 ~ 32767> private short min_iAmp = Short.MIN_VALUE; private byte[] m_date;// 得到的数据 private int m_lenght;// 得到数据的有效长度 private int m_iFangBo = 58; private int sampleRateInHz = 44100; // 采样率 private int m_channel = AudioFormat.CHANNEL_CONFIGURATION_MONO;// 声道 :单声道 private int m_sampBit = AudioFormat.ENCODING_PCM_16BIT;// 采样精度 :16bit private AudioTrack audioTrackF; private short[] m_bitDateF ; // AudioTrack创建所需的缓冲区大小 final int bufferSize = AudioTrack.getMinBufferSize(sampleRateInHz, m_channel, m_sampBit); public WaveOutF() { System.out.println(bufferSize+"---------------"); audioTrackF = new AudioTrack(AudioManager.STREAM_SYSTEM, sampleRateInHz, m_channel, m_sampBit, bufferSize, AudioTrack.MODE_STREAM); audioTrackF.setStereoVolume(0.0f, 1.0f);// 设置左右声道音量 // audioTrackF.play(); } public void sendByteDate(byte[] byteDate, int len) { if(audioTrackF == null){ Log.i("Fangbo", " null"); } this.m_date = byteDate; this.m_lenght = len; m_bitDateF = null; m_bitDateF = getShortDate();// audioTrack 播放时的数据 Thread audioTrackFThread = new Thread(new AudioTrackFThread()); // pool.execute(audioTrackThread); audioTrackFThread.start(); this.m_date = null; this.m_lenght = 0; } // 通过byteDate转为short型 的声音数据 private short[] getShortDate() { int j = 0; String strBinary = getstrBinary(this.m_date, this.m_lenght); int len = strBinary.length(); int m_bitDateSize = len * m_iFangBo; short[] m_pRightDate = new short[m_bitDateSize]; for (int i = 0; i < len; i++) { int ct = m_iFangBo; if (strBinary.charAt(i) == '1') { while (ct-- > 0) { // m_pRightDate[j++] = min_iAmp; m_pRightDate[j++] = max_iAmp; } } else { while (ct-- > 0) { // m_pRightDate[j++] = max_iAmp; m_pRightDate[j++] = min_iAmp; } } } return m_pRightDate; } // 将一个字节编码转为2进制字符串 private String getstrBinary(byte[] date, int lenght) { StringBuffer strDate = new StringBuffer(lenght * 8); for (int i = 0; i < lenght; i++) { String str = Integer.toBinaryString(date[i]); // System.out.println("str:"+str); while (str.length() < 8) { str = '0' + str; } strDate.append(str); } Log.i("strDate: " ,strDate+""); return strDate.toString(); } private void playWaveF(short[] m_bitDateF) { audioTrackF.play(); audioTrackF.write(m_bitDateF, 0, m_bitDateF.length);// 该方法是阻塞的 } public void closeAudioTrack() { if (audioTrackF != null) { audioTrackF.stop(); audioTrackF.release(); } } class AudioTrackFThread implements Runnable{ @Override public void run() { playWaveF(m_bitDateF); audioTrackF.flush(); // Log.i("Thread","run over"); } } }
package scl.waveout; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; public class WaveOutZ { private int sampleRateInHz = 44100; // 采样率 private int mChannel = AudioFormat.CHANNEL_CONFIGURATION_MONO;// 声道 :单声道 private int mSampBit = AudioFormat.ENCODING_PCM_16BIT;// 采样精度 :16bit private AudioTrackZThread audioTrackZThread; private boolean isRunning = false; private AudioTrack audioTrackz; public WaveOutZ() { int bufferSize = AudioTrack.getMinBufferSize(sampleRateInHz, mChannel, mSampBit); audioTrackz = new AudioTrack(AudioManager.STREAM_SYSTEM, sampleRateInHz, mChannel, mSampBit, bufferSize * 2, AudioTrack.MODE_STREAM); audioTrackz.setStereoVolume(1.0f, 0.0f); audioTrackz.play(); } public void palyWaveZ() { audioTrackZThread = new AudioTrackZThread(); audioTrackZThread.start(); } public void colseWaveZ() { if (audioTrackz != null) { if (!AudioTrackZThread.interrupted()) { isRunning = false; } // audioTrackz.stop(); // audioTrackz.release(); } } class AudioTrackZThread extends Thread { private short m_iAmp = Short.MAX_VALUE; private short m_bitDateZ[] = new short[44100]; private double x = 2.0 * Math.PI * 8250.0 / 44100.0; @Override public void run() { isRunning = true; for (int i = 0; i < 44100; i++) { m_bitDateZ[i] = (short) (m_iAmp * Math.sin(x * i)); } int m_bitDateZSize = m_bitDateZ.length; do { audioTrackz.write(m_bitDateZ, 0, m_bitDateZSize); // Log.v("isRunn", isRunning+""); } while (isRunning); super.run(); } } }