diff --git a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt index 4b013ec..ff4839c 100644 --- a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt +++ b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt @@ -1,129 +1,142 @@ package mg.dot.feufaro.midi -import java.io.File import android.media.MediaPlayer -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import java.io.File +import java.io.FileInputStream private var androidMediaPlayer: MediaPlayer?= null actual class MediaPlayer actual constructor(filename: String, onFinished: () -> Unit) { - private val player = androidMediaPlayer?.apply { - setDataSource(filename) - prepare() - setOnCompletionListener { - seekTo(0) - onFinished() - } -// val file = File(android.app.Instrumentation().context.filesDir, filename) -// if(file.exists()){ -// androidMediaPlayer?.setDataSource(file.path) -// androidMediaPlayer?.prepare() -// androidMediaPlayer?.setOnCompletionListener { -// onFinished() -// } -// } else { -// println("Fichier midi non trouvée") -// } - } + private var mediaPlayer: MediaPlayer? = MediaPlayer() private val voiceStates = mutableListOf(true, true, true, true) - private var currentGlobalVolume: Float = 0.8f +// private var currentGlobalVolume: Float = 0.8f private var pointA: Long = -1L private var pointB: Long = -1L + + private val playerScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) + private var abJob: Job? = null + private var isLoopingAB: Boolean = false + + init { + try { + val file = File(filename) + if (file.exists()) { + val fis = FileInputStream(file) + mediaPlayer?.setDataSource(fis.fd) + mediaPlayer?.prepare() + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + val params = mediaPlayer?.playbackParams ?: android.media.PlaybackParams() + params.speed = 1.0f + mediaPlayer?.playbackParams = params + } + fis.close() + + mediaPlayer?.setOnCompletionListener { + onFinished() + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + actual fun play() { -// player?.start() + mediaPlayer?.start() } actual fun pause() { -// player?.pause() + mediaPlayer?.pause() } actual fun stop() { -// player?.stop() + mediaPlayer?.stop() + mediaPlayer?.prepare() + mediaPlayer?.seekTo(0) + clearLoop() } + actual fun getDuration(): Long { -// player!!.duration.toLong() ?: 0L - return 4.toLong() + return mediaPlayer?.duration?.toLong() ?: 0L } + actual fun getCurrentPosition(): Long { -// player!!.currentPosition.toLong() - return 4.toLong() + return mediaPlayer?.currentPosition?.toLong() ?: 0L } + actual fun seekTo(position: Long) { -// player!!.seekTo(position.toInt()) + mediaPlayer?.seekTo(position.toInt()) + } +actual fun setVolume(level: Float) { + mediaPlayer?.setVolume(level, level) } - actual fun setVolume(level: Float){ -// currentGlobalVolume =level -// player?.setVolume(level, level) + actual fun setPointA() { + pointA = mediaPlayer?.currentPosition?.toLong() ?: 0L } - actual fun getLoopState() = Triple(pointA, pointB, isLoopingAB) - - actual fun toggleVoice(index: Int): Unit{ - voiceStates[index] = !voiceStates[index] - } - - actual fun getVoiceStates(): List = voiceStates - - actual fun changeInstru(noInstru: Int): Unit{ - - } - - actual fun setPointA(): Unit{ -// pointA = getCurrentPosition() - } - - actual fun setPointB(): Unit{ - pointB = getCurrentPosition() - if(pointB > pointA) { + actual fun setPointB() { + pointB = mediaPlayer?.currentPosition?.toLong() ?: 0L + if (pointB > pointA && pointA != -1L) { isLoopingAB = true startABMonitor() } } - actual fun clearLoop(): Unit{ + actual fun clearLoop() { isLoopingAB = false + pointA = -1L + pointB = -1L + abJob?.cancel() } - private fun startABMonitor(){ - GlobalScope.launch { - while(isLoopingAB){ -// if ((player?.currentPosition ?: 0) >= pointB){ -// player?.seekTo(pointA?.toInt()) -// } + + private fun startABMonitor() { + abJob?.cancel() + abJob = playerScope.launch { + while (isLoopingAB) { + val currentPos = mediaPlayer?.currentPosition?.toLong() ?: 0L + if (currentPos >= pointB) { + mediaPlayer?.seekTo(pointA.toInt()) + } delay(50) } } } - actual fun setTempo(factor: Float){ + actual fun getLoopState() = Triple(pointA, pointB, isLoopingAB) + actual fun toggleVoice(index: Int) { + voiceStates[index] = !voiceStates[index] + println("Toggle voice $index (Limitation: Nécessite SoundFont/Fluidsynth sur Android)") + } + + actual fun getVoiceStates(): List = voiceStates + + actual fun changeInstru(noInstru: Int) { + } + + actual fun setTempo(factor: Float) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + mediaPlayer?.let { + val params = it.playbackParams + params.speed = factor + it.playbackParams = params + } + } } actual fun getCurrentBPM(): Float { return 120f } -} -/* -actual fun MidiPlayer(filename: String, onFinished: () -> Unit) { - StopMidi() - val file = File(android.app.Instrumentation().context.filesDir, filename) - if(file.exists()){ - androidMediaPlayer?.setDataSource(file.path) - androidMediaPlayer?.prepare() - androidMediaPlayer?.setOnCompletionListener { - onFinished() - } - androidMediaPlayer?.start() - } -} -actual fun StopMidi() { - androidMediaPlayer?.let { - if (it.isPlaying) { - it.stop() - it.release() - } + fun release() { + mediaPlayer?.release() + mediaPlayer = null + playerScope.coroutineContext.cancel() } - androidMediaPlayer = null -}*/ +} \ No newline at end of file