Little Strange Software

スマホアプリの開発を行う LittleStrangeSoftware のブログです。

自作アプリに効果音を!KotlinでSoundPoolを扱ってみる

 どうも!LSSです!

 

 こないだ、

little-strange.hatenablog.com

 ↑の記事で効果音を作ったので、それをSoundPoolってのを使ってアプリ内で鳴らす方法について書きます!

 

 

 

 

はじめに

  前回の自作アプリにBGMを!KotlinでMediaPlayerを扱ってみるに、ちょろっと書きましたが、宣言・初期設定する部分がちょっとメンドクサイです。
Lolipop(Android5.0)からの仕様変更でこういう書き方になったそうです。ネット上により詳しい解説がありますが、古い情報が残ってる場合もあるのでそこだけ要注意で。

 

  それを乗り越えてしまえば、実際に鳴らす部分は1行で済みます!

 

 素材の用意は、BGMと同様、resの下にrawディレクトリを作り、その中に入れておきます。←前回の画像入り解説へのリンク張っときます。

 

 

 

今回の肝!

 

SoundPoolを使うにあたって必要な記載です。

なお、以下に出てくるsp0 snd0 snd1 aa0は任意の名前(好きに変えちゃってOK!な部分)、
atari hazureは任意の音声ファイルのファイル名(から拡張子を除いたもの)です。

 

SoundPoolの インスタンスと、効果音ごとのsoundIDを、Activity直下で宣言

lateinit var sp0:SoundPool
var snd0=0
var snd1=0

 

SoundPoolのインスタンスにて鳴らす音の種類を、onCreate内で設定 あわせて、soundIDに割り当てる音声ファイルを指定

val aa0=AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_GAME).setContentType(AudioAttributes.CONTENT_TYPE_SPEECH).build()
sp0=SoundPool.Builder().setAudioAttributes(aa0).setMaxStreams(2).build()
snd0=sp0.load(this,R.raw.atari,1)
snd1=sp0.load(this,R.raw.hazure,1)

 

再生を開始させたいところに記載

sp0.play(snd0,1.0f,1.0f,0,0,1.0f)

または

sp0.play(snd1,1.0f,1.0f,0,0,1.0f)

 

onDestroy内に記載

sp0.release()



コード全文はこんな感じになります

  SoundPool関連の行を赤文字にしています。

 また、下記コードはサンプル用にxmlのほうでボタンを2つ用意し、それぞれの「onClick」に「bt0clk」「bt1clk」と記載した場合のものです。

 

package jp.littlestrangesoftware.a20191110soundpool

import android.media.AudioAttributes
import android.media.SoundPool
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View

class MainActivity : AppCompatActivity() {

lateinit var sp0:SoundPool
var snd0=0
var snd1=
0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val aa0=AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_GAME).setContentType(AudioAttributes.CONTENT_TYPE_SPEECH).build()
sp0=SoundPool.Builder().setAudioAttributes(aa0).setMaxStreams(2).build()
snd0=sp0.load(this,R.raw.atari,1)
snd1=sp0.load(this,R.raw.hazure,1)
}

fun bt0clk(v:View){
sp0.play(snd0,1.0f,1.0f,0,0,1.0f)
}

fun bt1clk(v:View){
sp0.play(snd1,1.0f,1.0f,0,0,1.0f)
}

override fun onDestroy() {
sp0.release()
super.onDestroy()
}
}

 

 

ちょこっと解説

 SoundPoolインスタンスと、鳴らす音ごとのsoundIDをそれぞれ定義する必要がある事と、SoundPoolを定義するためにAudioAttributesってのもまた定義しないといけない、ってあたりが個人的にメンドクサイって感じる部分です。

  また、AudioAttributesの定義が長いwww
(実際には各ドットの手前で改行して複数行にしても良くて、その方が見やすいコードになります)

val aa0=AudioAttributes
.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build()

↑こういう感じですね。

 

 また、このAudioAttributesは次の行、

sp0=SoundPool.Builder().setAudioAttributes(aa0).setMaxStreams(2).build()

で使用するだけなのでlateinitせずに書きました。

 また、ここの

.setMaxStreams(2)

についてですが、この2という数値部分が「同時に鳴らせる音の数」を指定する部分になります。(増やしすぎるとリソースを圧迫するみたいです)

 

 さらに次の行、

snd0=sp0.load(this,R.raw.atari,1)

ここで、soundIDであるsnd0に音声ファイルatari.wavの音を割り当てると同時に、音声ファイルがメモリに読み込まれます。
 これを「鳴らす直前に読み込み」とかにしちゃうと、読み込みが間に合わなくて鳴らない、って事が起こりうるので、onCreate内であらかじめ読み込んでおくのがよさげ!

 最後のは「優先度」との事ですが、正直、活用の方法が分かりませんでした^^;

 

 んで、実際に鳴らす場面。
 数字がやたら並ぶので、ちょっと見やすいように広げると、

sp0.play( snd0 , 1.0f , 1.0f , 0 , 0 , 1.0f )

sp0.play、SoundPoolのplayメソッドですね。

最初の引数、snd0が鳴らしたいsoundIDで、onCreateで読み込んだモノ。

第二、第三の因数、1.0f,1.0fは「左右の音量」を0.0~1.0の範囲のfloat型の数値で指定します。この例だと左右ともに最大、って指定です。

第四の引数、0はまた優先度、こちらも活用方法は不明です^^;

第五の引数、0はループ回数(繰り返し回数といった方がいいかも)で、
0 だと一回鳴ります。
-1 だと無制限に鳴り続けます。
1 だと一回繰り返されます。(結果、2回鳴る事になります)
2 だと二回繰り返されます。(結果、3回鳴る事になります)

最後の引数、1.0fは再生速度で、0.5~2.0の範囲のfloat型の数値で指定します。
1.0f だと元のwavファイルそのままの音が再生され、
0.5f だとゆっくり、
2.0f だと速く再生される事になります。

 

 

注意点!

 SoundPoolで使用する音声ファイルは、あまり大きいものが使えないそうです。
 ネットで調べた限り「5秒程度」「10秒程度」にとどめておいたほうが良い、との事でした。

 

 また、用意するsoundIDは「256個まで」という制約があります。
(そんなに効果音を利用する事あるんだろうかって、まず思ったwww)

 

 

疑問に思った事があったので、ちょっと実験してみました!

いかにも初心者しか疑問に思わないところかと思いますがw

 var snd0=0

 って、最初にsoundIDを定義していますが、この書き方だとsnd0型推論によって「Int型」って事にされるはずですが、それに対してsnd0=sp0.load(this,R.raw.atari,1)

とかしてるのって、どうなんだろう?と思ったので…onCreate内で、設定が終わった後に続けて、

bt0.text=snd0.toString()
bt1.text=snd1.toString()

 って書いてみました。(bt0とbt1はxmlで書いたレイアウトのほうで2つのbuttonにつけたIDです)

 

 その結果、

f:id:little_strange:20191110221418p:plain

実行すると、画面はこうなりました!

 やっぱsnd0は定義した時の通り、ただのInt型で

snd0=sp0.load(this,R.raw.atari,1)

の右辺が、「音声ファイルをメモリに読み込むと同時に、その識別番号を数値として左辺に渡す」という認識で良さそうです。
 読み込んだデータ自体は別にあって(おそらくsp0の持つデータとして)、snd0はただの数値なんですね。

(クラスのメソッドに対する理解が足りてないのが見え見えになってしまう記事ですね、これw)

 

 

MediaPlayerよりは面倒、でも…

 宣言・設定に3+4行(音2種類の場合)、使用するのに1行、解放に1行、やたら長い行もあってメンドクサイと思いましたが、 

little-strange.hatenablog.com

↑これよりは、はるかにマシか、って思い直しましたw

 

 あと、なんとなくAmazonで「効果音」で検索してみたところ、

こんな感じでAmazonでも売られてるんですね。

 そういえばむかーし、CDショップでこういった効果音CD買ったのに全く使わなかった、って過去をふと思い出しましたw

 

 

追記:公開済みのゲームで、使用しています

 GooglePlayで公開中の足し算ゲーム、 

ぷらすまにあ

の効果音演奏(正解時・ハズレ時)に、このSoundPoolを使用しています。

 実装したらどんな感じなのか見て(聴いて?)みたい方、DLしてみてねw(宣伝)

 

 

参考書籍

↑サンプルコードは全部Javaですが、Androidで「あれやりたい」「これやりたい」がぎっしり詰まっている本。

 辞書的に重宝しています^^

 

 

 

 では今回はこのへんで!

 次回もまたよろしくです^^

 

 

AndroidStudio+Kotlin記事インデックス