Android左右聲道的控制


效果圖

效果圖

源碼

源碼下載,請先移步Android左右聲道的控制

我這里主要是用到了AudioTrack實現的左右聲道的控制,手機一般都只有兩個聲道,即左聲道和右聲道,我們在輸出的時候可以選擇單聲道,也可以選擇雙聲道(立體聲)。

查看了AudioTrack的API,提供了play()pause()stop()write()等一系列的方法。
通過write()方法,可以實現將音頻數據發送出去(播放出來)。

AudioTrack對象的構造

有三個構造方法

AudioTrack (int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)
AudioTrack (int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode, int sessionId)
AudioTrack (AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int mode, int sessionId)

主要參數有如下幾個

  • streamType:以什么形式播放

    • STREAM_VOICE_CALL
    • STREAM_SYSTEM
    • STREAM_RING
    • STREAM_MUSIC
    • STREAM_ALARM
    • STREAM_NOTIFICATION
  • sampleRateInHz:采樣率

  • channelConfig:聲道

    • AudioFormat.CHANNEL_OUT_MONO:輸出單聲道音頻數據
    • AudioFormat.CHANNEL_OUT_STEREO:輸出雙聲道音頻數據(立體聲)
  • audioFormat:音頻數據格式

  • mode:緩沖模式

    • MODE_STATIC:一次性將音頻載入以后再播放
    • MODE_STREAM:以流的形式,加載一點就播放一點

把channelConfig的相關參數都看了一遍,沒發現有可以指定向某聲道發送數據的,只能通過AudioFormat.CHANNEL_OUT_MONOAudioFormat.CHANNEL_OUT_STEREO選擇是輸出單聲道的音頻數據還是雙聲道的音頻數據。

左右聲道控制

構造的時候不能選擇指定聲道輸出音頻,但是有這樣一個方法

setStereoVolume(float leftGain, float rightGain)

可以通過把某一個聲道的音量設置到最小,達到只想某個聲道輸出音頻的效果。
我自己也有點”呵呵“,但是也沒有發現還有別的方法可以實現這樣的效果。

這個方法還有一點小問題,在個別手機上,即使將某個聲道的聲音設置到了最小,也還是會有一點聲音,這個我也還沒有搞清楚為什么,個人猜測可能和手機硬件有關系。

封裝

我這里的緩沖模式使用的MODE_STREAM的形式,以流的形式播放,因為這個邏輯要稍微復雜一點,尤其是暫停以后再繼續播放的位置。

package kong.qingwei.androidsoundmanagerdemo;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
* Created by kqw on 2016/8/26.
* 播放音樂的線程
*/

public class PlayThread extends Thread {

// 采樣率
private int mSampleRateInHz = 16000;
// 單聲道
private int mChannelConfig = AudioFormat.CHANNEL_OUT_MONO;
// 雙聲道(立體聲)
// private int mChannelConfig = AudioFormat.CHANNEL_OUT_STEREO;

private static final String TAG = "PlayThread";
private Activity mActivity;
private AudioTrack mAudioTrack;
private byte[] data;
private String mFileName;

public PlayThread(Activity activity, String fileName) {
mActivity = activity;
mFileName = fileName;

int bufferSize = AudioTrack.getMinBufferSize(mSampleRateInHz, mChannelConfig, AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
mSampleRateInHz,
mChannelConfig,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
AudioTrack.MODE_STREAM);
}

@Override
public void run() {
super.run();
try {
if (null != mAudioTrack)
mAudioTrack.play();

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
InputStream inputStream = mActivity.getResources().getAssets().open(mFileName);

// 緩沖區
byte[] buffer = new byte[1024];
// 播放進度
int playIndex = 0;
// 是否緩沖完成
boolean isLoaded = false;
// 緩沖 + 播放
while (null != mAudioTrack && AudioTrack.PLAYSTATE_STOPPED != mAudioTrack.getPlayState()) {
// 字符長度
int len;
if (-1 != (len = inputStream.read(buffer))) {
byteArrayOutputStream.write(buffer, 0, len);
data = byteArrayOutputStream.toByteArray();
Log.i(TAG, "run: 已緩沖 : " + data.length);
} else {
// 緩沖完成
isLoaded = true;
}

if (AudioTrack.PLAYSTATE_PAUSED == mAudioTrack.getPlayState()) {
// TODO 已經暫停
}
if (AudioTrack.PLAYSTATE_PLAYING == mAudioTrack.getPlayState()) {
Log.i(TAG, "run: 開始從 " + playIndex + " 播放");
playIndex += mAudioTrack.write(data, playIndex, data.length - playIndex);
Log.i(TAG, "run: 播放到了 : " + playIndex);
if (isLoaded && playIndex == data.length) {
Log.i(TAG, "run: 播放完了");
mAudioTrack.stop();
}

if (playIndex < 0) {
Log.i(TAG, "run: 播放出錯");
mAudioTrack.stop();
break;
}
}
}
Log.i(TAG, "run: play end");
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 設置左右聲道平衡
*
* @param max 最大值
* @param balance 當前值
*/

public void setBalance(int max, int balance) {
float b = (float) balance / (float) max;
Log.i(TAG, "setBalance: b = " + b);
if (null != mAudioTrack)
mAudioTrack.setStereoVolume(1 - b, b);
}

/**
* 設置左右聲道是否可用
*
* @param left 左聲道
* @param right 右聲道
*/

public void setChannel(boolean left, boolean right) {
if (null != mAudioTrack) {
mAudioTrack.setStereoVolume(left ? 1 : 0, right ? 1 : 0);
mAudioTrack.play();
}
}

public void pause() {
if (null != mAudioTrack)
mAudioTrack.pause();
}

public void play() {
if (null != mAudioTrack)
mAudioTrack.play();
}

public void stopp() {
releaseAudioTrack();
}

private void releaseAudioTrack() {
if (null != mAudioTrack) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
}

使用

從頭開始播放

mPlayThread = new PlayThread(this, "tts1.pcm");
mPlayThread.start();

暫停

mPlayThread.pause();

暫停后繼續播放

mPlayThread.play();

停止播放

mPlayThread.stopp();
mPlayThread = null;

左右聲道控制

// 禁用左聲道(右聲道同理)
mPlayThread.setChannel(false, true);

向左右聲道單獨輸出不同的音頻數據

也是一個很”呵呵“的做法,但是依然還沒有找到更好的方法。
構造兩個AudioTrack對象,分別輸出兩個音頻,一個禁用左聲道,一個禁用右聲道,達到預期效果。

mChannelLeftPlayer = new PlayThread(this, "tts1.pcm");
mChannelRightPlayer = new PlayThread(this, "tts2.pcm");

mChannelLeftPlayer.setChannel(true, false);
mChannelRightPlayer.setChannel(false, true);

mChannelLeftPlayer.start();
mChannelRightPlayer.start();

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com