diff --git a/api/src/commonMain/kotlin/com/eimsound/daw/api/clips/AudioClip.kt b/api/src/commonMain/kotlin/com/eimsound/daw/api/clips/AudioClip.kt index afff88c4..e137ddaf 100644 --- a/api/src/commonMain/kotlin/com/eimsound/daw/api/clips/AudioClip.kt +++ b/api/src/commonMain/kotlin/com/eimsound/daw/api/clips/AudioClip.kt @@ -1,6 +1,5 @@ package com.eimsound.daw.api.clips -import com.eimsound.audioprocessor.AudioProcessorParameter import com.eimsound.audiosources.AudioSource import com.eimsound.dsp.data.AudioThumbnail import com.eimsound.dsp.data.EnvelopePointList @@ -13,8 +12,8 @@ import java.nio.file.Path interface AudioClip : Clip, AutoCloseable { var target: AudioSource val timeInSeconds: Float - val speedRatio: AudioProcessorParameter - val semitones: AudioProcessorParameter + var speedRatio: Float + var semitones: Float var timeStretcher: String var bpm: Float @Transient diff --git a/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt b/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt index b9f82927..222d5369 100644 --- a/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt +++ b/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt @@ -5,7 +5,10 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.text.BasicTextField +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -14,6 +17,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.UiComposable +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.PointerIcon @@ -197,13 +201,25 @@ fun DropdownSelector( { filter.value = it.ifEmpty { null } }, boxModifier.pointerHoverIcon(PointerIcon.Hand), label = if (label == null) null else ({ Text(label) }), - singleLine = true + singleLine = true, + suffix = { + Icon( + Icons.Filled.ExpandMore, "Expand", + Modifier.size(20.dp).pointerHoverIcon(PointerIcon.Hand).clip(CircleShape).clickable { } + ) + } ) else CustomTextField( filter.value ?: selected?.displayName ?: "", { filter.value = it.ifEmpty { null } }, boxModifier.pointerHoverIcon(PointerIcon.Hand), label = if (label == null) null else ({ Text(label) }), singleLine = true, + suffix = { + Icon( + Icons.Filled.ExpandMore, "Expand", + Modifier.size(20.dp).pointerHoverIcon(PointerIcon.Hand).clip(CircleShape).clickable { } + ) + } ) } else content() } diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/AudioClipEditor.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/AudioClipEditor.kt index b8a15150..1015c5d6 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/AudioClipEditor.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/AudioClipEditor.kt @@ -96,7 +96,7 @@ class AudioClipEditor(private val clip: TrackClip) : ClipEditor { clip.clip.thumbnail, EchoInMirror.currentPosition, scrollXPPQ, widthPPQ, - clip.clip.speedRatio.value, + clip.clip.speedRatio, clip.clip.volumeEnvelope, color, modifier = Modifier.width(noteWidthValue * widthPPQ) ) diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/AudioClipImpl.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/AudioClipImpl.kt index 0b20c80c..2aa48387 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/AudioClipImpl.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/AudioClipImpl.kt @@ -18,6 +18,7 @@ import com.eimsound.daw.commons.json.asFloat import com.eimsound.daw.commons.json.asString import com.eimsound.daw.commons.json.putNotDefault import com.eimsound.daw.components.* +import com.eimsound.daw.utils.observableMutableStateOf import com.eimsound.dsp.data.* import com.eimsound.dsp.data.midi.MidiNoteTimeRecorder import com.eimsound.dsp.detectBPM @@ -58,17 +59,17 @@ class AudioClipImpl( return } value.initialise(target.sampleRate, EchoInMirror.currentPosition.bufferSize, target.channels) - value.semitones = semitones.value - value.speedRatio = speedRatio.value + value.semitones = semitones + value.speedRatio = speedRatio field = value } override val timeInSeconds: Float get() = target.timeInSeconds * (stretcher?.speedRatio ?: 1F) - override val speedRatio = SimpleAudioProcessorParameter("speedRatio", initialValue = 1F) { - stretcher?.speedRatio = it.value + override var speedRatio by observableMutableStateOf(1F) { + stretcher?.speedRatio = it } - override val semitones = SimpleAudioProcessorParameter("Semitones") { - stretcher?.semitones = it.value + override var semitones by observableMutableStateOf(0F) { + stretcher?.semitones = it } private var timeStretcherName by mutableStateOf("") override var timeStretcher @@ -87,7 +88,7 @@ class AudioClipImpl( logger.warn(e) { "Failed to create time stretcher \"$value\"" } } } - override var bpm by mutableStateOf(0F) + override var bpm = 0F override val defaultDuration get() = EchoInMirror.currentPosition .convertSamplesToPPQ((target.length * (stretcher?.speedRatio ?: 1F)).roundToLong()) override val maxDuration get() = defaultDuration @@ -120,8 +121,8 @@ class AudioClipImpl( put("factory", factory.name) put("target", target.toJson()) putNotDefault("timeStretcher", timeStretcher) - putNotDefault("speedRatio", speedRatio.value, 1F) - putNotDefault("semitones", semitones.value) + putNotDefault("speedRatio", speedRatio, 1F) + putNotDefault("semitones", semitones) putNotDefault("volumeEnvelope", volumeEnvelope) putNotDefault("bpm", bpm) } @@ -142,8 +143,8 @@ class AudioClipImpl( stretcher = timeStretcher } } - semitones.setValue(json["semitones"]?.asFloat() ?: 0F, false) - speedRatio.setValue(json["speedRatio"]?.asFloat() ?: 1F, false) + semitones = json["semitones"]?.asFloat() ?: 0F + speedRatio = json["speedRatio"]?.asFloat() ?: 1F } private var tempInBuffers: Array = emptyArray() @@ -253,8 +254,7 @@ class AudioClipFactoryImpl: AudioClipFactory { Box { Waveform( clip.clip.thumbnail, EchoInMirror.currentPosition, startPPQ, widthPPQ, -// clip.clip.timeStretcher?.speedRatio ?: 1F, - 1F, + clip.clip.speedRatio, clip.clip.volumeEnvelope, contentColor, isDrawMinAndMax ) remember(clip) { @@ -287,7 +287,10 @@ class AudioClipFactoryImpl: AudioClipFactory { override fun copy(clip: AudioClip) = createClip(clip.target.copy()).apply { volumeEnvelope.addAll(clip.volumeEnvelope.copy()) -// timeStretcher = clip.timeStretcher?.copy() + bpm = clip.bpm + speedRatio = clip.speedRatio + semitones = clip.semitones + timeStretcher = clip.timeStretcher } override fun merge(clips: Collection>): List> = emptyList() diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/EditorControls.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/EditorControls.kt index 8bec0f72..0e31a072 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/EditorControls.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/clips/audio/EditorControls.kt @@ -41,20 +41,20 @@ internal fun EditorControls(clip: TrackClip) { val timeStretchers = TimeStretcherManager.timeStretchers Row(verticalAlignment = Alignment.CenterVertically) { CustomOutlinedTextField( - "%.2f".format(c.speedRatio.value), + "%.2f".format(c.speedRatio), { c.timeStretcher.ifEmpty { c.timeStretcher = timeStretchers.firstOrNull() ?: "" } - c.speedRatio.value = it.toFloatOrNull() ?: 1F + c.speedRatio = it.toFloatOrNull() ?: 1F }, Modifier.height(40.dp).weight(1F), label = { Text("变速") }, singleLine = true ) Gap(8) CustomOutlinedTextField( - "%.2f".format(c.semitones.value), + "%.2f".format(c.semitones), { c.timeStretcher.ifEmpty { c.timeStretcher = timeStretchers.firstOrNull() ?: "" } - c.semitones.value = it.toFloatOrNull() ?: 0F + c.semitones = it.toFloatOrNull() ?: 0F }, Modifier.height(40.dp).weight(1F), label = { Text("变调") }, singleLine = true