音を扱うクラスとしてSoundPoolがあり、比較的短いものを遅延が少なく再生できるのでゲームの効果音の再生などに向いています。
下はAnimationとsound再生を同時に行った例です。
2024.1.1
SoundPool
Audioの再生にはこのSoundPool以外にもMediaPlayerやAudioTrackがあります。
- SoundPool
- 再生可能なフォーマット:mp3, ogg, wav(非圧縮), etc.
- Audio Mixerのように複数の音源を重ねて再生できる
- 事前にデコードしてメモリに展開するので低レイテンシー
- 再生できるのは5秒程度
- 繰り返し再生
- 再生スピードを可変できる
- それぞれの音源に優先度をつけられる
- ゲームの効果音再生に適している
- MediaPlayer
- Supported media formatsおそらくこれらをサポート
- BGMとしてあるいは楽曲の再生用途で使う
- AudioTrack
- メモリに直接展開して再生するので低レイテンシー
- 細かい操作ができる(してあげないといけない)
SoundPool
SoundPoolはLollipop以降使い方が変わりました。
- val audioAttributes = AudioAttributes.Builder()…
- val soundPool = SoundPool.Builder()…
- soundPool.load(…)
- soundPool.play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
- soundID: load()で決めたID
- leftVolume, rightVolume: 左音量, 右音量(range = 0.0 to 1.0)
- priority: (0 = lowest priority) default 0
- loop: (0 = no loop, -1 = loop forever)
- rate: (1.0 = normal playback, range 0.5 to 2.0)
SoundPoolは、事前に音源をロードしておく必要があります。いきなりロード再生ではうまくいきません。そのためアプリとして音源を抱えたままになりかねないため、release, unload等を使って
Out Of memory
に陥らない対策が必要です。
SoundPool、AudioAttributes
wavファイルを res/raw/ 以下に置きます。
「res」から「New」「Directory」
res以下にできたディレクトリーにwavファイルを入れます。参考までに以下wavファイルを名前をつけて保存できます。
one two
多くの音声ファイルをフォルダ階層で管理したい場合はassetsに置きます。ただし、assetsからのデータ取得はAssetManagerを使う作業が必要です。
サンプルコード
MainActivity.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
//package com.example.kotlinsoundpool import android.media.AudioAttributes import android.media.SoundPool import android.os.Bundle import android.util.Log import android.view.animation.RotateAnimation import android.widget.Button import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat class MainActivity : AppCompatActivity() { private lateinit var soundPool: SoundPool private var soundOne = 0 private var soundTwo = 0 private lateinit var button1: Button private lateinit var button2: Button override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContentView(R.layout.activity_main) ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } val audioAttributes = AudioAttributes.Builder() // USAGE_MEDIA // USAGE_GAME .setUsage(AudioAttributes.USAGE_GAME) // CONTENT_TYPE_MUSIC // CONTENT_TYPE_SPEECH, etc. .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build() soundPool = SoundPool.Builder() .setAudioAttributes(audioAttributes) // ストリーム数に応じて .setMaxStreams(2) .build() // one.wav をロードしておく soundOne = soundPool.load(this, R.raw.one, 1) // two.wav をロードしておく soundTwo = soundPool.load(this, R.raw.two, 1) // load が終わったか確認する場合 soundPool.setOnLoadCompleteListener { soundPool, sampleId, status -> Log.d("debug", "sampleId=$sampleId") Log.d("debug", "status=$status") } button1 = findViewById(R.id.button1) button2 = findViewById(R.id.button2) button1.setOnClickListener { v -> // one.wav の再生 // play(ロードしたID, 左音量, 右音量, 優先度, ループ,再生速度) soundPool.play(soundOne, 1.0f, 1.0f, 0, 0, 1.0f) val bw1: Float = button1.getWidth().toFloat() val bh1: Float = button1.getHeight().toFloat() // ボタンの回転アニメーション val buttonRotation = RotateAnimation( 0f, 360f, bw1 / 2.0f, bh1 / 2.0f ) buttonRotation.duration = 2000 button1.startAnimation(buttonRotation) } button2.setOnClickListener { v -> // two.wav の再生 soundPool.play(soundTwo, 1.0f, 1.0f, 1, 0, 1.0f) val bw2: Float = button2.getWidth().toFloat() val bh2: Float = button2.getHeight().toFloat() // ボタンの回転アニメーション val buttonRotation = RotateAnimation( 0f, 360f, bw2 / 2.0f, bh2 / 2.0f ) buttonRotation.duration = 2000 button2.startAnimation(buttonRotation) } } } |
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/button1" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_margin="40dp" android:text="@string/one" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.497" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.35" /> <Button android:id="@+id/button2" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_margin="40dp" android:text="@string/two" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.573" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.65" /> </androidx.constraintlayout.widget.ConstraintLayout> |
strings.xml
1 2 3 4 5 |
<resources> <string name="app_name">KotlinSoundPool</string> <string name="one">One</string> <string name="two">Two</string> </resources> |
ついでに、ボタンを押してSoundPoolで音を再生し、ボタンを回転させてみました。
References:
SoundPool
AudioAttributes