Android开发音效增强中铃声播放Ringtone及声音池调度SoundPool的讲解及实战(超详细 附源码)

news2025/7/15 13:26:54

需要源码请点赞关注收藏后评论区留下QQ~~~

一、铃声播放

虽然媒体播放器MediaPlayer既可用来播放视频,也可以用来播放音频,但是在具体的使用场合,MediaPlayer存在某些播音方面的不足之处 包括以下几点

1:初始化比较消耗资源 尤其是播放短铃声时反应偏慢

2:同时只能播放一个媒体文件  无法同时播放多个声音

3:只能播放已经完成转码的音频文件,无法播放原始音频,也不能进行流式播放

Android提供了铃声工具Ringtone处理铃声的播放 下面式Ringtone的常用方法

play 开始播放铃声

stop 停止播放铃声

isPlaying  判断铃声是否正在播放 

下面是实战效果 可以在下拉框中选择不同的声音

此处带着耳机或者真机测试可以听到不同的声音~~

 

 代码如下

Java类

package com.example.audio;

import android.annotation.SuppressLint;
import android.content.Context;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

@SuppressLint("DefaultLocale")
public class RingToneActivity extends AppCompatActivity {
    private TextView tv_volume; // 声明一个文本视图对象
    private Ringtone mRingtone; // 声明一个铃声对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ring_tone);
        tv_volume = findViewById(R.id.tv_volume);
        initVolumeInfo(); // 初始化音量信息
        initRingSpinner(); // 初始化铃声下拉框
        // 生成本App自带的铃声文件res/raw/ring.ogg的Uri实例
        uriArray[uriArray.length - 1] = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.ring);
    }

    // 初始化音量信息
    private void initVolumeInfo() {
        // 从系统服务中获取音频管理器
        AudioManager audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        // 获取铃声的最大音量
        int maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_RING);
        // 获取铃声的当前音量
        int nowVolume = audio.getStreamVolume(AudioManager.STREAM_RING);
        String desc = String.format("当前铃声音量为%d,最大音量为%d,请先将铃声音量调至最大",
                nowVolume, maxVolume);
        tv_volume.setText(desc);
    }

    // 初始化铃声下拉框
    private void initRingSpinner() {
        ArrayAdapter<String> ringAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, ringArray);
        Spinner sp_ring = findViewById(R.id.sp_ring);
        sp_ring.setPrompt("请选择要播放的铃声");
        sp_ring.setAdapter(ringAdapter);
        sp_ring.setOnItemSelectedListener(new RingSelectedListener());
        sp_ring.setSelection(0);
    }

    private String[] ringArray = {"来电铃声", "通知铃声", "闹钟铃声",
            "相机快门声", "视频录制声", "门铃叮咚声"};
    private Uri[] uriArray = {
            RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), // 来电铃声
            RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), // 通知铃声
            RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM), // 闹钟铃声
            Uri.parse("file:///system/media/audio/ui/camera_click.ogg"), // 相机快门声
            Uri.parse("file:///system/media/audio/ui/VideoRecord.ogg"), // 视频录制声
            null
    };

    class RingSelectedListener implements OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            if (mRingtone != null) {
                mRingtone.stop(); // 停止播放铃声
            }
            // 从铃声文件的URI中获取铃声对象
            mRingtone = RingtoneManager.getRingtone(RingToneActivity.this, uriArray[arg2]);
            mRingtone.play(); // 开始播放铃声
        }

        public void onNothingSelected(AdapterView<?> arg0) {}
    }

    @Override
    protected void onStop() {
        super.onStop();
        mRingtone.stop(); // 停止播放铃声
    }

}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_volume"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:paddingLeft="5dp"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="待播放的铃声:"
            android:textColor="@color/black"
            android:textSize="17sp" />
        
        <Spinner
            android:id="@+id/sp_ring"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="left|center"
            android:spinnerMode="dialog" />
        
    </LinearLayout>
    
</LinearLayout>

二、声音池调度

对于MediaPlayer无法同时播放多个声音的问题,Android提供了声音池工具SoundPoo,通过声音池即可同时播放多个音频,声音池可以事先加载多个音频,在需要时再播放指定音频 有以下几个好处

1:资源占用最小 不像MediaPlayer那么占用资源

2:相对MediaPlayer来说延迟时间非常短

3:可以同时播放多个音频 从而实现游戏过程中多个声音叠加的情景

SoundPool同样有以下几个缺陷

1:声音池最大只能申请1MB的内存 只能播放一些很短的声音片段

2:不要轻易调用pause和stop方法 容易引起App崩溃

3:建议使用声音池播放ogg格式的音频 对WAV格式的支持不太友好

4:待播放的音频要提前加载到声音池中 不要等到播放的时候才加载 否则可能播放不出来 

实战效果如下

点击按钮即可播放对应的音频 注意此处可以多种音频同时播放

 

 代码如下

Java类

package com.example.audio;

import android.annotation.SuppressLint;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.HashMap;

@SuppressLint("DefaultLocale")
public class SoundPoolActivity extends AppCompatActivity implements OnClickListener {
    private TextView tv_volume; // 声明一个文本视图对象
    private SoundPool mSoundPool; // 初始化一个声音池对象
    private HashMap<Integer, Integer> mSoundMap = new HashMap<>(); // 声音编号映射

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sound_pool);
        tv_volume = findViewById(R.id.tv_volume);
        findViewById(R.id.btn_play_all).setOnClickListener(this);
        findViewById(R.id.btn_play_first).setOnClickListener(this);
        findViewById(R.id.btn_play_second).setOnClickListener(this);
        findViewById(R.id.btn_play_third).setOnClickListener(this);
        initVolumeInfo(); // 初始化音量信息
        initSound(); // 初始化声音池
    }

    // 初始化音量信息
    private void initVolumeInfo() {
        // 从系统服务中获取音频管理器
        AudioManager audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        // 获取音乐的最大音量
        int maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        // 获取音乐的当前音量
        int nowVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
        String desc = String.format("当前音乐音量为%d,最大音量为%d,请先将音乐音量调至最大",
                nowVolume, maxVolume);
        tv_volume.setText(desc);
    }

    // 初始化声音池
    private void initSound() {
        // 初始化声音池,最多容纳三个声音
        AudioAttributes attributes = new AudioAttributes.Builder()
                .setLegacyStreamType(AudioManager.STREAM_MUSIC).build();
        SoundPool.Builder builder = new SoundPool.Builder();
        builder.setMaxStreams(3).setAudioAttributes(attributes);
        mSoundPool = builder.build();
        loadSound(1, R.raw.beep1); // 加载第一个声音
        loadSound(2, R.raw.beep2); // 加载第二个声音
        loadSound(3, R.raw.ring); // 加载第三个声音
    }

    // 把音频资源添加进声音池
    private void loadSound(int seq, int resid) {
        // 把声音文件加入到声音池中,同时返回该声音文件的编号
        int soundID = mSoundPool.load(this, resid, 1);
        mSoundMap.put(seq, soundID);
    }

    // 播放指定序号的声音
    private void playSound(int seq) {
        int soundID = mSoundMap.get(seq);
        // 播放声音池中指定编号的音频
        mSoundPool.play(soundID, 1.0f, 1.0f, 1, 0, 1.0f);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_play_all) { //同时播放三个声音
            playSound(1); // 播放指定序号的声音
            playSound(2); // 播放指定序号的声音
            playSound(3); // 播放指定序号的声音
        } else if (v.getId() == R.id.btn_play_first) { // 播放第一个声音
            playSound(1); // 播放指定序号的声音
        } else if (v.getId() == R.id.btn_play_second) { // 播放第二个声音
            playSound(2); // 播放指定序号的声音
        } else if (v.getId() == R.id.btn_play_third) { // 播放第三个声音
            playSound(3); // 播放指定序号的声音
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mSoundPool != null) {
            mSoundPool.release(); // 释放声音池资源
        }
    }

}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_volume"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

    <Button
        android:id="@+id/btn_play_all"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="播放所有ogg音效"
        android:textColor="@color/black"
        android:textSize="17sp" />

        <Button
            android:id="@+id/btn_play_first"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="播放第一支ogg"
            android:textColor="@color/black"
            android:textSize="17sp" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/btn_play_second"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="播放第二支ogg"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Button
            android:id="@+id/btn_play_third"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="播放第三支ogg"
            android:textColor="@color/black"
            android:textSize="17sp" />
    </LinearLayout>

</LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/38156.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件开发工程师笔试记录--关键路径,浮点数计算,地址变换,中断向量,I/O接口,海明码

时间&#xff1a;2022年11月26日 10&#xff1a;00 -11&#xff1a;00 &#xff08;可提前登录15分钟&#xff09; 公司&#xff1a;XX&#xff08;rongyu&#xff09; 岗位&#xff1a;软件开发工程师&#xff08;我的简历语言是Java&#xff09; 题型&#xff1a;选择题&…

一次应用多次fgc原因的排查及解决

应用多次fgc性能排查&#xff08;一次抢购引起的性能问题&#xff09; 大家好我是魔性的茶叶&#xff0c;今天分享一个项目jvm多次fgc的整个排查流程 上班后不久运维突然通知我们组&#xff0c;有一个应用在短时间内多次fgc&#xff0c;即将处于挂掉的状态。 首先我登录skyw…

客户听不进去,很强势,太难沟通了,怎么办?

案例 最近接手一项目,项目范围蔓延,成本超支,进度延期,问项目经理怎么回事? 项目经理C无奈诉苦到:用户A大领导,是业主B的领导,咱们业主B不敢反驳他,让我直接与用户A对接,说A提的需求,默认答应,我有什么办法啊,只能接了。 分析 经过复盘,导致项目蔓延的主要原因…

【参赛经历总结】第五届“传智杯”全国大学生计算机大赛(初赛B组)

成绩 比赛链接 比赛过程 被虐4h 比赛体验不是很好我开始五分后才在qq上看到题第一题签到题第二题调了1h吧&#xff0c;算法题做的不多&#xff0c;题目没读全&#xff0c;wa了几发&#xff0c;有几发是网络问题&#xff0c;交了显示失败&#xff0c;但还是判wa了第三题知道…

mulesoft What‘s the typeOf(payload) of Database Select

Whats the typeOf payload of Database SelectQuestionOptionExplanationMule ApplicationDebugQuestion Refer to the exhibit. The Database Select operation returns five rows from a database. What is logged by the Logger component? Option A “Array” B “Objec…

第五届传智杯-初赛【B组-题解】

A题 题目背景 在宇宙射线的轰击下&#xff0c;莲子电脑里的一些她自己预定义的函数被损坏了。 对于一名理科生来说&#xff0c;各种软件在学习和研究中是非常重要的。为了尽快恢复她电脑上的软件的正常使用&#xff0c;她需要尽快地重新编写这么一些函数。 你只需输出fun(a,…

数据库错误知识集3(摘)

&#xff08;摘&#xff09; 逻辑独立性是外模式不变&#xff0c;模式改变时&#xff0c;如增加新的关系&#xff0c;新的属性&#xff0c;改变属性的数据类型&#xff0c;由数据库管理员对各个外模式/模式的映像做相应改变&#xff0c;可以使得外模式不变&#xff0c;因为应用…

sdfsdfasfsdfdsfasfdfasfasadsfasdfasf

白包api 图片编辑功能&#xff1a; 1、你变体下 SnapEditMenuManager 的 这个方法 public void initEditMainMenu(final EditUITabMenu editUITabMenu) 换成你需要的配置&#xff0c;需要哪个功能 就拿哪一坨&#xff0c;别拿多了 //相框 MenuBean menuBean new MenuBean…

线程的学习

v# 1. 线程基本概念 1.1进程 进程是指运行中的程序&#xff0c;比如启动了QQ&#xff0c;就相当于启动了一个进程&#xff0c;操作系统会为该进程分配空间&#xff1b;当我们使用迅雷&#xff0c;就相当于又启动了一个进程&#xff0c;操作系统将为迅雷分配新的内存空间&…

坦克大战①

1. java绘图技术 JFrame&#xff1a;画板 Jpanel&#xff1a;画板 Graphics&#xff1a;画笔 初始化画板&#xff0c;定义画框的大小&#xff0c;设置可视化&#xff1b; 1.1 画坦克 初始化我方坦克、敌方坦克 绘图&#xff1a;(1)更改背景颜色&#xff1b;(2)绘制敌方坦克…

【Docker】常用命令

背景 当下&#xff0c;docker技术已成为开发者常用的技术栈。不管是开发过程中需要应对的各种复杂多变的开发环境的搭建&#xff0c;还是生产部署环节需要的自动化运维&#xff0c;都离不开docker&#xff0c;本文简单介绍相关命令的含义&#xff0c;用作平时查询使用。 1. doc…

【计算机毕业设计】38.网上轰趴预订系统

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘要 在网上轰趴发展的整个过程中&#xff0c;网上轰趴预定担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类网上轰趴程序也在不断改进。本课题所设计的网上轰趴预定系统&#xff0c;使用SSM框架…

一文看懂Transformer(详解)

文章目录Transformer前言网络结构图&#xff1a;EncoderInput EmbeddingPositional Encoderself-attentionPadding maskAdd & NormFeed ForwardDecoderinputmasked Multi-Head Attentiontest时的Decoder预测Transformer 前言 Transformer最初是用于nlp领域的翻译任务。 …

大屏图表,ECharts 从“熟练”到入门

&#x1f4d6;阅读本文&#xff0c;你将 了解 配置驱动 的思想理解 Echarts 基本概念了解 graphic 和 动画基本玩法。了解 Echarts 基底组件的封装的思路 一、不是标题党&#xff01;Echarts&#xff0c;简历上人均 “熟练”&#xff1f; 公司最近在招外包&#xff0c;而因为…

基于ASP学生资助管理系统的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做ASP程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问题…

用Python蹭别人家图片接口,做一个【免费图床】吧

打开本文&#xff0c;相信你确实需要一个免费且稳定的图床&#xff0c;这篇博客就让你实现。 文章目录⛳️ 谁家的图床⛳️ 实战编码⛳️ 第一轮编码⛳️ 第二轮编码⛳️ 第三轮编码⛳️ 第四轮编码⛳️ 谁家的图床 这次咱们用新浪微博来实现【免费图床应用】&#xff0c;通过…

栈浅谈(上)

目录 栈的定义 栈的实现 初始化 判断栈是否为空 入栈操作 获取栈顶元素 出栈操作 遍历栈 销毁栈 完整代码演示 栈—STL 基本操作 例题 参考代码 栈的定义 说到栈&#xff0c;一些不会计算机语言的“小白”&#xff08;我就是&#xff09;就会想到栈道之类的词语…

基于JavaWeb的婚恋交友网站设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

如何平衡新老策略的好与坏,一道常见风控送命题解答

作为一个风控策略从业者&#xff0c;在做风险管理的过程中一直在做的就是&#xff0c;不断的挖掘有效的变量特征来填充风控决策体系&#xff0c;使决策体系的功效变大变强&#xff0c;最终形成一套可变的稳定风险护盾。我们常见的一个场景比如如何筛选一些新策略来挑战老策略&a…

【C++中预处理语句 include、define、if】

1.预处理阶段 预处理阶段&#xff0c;在实际发生编译之前就根据对应的预处理语句进行操作&#xff0c;等到预处理阶段完成之后才进行编译阶段 。 2.预处理语句 预处理语句主要有include、define、if 和 program。利用 # 进行标记 2.1 include语句 include语句就是将所包含的…