Android音视频开发-AudioTrack

Android音视频开发-AudioTrack

本篇文章我们主要介绍下AudioTrack.

1: 简介

AudioTrack是Android平台上的一个类,用于播放音频数据.

它允许PCM音频缓冲区流式传输到音频接收器进行播放.

  1. 创建AudioTrack对象:可以通过构造函数创建AudioTrack对象,需要指定音频流类型、采样率、音频通道配置和音频格式等参数。
  2. 写入音频数据:可以使用write()方法将音频数据写入AudioTrack对象。写入的数据可以是PCM格式的原始音频数据,也可以是压缩格式的音频数据(如MP3、AAC等)。
  3. 播放音频数据:在写入音频数据后,可以调用play()方法开始播放音频数据。可以使用pause()方法暂停播放,使用stop()方法停止播放。
  4. 设置音量:可以使用setVolume()方法设置音量大小,范围为0.0到1.0之间。
  5. 设置播放模式:可以使用setPlaybackRate()方法设置播放速率,使用setLoopPoints()方法设置循环播放的起始点和结束点。
  6. 监听播放状态:可以使用OnPlaybackPositionUpdateListener接口监听播放进度和播放完成事件。
  7. 释放资源:在不再需要使用AudioTrack对象时,应该调用release()方法释放资源。

2: 创建AudioTrack对象

java 复制代码
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
        int bufferSizeInBytes, int mode)
throws IllegalArgumentException {
    this(streamType, sampleRateInHz, channelConfig, audioFormat,
            bufferSizeInBytes, mode, AudioManager.AUDIO_SESSION_ID_GENERATE);
}

参数配置:

1: streamType

  1. AudioManager.STREAM_VOICE_CALL:用于语音通话的音频流类型。
  2. AudioManager.STREAM_SYSTEM:用于系统声音的音频流类型,例如按键声音、提示音等。
  3. AudioManager.STREAM_RING: 用于电话铃声的音频流类型。
  4. AudioManager.STREAM_MUSIC:用于音乐播放的音频流类型。
  5. AudioManager.STREAM_ALARM:用于闹钟的音频流类型。
  6. AudioManager.STREAM_NOTIFICATION:用于通知的音频流类型。
  7. AudioManager.STREAM_DTMF:用于双音多频信号的音频流类型

2: sampleRateInHz

采样率的大小限制是4000~192000; 可以在源码中查看:

java 复制代码
/** Minimum value for sample rate,
 *  assuming AudioTrack and AudioRecord share the same limitations.
 * @hide
 */
// never unhide
public static final int SAMPLE_RATE_HZ_MIN = 4000;
/** Maximum value for sample rate,
 *  assuming AudioTrack and AudioRecord share the same limitations.
 * @hide
 */
// never unhide
public static final int SAMPLE_RATE_HZ_MAX = 192000;
/** Sample rate will be a route-dependent value.
 * For AudioTrack, it is usually the sink sample rate,
 * and for AudioRecord it is usually the source sample rate.
 */
public static final int SAMPLE_RATE_UNSPECIFIED = 0;

如果不在这个区间,系统则抛出异常:

调用setSampleRate指定sampleRate时,如果不在4000~192000区间则怕出new IllegalArgumentException("Invalid sample rate " + sampleRate).

java 复制代码
public Builder setSampleRate(int sampleRate) throws IllegalArgumentException {
    // TODO Consider whether to keep the MIN and MAX range checks here.
    // It is not necessary and poses the problem of defining the limits independently from
    // native implementation or platform capabilities.
    if (((sampleRate < SAMPLE_RATE_HZ_MIN) || (sampleRate > SAMPLE_RATE_HZ_MAX)) &&
            sampleRate != SAMPLE_RATE_UNSPECIFIED) {
        throw new IllegalArgumentException("Invalid sample rate " + sampleRate);
    }
    mSampleRate = sampleRate;
    mPropertySetMask |= AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE;
    return this;
}

至于初始化时,校验的方法在audioParamCheck方法中:

java 复制代码
// sample rate, note these values are subject to change
if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
        sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
        sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
    throw new IllegalArgumentException(sampleRateInHz
            + "Hz is not a supported sample rate.");
}

3: channelConfig

声道配置同样在AudioFormat中定义,常用的有:

AudioFormat.CHANNEL_OUT_MONO:单声道 AudioFormat.CHANNEL_OUT_STEREO:双声道

4:audioFormat

该参数定义音频格式:

  1. AudioFormat.ENCODING_PCM_8BIT:8位PCM编码
  2. AudioFormat.ENCODING_PCM_16BIT:16位PCM编码
  3. AudioFormat.ENCODING_PCM_FLOAT:浮点型PCM编码

5:bufferSizeInBytes

音频缓冲区的大小,以字节为单位.我们可以通过getMinBufferSize()来获取最小缓冲区大小.

6: mode

指定音频输出模式.

Android 系统提供了两种模式如下:

java 复制代码
/**
 * Creation mode where audio data is transferred from Java to the native layer
 * only once before the audio starts playing.
 */
public static final int MODE_STATIC = 0;
/**
 * Creation mode where audio data is streamed from Java to the native layer
 * as the audio is playing.
 */
public static final int MODE_STREAM = 1;
  1. MODE_STATIC:静态模式,适用于一次性播放完整音频数据的场景
  2. MODE_STREAM:流模式,不间断地写入音频数据.

创建代码如下:

java 复制代码
int streamType = AudioManager.STREAM_MUSIC; // 音频流类型
int sampleRateInHz = 44100; // 采样率
int channelConfig = AudioFormat.CHANNEL_OUT_MONO; // 声道配置
int audioFormat = AudioFormat.ENCODING_PCM_16BIT; // 音频格式
int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); // 缓冲区大小
audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);

3: 写入音频数据+播放

java 复制代码
    /**
     * 播放录音
     */
    private void playAudio() {
        stopPlay();

        File file = new File("sdcard/audioRecord.pcm");
        if (!file.exists()) return;

        int streamType = AudioManager.STREAM_MUSIC; // 音频流类型
        int sampleRateInHz = 44100; // 采样率
        int channelConfig = AudioFormat.CHANNEL_OUT_STEREO; // 声道配置
        int audioFormat = AudioFormat.ENCODING_PCM_16BIT; // 音频格式
        int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); // 缓冲区大小
        audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);


//        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
//            /**
//             * 设置音频信息属性
//             * 1.设置支持多媒体属性,比如audio,video
//             * 2.设置音频格式,比如 music
//             */
//            AudioAttributes attributes = new AudioAttributes.Builder()
//                    .setUsage(AudioAttributes.USAGE_MEDIA)
//                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
//                    .build();
//            /**
//             * 设置音频格式
//             * 1. 设置采样率
//             * 2. 设置采样位数
//             * 3. 设置声道
//             */
//            AudioFormat format = new AudioFormat.Builder()
//                    .setSampleRate(sampleRateInHz)
//                    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
//                    .setChannelMask(channelConfig)
//                    .build();
//            audioTrack = new AudioTrack(attributes,format,bufferSizeInBytes,AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE);
//
//        }
        audioTrack.play();
        new Thread(() -> {
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(file);
                byte[] buffer = new byte[bufferSizeInBytes];
                Log.i(TAG, "playAudio: "+bufferSizeInBytes);
                int read = 0;
                while (read != -1) {
                    read = fileInputStream.read(buffer);
                    //将缓冲区buffer写入audioTrack进行播放
                    audioTrack.write(buffer, 0, buffer.length);

                }
                audioTrack.stop();
                audioTrack.release();
            } catch (Throwable e) {

            }

        }).start();
    }

4: 释放

java 复制代码
/**
 * 停止播放录音,并释放资源
 */
private void stopPlay() {
    if (audioTrack != null) {
        audioTrack.release();
    }
}

这里注意下,release方法内部实现了stop,所以我们不需要额外的调用stop停止播放.

另外,如果调用停止播放, 内部会判断当前状态,如果非STATE_INITIALIZED下调用,则会抛出异常.

java 复制代码
public void stop()
throws IllegalStateException {
    if (mState != STATE_INITIALIZED) {
        throw new IllegalStateException("stop() called on uninitialized AudioTrack.");
    }

    // stop playing
    synchronized(mPlayStateLock) {
        native_stop();
        baseStop();
        mPlayState = PLAYSTATE_STOPPED;
        mAvSyncHeader = null;
        mAvSyncBytesRemaining = 0;
    }
}
相关推荐
阿蒙Amon28 分钟前
【Python小工具】使用 OpenCV 获取视频时长的详细指南
python·opencv·音视频
还鮟1 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡2 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi002 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil4 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你4 小时前
Android View的绘制原理详解
android
正在走向自律5 小时前
第二章-AIGC入门-开启AIGC音频探索之旅:从入门到实践(6/36)
人工智能·aigc·音视频·语音识别·ai音乐·ai 音频·智能语音助手
移动开发者1号7 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号7 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best12 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android