implement realtime solfa highlighting with sync
This commit is contained in:
parent
c9fd0fe81b
commit
4e8866a4e7
5 changed files with 260 additions and 113 deletions
|
|
@ -149,6 +149,7 @@ object ScreenSolfa : Screen {
|
|||
LazyVerticalGridTUO(
|
||||
gridTUOData,
|
||||
gridWidthPx = gridWidthPx,
|
||||
sharedScreenModel = sharedScreenModel,
|
||||
onGridWidthMeasured = { width -> gridWidthPx = width }
|
||||
)
|
||||
FlowRow(
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ import androidx.compose.ui.text.SpanStyle
|
|||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.BaselineShift
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import SharedScreenModel
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
val FEUFAROO_TRIOLET_COLOR = Color.DarkGray
|
||||
val FEUFAROO_KEY_CHANGE_COLOR = Color.Blue
|
||||
|
|
@ -228,13 +231,14 @@ class TimeUnitObject (val pTemplate: PTemplate, val prevTUO: TimeUnitObject?, co
|
|||
fun TimeUnitComposable(
|
||||
tuo: TimeUnitObject,
|
||||
stanzaNumber: Int,
|
||||
gridColumnCount: Int
|
||||
gridColumnCount: Int,
|
||||
gridActive: Boolean
|
||||
) {
|
||||
val col = if (tuo.getNum() % 2 == 0) Color(0xff, 0xfa, 0xf7) else Color(0xfb, 0xf3, 0xff)
|
||||
val currentDensity = LocalDensity.current
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(col)
|
||||
.background(if(gridActive) Color.Cyan.copy(alpha = 0.5f) else col)
|
||||
) {
|
||||
if (TimeUnitObject._hasMarker) {
|
||||
val lineHeight = 20.sp
|
||||
|
|
@ -511,6 +515,7 @@ fun AutoResizingText(
|
|||
fun LazyVerticalGridTUO(
|
||||
viewModel: GridTUOData,
|
||||
gridWidthPx: Int,
|
||||
sharedScreenModel: SharedScreenModel,
|
||||
onGridWidthMeasured: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
|
|
@ -547,6 +552,16 @@ fun LazyVerticalGridTUO(
|
|||
|
||||
val currentStanza = viewModel.stanza
|
||||
|
||||
val currentPos by sharedScreenModel.currentPos.collectAsState()
|
||||
val duration by sharedScreenModel.duration.collectAsState()
|
||||
val isPlay by sharedScreenModel.isPlay.collectAsState()
|
||||
val displayedList = tuoList.drop(1)
|
||||
val nbTotalDesRow = displayedList.size
|
||||
val activeRowIndex = if (duration > 0f) {
|
||||
((currentPos / duration) * nbTotalDesRow).toInt().coerceIn(0, nbTotalDesRow - 1)
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
FlowRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
||||
|
|
@ -556,12 +571,16 @@ fun LazyVerticalGridTUO(
|
|||
// state = lazyGridState
|
||||
) {
|
||||
tuoList.drop(n=1).forEachIndexed { relativeIndex, oneTUO ->
|
||||
val isActive = (relativeIndex == activeRowIndex)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(flowRowSize)
|
||||
.background(Color.Transparent)
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
println("Clicked: ${oneTUO.numBlock} / relative index: $relativeIndex FL $flowRowSize GC $gridColumnCount")
|
||||
sharedScreenModel.updatePositionFromPartition(relativeIndex, nbTotalDesRow)
|
||||
println("590: relative $relativeIndex active? $isActive")
|
||||
println("TimeUnitObj:566 Clicked: ${oneTUO.numBlock} / relative index: $relativeIndex FL $flowRowSize totaRow $nbTotalDesRow")
|
||||
} ,
|
||||
onDoubleClick = {
|
||||
println("Double-Clicked: ${oneTUO.numBlock} / relative index: $relativeIndex")
|
||||
|
|
@ -571,7 +590,8 @@ fun LazyVerticalGridTUO(
|
|||
TimeUnitComposable(
|
||||
tuo = oneTUO,
|
||||
currentStanza,
|
||||
gridColumnCount
|
||||
gridColumnCount,
|
||||
gridActive = isActive
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,18 +74,23 @@ fun MainScreenWithDrawer(
|
|||
|
||||
|
||||
|
||||
var isDragging by remember { mutableStateOf(false) }
|
||||
var isPlay by remember { mutableStateOf(false) }
|
||||
var isPos by remember { mutableStateOf(true) }
|
||||
//var isDragging by remember { mutableStateOf(false) }
|
||||
//var isPlay by remember { mutableStateOf(false) }
|
||||
//var isPos by remember { mutableStateOf(true) }
|
||||
var isPlayMid by remember { mutableStateOf(false) }
|
||||
var currentPos by remember { mutableStateOf(0f) }
|
||||
var duration by remember { mutableStateOf(0f) }
|
||||
//var currentPos by remember { mutableStateOf(0f) }
|
||||
val isPlay by sharedScreenModel.isPlay.collectAsState()
|
||||
val isPos by sharedScreenModel.isPos.collectAsState()
|
||||
var isDragging = sharedScreenModel.isDragging
|
||||
val currentPos by sharedScreenModel.currentPos.collectAsState()
|
||||
val duration by sharedScreenModel.duration.collectAsState()
|
||||
var midiFile = "whawyd3.mid"
|
||||
var refreshTrigeer by remember { mutableStateOf(0)}
|
||||
|
||||
var volumelevel by remember { mutableStateOf(0.8f) }
|
||||
val volumelevel by sharedScreenModel.volumeLevel.collectAsState()
|
||||
|
||||
val mediaPlayer = remember(refreshTrigeer) {
|
||||
val player = sharedScreenModel.mediaPlayer
|
||||
/*val mediaPlayer = remember(refreshTrigeer) {
|
||||
MediaPlayer(filename = midiFile, onFinished = {
|
||||
isPos = true
|
||||
isPlay = false
|
||||
|
|
@ -94,9 +99,18 @@ val mediaPlayer = remember(refreshTrigeer) {
|
|||
println("fin de lecture du whawyd3.mid")
|
||||
|
||||
}).apply { setVolume(volumelevel) }
|
||||
}*/
|
||||
LaunchedEffect(isPlay, isPos) {
|
||||
if (isPlay && !isPos) {
|
||||
// while (isPlay && !isPos) {
|
||||
while (true) {
|
||||
sharedScreenModel.updateProgress()
|
||||
delay(100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(isPlay, isPos, mediaPlayer) {
|
||||
/*LaunchedEffect(isPlay, isPos, mediaPlayer) {
|
||||
if (isPlay && !isPos) {
|
||||
val d = mediaPlayer.getDuration().toFloat()
|
||||
if (d > 0) duration = d
|
||||
|
|
@ -108,13 +122,16 @@ LaunchedEffect(isPlay, isPos, mediaPlayer) {
|
|||
delay(100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
LaunchedEffect(isSearchActive) {
|
||||
if (isSearchActive) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
sharedScreenModel.loadNewSong("whawyd3.mid")
|
||||
}
|
||||
ModalNavigationDrawer(drawerState = drawerState, drawerContent = {
|
||||
SimpleDrawerContent(
|
||||
items,
|
||||
|
|
@ -128,11 +145,12 @@ LaunchedEffect(isPlay, isPos, mediaPlayer) {
|
|||
onScannerButtonClick()
|
||||
},
|
||||
onSongSelected = { newSong ->
|
||||
mediaPlayer?.stop()
|
||||
isPos = true
|
||||
isPlay = false
|
||||
currentPos = 0f
|
||||
refreshTrigeer++
|
||||
// mediaPlayer?.stop()
|
||||
// isPos = true
|
||||
// isPlay = false
|
||||
// currentPos = 0f
|
||||
sharedScreenModel.loadNewSong("whawyd3.mid")
|
||||
// refreshTrigeer++
|
||||
}
|
||||
)
|
||||
}, content = {
|
||||
|
|
@ -231,11 +249,12 @@ LaunchedEffect(isPlay, isPos, mediaPlayer) {
|
|||
) {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
isPlayMid = !isPlayMid
|
||||
if(mediaPlayer.getCurrentPosition() != 0L) {
|
||||
mediaPlayer?.seekTo(0)
|
||||
mediaPlayer?.stop()
|
||||
}
|
||||
isPlayMid = !isPlayMid
|
||||
// if(isPlayMid) sharedScreenModel.stopMidi()
|
||||
// if(mediaPlayer.getCurrentPosition() != 0L) {
|
||||
// mediaPlayer?.seekTo(0)
|
||||
// mediaPlayer?.stop()
|
||||
// }
|
||||
}, modifier = Modifier.alpha(0.45f)
|
||||
) {
|
||||
Icon(
|
||||
|
|
@ -250,6 +269,7 @@ LaunchedEffect(isPlay, isPos, mediaPlayer) {
|
|||
onClick = {
|
||||
isExpanded = !isExpanded
|
||||
refreshTrigeer++
|
||||
sharedScreenModel.loadNewSong("whawyd3.mid")
|
||||
}, modifier = Modifier.alpha(0.45f)
|
||||
) {
|
||||
Icon(
|
||||
|
|
@ -267,13 +287,14 @@ LaunchedEffect(isPlay, isPos, mediaPlayer) {
|
|||
Box(
|
||||
modifier = Modifier.fillMaxWidth(0.9f)
|
||||
) {
|
||||
MidiControlPanel(
|
||||
isPause = isPos,
|
||||
currentPos = currentPos,
|
||||
volume = volumelevel,
|
||||
duration = duration,
|
||||
onPlayPauseClick = {
|
||||
if(isPlay){
|
||||
if(player != null) {
|
||||
MidiControlPanel(
|
||||
isPause = isPos,
|
||||
currentPos = currentPos,
|
||||
volume = volumelevel,
|
||||
duration = duration,
|
||||
onPlayPauseClick = {
|
||||
sharedScreenModel.togglePlayPause()/*if(isPlay){
|
||||
mediaPlayer?.pause()
|
||||
isPlay = false
|
||||
isPos = true
|
||||
|
|
@ -284,49 +305,51 @@ LaunchedEffect(isPlay, isPos, mediaPlayer) {
|
|||
mediaPlayer?.play()
|
||||
mediaPlayer?.setVolume(volumelevel)
|
||||
isPlay = true
|
||||
isPos = false
|
||||
}
|
||||
/* if(!isPlay) {
|
||||
if (currentPos == 0f) mediaPlayer.seekTo(0)
|
||||
mediaPlayer?.play()
|
||||
isPlay = true
|
||||
isPos = false
|
||||
} else {
|
||||
mediaPlayer?.stop()
|
||||
isPlay = false
|
||||
isPos = true
|
||||
}*/
|
||||
isPos = false*/
|
||||
},
|
||||
/* if(!isPlay) {
|
||||
if (currentPos == 0f) mediaPlayer.seekTo(0)
|
||||
mediaPlayer?.play()
|
||||
isPlay = true
|
||||
isPos = false
|
||||
} else {
|
||||
mediaPlayer?.stop()
|
||||
isPlay = false
|
||||
isPos = true
|
||||
}*/
|
||||
|
||||
println("je clique pause = $isPos play = $isPlay")
|
||||
// if(isPos) {
|
||||
// mediaPlayer.play()
|
||||
// isPos = false
|
||||
// } else {
|
||||
// mediaPlayer.pause()
|
||||
// isPos = true
|
||||
// }
|
||||
// println("je clique pause = ${sharedScreenModel.isPlay} play = ${sharedScreenModel.isPos}")
|
||||
// if(isPos) {
|
||||
// mediaPlayer.play()
|
||||
// isPos = false
|
||||
// } else {
|
||||
// mediaPlayer.pause()
|
||||
// isPos = true
|
||||
// }
|
||||
/*if (isPlayMid) {
|
||||
// mediaPlayer.seekTo(0f.toLong())
|
||||
mediaPlayer.play()
|
||||
isPlayMid = false
|
||||
}*/
|
||||
},
|
||||
onSeek = { newPos ->
|
||||
currentPos = newPos
|
||||
isDragging = true
|
||||
mediaPlayer.seekTo(newPos.toLong())
|
||||
scope.launch {
|
||||
delay(100)
|
||||
isDragging = false
|
||||
}
|
||||
},
|
||||
mediaPlayer = mediaPlayer,
|
||||
onVolumeChange = { newVolume ->
|
||||
volumelevel = newVolume
|
||||
mediaPlayer?.setVolume(newVolume)
|
||||
println("Changement volume $newVolume -l $volumelevel")
|
||||
}
|
||||
)
|
||||
}*/
|
||||
onSeek = { newPos -> // currentPos = newPos
|
||||
sharedScreenModel.setDragging(true)
|
||||
sharedScreenModel.seekTo(newPos)
|
||||
scope.launch {
|
||||
delay(100)
|
||||
sharedScreenModel.setDragging(false)
|
||||
}
|
||||
println("DrawerUI:335: mihetsika $newPos")
|
||||
},
|
||||
mediaPlayer = player,
|
||||
onVolumeChange = { newVolume -> // volumelevel = newVolume
|
||||
sharedScreenModel.setVolume(newVolume)
|
||||
println("Changement volume $newVolume -l $volumelevel")
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Text("Sélectionner un morceau")
|
||||
}
|
||||
/*Row(
|
||||
modifier = Modifier.align(Alignment.Center).padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -11,8 +13,9 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import mg.dot.feufaro.data.DrawerItem
|
||||
import mg.dot.feufaro.solfa.TimeUnitObject
|
||||
import mg.dot.feufaro.data.getDrawerItems
|
||||
import mg.dot.feufaro.solfa.TimeUnitObject
|
||||
import mg.dot.feufaro.midi.MediaPlayer
|
||||
|
||||
class SharedScreenModel() : ScreenModel {
|
||||
private val _nextLabel = MutableStateFlow<String>("Next ...")
|
||||
|
|
@ -90,7 +93,113 @@ class SharedScreenModel() : ScreenModel {
|
|||
}
|
||||
fun updateSearchTxt(searchValue: String) {
|
||||
_searchTitle.value = searchValue
|
||||
} fun appendData(otherData: String) {
|
||||
}
|
||||
|
||||
private var _mediaPlayer by mutableStateOf<MediaPlayer?>(null)
|
||||
val mediaPlayer: MediaPlayer? get() = _mediaPlayer
|
||||
|
||||
private val _isPlay = MutableStateFlow(false)
|
||||
val isPlay = _isPlay.asStateFlow()
|
||||
|
||||
private val _isPos = MutableStateFlow(true)
|
||||
val isPos = _isPos.asStateFlow()
|
||||
|
||||
private val _isDragging = MutableStateFlow(true)
|
||||
val isDragging = _isDragging.asStateFlow()
|
||||
|
||||
private val _currentPos = MutableStateFlow(0f)
|
||||
val currentPos = _currentPos.asStateFlow()
|
||||
private val _duration = MutableStateFlow(0f)
|
||||
val duration = _duration.asStateFlow()
|
||||
|
||||
private val _volumeLevel = MutableStateFlow(0.8f)
|
||||
val volumeLevel = _volumeLevel.asStateFlow()
|
||||
|
||||
private val _isPlayMid = MutableStateFlow(false)
|
||||
val isPlayMid = _isPlayMid.asStateFlow()
|
||||
|
||||
private var midiFile = "whawyd3.mid"
|
||||
|
||||
fun loadNewSong(newMidiFile: String) {
|
||||
_mediaPlayer?.stop()
|
||||
_isPos.value = true
|
||||
_isPlay.value = false
|
||||
_currentPos.value = 0f
|
||||
_mediaPlayer = MediaPlayer(filename = newMidiFile, onFinished = {
|
||||
_isPos.value = true
|
||||
_isPlay.value = false
|
||||
_currentPos.value = 0f
|
||||
println("fin de lecture du Midi $newMidiFile")
|
||||
})
|
||||
println("New media Player crée $newMidiFile")
|
||||
}
|
||||
// val mediaPlayer =
|
||||
|
||||
fun togglePlayPause() {
|
||||
_mediaPlayer?.let { player ->
|
||||
if (_isPlay.value) {
|
||||
_isPlay.value = false
|
||||
_isPos.value = true
|
||||
player.pause()
|
||||
} else {
|
||||
_isPlay.value = true
|
||||
_isPos.value = false
|
||||
player.play()
|
||||
player.setVolume(_volumeLevel.value)
|
||||
if(currentPos.value == 0f) {
|
||||
player.seekTo(0)
|
||||
}
|
||||
}
|
||||
println("128: Status de isPlay ${_isPlay.value} \nisPos ${_isPos.value} \ncurrentPos ${_currentPos.value} \n volume ${_volumeLevel.value}")
|
||||
// _isPlay.value = !_isPlay.value
|
||||
}
|
||||
}
|
||||
fun stopMidi() {
|
||||
_mediaPlayer?.let { player ->
|
||||
_isPlay.value = false
|
||||
_isPos.value = true
|
||||
player.pause()
|
||||
}
|
||||
}
|
||||
fun seekTo(pos: Float) {
|
||||
_currentPos.value = pos
|
||||
_mediaPlayer?.let { player ->
|
||||
player.seekTo(pos.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
fun setDragging(dragState: Boolean) {
|
||||
_isDragging.value = dragState
|
||||
}
|
||||
fun setVolume(level: Float) {
|
||||
_volumeLevel.value = level
|
||||
_mediaPlayer?.let { player ->
|
||||
player.setVolume(level) }
|
||||
}
|
||||
fun updateProgress(){
|
||||
_mediaPlayer?.let { player->
|
||||
if (_isPlay.value) {
|
||||
val p = player.getCurrentPosition().toFloat()
|
||||
val d = player.getDuration().toFloat()
|
||||
|
||||
if (p >= 0) _currentPos.value = p
|
||||
if ((d > 0) && _duration.value != d) _duration.value = d
|
||||
}
|
||||
}
|
||||
}
|
||||
var currentNoteIndex by mutableStateOf(0f)
|
||||
|
||||
fun updatePositionFromPartition(index: Int, totalRow: Int) {
|
||||
val duration = _duration.value
|
||||
if(totalRow > 0) {
|
||||
currentNoteIndex = index.toFloat()
|
||||
val newPos = (currentNoteIndex / totalRow.toFloat()) * duration
|
||||
seekTo(newPos)
|
||||
println("Shared:196 currentNoteIndex $currentNoteIndex, Index $index et curret = ${_currentPos.value}")
|
||||
}
|
||||
}
|
||||
|
||||
fun appendData(otherData: String) {
|
||||
_nextLabel.value += otherData
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ import java.io.ByteArrayInputStream
|
|||
import java.io.File
|
||||
import javax.sound.midi.MidiSystem
|
||||
import javax.sound.midi.Sequencer
|
||||
import javax.sound.midi.ShortMessage
|
||||
import javax.sound.midi.Synthesizer
|
||||
import javax.sound.sampled.*
|
||||
import javax.sound.midi.Synthesizer //import javax.sound.midi.ShortMessage
|
||||
//import javax.sound.midi.Synthesizer
|
||||
import javax.sound.sampled.AudioFormat
|
||||
import javax.sound.sampled.AudioSystem
|
||||
import javax.sound.sampled.FloatControl
|
||||
|
||||
//private var sequencer: javax.sound.midi.Sequencer?= null
|
||||
actual class MediaPlayer actual constructor(
|
||||
|
|
@ -20,11 +22,12 @@ actual class MediaPlayer actual constructor(
|
|||
private var sequencer: Sequencer? = try {
|
||||
MidiSystem.getSequencer(false)
|
||||
} catch (e: Exception){
|
||||
|
||||
println("Erreur impossible obtenir ${e.message}")
|
||||
null
|
||||
}
|
||||
|
||||
private var synthetizer: Synthesizer? = MidiSystem.getSynthesizer()
|
||||
private var synthetizer = MidiSystem.getSynthesizer() as Synthesizer?
|
||||
|
||||
private var pointA: Long = -1L
|
||||
private var pointB: Long = -1L
|
||||
|
|
@ -37,24 +40,28 @@ actual class MediaPlayer actual constructor(
|
|||
private var currentTempo: Float = 1.0f
|
||||
|
||||
init {
|
||||
sequencer?.open()
|
||||
synthetizer?.open()
|
||||
try {
|
||||
sequencer?.open()
|
||||
synthetizer?.open()
|
||||
val transmitter = sequencer?.transmitter
|
||||
val synthReceiver = synthetizer?.receiver
|
||||
transmitter?.receiver = synthReceiver
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
val transmitter = sequencer?.transmitter
|
||||
val synthReceiver = synthetizer?.receiver
|
||||
transmitter?.receiver = synthReceiver
|
||||
|
||||
val file = File(filename)
|
||||
if (file.exists()){
|
||||
sequencer?.sequence = MidiSystem.getSequence(file)
|
||||
applyVoiceStates()
|
||||
sequencer?.addMetaEventListener { meta ->
|
||||
if(meta.type == 47){
|
||||
onFinished()
|
||||
}
|
||||
val file = File(filename)
|
||||
if (file.exists()){
|
||||
sequencer?.sequence = MidiSystem.getSequence(file)
|
||||
applyVoiceStates()
|
||||
sequencer?.addMetaEventListener { meta ->
|
||||
if(meta.type == 47){
|
||||
onFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actual fun play(){
|
||||
if (sequencer!!.isOpen){
|
||||
sequencer?.start()
|
||||
|
|
@ -67,7 +74,8 @@ actual class MediaPlayer actual constructor(
|
|||
actual fun stop(){
|
||||
sequencer?.stop()
|
||||
sequencer?.microsecondPosition = 0
|
||||
// disableLoop()
|
||||
clearLoop()
|
||||
release()
|
||||
}
|
||||
actual fun getDuration(): Long {
|
||||
return (sequencer?.microsecondLength ?: 0L) / 1000
|
||||
|
|
@ -80,7 +88,7 @@ actual class MediaPlayer actual constructor(
|
|||
}
|
||||
fun release() {
|
||||
sequencer?.close()
|
||||
// synthetizer?.close()
|
||||
synthetizer?.close()
|
||||
}
|
||||
actual fun setVolume(level: Float) {
|
||||
try {
|
||||
|
|
@ -209,32 +217,18 @@ actual class MediaPlayer actual constructor(
|
|||
sequencer?.tempoFactor = factor
|
||||
}
|
||||
fun getTempo(): Float = currentTempo
|
||||
}
|
||||
private val MS8PER_NOTE = 500L
|
||||
|
||||
|
||||
/*
|
||||
|
||||
private var sequencer: javax.sound.midi.Sequencer?= null
|
||||
actual fun MidiPlayer(filename: String, onFinished: () -> Unit) {
|
||||
val file = File(filename)
|
||||
if (file.exists()){
|
||||
StopMidi()
|
||||
sequencer = MidiSystem.getSequencer().apply {
|
||||
open()
|
||||
sequence = MidiSystem.getSequence(file)
|
||||
addMetaEventListener { meta ->
|
||||
if(meta.type == 47){
|
||||
onFinished()
|
||||
fun seekToNote(index: Int) {
|
||||
try {
|
||||
sequencer?.let { sequencer ->
|
||||
if (sequencer.isOpen) {
|
||||
val targetPos = index * MS8PER_NOTE * 1000
|
||||
sequencer.microsecondPosition = targetPos
|
||||
}
|
||||
}
|
||||
start()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actual fun StopMidi() {
|
||||
if(sequencer?.isRunning == true){
|
||||
sequencer?.stop()
|
||||
sequencer?.close()
|
||||
}
|
||||
}*/
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue