はじめに
メトロノームアプリを作る際に、テンポを毎回更新するところで少し詰まったので記事にしました。
作ったのはこんなアプリです。
- 中央のボタンでスタート・ストップ
- NumberPickerで速度調整(再生中の速度変更も可)
音がないのでわかりづらいですが、スタートボタンを押すと表示されているテンポで音が流れます。
画面を作る
NumberPicker(id:tempoPicker
)と再生・停止用にImageView(id:startButton
)だけあれば大丈夫です。
(ボタンをImageViewで作ってます)
素材を入れる
- ボタンの画像を切り替えるための「start.png」「stop.png」
- メトロノームのSE用の「metronome.mp3」
を入れておきます。SEは途中でカットされるので1秒以上あるものならなんでも大丈夫です。
コードを書く
MainActivity.ktに以下のコードを書いてください。
package パッケージ名
import android.media.MediaPlayer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.concurrent.timer
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var tempo : Int = 40 //テンポ
val handler : Handler = Handler() //タイマーで画面の変更が行われるときに必要
var interval : Long = (60000/tempo).toLong() //テンポをインターバルに変換
val mediaPlayer = MediaPlayer.create(this, R.raw.metronome)
var metronome = timer(period = interval) {}
var isPlaying : Boolean = false //メトロノームの再生状態(true -> 再生中, false -> 停止)
tempoPicker.minValue = 40
tempoPicker.maxValue = 208
fun setMetronome() {
interval = (60000/tempo).toLong()
metronome.cancel()
metronome = timer(period = interval) {
handler.post {
mediaPlayer.stop()
mediaPlayer.prepare()
mediaPlayer.start()
}
}
}
tempoPicker.setOnValueChangedListener { picker, oldVal, newVal ->
tempo = newVal
if(isPlaying) setMetronome()
}
startButton.setOnClickListener {
when(isPlaying) {
true -> {
startButton.setImageResource(R.drawable.start)
metronome.cancel()
isPlaying = false
}
false -> {
setMetronome()
startButton.setImageResource(R.drawable.stop)
isPlaying = true
}
}
}
}
}
とりあえずこれで動きます。
詰まった点
Timer
タイマーの引数period
には待機時間が入ります。
今回のアプリでは、テンポの更新に伴って待機時間も連動するようにしたかったのですが、変数を指定しただけでは反映されませんでした。
なので、テンポが更新される度に下記のようにタイマーを再定義する必要があります。
また、metronome.cancel()
を直前に入れないと新しいタイマーがどんどん生成されてしまうので注意です。
//更新する度に実行
metronome.cancel()
metronome = timer(period = interval) {
//定期的に実行したい命令
}
MediaPlayer
メトロノームのように、短いSEを定期的に鳴らす場合は一度前の音をstop()
で停止し、prepare()
、start()
の順で行う必要があります。
mediaPlayer.stop()
mediaPlayer.prepare()
mediaPlayer.start()