如何更改 SoundPool

Posted

技术标签:

【中文标题】如何更改 SoundPool【英文标题】:How to change a SoundPool 【发布时间】:2019-06-28 06:09:51 【问题描述】:

我正在开发一个听力训练应用程序,并希望让用户决定想要识别什么样的声音(不同的乐器、合成器声音等)

因此,作为“虚拟声音”,我有一个类似声纳的 ping,我将其加载到 SoundPool 中:

open class PingSoundPool(context: Context) 

    open var mAttributes = AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .setUsage(AudioAttributes.USAGE_GAME)
        .build()

    open var mSoundPool = SoundPool.Builder()
        .setMaxStreams(9)
        .setAudioAttributes(mAttributes)
        .build()

    open var babping = mSoundPool.load(context, R.raw.ab830ping, 1)
    open var aaping = mSoundPool.load(context, R.raw.a220ping, 1)
    open var abbping = mSoundPool.load(context, R.raw.bb233ping, 1)
    open var abping = mSoundPool.load(context, R.raw.b247ping, 1)
[and others]

open fun loadPings(note: Int) 
    println(note.toString())
    if (note == 0) 
    if(note == 1)
        mSoundPool.play(acping, 2.55f, 2.55f, 1, 0, 1f)
    if(note == 2)
    mSoundPool.play(adbping, 2.5f, 2.5f, 1, 0, 1f)
    if(note == 3)
        mSoundPool.play(adping, 2.45f, 2.45f, 1, 0, 1f)
    if(note == 4)
        mSoundPool.play(aebping, 2.4f, 2.4f, 1, 0, 1f)
[and so on]

现在我可以在我的活动中访问它:

companion object 
    lateinit var pingSoundPool: PingSoundPool

在 onCreate 中做pingSoundPool = PingSoundPool(this)

像这样,我可以用FullscreenActivity.pingSoundPool.loadPings(note: Int)播放这些声音中的任何一个

现在问题来了,我想改变声音。第一步是创建一个首选项,给我一个带有所需声音的字符串(“ping”、“pong”、“flute”、“violin”等)。第二步是获取所述字符串并相应地加载 SoundPool。

现在如果我尝试把这个 -

val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val pingsound = sharedPreferences.getString("pingsound", "")

if(pingsound == "ping")
    open var babping = mSoundPool.load(context, R.raw.ab830ping, 1)
    open var aaping = mSoundPool.load(context, R.raw.a220ping, 1)
    open var abbping = mSoundPool.load(context, R.raw.bb233ping, 1)
    open var abping = mSoundPool.load(context, R.raw.b247ping, 1)
[etc]

在我的 SoundPool 类中,我收到一条错误消息,“期待一个成员声明。”/“函数声明必须有一个名称。”

如果我只是复制整个班级的内容并像这样创建一个新班级

class PongSoundPool(context: Context): PingSoundPool(context) 

    override var mAttributes = AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .setUsage(AudioAttributes.USAGE_GAME)
        .build()

    override var mSoundPool = SoundPool.Builder()
        .setMaxStreams(9)
        .setAudioAttributes(mAttributes)
        .build()

    open var babping = mSoundPool.load(context, R.raw.othersound1, 1)
    open var aaping = mSoundPool.load(context, R.raw.othersound2, 1)
    open var abbping = mSoundPool.load(context, R.raw.othersound3, 1)
    open var abping = mSoundPool.load(context, R.raw.othersound4, 1)

并像这样在我的活动中分配pingSoundPool 的值

        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        val pingsound = sharedPreferences.getString("pingsound", "")

        when (pingsound) 
        "ping" -> pingSoundPool = PingSoundPool(this)
        "pong" -> pingSoundPool = PongSoundPool(this)
    

然后,一旦我将首选项更改为“pong”,什么也没有发生。重新启动应用程序时出现运行时错误:

    java.lang.RuntimeException: Unable to start activity ComponentInfocom.example/com.example.FullscreenActivity: java.lang.IllegalArgumentException: Invalid null AudioAttributes
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
    at android.app.ActivityThread.-wrap11(Unknown Source:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
    at android.os.Handler.dispatchMessage(Handler.java:105)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6541)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
 Caused by: java.lang.IllegalArgumentException: Invalid null AudioAttributes
    at android.media.SoundPool$Builder.setAudioAttributes(SoundPool.java:601)
    at com.example.PingSoundPool.<init>(PingSoundPool.kt:16)
    at com.example.PongSoundPool.<init>(PongSoundPool.kt:7)
    at com.example.FullscreenActivity.onCreate(FullscreenActivity.kt:97)
    at android.app.Activity.performCreate(Activity.java:6975)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
    at android.app.ActivityThread.-wrap11(Unknown Source:0) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
    at android.os.Handler.dispatchMessage(Handler.java:105) 
    at android.os.Looper.loop(Looper.java:164) 
    at android.app.ActivityThread.main(ActivityThread.java:6541) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

它在 PingSoundPool 中指向的行是:.setAudioAttributes(mAttributes)

现在我不知道如何实现这一点。有人帮忙吗?


编辑:我仍在寻找解决方案。

我尝试过不加载新的 SoundPool,而是直接从 PongSoundPool 访问加载的声音,如下所示:override var babping = FullscreenActivity.pingSoundPool.mSoundPool.load(context, R.raw.othersound, 1)

当我这样做时,我不会崩溃,而且声音也不会加载。我在 logcat 中得到了sample X not READY,即使是在几分钟之后。

【问题讨论】:

【参考方案1】:

您的第一次崩溃是由于不正确的继承。如果您在PingSoundPool 中实现了var mAttributes 和var mSoundPool,请不要在PongSoundPool 上覆盖它。您也可以通过创建 not PingSoundPool 作为父类来改进您的架构。但是BaseSoundPool 具有最终属性mAttributesmSoundPool 以及抽象方法fun play(note: Int)

abstract class BaseSoundPool(val context: Context) 

    protected var loadedSoundChannels: IntArray? = null

    private val attributes = AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .setUsage(AudioAttributes.USAGE_GAME)
        .build()

    final val soundPool = SoundPool.Builder()
        .setMaxStreams(9)
        .setAudioAttributes(attributes)
        .build()

    fun load(rawSoundIds: IntArray) 
        loadedSoundChannels?.run  unload(this)  // todo implement unload fun by yourself
        loadedSoundChannels = IntArray(rawSoundIds.size) 
            soundPool.load(context, rawSoundIds[it], 1)
        
    

    abstract fun play(note: Int)


class PingSoundPool(context: Context) : BaseSoundPool(context) 
    override fun play(note: Int) 
        loadedSoundChannels?.getOrNull(note)?.let 
            soundPool.play(note, 1f, 1f, 1, 0, 1f);
        
    

还要记住SoundPool.load是异步操作,所以你不能在加载后立即调用play。此外,在加载新的声音数据时,您应该卸载之前加载的数据。 也许你会发现我对SoundPool 的实现很有用,它在不同的 API 上更可预测。

【讨论】:

以上是关于如何更改 SoundPool的主要内容,如果未能解决你的问题,请参考以下文章

SoundPool 中的声音不会在 android 中加载

Android自助餐之SoundPool

如何更改主题更改时浮动操作按钮的颜色?

Ubuntu系统如何更改用户密码

更改模式时如何更改导航阴影颜色?

如何更改分区ID为AF?