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

之前一片文章简单讲述了通过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 ,但是还需要一些降噪处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Android 项目是使用 Android 操作系统和相关开发工具开发的一款移动应用程序。Android 平台提供了丰富的功能和接口,开发人员可以使用 Java 或 Kotlin 等编程语言编写 Android 应用程序。Android 项目也可以是针对特定设备或特定需求进行自定义开发的软件解决方案。 以下是 Android 项目的一些主要特点和资料介绍: 1. 开放源代码:Android 是基于 Linux 内核的开源操作系统,开发人员可以自由获取、使用和修改源代码。 2. 多样化的硬件设备支持:Android 支持多种硬件设备和屏幕尺寸,可以运行于手机、平板电脑、电视、手表等多种设备上。 3. 灵活的用户界面:Android 提供了丰富的用户界面控件和布局方式,可以实现漂亮、个性化的用户界面。 4. 响应式设计:Android 应用程序可以根据设备类型、屏幕尺寸等因素调整布局和显示方式,以适应不同的设备和用户需求。 5. 多媒体支持:Android 支持常见的音频、视频、图像等多媒体格式,可以实现各种多媒体应用。 6. 数据存储:Android 提供了多种数据存储方式,包括 SQLite 数据库、文件存储、SharedPreferences 等。 7. 网络通信:Android 支持多种网络通信方式,包括 HTTP、TCP、UDP 等。 8. 社交媒体集成:Android 提供了集成社交媒体的功能,可以实现与 Facebook、Twitter、Google+ 等社交媒体的交互。 # 注意 1. 本资源仅用于开源学习和技术交流。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。 3. 不可商用,一切后果由使用者承担。
### 回答1: 原生Android TV固件是指运行原生Android操作系统的电视设备的固件。与传统的定制化操作系统不同,原生Android TV固件基于Google开源的Android操作系统,提供了更纯净的用户体验和更丰富的功能。 原生Android TV固件的优势在于其稳定性和兼容性。由于采用了原生Android操作系统,它能够充分利用Google官方提供的最新更新和安全补丁,保证系统的稳定运行。同时,原生Android TV固件与其他Android设备具有高度的兼容性,可以轻松与各种应用和设备进行连接和交互,提供更广阔的应用选择和功能拓展空间。 此外,原生Android TV固件还提供了丰富的媒体功能和智能化体验。它支持高清视频播放、声音输出和网络连接,可以通过各种应用快速访问流媒体服务和在线视频内容。另外,原生Android TV固件还具有语音控制和智能推荐功能,用户可以通过语音命令轻松控制电视和应用,智能推荐算法也能够根据用户的观看习惯和喜好提供个性化的内容推荐。 然而,原生Android TV固件也有一些限制。由于操作系统的开放性和多样性,某些应用可能不适配或无法在原生Android TV上运行。此外,原生Android TV固件在硬件方面对电视设备的要求较高,低端设备可能无法提供流畅的使用体验。 总体而言,原生Android TV固件是一种值得推荐的选择,它提供了更稳定、兼容和智能化的电视体验,让用户能够享受到更多的媒体和应用内容。 ### 回答2: 原生Android TV固件是指由Google官方开发和维护的操作系统软件,专门为智能电视设备设计的。它采用了与智能手机和平板电脑相似的Android操作系统,并针对大屏幕和遥控器进行了优化。 原生Android TV固件具有以下特点: 1. 用户界面:原生Android TV固件提供了一个简洁、直观的用户界面,用户可以通过遥控器轻松地导航和操作电视。主屏幕上的应用图标和内容推荐帮助用户快速访问他们喜欢的应用和媒体内容。 2. 应用和媒体内容:原生Android TV固件兼容众多电视应用和媒体服务,包括影片、音乐、游戏和社交媒体等。用户可以通过Google Play商店下载和安装各种应用程序,以满足他们的个性化需求。 3. 语音控制:原生Android TV固件支持语音搜索和语音操作,用户可以通过麦克风遥控器或是电视内置的语音识别功能轻松实现语音控制。 4. 联网功能:原生Android TV固件可以通过内置的Wi-Fi或有线网络连接到互联网,用户可以浏览网页、观看在线视频和进行网络游戏等。 5. 兼容性:原生Android TV固件具有良好的兼容性,可与各种电视设备和外部设备(如音频系统、游戏手柄等)配对使用,并通过HDMI接口提供高清视频输出。 总之,原生Android TV固件为用户提供了一个全面的智能电视体验,结合了丰富的应用和媒体内容、简单直观的界面、语音控制和联网功能。它使用户能够轻松访问他们的喜爱内容,并提供了个性化、智能化的娱乐和信息服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值