Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

音频可视化(二)——Android实现 #190

Open
soapgu opened this issue Feb 3, 2023 · 0 comments
Open

音频可视化(二)——Android实现 #190

soapgu opened this issue Feb 3, 2023 · 0 comments
Labels

Comments

@soapgu
Copy link
Owner

soapgu commented Feb 3, 2023

  • 前言

上一篇音频可视化完全是理论依据。由于本身自身基础的薄弱,造成补了很多课才勉强能了解个概要,其实还是有太多空白需要填补了(这债算起来要从大一开始欠了)。
计算机是一门实践科学,理论和实践两手都要抓都要硬。
其实从实现角度来说,反而比理论要“简单”,因为又很多好用的api和第三方库作为支持,可以少走很多弯路。

  • 安卓实现概要实现过程

图片
根据官网以及其他参考,实现的主要路径如上。
很可惜,目前只能对声音播放过程中同步实现可视化。暂时边录音边可视化这条路走不通

  • 获取audio session id

从实现角度讲,Visualizer是实现音频可视化的核心。
他有一个重要入参为audio sessio id来关联音频。
看官方文档,audio sessio id是由MediaPlayer或者AudioTrack来提供,AudioTrack和MediaPlayer看起来都属于比较“悠久”的项目。而且一般现在手机的存储里面也是以图片和视频为主。存mp3文件这种可能性比较低。
我觉得还是用顺手一点的。
VideoView这个控件就顺眼的多,首先他的底层也是MediaPlayer实现的,getAudioSessionId()成员方法也可以达成相同的目的。那就先他了

  • 获取并播放制定音频文件

以前都是通过存储到应用到私有空间实现,但是实际场景都是从手机媒体库里面获取资源的。类似我们windows系统的“我的文档”。这次我们需要补上这一课。
从一开始路走的有一点点偏,访问共享存储空间中的媒体文件,这是访问整个媒体库的api。对于我只是要选择一个视频文件的“需求”来说显然是过了

这时候我们又要把Intent捡起来了

这里要和registerForActivityResult结合实现,ActivityResultContracts.GetContent()是现成的
内部的实现是ACTION_GET_CONTENT

这里容易和ACTION_OPEN_DOCUMENT有混淆,有细微区别,一个目的是检索,一个目的是读取,请意会

ActivityResultLauncher<String> pickVideo = registerForActivityResult(new ActivityResultContracts.GetContent(),
            new ActivityResultCallback<Uri>() {
                @Override
                public void onActivityResult(Uri result) {
                    //msg.setText( result.toString() );
                    videoView.setVideoURI(result);
                    videoView.start();
                }
            });

this.findViewById(R.id.button_select).setOnClickListener( v -> {
            onSelectHandle();
        } );

private void onSelectHandle() {
        pickVideo.launch( "video/*" );
    }

代码实现还是比较简单,基本上启动 activity 以获取其结果的示例代码一致。

  • 通过Visualizer获取音频数据

  1. 申请RECORD_AUDIO权限
    虽然Visualizer用于可视化目的。它不是录音接口。但是它还是要录音权限

  2. 传入audioSessionId并初始化

  3. 初始化 设置OnDataCaptureListener回调函数

  4. 同步播放文件接收数据

private void onVisualizeSound( int audioSessionId ){
        this.visualizer = new Visualizer(audioSessionId);
        this.visualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
            @Override
            public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
                //不会触发
            }

            @Override
            public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
                //接收傅立叶变换数据
            }
        },Visualizer.getMaxCaptureRate() / 2, false, true);
        visualizer.setEnabled(true);
    }
  1. 计算傅立叶数据
float[] model = new float[fft.length / 2 + 1];
int j = 1;
for (int i = 2; i < mCount*2;) {
    model[j] = (float) Math.hypot(fft[i], fft[i + 1]);
    i += 2;
    j++;
}

这里fft数组一共1024个数据,分为512个复数
这里频谱的数据以笛卡尔形式表达,这里略过傅立叶变换的理论,以后补~

最终计算出每个频率下的振幅,这里先暂时忽略相位

  • 呈现

这里为了简化就暂时使用控件来实现了

图片

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant