- README riscritto: badge CI/release, screenshot reali (emulatore), funzionalità, architettura, istruzioni di build - StaffLayout.lineBreaks: capienza riga in peso (√durata) con chiusura preferita alla stanghetta (5 test nuovi); usato da StaffFullView - StaffFullView: travature globali clippate per riga (origine di beat corretta anche su righe spezzate a metà battuta) - Cifre del tempo centrate su asse comune (metri tipo 12/8) - PracticeSession persiste beats/beatType: il replay dalla cronologia conserva il metro (2 test, retrocompatibile con le sessioni salvate) - fix: escaping Android-style (\' e \") reso letteralmente da compose resources in tutte le stringhe localizzate Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
38 lines
1.0 KiB
Kotlin
38 lines
1.0 KiB
Kotlin
package com.notemusicali.history
|
|
|
|
import kotlinx.serialization.Serializable
|
|
|
|
@Serializable
|
|
data class NoteResult(
|
|
val noteName: String,
|
|
val midiNumber: Int,
|
|
val centsDeviation: Float,
|
|
val reactionTimeMs: Long,
|
|
val firstAttemptSuccess: Boolean,
|
|
)
|
|
|
|
@Serializable
|
|
data class PracticeSession(
|
|
val id: String,
|
|
val sequenceName: String,
|
|
val instrumentName: String,
|
|
val dateMillis: Long,
|
|
val totalTimeMs: Long,
|
|
val noteResults: List<NoteResult>,
|
|
val noteMidiNumbers: List<Int>,
|
|
val completed: Boolean = true,
|
|
val beats: Int = 4,
|
|
val beatType: Int = 4,
|
|
) {
|
|
val averageCents: Float
|
|
get() = if (noteResults.isEmpty()) 0f
|
|
else noteResults.map { kotlin.math.abs(it.centsDeviation) }.average().toFloat()
|
|
|
|
val perfectNotes: Int
|
|
get() = noteResults.count { kotlin.math.abs(it.centsDeviation) <= 10f }
|
|
|
|
val firstAttemptRate: Float
|
|
get() = if (noteResults.isEmpty()) 0f
|
|
else noteResults.count { it.firstAttemptSuccess }.toFloat() / noteResults.size
|
|
}
|