Update mode toggle, split flash visuals, and interval-ms controls

This commit is contained in:
Tretzi
2026-03-10 23:24:49 -05:00
parent fdf3bf01d3
commit bb5d77fea3
9 changed files with 137 additions and 125 deletions

View File

@@ -67,7 +67,7 @@ class MainViewModel(
fun setMode(mode: SessionMode) = _ui.update { it.copy(config = it.config.copy(mode = mode), error = null) }
fun setDurationMin(min: Int) = _ui.update { it.copy(config = it.config.copy(durationSec = (min.coerceIn(1, 30) * 60)), error = null) }
fun setBlinkFrequency(value: Float) = _ui.update { it.copy(config = it.config.copy(blinkFrequencyHz = value.coerceIn(1f, 20f)), error = null) }
fun setFlashIntervalMs(value: Int) = _ui.update { it.copy(config = it.config.copy(flashIntervalMs = value.coerceIn(50, 2000)), error = null) }
fun setCarrier(value: Float) = _ui.update { it.copy(config = it.config.copy(carrierFrequencyHz = value.coerceIn(80f, 400f)), error = null) }
fun setDifference(value: Float) = _ui.update { it.copy(config = it.config.copy(binauralDifferenceHz = value.coerceIn(0.5f, 20f)), error = null) }

View File

@@ -6,7 +6,7 @@ import com.mindmachine.mvp.domain.SessionMode
object SessionValidator {
fun validate(config: SessionConfig, headsetAvailable: Boolean): String? {
if (config.durationSec !in 60..(30 * 60)) return "Duration must be 1 to 30 minutes."
if (config.blinkFrequencyHz !in 1f..20f) return "Blink frequency must be 1.0 to 20.0 Hz."
if (config.flashIntervalMs !in 50..2000) return "Flash interval must be 50 to 2000 ms."
if (config.carrierFrequencyHz !in 80f..400f) return "Carrier frequency must be 80 to 400 Hz."
if (config.binauralDifferenceHz !in 0.5f..20f) return "Binaural difference must be 0.5 to 20 Hz."
if (config.mode != SessionMode.VISUAL_ONLY && !headsetAvailable) {

View File

@@ -1,30 +1,26 @@
package com.mindmachine.mvp.session
import com.mindmachine.mvp.domain.VisualPattern
import kotlin.math.PI
import kotlin.math.sin
enum class FlashColor {
RED,
GREEN,
BLACK,
}
fun computeVisualAlpha(
visualPattern: VisualPattern,
data class SplitFlashFrame(
val left: FlashColor,
val right: FlashColor,
)
fun computeSplitFlashFrame(
frameTimeNanos: Long,
blinkFrequencyHz: Float,
intensityPercent: Int,
): Float {
val intensity = (intensityPercent.coerceIn(0, 100) / 100f)
if (intensity <= 0f) return 0f
val frequency = blinkFrequencyHz.coerceIn(1f, 20f).toDouble()
val timeSeconds = frameTimeNanos / 1_000_000_000.0
return when (visualPattern) {
VisualPattern.FLASH -> {
val phase = (timeSeconds * frequency) % 1.0
if (phase < 0.5) intensity else 0f
}
VisualPattern.PULSE -> {
val pulse = ((sin(2.0 * PI * frequency * timeSeconds) + 1.0) / 2.0).toFloat()
(intensity * pulse).coerceIn(0f, intensity)
}
flashIntervalMs: Int,
): SplitFlashFrame {
val intervalMs = flashIntervalMs.coerceIn(50, 2000).toLong()
val elapsedMs = frameTimeNanos / 1_000_000L
return when (((elapsedMs / intervalMs) % 4).toInt()) {
0 -> SplitFlashFrame(left = FlashColor.RED, right = FlashColor.GREEN)
1 -> SplitFlashFrame(left = FlashColor.BLACK, right = FlashColor.BLACK)
2 -> SplitFlashFrame(left = FlashColor.GREEN, right = FlashColor.RED)
else -> SplitFlashFrame(left = FlashColor.BLACK, right = FlashColor.BLACK)
}
}