[摘]AudioTrack简单简介之二

Android  2019年3月14日 pm7:48发布2个月前更新 城堡大人
112 0 0

上回说到AudioTrack播放有两种模式,即MODE_STATICMODE_STREAM,至于区别,上回也说过,如下:

MODE_STREAM

在这种模式下,需要先play,然后通过write一次次把音频数据写到AudioTrack中(我在试验中可以先write再play,可能是数据太小了的原因)。每次都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引入延时。

适用于大多数的场景,将audio buffers从java层传递到native层即返回。

如果audio buffers占用内存多,应该使用MODE_STREAM。

比如播放时间很长的声音文件,

比如音频文件使用高采样率,

比如动态的处理audio buffer等

MODE_STATIC

这种模式下,需要先write,再play.。先把所有数据通过一次write调用传递到AudioTrack中的内部缓冲区,后续就不必再传递数据了。但它也有一个缺点,就是一次write的数据不能太多,否则系统无法分配足够的内存来存储全部数据。

一次性将全部的音频资源从java传递到native层,这种方式延迟低,但也有局限性。

音频文件短且占用内存小。

适用于短促的游戏音效,并且对播放延迟真的有很高要求。

下面展示我参考网上写的demo

音频文件是下载了网上 wav的,自己可以网上百度,同时要注意采样率,虽然大多数是44100,但还是存在不一样的。可以使用Cool Edit Pro 2.1查询一下。

MODE_STREAM实例1

    private static int[] dms_alarm_sounds = new int[]{R.raw.mall, R.raw.mall, R.raw.mall, R.raw.mall};
    private static byte[] audioData;
    private static AudioTrack audioTrack;
    private static boolean isPlaying = false;
    private static final int SAMPLERATEINHZ = 44100;//44100 ;//16000

    /**
     * init sound
     *
     * @param soundID
     */
    private static void initPlaySoundS(int soundID) {
        InputStream inputStream = mContext.getResources().openRawResource(dms_alarm_sounds[soundID]);
        try {
            audioData = new byte[inputStream.available()];
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            inputStream.read(audioData);
            inputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return;
    }


    /**
     * start play
     *
     * @param soundID
     */
    public void play(final int soundID) {
        if (soundID < 0 || soundID > MAX_SOUMD_NUM - 1) {
            Log.d(TAG, "-----Error Sound ID---:" + soundID);
            return;
        }
        initPlaySoundS(soundID);
        int bufSize = android.media.AudioTrack.getMinBufferSize(SAMPLERATEINHZ,
                AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_16BIT);
        Log.d(TAG, "--startAudioTrack---audioData:" + audioData.length + "---bufSize:" + bufSize);
        audioTrack = new AudioTrack(AudioManager.STREAM_RING,
                SAMPLERATEINHZ, AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_16BIT, bufSize,
                AudioTrack.MODE_STREAM);
        new Thread(new Runnable() {
            @Override
            public void run() {

                if (audioTrack != null) {
                    try {
                        audioTrack.play();
                        audioTrack.write(audioData, 0, audioData.length);
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }).start();
        Log.d(TAG, "Playing");
        return;
    }

    /**
     * stop  play
     */
    public void stop() {
        isPlaying = false;
        if (audioTrack != null) {
            audioTrack.pause();
            audioTrack.flush();
            audioTrack.stop();
            audioTrack.release();
            audioTrack = null;
            Log.d(TAG, "-----stop--------");
        }
        return;
    }

PS:上面是先play再write

MODE_STREAM实例2

这里就只写一部分代码了


    /**
     * start play
     *
     * @param soundID
     */
    public void play(final int soundID) {
        if (soundID < 0 || soundID > MAX_SOUMD_NUM - 1) {
            Log.d(TAG, "-----Error Sound ID---:" + soundID);
            return;
        }
        isPlaying = true;
        final int bufSize = android.media.AudioTrack.getMinBufferSize(SAMPLERATEINHZ,
                AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_16BIT);
        Log.d(TAG, "-----play---444-----soundID:" + soundID + "----bufSize:" + bufSize);
        audioTrack = new AudioTrack(AudioManager.STREAM_NOTIFICATION,
                SAMPLERATEINHZ, AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_16BIT, bufSize,
                AudioTrack.MODE_STREAM);
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (isPlaying) {
                    try {
                        InputStream inputStream = mContext.getResources().openRawResource(dms_alarm_sounds[soundID]);
                        try {
                            audioData = new byte[bufSize];
                            int lenght;
                            while ((lenght = inputStream.read(audioData)) > 0) {
                                int tag = audioTrack.write(audioData, 0, lenght);
                                if (tag == AudioTrack.ERROR_INVALID_OPERATION || tag == AudioTrack.ERROR_BAD_VALUE) {
                                    continue;
                                }
                                audioTrack.play();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        return;
    }

PS:注意红色代码,这里是边write边play。。。(只试过小文件,也就是可以一次性写入。。。)

看文档,还是不建议这样写,,,最好先play,在write

MODE_STATIC实例

    /**
     * start play MODE_STATIC
     * @param soundID
     */
    public void play(final int soundID) {
        if (soundID < 0 || soundID > MAX_SOUMD_NUM - 1) {
            Log.d(TAG, "-----Error Sound ID---:" + soundID);
            return;
        }

        try {
            InputStream inputStream = mContext.getResources().openRawResource(dms_alarm_sounds[soundID]);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int b;
            while ((b = inputStream.read()) != -1) {
                out.write(b);
            }
            audioData = out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                audioTrack = new AudioTrack(
                        new AudioAttributes.Builder()
                                .setUsage(AudioAttributes.USAGE_MEDIA)
                                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                                .build(),
                        new AudioFormat.Builder()
                                .setSampleRate(SAMPLERATEINHZ)
                                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                                .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                                .build(),
                        audioData.length,
                        AudioTrack.MODE_STATIC, AudioManager.AUDIO_SESSION_ID_GENERATE
                );
                audioTrack.write(audioData, 0, audioData.length);
                audioTrack.play();
            }
        }).start();

        return;
    }

PS:红色的是先write后play

上面三个demo都是验证ok,如有问题可以留言,谢谢。

本文摘抄《Android-音视频(3):用AudioTrack播放音频PCM》、《音频播放AudioTrack之入门篇》和《AudioTrack中MODE_STATIC和MODE_STREAM的差异》自己整理得出。不懂的可访问相关链接。

https://www.biumall.com/  笔友城堡,为你导航!

 历史上的今天

  1. 2023: [代码片段]Android像素转换工具类(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

[摘]Android IO流读写文件

一、概念文件在程序中是以流的形式来操作的。流:是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两个存储位置之间的传输称为流。流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。二、分类1、按照流向划分:输入流、输出流以内存为参照:输...

王单单:在江边喝酒

古人说的话,我不信江水清不清,月亮都是白的 这样的夜晚,浪涛拍击被缚的旧船江风吹着渔火,晃荡如心事 这一次,兄弟我有言在先只许喝酒,不准流泪谁先喊出命中的疼,罚酒一杯 兄弟你应该知道,回不去了所有的老去都在一夜之间兄弟你只管喝,不言钱少酒家打烊...

IllegalArgumentException: Failed to find configured root

前言在使用FileProvider分享文件时出行如下异常。AndroidRuntime: FATAL EXCEPTION: mainAndroidRuntime: Process: com.biumall.file2, PID: 10871AndroidRuntime: java.lang...

郑愁予:归航曲

飘泊得很久,我想归去了彷佛,我不再属于这里的一切我要摘下久悬的桅灯摘下航程里最后的信号我要归去了……每一片帆都会驶向斯培西阿海湾原注:像疲倦的太阳在那儿降落,我知道每一朵云都会俯吻汩罗江渚,像清浅的水涡一样在那儿旋没……我要归去了天隅有幽蓝的空席有星座们洗尘的酒宴在隐去...

Android文本绘制高宽获取

前言自定义View中通过canvas.drawText()显示文本是,需要考虑文本的中心位置,因此需要计算文本的高度。今天就记录一下文本绘制相关的注意事项,方便自己查阅。正文下面介绍一下获取高度和文本宽度等方法FontMetrics绘制文本,需要注意字体的基准线,如图我们知道And...

Android监听Home键和Back键的简介

前言Home键和BACK是Android中最实用的,因此本文就简单的介绍一下监听HOME和BACK键。好记性不如烂笔头正文由于Android的管控越来越严格,HOME键已经无法拦截,但可以监听,BACK键的监听和拦截都可以。监听HOME键目前监听HOME的按键,可以监听:public...