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();
时间: 2024-10-14 16:52:28

Android左右声道的控制的相关文章

Android 应用开发耗电量控制。。

当程序启动手机越多的模块,那耗电就越快 当你的程序运行时只占用CPU的时候,这时候耗电量是最少的. 当然这时候如果cpu的运行速度很慢那是最好的.. 程序耗电量控制首要从下面3个方面抓起: 1.频繁的I/O操作(启动本地外部存储模块和网络通信模块) 2.同时开过多的线程运行(cpu模块). 3.频繁的内存分配以及产生的内存碎片(频繁使用内存模块) 减少I/O操作: 判断是不是连上了wifi或者是不是存在可用的网络,假如不符合条件,就不要发动网路通信. 使用压缩来减少网络中传输的数据. 在读写本地

Android学习之------SeekBar(控制wav音频的声音)

使用SeekBar调节声音 SeekBar控件其实就是一个高级点的进度条,就像我们在听歌,看电影用的播放器上的进度条一样,是可以拖动的,可以改变进度的一个进度条控件! SeekBar常用属性: android:max[integer]//设置拖动条的最大值 android:progress[integer]//设置当前的进度值 android:secondaryProgress[integer]//设置第二进度,通常用做显示视频等的缓冲效果 android:thumb[drawable]//设置

[Android] [Java] Process 创建+控制+分析 经验浅谈

无论是Android亦或者Java中或多或少需要调用底层的一些命令,执行一些参数: 此时我们需要用到Java的Process来创建一个子进程,之所以是子进程是因为此进程依赖于发起创建请求的进程,如果发起者被Kill那个子进程也将Kill. 对于Process相信使用过的朋友一定不会陌生,它具有如下特点: 1.创建简单 2.控制难 3.容易导致无法创建子进程 4.如果是多线程那么很有可能造成内存溢出 以上现象如果你只是偶尔使用一次,创建一个进程或许你什么都没有感觉到,但是如果你使用了多线程,进行了

ZigBee与Android的智能家居控制系统设计

智能家居是在传统住宅的基础上,利用现代科学技术,诸如网络通信.安全防范.自动控制.音视频等技术将家居生活有关的各种家居设施集成,构成的高效.便利.舒适.节能环保的家居环境.随着无线移动网络的快速布局,现代的智能家居只要有一个无线智能设备,即能通过客户端实时查看到住宅中的一切动态.在目前,智能家居控制系统中,有基于面板和红外遥控器或蓝牙的智能家居控制终端解决方案,也有完全基于PC机的智能家居控制终端解决方案,同时还有采用手机作为家居控制终端,利用GSM电话网络通信,实现短信或者语音控制.而以上多种

Android 下led 的控制

首先说一下我的开发环境,硬件环境开发板使用的是全志的CQA83T板子,Android开发是windows下的eclipse.关于Android下控制led,主要有两大部分,一是Android程序,二是Linux驱动开发.Android部分的开发肯定要使用Android ndk,jni编程,通过jni来调用Linux下的C函数从而控制led设备.关于ndk的安装,和简单使用我在另外的博客里面已经写了,有兴趣的可以自己看看.这篇博客住要是讲一下Android部分的开发,这里默认led驱动正常. 先看

Android开发Git版本号控制,究竟哪些文件不要提交

安卓开发的小伙伴们.想必大家在创建项目的时候,都有为project的哪些文件须要提交的git的版本号控制而犯愁过吧.深怕有些文件提交了影响团队其它成员的更新使用.尤其是把一些暂时的编译构建文件给提交了,被Team Leader骂的不要不要的. 下面就是不须要经过git版本号控制来提交的文件和文件夹,详细代码能够查看project的.gitignore文件: IntelliJ IDEA(IDE相关的设置) .idea *.iml *.ipr *.iws Gradle(gradle相关的) .gra

[Java][Android][Process] Process 创建+控制+分析 经验浅谈

不管是Android亦或者Java中或多或少须要调用底层的一些命令.运行一些參数: 此时我们须要用到Java的Process来创建一个子进程.之所以是子进程是由于此进程依赖于发起创建请求的进程,假设发起者被Kill那个子进程也将Kill. 对于Process相信使用过的朋友一定不会陌生,它具有例如以下特点: 1.创建简单 2.控制难 3.easy导致无法创建子进程 4.假设是多线程那么非常有可能造成内存溢出 以上现象假设你仅仅是偶尔使用一次,创建一个进程也许你什么都没有感觉到,可是假设你使用了多

Android实例-闪光灯的控制(XE8+小米2)

1 unit Unit1; 2 3 interface 4 5 uses 6 System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 7 FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Media, 8 FMX.Controls.Presentation, FMX.StdCtrls; 9 10 type 11

android测试中如何控制wifi

在进行robotium 编写自动化测试用例的时候,有些case 需要控制网络来进行验证,这个时候就需要来回关闭,打开wifi 连接.在网上搜了一下,使用方法比较简单,整理如下: 1.首先需要在app中的 AndroidManifest.xml 文件中申请wifi的访问权限,配置如下: Xml代码   <!--申请操作wifi的权限 --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE&qu