diff --git a/audio-processors/src/commonMain/kotlin/com/eimsound/audioprocessor/AudioPlayer.kt b/audio-processors/src/commonMain/kotlin/com/eimsound/audioprocessor/AudioPlayer.kt index 9b81aed7..589dbcc8 100644 --- a/audio-processors/src/commonMain/kotlin/com/eimsound/audioprocessor/AudioPlayer.kt +++ b/audio-processors/src/commonMain/kotlin/com/eimsound/audioprocessor/AudioPlayer.kt @@ -18,6 +18,7 @@ interface AudioPlayer : AutoCloseable { val availableSampleRates: List val availableBufferSizes: List val sampleRate: Int + val bufferSize: Int @Composable fun Controls() fun onClose(callback: () -> Unit) @@ -30,6 +31,7 @@ abstract class AbstractAudioPlayer( val currentPosition: MutableCurrentPosition, private val processor: AudioProcessor, preferredSampleRate: Int? = null, + preferredBufferSize: Int? = null, ) : AudioPlayer { final override var cpuLoad by mutableStateOf(0F) private set @@ -39,6 +41,8 @@ abstract class AbstractAudioPlayer( private var closeCallback: (() -> Unit)? = null override var sampleRate by mutableStateOf(preferredSampleRate ?: currentPosition.sampleRate) protected set + override var bufferSize by mutableStateOf(preferredBufferSize ?: currentPosition.bufferSize) + protected set override val availableSampleRates = listOf(44100, 48000, 88200, 96000) override val availableBufferSizes = listOf(256, 512, 1024, 2048, 4096) @@ -98,7 +102,7 @@ interface AudioPlayerFactory { suspend fun getPlayers(): List fun create( name: String, currentPosition: MutableCurrentPosition, processor: AudioProcessor, - preferredSampleRate: Int? = null, + preferredSampleRate: Int? = null, preferredBufferSize: Int? = null, ): AudioPlayer } @@ -113,7 +117,7 @@ interface AudioPlayerManager : Reloadable { fun create( factory: String, name: String, currentPosition: MutableCurrentPosition, processor: AudioProcessor, - preferredSampleRate: Int? = null, + preferredSampleRate: Int? = null, preferredBufferSize: Int? = null, ): AudioPlayer fun createDefaultPlayer(currentPosition: MutableCurrentPosition, processor: AudioProcessor): AudioPlayer } diff --git a/audio-processors/src/commonMain/kotlin/com/eimsound/audioprocessor/impl/AudioPlayerManagerImpl.kt b/audio-processors/src/commonMain/kotlin/com/eimsound/audioprocessor/impl/AudioPlayerManagerImpl.kt index c17e1aeb..a81cf5aa 100644 --- a/audio-processors/src/commonMain/kotlin/com/eimsound/audioprocessor/impl/AudioPlayerManagerImpl.kt +++ b/audio-processors/src/commonMain/kotlin/com/eimsound/audioprocessor/impl/AudioPlayerManagerImpl.kt @@ -16,7 +16,8 @@ class AudioPlayerManagerImpl : AudioPlayerManager { currentPosition: MutableCurrentPosition, processor: AudioProcessor, preferredSampleRate: Int?, - ) = factories[factory]?.create(name, currentPosition, processor, preferredSampleRate) + preferredBufferSize: Int?, + ) = factories[factory]?.create(name, currentPosition, processor, preferredSampleRate, preferredBufferSize) ?: throw NoSuchFactoryException(factory) override fun createDefaultPlayer(currentPosition: MutableCurrentPosition, processor: AudioProcessor): AudioPlayer { diff --git a/components/src/commonMain/kotlin/com/eimsound/daw/components/CustomTextField.kt b/components/src/commonMain/kotlin/com/eimsound/daw/components/CustomTextField.kt index 97469884..854604cc 100644 --- a/components/src/commonMain/kotlin/com/eimsound/daw/components/CustomTextField.kt +++ b/components/src/commonMain/kotlin/com/eimsound/daw/components/CustomTextField.kt @@ -132,3 +132,9 @@ fun NumberInputArrows(onValueChange: (value: Int) -> Unit) { .pointerHoverIcon(PointerIcon.Hand).clip(CircleShape).clickable { onValueChange(-1) }) } } + +@Composable +fun textFieldGrayColors() = TextFieldDefaults.colors( + unfocusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, +// focusedIndicatorColor = MaterialTheme.colorScheme.outline, +) diff --git a/components/src/commonMain/kotlin/com/eimsound/daw/components/EnvelopeEditor.kt b/components/src/commonMain/kotlin/com/eimsound/daw/components/EnvelopeEditor.kt index d2817082..17675014 100644 --- a/components/src/commonMain/kotlin/com/eimsound/daw/components/EnvelopeEditor.kt +++ b/components/src/commonMain/kotlin/com/eimsound/daw/components/EnvelopeEditor.kt @@ -499,8 +499,6 @@ class EnvelopeEditor( val range = valueRange.range val hoveredId = hoveredIndex - println(selectedPoints.firstOrNull()) - // draw points val tmpOffsetX = offsetX val tmpOffsetY = offsetY 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 02a5441b..777ac09f 100644 --- a/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt +++ b/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.input.pointer.PointerIcon import androidx.compose.ui.input.pointer.pointerHoverIcon import androidx.compose.ui.layout.Layout import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp @@ -128,12 +129,47 @@ fun OutlinedDropdownSelector( isSelected: ((T) -> Boolean)? = null, itemContent: (@Composable (T) -> Unit)? = null, label: String? = null, + readonly: Boolean = false, + colors: TextFieldColors? = null, content: (@Composable () -> Unit)? = null, ) { @OptIn(ExperimentalFoundationApi::class) DropdownSelector( onSelected, items, selected, boxModifier, enabled, PointerMatcher.Primary, - isSelected, itemContent, label, true, content + isSelected, itemContent, label, true, readonly, colors, content + ) +} + +@Composable +fun AutoWidthOutlinedDropdownSelector( + onSelected: (T) -> Unit, + items: Collection, + selected: T?, + boxModifier: Modifier = Modifier, + enabled: Boolean = true, + isSelected: ((T) -> Boolean)? = null, + itemContent: (@Composable (T) -> Unit)? = null, + label: String? = null, + colors: TextFieldColors? = null +) { + var modifier = boxModifier + val textMeasurer = rememberTextMeasurer() + val textWidth = remember(items, items.size) { + (items.maxOfOrNull { textMeasurer.measure(it.displayName).size.width.dp } ?: 0.dp) + 32.dp + } + val maxWidth = if (textWidth > 256.dp) 256.dp else textWidth + modifier = modifier.width(maxWidth) + OutlinedDropdownSelector( + onSelected, + items, + selected, + modifier, + enabled, + isSelected, + itemContent, + label, + readonly = true, + colors ) } @@ -147,12 +183,14 @@ fun DropdownSelector( isSelected: ((T) -> Boolean)? = null, itemContent: (@Composable (T) -> Unit)? = null, label: String? = null, + readOnly: Boolean = false, + colors: TextFieldColors? = null, content: (@Composable () -> Unit)? = null, ) { @OptIn(ExperimentalFoundationApi::class) DropdownSelector( onSelected, items, selected, boxModifier, enabled, PointerMatcher.Primary, - isSelected, itemContent, label, false, content + isSelected, itemContent, label, false, readOnly, colors, content ) } @@ -168,6 +206,8 @@ fun DropdownSelector( itemContent: (@Composable (T) -> Unit)? = null, label: String? = null, isOutlined: Boolean = false, + readonly: Boolean = false, + colors: TextFieldColors? = null, content: (@Composable () -> Unit)? = null, ) { val filter = remember { mutableStateOf(null) } @@ -205,6 +245,8 @@ fun DropdownSelector( boxModifier.pointerHoverIcon(PointerIcon.Hand), label = if (label == null) null else ({ Text(label) }), singleLine = true, + readOnly = readonly, + colors = colors ?: TextFieldDefaults.colors(), suffix = { Icon( Icons.Filled.ExpandMore, "Expand", @@ -217,6 +259,8 @@ fun DropdownSelector( boxModifier.pointerHoverIcon(PointerIcon.Hand), label = if (label == null) null else ({ Text(label) }), singleLine = true, + readOnly = readonly, + colors = colors ?: TextFieldDefaults.colors(), suffix = { Icon( Icons.Filled.ExpandMore, "Expand", diff --git a/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt b/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt index e13341cd..cc6cb5bb 100644 --- a/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt +++ b/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt @@ -1,68 +1,23 @@ package com.eimsound.daw.components + import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.input.pointer.PointerIcon -import androidx.compose.ui.input.pointer.pointerHoverIcon import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -private val EXPANDER_PADDING_HORIZONTAL = 16.dp private val EXPANDER_PADDING_VERTICAL = 8.dp private val EXPANDER_CARD_GAP = 4.dp private val LIST_HEIGHT = 36.dp private val CARD_HEIGHT = 64.dp -private val EXPAND_ICON_SIZE = 20.dp -private val MENU_MAX_WIDTH = 256.dp -@Composable -fun SettingsMenu( - items: Collection?, - selected: T, - toString: (T) -> String = { it.toString() }, - onSelect: (T) -> Unit -) { - if (items == null) return - val textMeasurer = rememberTextMeasurer() - val itemsMap = items.associateBy { toString(it) } - val textWidth = (items.maxOfOrNull { textMeasurer.measure(toString(it)).size.width.dp } ?: 0.dp) + EXPANDER_PADDING_HORIZONTAL * 2 - val maxWidth = if (textWidth > MENU_MAX_WIDTH) MENU_MAX_WIDTH else textWidth - OutlinedDropdownSelector( - { itemsMap[it]?.let { selected -> onSelect(selected) } }, - itemsMap.keys, - selected = toString(selected), - ) { - CustomOutlinedTextField( - toString(selected), { }, - Modifier.width(maxWidth).height(LIST_HEIGHT).pointerHoverIcon(PointerIcon.Hand), - readOnly = true, - textStyle = MaterialTheme.typography.labelLarge.copy(LocalContentColor.current), - suffix = { - Icon(Icons.Filled.ExpandMore, "Expand", - Modifier.size(EXPAND_ICON_SIZE).pointerHoverIcon(PointerIcon.Hand).clip(CircleShape) - .clickable { } - ) - }, - colors = TextFieldDefaults.colors( - unfocusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - focusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - ), - paddingValues = TextFieldDefaults.contentPaddingWithLabel(8.dp, 4.dp, 3.dp, 4.dp) - ) - } -} @Composable fun SettingsSection( @@ -89,7 +44,8 @@ fun SettingsCard( ) { Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.background(MaterialTheme.colorScheme.surface).padding(EXPANDER_PADDING_HORIZONTAL, EXPANDER_PADDING_VERTICAL) + modifier = Modifier.background(MaterialTheme.colorScheme.surface) + .padding(16.dp, EXPANDER_PADDING_VERTICAL) ) { Text(header) Filled() diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/Configuration.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/Configuration.kt index 80dfe2a5..876ca16f 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/Configuration.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/Configuration.kt @@ -45,6 +45,7 @@ object Configuration : JsonSerializable { var audioDeviceFactoryName by mutableStateOf("") var audioDeviceName by mutableStateOf("") var preferredSampleRate by mutableStateOf(-1) + var preferredBufferSize by mutableStateOf(-1) var autoCutOver0db by mutableStateOf(true) var isTimeDisplayInBeats by mutableStateOf(false) var themeMode by observableMutableStateOf(2) { @@ -113,6 +114,7 @@ object Configuration : JsonSerializable { put("audioDeviceFactoryName", audioDeviceFactoryName) put("audioDeviceName", audioDeviceName) put("preferredSampleRate", preferredSampleRate) + put("preferredBufferSize", preferredBufferSize) put("autoCutOver0db", autoCutOver0db) put("themeMode", themeMode) put("isTimeDisplayInBeats", isTimeDisplayInBeats) @@ -126,6 +128,7 @@ object Configuration : JsonSerializable { json["audioDeviceFactoryName"]?.asString()?.let { audioDeviceFactoryName = it } json["audioDeviceName"]?.asString()?.let { audioDeviceName = it } json["preferredSampleRate"]?.asInt()?.let { preferredSampleRate = it } + json["preferredBufferSize"]?.asInt()?.let { preferredBufferSize = it } json["autoCutOver0db"]?.asBoolean()?.let { autoCutOver0db = it } json["themeMode"]?.asInt()?.let { themeMode = it diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/components/app/AppBar.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/components/app/AppBar.kt index 5d95cba2..ac55df60 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/components/app/AppBar.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/components/app/AppBar.kt @@ -68,10 +68,7 @@ private fun CurrentTime() { Modifier.size(20.dp).pointerHoverIcon(PointerIcon.Hand).clip(CircleShape).clickable { } ) }, - colors = TextFieldDefaults.colors( - unfocusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - focusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - ), + colors = textFieldGrayColors(), paddingValues = TextFieldDefaults.contentPaddingWithLabel(8.dp, 4.dp, 3.dp, 4.dp) ) } @@ -81,7 +78,7 @@ private fun CurrentTime() { private fun Quantification() { DropdownSelector( { EchoInMirror.quantification = it }, - QUANTIFICATION_UNITS, EchoInMirror.quantification, + QUANTIFICATION_UNITS, EchoInMirror.quantification, Modifier.pointerHoverIcon(PointerIcon.Hand), isSelected = { it.getEditUnit(EchoInMirror.currentPosition) == EchoInMirror.editUnit }, itemContent = { Text(it.name, fontWeight = if (it.isSpecial) FontWeight.Bold else null) @@ -101,10 +98,7 @@ private fun Quantification() { Modifier.size(20.dp).pointerHoverIcon(PointerIcon.Hand).clip(CircleShape).clickable { } ) }, - colors = TextFieldDefaults.colors( - unfocusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - focusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - ), + colors = textFieldGrayColors(), paddingValues = TextFieldDefaults.contentPaddingWithLabel(8.dp, 4.dp, 3.dp, 4.dp) ) } @@ -171,10 +165,7 @@ private fun BPM() { EchoInMirror.currentPosition.bpm = (EchoInMirror.currentPosition.bpm + it).coerceIn(1.0, 600.0) } }, - colors = TextFieldDefaults.colors( - unfocusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - focusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - ), + colors = textFieldGrayColors(), keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number), paddingValues = TextFieldDefaults.contentPaddingWithLabel(6.dp, 6.dp, 3.dp, 4.dp) ) diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/CurrentPositionImpl.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/CurrentPositionImpl.kt index 8f8d5f61..9fd91b73 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/CurrentPositionImpl.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/CurrentPositionImpl.kt @@ -15,8 +15,8 @@ class CurrentPositionImpl( override var bpm by mutableStateOf(140.0) override var timeInSeconds by mutableStateOf(0.0) override var ppq by mutableStateOf(96) - override var bufferSize by mutableStateOf(1024) - override var sampleRate by mutableStateOf(44100) + override var bufferSize = 1024 + override var sampleRate = 44100 override var timeSigNumerator by mutableStateOf(4) override var timeSigDenominator by mutableStateOf(4) override var loopingRange by mutableStateOf(0..0) diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/EchoInMirrorImpl.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/EchoInMirrorImpl.kt index 1d09c94e..fb553218 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/EchoInMirrorImpl.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/impl/EchoInMirrorImpl.kt @@ -66,6 +66,8 @@ class EchoInMirrorImpl : IEchoInMirror { Configuration.audioDeviceFactoryName, Configuration.audioDeviceName, currentPosition, bus!!, if (Configuration.preferredSampleRate > 0) Configuration.preferredSampleRate + else null, + if (Configuration.preferredBufferSize > 0) Configuration.preferredBufferSize else null ) } catch (e: Exception) { @@ -86,6 +88,10 @@ class EchoInMirrorImpl : IEchoInMirror { Configuration.preferredSampleRate = -1 flag = true } + if (Configuration.preferredBufferSize != ret.bufferSize) { + Configuration.preferredBufferSize = -1 + flag = true + } if (flag) Configuration.save() return ret } diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/QuickLoadDialog.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/QuickLoadDialog.kt index 2c1b4775..f6cc0bc8 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/QuickLoadDialog.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/QuickLoadDialog.kt @@ -239,10 +239,7 @@ fun FloatingLayerProvider.openQuickLoadDialog(onClose: ((AudioProcessorDescripti val factory = descriptionsToFactory[it] ?: return@DescList null setFloatingLayerShow(KEY, false) AudioProcessorDescriptionAndFactory(it, factory) - }, onDragEnd = { - closeFloatingLayer(KEY) - println(2333) - } + }, onDragEnd = { closeFloatingLayer(KEY) } ) { desc -> if (desc == null) return@DescList if (onClose != null && selectedDescription == desc) { diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt index e7325255..8cf6938a 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt @@ -53,61 +53,65 @@ internal object AudioSettings : SettingTab { @Composable override fun content() { + val colors = textFieldGrayColors() Column { SettingsSection("设备与音频设置") { SettingsCard("音频工厂") { - SettingsMenu( + AutoWidthOutlinedDropdownSelector( + { + if (Configuration.audioDeviceFactoryName == it) return@AutoWidthOutlinedDropdownSelector + EchoInMirror.player?.close() + Configuration.audioDeviceName = "" + Configuration.audioDeviceFactoryName = it + reopenAudioDevice() + }, AudioPlayerManager.instance.factories.keys, - Configuration.audioDeviceFactoryName - ) { - if (Configuration.audioDeviceFactoryName == it) return@SettingsMenu - EchoInMirror.player?.close() - Configuration.audioDeviceName = "" - Configuration.audioDeviceFactoryName = it - reopenAudioDevice() - } + Configuration.audioDeviceFactoryName, + colors = colors + ) } SettingsCard("音频设备") { var playerNames by remember { mutableStateOf(emptyList()) } LaunchedEffect(Configuration.audioDeviceName) { playerNames = AudioPlayerManager.instance.factories[Configuration.audioDeviceFactoryName]!!.getPlayers() } - SettingsMenu( + AutoWidthOutlinedDropdownSelector( + { + if (Configuration.audioDeviceName == it) return@AutoWidthOutlinedDropdownSelector + EchoInMirror.player?.close() + Configuration.audioDeviceName = it + reopenAudioDevice() + }, playerNames, - Configuration.audioDeviceName - ) { - if (Configuration.audioDeviceName == it) return@SettingsMenu - EchoInMirror.player?.close() - Configuration.audioDeviceName = it - reopenAudioDevice() - } + Configuration.audioDeviceName, + colors = colors + ) } SettingsCard("缓冲区大小") { - SettingsMenu( - EchoInMirror.player?.availableBufferSizes?.toList(), - EchoInMirror.currentPosition.bufferSize, - { "$it 个采样" } - ) { - EchoInMirror.player?.close() -// EchoInMirror.currentPosition.setSampleRateAndBufferSize( -// EchoInMirror.currentPosition.sampleRate, it -// ) + AutoWidthOutlinedDropdownSelector( + { + EchoInMirror.player?.close() + Configuration.preferredBufferSize = it - reopenAudioDevice() - } + reopenAudioDevice() + }, + EchoInMirror.player?.availableBufferSizes ?: emptyList(), + EchoInMirror.player?.bufferSize, + colors = colors + ) } SettingsCard("采样率") { - var sampleRate by remember { mutableStateOf(EchoInMirror.currentPosition.sampleRate) } - SettingsMenu( - EchoInMirror.player?.availableSampleRates?.toList(), - sampleRate, - ) { - sampleRate = it - EchoInMirror.player?.close() - Configuration.preferredSampleRate = it + AutoWidthOutlinedDropdownSelector( + { + EchoInMirror.player?.close() + Configuration.preferredSampleRate = it - reopenAudioDevice() - } + reopenAudioDevice() + }, + EchoInMirror.player?.availableSampleRates ?: emptyList(), + EchoInMirror.player?.sampleRate, + colors = colors + ) } if (SystemUtils.IS_OS_WINDOWS) SettingsCard("后台共享音频设备") { Switch( diff --git a/native/src/jvmMain/kotlin/com/eimsound/dsp/native/players/JvmAudioPlayer.kt b/native/src/jvmMain/kotlin/com/eimsound/dsp/native/players/JvmAudioPlayer.kt index 08984687..6c126508 100644 --- a/native/src/jvmMain/kotlin/com/eimsound/dsp/native/players/JvmAudioPlayer.kt +++ b/native/src/jvmMain/kotlin/com/eimsound/dsp/native/players/JvmAudioPlayer.kt @@ -19,8 +19,9 @@ class JvmAudioPlayer( private val mixer: Mixer.Info, currentPosition: MutableCurrentPosition, processor: AudioProcessor, - preferredSampleRate: Int? -) : AbstractAudioPlayer(factory, mixer.name, 2, currentPosition, processor, preferredSampleRate), Runnable { + preferredSampleRate: Int?, + preferredBufferSize: Int?, +) : AbstractAudioPlayer(factory, mixer.name, 2, currentPosition, processor, preferredSampleRate, preferredBufferSize), Runnable { private var thread: Thread? = null private var sdl: SourceDataLine? = null private val bits = 2 @@ -46,6 +47,10 @@ class JvmAudioPlayer( sampleRate = availableSampleRates.getOrNull(availableSampleRates.lowerBound { it < currentPosition.sampleRate }) ?: throw LineUnavailableException("Failed to open audio player!") } + if (!availableBufferSizes.contains(bufferSize)) { + bufferSize = availableBufferSizes.getOrNull(availableBufferSizes.lowerBound { it < currentPosition.bufferSize }) + ?: throw LineUnavailableException("Failed to open audio player!") + } tryOpenAudioDevice() } catch (e: Throwable) { le = e @@ -108,13 +113,13 @@ class JvmAudioPlayer( private fun tryOpenAudioDevice() { val af = AudioFormat(AudioFormat.Encoding.PCM_SIGNED, sampleRate.toFloat(), bits * 8, channels, bits * channels, sampleRate.toFloat(), false) - val bufferSize = currentPosition.bufferSize val catchBufferSize = channels * bufferSize * bits this.sdl = AudioSystem.getSourceDataLine(af, mixer).apply { open(af, catchBufferSize) start() } outputLatency = (bufferSize * 1.5).roundToInt() + currentPosition.bufferSize = bufferSize inputLatency = outputLatency outputBuffer = ByteArray(catchBufferSize) runBlocking { prepareToPlay() } @@ -131,15 +136,16 @@ class JvmAudioPlayerFactory : AudioPlayerFactory { } override fun create( name: String, currentPosition: MutableCurrentPosition, processor: AudioProcessor, - preferredSampleRate: Int? + preferredSampleRate: Int?, preferredBufferSize: Int?, ): JvmAudioPlayer { val info = AudioSystem.getMixerInfo() val target = info.find { it.name == name } var le: Throwable? = null - return if (target != null) JvmAudioPlayer(this, target, currentPosition, processor, preferredSampleRate) + return if (target != null) + JvmAudioPlayer(this, target, currentPosition, processor, preferredSampleRate, preferredBufferSize) else info.firstNotNullOfOrNull { try { - JvmAudioPlayer(this, it, currentPosition, processor, preferredSampleRate) + JvmAudioPlayer(this, it, currentPosition, processor, preferredSampleRate, preferredBufferSize) } catch (e: Throwable) { le = e null diff --git a/native/src/jvmMain/kotlin/com/eimsound/dsp/native/players/NativeAudioPlayer.kt b/native/src/jvmMain/kotlin/com/eimsound/dsp/native/players/NativeAudioPlayer.kt index 7222d2e2..89083673 100644 --- a/native/src/jvmMain/kotlin/com/eimsound/dsp/native/players/NativeAudioPlayer.kt +++ b/native/src/jvmMain/kotlin/com/eimsound/dsp/native/players/NativeAudioPlayer.kt @@ -27,6 +27,7 @@ class NativeAudioPlayer( currentPosition: MutableCurrentPosition, processor: AudioProcessor, preferredSampleRate: Int?, + preferredBufferSize: Int?, execFile: String, enabledSharedMemory: Boolean = IS_SHM_SUPPORTED && System.getProperty("eim.dsp.nativeaudioplayer.sharedmemory", "1") != "false", @@ -63,7 +64,7 @@ class NativeAudioPlayer( add(Json.encodeToString(type)) } add("-B") - add(currentPosition.bufferSize.toString()) + add(preferredBufferSize.toString()) add("-R") add(preferredSampleRate.toString()) sharedMemory?.let { @@ -179,7 +180,7 @@ class NativeAudioPlayer( inputLatency = inputStream.readVarInt() outputLatency = inputStream.readVarInt() sampleRate = inputStream.readVarInt() - val bufferSize = inputStream.readVarInt() + bufferSize = inputStream.readVarInt() currentPosition.bufferSize = bufferSize availableSampleRates = List(inputStream.readVarInt()) { inputStream.readVarInt() } availableBufferSizes = List(inputStream.readVarInt()) { inputStream.readVarInt() } @@ -211,19 +212,19 @@ class NativeAudioPlayerFactory : AudioPlayerFactory { } override fun create( name: String, currentPosition: MutableCurrentPosition, processor: AudioProcessor, - preferredSampleRate: Int? + preferredSampleRate: Int?, preferredBufferSize: Int?, ) = try { "\\[(.+?)] ".toRegex().find(name)?.let { NativeAudioPlayer(this, it.groupValues[1], name.substring(it.range.last + 1), - currentPosition, processor, preferredSampleRate, execFile + currentPosition, processor, preferredSampleRate, preferredBufferSize, execFile ) } } catch (e: Throwable) { e.printStackTrace() null } ?: NativeAudioPlayer(this, "", "", - currentPosition, processor, preferredSampleRate, execFile + currentPosition, processor, preferredSampleRate, preferredBufferSize, execFile ) }