0
点赞
收藏
分享

微信扫一扫

音视频之AudioTrack的使用


资料

音频播放AudioTrack之入门篇

配置基本参数

StreamType 音频流类型

最主要的几种STREAM
AudioManager.STREAM_MUSIC:用于音乐播放的音频流。
AudioManager.STREAM_SYSTEM:用于系统声音的音频流。
AudioManager.STREAM_RING:用于电话铃声的音频流。
AudioManager.STREAM_VOICE_CALL:用于电话通话的音频流。
AudioManager.STREAM_ALARM:用于警报的音频流。
AudioManager.STREAM_NOTIFICATION:用于通知的音频流。
AudioManager.STREAM_BLUETOOTH_SCO:用于连接到蓝牙电话时的手机音频流。
AudioManager.STREAM_SYSTEM_ENFORCED:在某些国家实施的系统声音的音频流。
AudioManager.STREAM_DTMF:DTMF音调的音频流。
AudioManager.STREAM_TTS:文本到语音转换(TTS)的音频流。
为什么分那么多种类型,其实原因很简单,比如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。

其实系统将这几种声音的数据分开管理,STREAM参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。

MODE模式(static和stream两种)

AudioTrack.MODE_STREAM

STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到AudioTrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到AudioTrack。这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。

AudioTrack.MODE_STATIC

STATIC就是数据一次性交付给接收方。好处是简单高效,只需要进行一次操作就完成了数据的传递;缺点当然也很明显,对于数据量较大的音频回放,显然它是无法胜任的,因而通常只用于播放铃声、系统提醒等对内存小的操作

采样率:mSampleRateInHz

采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)

通道数目:mChannelConfig

首先得出声道数,目前最多只支持双声道。为什么最多只支持双声道?看下面的源码

static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
      int channelCount = 0;
      switch(channelConfig) {
      case AudioFormat.CHANNEL_OUT_MONO:
      case AudioFormat.CHANNEL_CONFIGURATION_MONO:
          channelCount = 1;
          break;
      case AudioFormat.CHANNEL_OUT_STEREO:
      case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
          channelCount = 2;
          break;
      default:
          if (!isMultichannelConfigSupported(channelConfig)) {
              loge("getMinBufferSize(): Invalid channel configuration.");
              return ERROR_BAD_VALUE;
          } else {
              channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
          }
      }

  .......

  }

音频量化位数:mAudioFormat(只支持8bit和16bit两种)

if ((audioFormat !=AudioFormat.ENCODING_PCM_16BIT)  && (audioFormat !=AudioFormat.ENCODING_PCM_8BIT)) {
  		return AudioTrack.ERROR_BAD_VALUE;
  }

源码

public class AudioTrackManager {

    private AudioTrack mAudioTrack;
    private DataInputStream mDis; //播放文件的数据流
    private Thread mRecordThread;
    private boolean isStart = false;
    private volatile static AudioTrackManager mInstance;

    // 音频流类型
    private static final int mStreamType = AudioManager.STREAM_MUSIC;
    // 指定采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)
    private static final int mSampleRateInHz = 44100;
    // 指定捕获音频的声道数目。在AudioFormat类中指定用于此的常量
    private static final int mChannelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;//单声道
    // 指定音频量化位数 ,在AudioFormat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。
    // 因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。
    private static final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
    // 指定缓冲区大小。调用AudioRecord类的getMinBufferSize方法可以获得。
    private int mMinBufferSize;
    // STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,
    // 应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
    private static final int mMode = AudioTrack.MODE_STREAM;

    public AudioTrackManager() {
        initData();
    }

    private void initData() {
        // 根据采样率,采样精度,单双声道得到frame的大小。
        mMinBufferSize = AudioTrack.getMinBufferSize(mSampleRateInHz, mChannelConfig, mAudioFormat);
        // 注意,按照数字音频的知识,这个算出来得失一秒钟的大小.
        // 创建AudioTrack
        mAudioTrack = new AudioTrack(mStreamType, mSampleRateInHz, mChannelConfig, mAudioFormat, mMinBufferSize, mMode);
    }

    // 获取单例引用
    public static AudioTrackManager getInstance() {
        if (mInstance == null) {
            synchronized (AudioTrackManager.class) {
                if (mInstance == null) {
                    mInstance = new AudioTrackManager();
                }
            }
        }
        return mInstance;
    }

    // 销毁线程方法
    private void destroyThread() {
        try {
            isStart = false;
            if (null != mRecordThread && Thread.State.RUNNABLE == mRecordThread.getState()) {
                try {
                    Thread.sleep(500);
                    mRecordThread.interrupt();
                } catch (Exception e) {
                    mRecordThread = null;
                }
            }
            mRecordThread = null;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mRecordThread = null;
        }
    }

    // 启动播放线程
    private void startThread() {
        destroyThread();
        isStart = true;
        if (mRecordThread == null) {
            mRecordThread = new Thread(recordRunnable);
            mRecordThread.start();
        }
    }

    // 播放线程
    Runnable recordRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                // 设置线程的优先级
                android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
                byte[] tempBuffer = new byte[mMinBufferSize];
                int readCount = 0;
                while (mDis.available() > 0) {
                    readCount = mDis.read(tempBuffer);
                    if (readCount == AudioTrack.ERROR_INVALID_OPERATION || readCount == AudioTrack.ERROR_BAD_VALUE) {
                        continue;
                    }
                    if (readCount != 0 && readCount != -1) {
                        // 判断AudioTrack未初始化,停止播放的时候释放了,状态就为STATE_UNINITIALIZED
                        if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED) {
                            initData();
                        }
                        mAudioTrack.play();
                        mAudioTrack.write(tempBuffer, 0, readCount);
                    }
                }
                stopPlay();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    // 播放文件
    private void setPath(String path) throws Exception {
        File file = new File(path);
        mDis = new DataInputStream(new FileInputStream(file));
    }

    // 设置音频流
    private void setInputStream(InputStream is) {
        mDis = new DataInputStream(is);
    }

    // 启动播放
    public void startPlay(InputStream is) {
        try {
            // AudioTrack未初始化
            // if(mAudioTrack.getState() == AudioTrack.STATE_UNINNITIALZED) {
            //     throw new RuntimeException("The AudioTrack is not uninitialized");
            // } // AudioRecord.getMiniBufferSize的参数是否支持当前的硬件设备
            // else if(AudioTrack.ERROR_BAD_VALUE == mMinBufferSize || AudioTrack.ERROR == mMinBufferSize) {
            //     throw new RuntimeException("AudioTrack unable to getMinBufferSize()");
            // } else {
            if (is != null) { // 输入音频输入流
                setInputStream(is);
                startThread();
            }
            // }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 停止播放
    public void stopPlay() {
        try {
            destroyThread(); //销毁线程
            if (mAudioTrack != null) {
                if (mAudioTrack.getState() == AudioRecord.STATE_INITIALIZED) {
                    mAudioTrack.stop(); // 停止播放
                }
                mAudioTrack.release(); // 释放audioTrack资源
            }
            if (mDis != null) {
                mDis.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 暂停播放
    public void pause() {
        if (mAudioTrack != null && mAudioTrack.getState() == AudioRecord.STATE_INITIALIZED) {
            mAudioTrack.pause();
        }
    }
}


举报

相关推荐

0 条评论