Android音视频 初始AudioTrack和PCM的录制与播放

初始AudioTrack和PCM的录制与播放

Audio系统

先看看Audio里边有哪些东西?通过Android的SDK文档,发现主要有三个:

  • AudioManager:这个主要是用来管理Audio系统的
  • AudioTrack:这个主要是用来播放声音的
  • AudioRecord:这个主要是用来录音的

其中AudioManager的理解需要考虑整个系统上声音的策略问题,例如来电话铃声,短信铃声等,主要是策略上的问题。
上一篇文章中已经讲述了audio’re’cord的使用,和相关参数的解释,没有看过的可以看一哈 音视频开发 原声方法生成PCM 以及PCM转为wav ,如果你本身已经对audiorecord有一定的了解,开始吧

AudioTrack的创建和配置

`
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode)

`

上篇文章中大部分参数都已经介绍过,现在只介绍新的参数

  • streamType

    这个在构造AudioTrack的第一个参数中使用。这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。

    Android将系统的声音分为以下几类常见的(未写全):
    l STREAM_ALARM:警告声
    l STREAM_MUSCI:音乐声,例如music等
    l STREAM_RING:铃声
    l STREAM_SYSTEM:系统声音
    l STREAM_VOCIE_CALL:电话声音

    为什么要分这么多呢?以前在台式机上开发的时候很少知道有这么多的声音类型,不过仔细思考下,发现这样做是有道理的。例如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。
    其实系统将这几种声音的数据分开管理,所以,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。

  • mode
    AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
    这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
    而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
    这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。

AudioTrack播放代码实现

private int mChannelConfig = AudioFormat.CHANNEL_IN_MONO;
private int mPlayChannelConfig = AudioFormat.CHANNEL_IN_STEREO;
private int mAudioEncoding = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioTrack.getMinBufferSize(mFrequence,
                    mPlayChannelConfig, mAudioEncoding);
            short[] buffer = new short[bufferSize ];
            try {
                // 定义输入流,将音频写入到AudioTrack类中,实现播放
                DataInputStream dis = new DataInputStream(
                        new BufferedInputStream(new FileInputStream(mAudioFile)));
                // 实例AudioTrack
                AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC,
                        mFrequence,
                        mPlayChannelConfig, mAudioEncoding, bufferSize,
                        AudioTrack.MODE_STREAM);
                // 开始播放
                track.play();
                // 由于AudioTrack播放的是流,所以,我们需要一边播放一边读取
                while (mIsPlaying && dis.available() > 0) {
                    int i = 0;
                    while (dis.available() > 0 && i < buffer.length) {
                        buffer[i] = dis.readShort();
                        i++;
                    }
                    // 然后将数据写入到AudioTrack中
                    track.write(buffer, 0, buffer.length);
                }


                // 播放结束
                track.stop();
                dis.close();
            } catch (Exception e) {
                // TODO: handle exception
                Log.e("slack","error:" + e.getMessage());
            }

还要注意自愿的释放

 if (track != null) {
            if (track.getState() == AudioRecord.STATE_INITIALIZED) {
                track.stop();
            }
            if (track != null) {
                track.release();
            }
        }
        if (dis != null) {
            dis.close();
        }

与audiorecord录音的比较

mRecorder.startRecording();
            //writeToFileHead();
            while (isStart) {
                if (null != mRecorder) {
                    //返回的是读取的字节数
                    bytesRecord = mRecorder.read(tempBuffer, 0, bufferSize);
                    if (bytesRecord == AudioRecord.ERROR_INVALID_OPERATION || bytesRecord == AudioRecord.ERROR_BAD_VALUE) {
                        continue;
                    }
                    if (bytesRecord != 0 && bytesRecord != -1) {
                        //在此可以对录制音频的数据进行二次处理 比如变声,压缩,降噪,增益等操作
                        //我们这里直接将pcm音频原数据写入文件 这里可以直接发送至服务器 对方采用AudioTrack进行播放原数据
                        dos.write(tempBuffer, 0, bytesRecord);
                    } else {
                        break;
                    }
                }
            }

同时注意释放资源

  if (mRecorder != null) {
            if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
                mRecorder.stop();
            }
            if (mRecorder != null) {
                mRecorder.release();
            }
        }
        if (dos != null) {
            dos.flush();
            dos.close();
        }

猜你喜欢

转载自blog.csdn.net/yangyasong/article/details/80535223