文章目录
- 使用 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 ,但是还需要一些降噪处理。