package cadencechords.modules.metronome import android.media.AudioAttributes import android.media.SoundPool import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch class MetronomeModule : Module() { private var bpm: Int = 120 private var job: Job? = null private var isRunning: Boolean = false private var soundPool: SoundPool? = null private var soundId: Int = 0 override fun definition() = ModuleDefinition { Name("Metronome") OnCreate { setUpSoundPool() } OnDestroy { soundPool?.release() soundPool = null job?.cancel() } Function("setBpm") { bpm: Int -> this@MetronomeModule.bpm = bpm if (isRunning) { stop() start() } } Function("start") { start() } Function("stop") { stop() } } private fun playSound() { soundPool!!.play(soundId, 1f, 1f, 0, 0, 1f) } private fun setUpSoundPool() { val audioAttributes = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build() soundPool = SoundPool.Builder() .setMaxStreams(1) .setAudioAttributes(audioAttributes) .build() soundId = soundPool!!.load(appContext.reactContext, R.raw.low, 1) } private fun start() { if (isRunning) return if (soundPool == null) setUpSoundPool() playSound() isRunning = true job = CoroutineScope(Dispatchers.Default).launch { while(isRunning) { val intervalMillis = (60000 / bpm).toLong() delay(intervalMillis) if (isRunning) { playSound() } } } } private fun stop() { isRunning = false job?.cancel() } }