[摘]AudioTrack简单简介之二

Android  2019年3月14日 pm7:48发布3周前更新 91es.com站长
81 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.91es.com/  91易搜,为你导航!

 历史上的今天

  1. 2023: [代码片段]Android像素转换工具类(0条评论)
版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站内容: 部分来源于网络,仅供学习和参考,若侵权请留言
3、 本站申明: 个人流水账日记,内容并不保证有效

暂无评论

暂无评论...

随机推荐

startActivity之进程启动

前言今天简单的记录一下App进程的启动过程。继之前startActivity的分析(《startActivity源码分析1》和《startActivity源码分析2》)中的进程启动是一带而过的,聪明的你会发现,进程启动的大部分跟《Zygote的启动之二ZygoteInit》重合了。正文回到...

夏丏尊:幽默的叫卖声

住在都市里,从早到晚,从晚到早,不知要听到多少种类多少次数的叫卖声。深巷的卖花声是曾经入过诗的,当然富于诗趣,可惜我们现在实际上已不大听到。寒夜的“茶叶蛋”、“细沙粽子”、“莲心粥”等等,声音发沙,十之七八似乎是“老枪”的喉咙,困在床上听去颇有些凄清。每种叫卖声,差不多都有着特殊的情调。我在这许多...

AccessibilityService自启动

自定义的AccessibilityService默认是关闭的,需要在无障碍界面手动打开或者自动启动。目前有两种方式可以自动启动改服务。需要修改源码或配置应用为系统应用。1、源码中修改frameworks\base\packages\SettingsProvider\src\com\androi...

沈从文 : 独处

在我一个自传里,我曾经提到过水给我种种的印象。檐溜、小小的河流、汪洋万顷的大海,莫不对于我有过极大的帮助。我学会用小小脑子去思索一切,全亏得是水。我对于宇宙认识得深一点,也亏得是水。“孤独一点,在你缺少一切的时节,你就会发现,原来还有个你自己。”这是一句真话。我有我自己的生活与理想,可以说是...

Android高版本getDrawable(int id)废弃后的替代方法

前言在Android 高版本上使用getDrawable(int id)时,如下,有提示使用的这个方法废弃了 mIvAlbum.setBackground(getResources().getDrawable(R.drawable.music_album_unknown));点进入源...

Android解析lrc文件代码片段

前言简单记录一下解析本地lrc文件中的文件代码。方便自己查阅。流水文,跳过吧。正文解析歌词分两种,一种是歌曲中内置的,另外一种是外置的。这里介绍外置的歌词,以常见的lrc歌词为例,也是比较简单的一种歌词文件。下面是项目中用的,涉及到文本编码格式的判断等。隐藏内容!付费阅读后才能查...