Android 原生播放音频有哪些方式

news/2024/11/20 1:36:20/

文章目录

    • 使用 MediaPlayer
      • MediaPlayer特点
    • 关于 SoundPool
    • 关于 AudioTrack
    • 关于 AudioRecord
      • AudioRecord 降噪
    • 总结

之前一片文章简单讲述了通过TTS和Audio相关API控制文本输出语音的方法,接下来学习一下其他原生方式的语音播放功能。

使用 MediaPlayer

  • 项目中除了对需要播放文本有需求以外,有一些特殊固定音频的提示音,比如超速提醒等功能。这个功能我们使用的是通过MediaPlayer播放媒体文件的方式来提醒用户超速。主要代码如下
// 定义MediaPlayer
private var mediaPlayer: MediaPlayer?
// 初始化 MediaPlayer 传入了需要播报的音频文件 这是一个 .wav类型文件 (就是无损压缩格式,缺点文件大 优点兼容性好)
// 也可也通过方法传入新的音频文件
private fun initMediaPlayer(){mediaPlayer = MediaPlayer.create(TruckerPathApplication.appContext, R.raw.xxx)
}
  • 播放提示音
    /*** 播放提示音*/private fun playSpeedWarningRing() {try {// 上一篇讲过这个是获取音频焦点,使得提示音更突出,其他声音略微缩小audioFocusManager.requestAudioFocus()// 判断是否正在播放if (mediaPlayer?.isPlaying != true) {// 开始播放mediaPlayer?.start()}} catch (e: IllegalStateException) {if (BuildConfig.DEBUG) {e.printStackTrace()}} finally {// 一定最后要释放音频焦点audioFocusManager.abandonAudioFocus()}}
  • 释放 Media 资源
    /*** 释放mediaPlayer*/private fun releaseMediaPlayer() {mediaPlayer?.release()mediaPlayer = null}

MediaPlayer特点

MediaPlayer 更适合播放较长的音频文件,例如音乐、电影和电台等。它支持多种音频格式,如 MP3、AAC、WMA 等,同时还支持多媒体的控制,如暂停、停止、重复播放等。MediaPlayer 可以在后台运行,即使应用切换到后台或锁屏状态下仍然可以继续播放,也可以通过 Notification 和 MediaSession 等类实现前台播放通知和多媒体控制。

MediaPlayer 播放音频时,它只会在内存中缓存正在播放的音频数据,而不会一次性将整个音频文件加载到内存中。当 MediaPlayer 播放完缓存中的数据时,它会自动从硬盘中读取下一部分音频数据,并逐步地进行播放。
因此,相比于 SoundPool 在内存中预加载音频资源的方式,MediaPlayer 可以更加灵活地管理内存资源,避免资源浪费。同时,MediaPlayer 也可以播放较长的音频文件,适合用于播放音乐等长时间的音频文件。
不过,需要注意的是,MediaPlayer 在播放完音频文件后,并不会自动释放内存资源,这需要开发者手动调用 MediaPlayer 的 release() 方法来释放相关资源,避免内存泄漏的问题。

关于 SoundPool

SoundPool 对于 MediaPlayer 来说有如下特点:

SoundPool 主要用于播放短促音效,例如游戏中的背景音乐、点击声和爆炸声等。
优点:

提高播放性能:内存池可以避免频繁的 I/O 操作和内存分配,从而减少 CPU 的开销,提高播放性能。
降低功耗:由于内存池可以避免频繁的 I/O 操作,因此可以降低硬件设备的功耗,从而延长电池寿命。
支持多个音效同时播放:SoundPool 可以同时播放多个音效,这对于一些需要多种音效混合的场景非常有用。
实现音效的循环播放和变速播放等特效:SoundPool 支持音效的循环播放和变速播放等特效,这对于游戏等应用场景非常有用。

缺点:

SoundPool 也存在一些缺点:
可能存在资源浪费:由于 SoundPool 在内存中预加载音频资源,因此如果音频文件较大,可能会浪费过多的内存资源。
受限于硬件设备:SoundPool 的播放性能和效果可能受限于硬件设备的音频处理能力,因此在一些低端设备上可能会出现问题。
不支持播放较长的音频文件:由于 SoundPool 音频资源需要预加载到内存中,因此不适合播放较长的音频文件。

  • 使用 SoundPool 核心代码如下
// 在 Activity 中定义 SoundPool 变量
private SoundPool soundPool;// 在 onCreate() 方法中初始化 SoundPool
soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
// 加载音频文件
int soundId = soundPool.load(this, R.raw.sound_effect, 1);// 在需要播放音频文件的时候调用 play() 方法
soundPool.play(soundId, 1, 1, 0, 0, 1);
  • 下面是一些 SoundPool 常用的 API:
SoundPool(int maxStreams, int streamType, int srcQuality):构造函数,用于创建 SoundPool 对象。
maxStreams:最大能同时播放的音频数量。
streamType:指定音频流的类型,如 AudioManager.STREAM_MUSIC 等。
srcQuality:指定音频质量,取值范围为 0~4,0 表示最差,4 表示最好。
load(Context context, int resId, int priority):加载音频文件。
context:应用程序上下文。
resId:音频文件的资源 ID。
priority:指定加载音频文件的优先级。
setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener):设置加载音频文件完成的监听器。
play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate):播放音频文件。
soundID:音频文件的 ID。
leftVolume:左声道音量。
rightVolume:右声道音量。
priority:指定播放音频文件的优先级。
loop:指定循环播放的次数,-1 表示无限循环。
rate:指定播放速度,1 表示正常速度。
pause(int streamID):暂停指定 streamID 的音频文件播放。
resume(int streamID):恢复指定 streamID 的音频文件播放。
stop(int streamID):停止指定 streamID 的音频文件播放。
release():释放 SoundPool 对象占用的资源。
注意:SoundPool 在 Android 5.0 以上版本已经不建议使用,建议使用 SoundPool.Builder 或者 MediaPlayer 等其他方式来播放音频。

关于 AudioTrack

AudioTrack 是一个用于播放音频的类,它可以直接控制音频数据的采样率、位宽和声道数等参数,适用于需要对音频进行实时处理的场景。
相比于 MediaPlayer,AudioTrack 的优点是:

  • 实时性更高:由于直接控制音频数据的采样率等参数,AudioTrack 可以更快地响应和处理实时的音频数据;
  • 可控性更高:可以对音频数据进行更加精细的控制,例如控制播放的速率、音量等。

但是 AudioTrack 也有一些缺点,例如需要自己实现音频文件解码等功能。

  • 下面是简单的实现代码
int sampleRate = 44100;
int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,channelConfig, audioFormat, bufferSize, AudioTrack.MODE_STREAM);byte[] buffer = new byte[bufferSize];File file = new File(Environment.getExternalStorageDirectory(), "audio.pcm");
try {FileInputStream inputStream = new FileInputStream(file);audioTrack.play();int length;while ((length = inputStream.read(buffer)) > 0) {audioTrack.write(buffer, 0, length);}audioTrack.stop();audioTrack.release();inputStream.close();
} catch (IOException e) {e.printStackTrace();
}

关于 AudioRecord

AudioRecord是Android平台上用于实现音频采集的类。它可以从系统中的音频输入源(比如麦克风)读取音频数据,并将数据存储到内存中或写入到文件中。通常,我们可以将AudioRecord与AudioTrack一起使用,实现实时的音频录制和播放。

  • 使用AudioRecord类可以实现以下功能:
    音频数据采集:可以从不同的音频输入源(比如麦克风、电话线路、蓝牙耳机等)获取音频数据。
    音频数据读取:可以将采集到的音频数据存储到内存中或写入到文件中。
    音频格式支持:支持多种音频格式(比如PCM、AAC、MP3等)的采集和处理。
    需要注意的是,使用AudioRecord需要注意以下几个方面:
    音频源的选择:需要根据实际情况选择正确的音频输入源,比如麦克风、电话线路、蓝牙耳机等。
    音频格式的设置:需要根据实际情况设置正确的音频格式(比如采样率、位深、声道数等)。
    音频缓冲区的设置:需要根据实际情况设置正确的音频缓冲区大小,以确保能够正确地读取和处理音频数据。
    总之,AudioRecord是Android平台上非常重要的音频采集类之一,它可以帮助我们实现音频数据的采集、处理和存储等功能。

  • 通过 AudioRecord 获取音频,再通过 Google Speech-to-Text API 转换成文本 示例代码如下:

private static final int RECORDER_SAMPLERATE = 16000;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;private AudioRecord audioRecorder;
private boolean isRecording = false;// 启动录音
private void startRecording() {int bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);audioRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bufferSize);// 开启录音线程isRecording = true;new Thread(new Runnable() {public void run() {android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);byte[] buffer = new byte[bufferSize];audioRecorder.startRecording();while (isRecording) {int bytesRead = audioRecorder.read(buffer, 0, buffer.length);// 将录制的音频转换成文本信息if (bytesRead > 0) {try {// 将录制的音频数据传递给语音识别引擎SpeechRecognizer recognizer = SpeechRecognizer.createSpeechRecognizer(MainActivity.this);recognizer.setRecognitionListener(new RecognitionListener() {public void onResults(Bundle results) {ArrayList<String> voiceResults = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);if (voiceResults != null && voiceResults.size() > 0) {String text = voiceResults.get(0);// 处理识别结果Log.d(TAG, "识别结果:" + text);}}// 省略其他回调方法的实现});Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());recognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);recognizerIntent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS, 1500);recognizerIntent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS, 1500);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();DataOutputStream dataOutputStream = new DataOutputStream(outputStream);for (int i = 0; i < bytesRead; i++) {dataOutputStream.writeShort(buffer[i]);}byte[] bytes = outputStream.toByteArray();recognizer.startListening(recognizerIntent, new AudioRecord.AudioInputCaptureCallback() {public void onInputCaptured(byte[] audioData) {// 将录制的音频数据传递给语音识别引擎recognizer.writeAudio(audioData, 0, audioData.length);}});recognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, getPackageName());recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "en-US");

AudioRecord 降噪

udioRecord默认情况下是没有提供降噪功能的,因此需要通过一些额外的处理来实现降噪。常用的方法是使用数字信号处理(DSP)算法来滤除不想要的噪声,例如高通滤波器和自适应滤波器等。
在Android中,可以通过使用AudioRecord的setAudioSource方法设置声音来源,并在构造AudioRecord对象时指定降噪的配置参数。例如,可以通过设置音频来源为麦克风并启用降噪来实现降噪录音,示例代码如下:

int audioSource = MediaRecorder.AudioSource.MIC;
int sampleRate = 44100;
int channelConfig = AudioFormat.CHANNEL_IN_MONO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
NoiseSuppressor noiseSuppressor = NoiseSuppressor.create(audioRecord.getAudioSessionId());
audioRecord = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, bufferSizeInBytes);
if (NoiseSuppressor.isAvailable() && noiseSuppressor != null) {noiseSuppressor.setEnabled(true);
}

在上面的代码中,我们通过调用NoiseSuppressor.create()方法创建一个NoiseSuppressor实例,并将其应用到当前AudioRecord实例的音频会话中。另外,我们还可以通过调用NoiseSuppressor.isAvailable()方法来检查当前设备是否支持噪声抑制功能。

总结

关于音频播放:如果长音视频更适合 MediaPlayer 或者对于一些内存比较紧张的情况也适用,因为MediaPlayer 不会将整个视频读取到内存。如果是比较短的音频更适合SoundPool,性能好,使用简单,适合循环播放,因为直接从内存读取避免重复I/O操作。如果需要增加各种复杂处理 可以使用 AudioTrack ,缺点是需要自己做解码。如果需要录音等功能则可以使用 AudioRecord ,但是还需要一些降噪处理。


http://www.ppmy.cn/news/63474.html

相关文章

轻量级的安卓百分比屏幕适配方案SimpleAutoSize

AutoSize是基于今日头条的适配方案&#xff0c;但它有一些缺点&#xff0c;比如代码侵入性较强&#xff0c;在使用第三方的View框架时&#xff0c;可能会出现不兼容的情况。 我目前的sdk项目不能使用这样的框架&#xff0c;于是自己做了一个简单的工具类&#xff0c;也能够满足…

操作系统考试复习——第四章 对换 分页存储管理方式

对换技术也成为交换技术&#xff0c;由于当时计算机的内存都非常小&#xff0c;为了使该系统能分时运行多个用户程序而引入了对换技术。 1.对换的引入&#xff1a; 所谓“对换”&#xff0c;是指把内存中暂时不能运行的进程或者暂时不用的程序和数据调出到外存上&#xff0c;…

MATLAB 点云非均匀体素下采样 (8)

MATLAB 点云非均匀体素下采样的不同参数效果测试 (8) 一、实现效果二、算法介绍三、函数说明3.1 函数3.2 参数四、实现代码(详细注释!)五、与固定步长采样法比较5.1 代码5.2 效果一、实现效果 不同参数调整下的非均匀体素下采样结果如下图所示,后续代码复制黏贴即可: 可…

Android 自定义View 之 简易输入框

简易输入框 前言正文① 构造方法② XML样式③ 测量④ 绘制1. 绘制方框2. 绘制文字 ⑤ 输入1. 键盘布局2. 键盘接口3. 键盘弹窗4. 显示键盘5. 相关API 四、使用自定义View五、源码 前言 在日常工作开发中&#xff0c;我们时长会遇到各种各样的需求&#xff0c;不部分需求是可以通…

weblogic ssrf 漏洞复现

一.前言 Weblogic中存在一个SSRF漏洞&#xff0c;利用该漏洞可以发送任意HTTP请求&#xff0c;进而攻击内网中redis、fastcgi等脆弱组件。 二.环境搭建 在docker中开启环境 sudo docker-compose up -d sudo docker-compose ps #查看状态访问http://your-ip:7001/uddiexpl…

做SSM项目的步骤和优化

SSM框架整合 这里说的SSM整合&#xff0c;主要说的是Spring和mybatis之间的整合。因为spring和springMVC都是spring生态系统中的框架&#xff0c;所以spring和springMVC之间的整合是无缝的整合&#xff0c;即&#xff0c;我们在不知不觉中&#xff0c;其实spring和springMVC已…

AI数字人系统搭建源码

AI数字人系统的功能可以根据具体应用场景而定&#xff0c;以下是一些可能的功能&#xff1a; 语音识别和合成&#xff1a;将自然语言转换为机器可读的文本&#xff0c;或将机器生成的文本转换为自然语言的语音输出。 面部表情捕捉&#xff1a;利用摄像头等设备获取用户…

【Docker】使用 Docker 部署 Maven 仓库

在本文中&#xff0c;将介绍如何使用 Docker 部署一个 Maven 本地私服。Maven 私服可以帮助我们管理和共享本地的 Maven 依赖&#xff0c;提高开发效率。本文将使用 Sonatype Nexus 作为 Maven 私服&#xff0c;并使用 Docker Compose 来部署和管理容器。 准备工作 在开始之前…