将 MP3 转成 PCM 并使用 AudioTrack 播放

环境

Android Studio Bumblebee, Mac mini 2014

问题描述

需要实现功能为:将 MP3 转成 PCM 并通过 UDP 传出去。

任务可分解为:

  1. 将 MP3 转成 PCM
  2. 将 PCM 通过 AudioTrack 播放以确保转换的 PCM 文件正常
  3. 将 PCM 通过 UDP 传出去。

问题分析

注:写文章引用来源一直力求引用原创,但下面很多引文没找到原创文献

1 将 MP3 转成 PCM

面向搜索引擎编程,Java 直接有转换用的 API: AudioSystem。转换方式参照 CSDN 博文

这里有点要说明,如果直接使用,会提示找不到 javax.sound.sampled.AudioSystem, 原因是 Android 自带的包覆盖掉了 JRE 里面的文件。

解决方法是将 JRE 中包添加到 Android 醒目的 ‘build.gradle’ 的依赖里面。-- 此处博文忘记引文地址了。

implementation files('/Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/jre/lib/rt.jar')

问题是,我使用AudioSystem.write() 转换数据,总是提示 could not write audio file: file type not supported, 问题可能是 输出文件的 AudioFomat 设置有问题,但尝试了许久,没找到问题点。

不能总吊在一棵树上。此计不通,立马考虑其他方法。直接在外面将 MP3 转成 PCM。在网站即时工具箱 中将 MP3 转成 PCM. 该页面有很简单的转换设置项。转换后出来的文件,使用 AudioTrack 播放,出来的是滋滋滋的声音,网上是说可能是大小端的问题。但网页转换没有这个设置项,没法验证。办法二宣告失败。

网上另一种途径是使用 FFMPEG 将 MP3 转换成 PCM。把我树莓派搬出来。参考博文mp3文件转pcm文件,生成了 单通道-8k采样率 的 PCM 文件。但使用 ffplay 播放时报错 ‘could not initialize sdl - displayindex must be in the range 0 - -1。首先安装 SDL。安装完成后,播放 PCM 仍旧有相同报错。貌似调试 SDL 也得大费周章。

2 使用 AudioTrack 播放以验证 PCM 正常

鉴于调试 SDL 也是不小的工程,遂停止使用 SDL 播放的尝试,直接在 Android 里面使用 AudioTrack 播放,正常,谢天谢地。

AudioTrack udioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_OUT_DEFAULT,
 AudioFormat.ENCODING_PCM_16BIT, 15000, AudioTrack.MODE_STREAM);
 
 binding.play.setOnClickListener(new View.OnClickListener() {
    
    
 @Override
 public void onClick(View v) {
    
    
                new Thread(new Runnable() {
    
    
                    @Override
                    public void run() {
    
    
                        pcmAudioTrack.play();

                        InputStream inputStream = null;
                        byte[] data = new byte[5000];
                        AssetManager assetManager = getAssets();
                        try {
    
    
                            inputStream = assetManager.open("dujia.pcm");
                            int length = 0;
                            while((length = inputStream.read(data))!= -1) {
    
    
                                pcmAudioTrack.write(data, 0, length);
                                SystemClock.sleep(100);
                            }
                            pcmAudioTrack.stop();
                            inputStream.close();
                        } catch (IOException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
    });

3 将 PCM 通过 UDP 传出去

这里是与上一步类似,将 PCM 文件以流的方式读入并按照接收端定义的格式打包后通过 UDP 传出去。具体过程非重点,略过不表。

这里要强调两点:

public static ArrayList<String> getConnectedIP() {
    
    
 ArrayList<String> connectedIP = new ArrayList<>();
    try {
    
    
        BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/net/arp"));
        String line = bufferedReader.readLine();
        while ((line = bufferedReader.readLine()) != null) {
    
    
            String[] splitted = line.split(" +");
            if (splitted.length >= 4) {
    
    
                String ip = splitted[0];
                connectedIP.add(ip);
            }
        }
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
    return connectedIP;
}

需要注意两点:一是在 Android 10 及以上手机中可能提示没有权限,换低版本系统手机即可。二是在 Android Studio 终端 或者 Mac 终端 使用同样命令获取到设备IP:

adb shell
cat /proc/net/arp

猜你喜欢

转载自blog.csdn.net/dpdcsdn/article/details/127517866