Change note's duration type from int to Fraction and enable + operator support for note and int
This commit is contained in:
@@ -1,26 +1,38 @@
|
||||
package io.smnp.data.entity
|
||||
|
||||
import io.smnp.data.enumeration.Pitch
|
||||
import io.smnp.math.Fraction
|
||||
|
||||
data class Note(val pitch: Pitch, val octave: Int, val duration: Int, val dot: Boolean) {
|
||||
data class Builder(
|
||||
var pitch: Pitch = Pitch.A,
|
||||
var octave: Int = 4,
|
||||
var duration: Int = 4,
|
||||
var dot: Boolean = false
|
||||
) {
|
||||
fun pitch(pitch: Pitch) = apply { this.pitch = pitch }
|
||||
fun octave(octave: Int) = apply { this.octave = octave }
|
||||
fun duration(duration: Int) = apply { this.duration = duration }
|
||||
fun dot(dot: Boolean) = apply { this.dot = dot }
|
||||
fun build() = Note(pitch, octave, duration, dot)
|
||||
}
|
||||
class Note(val pitch: Pitch, val octave: Int, duration: Fraction, dot: Boolean) {
|
||||
val duration = if(dot) duration * Fraction(3, 2) else duration
|
||||
|
||||
operator fun plus(duration2: Fraction) = Note(pitch, octave, duration + duration2, false)
|
||||
|
||||
override fun toString(): String {
|
||||
return "${pitch}${octave}:${duration}${if (dot) "d" else ""}"
|
||||
return "${pitch}${octave}:(${duration})"
|
||||
}
|
||||
|
||||
fun intPitch(): Int {
|
||||
return octave * 12 + pitch.ordinal
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Note
|
||||
|
||||
if (pitch != other.pitch) return false
|
||||
if (octave != other.octave) return false
|
||||
if (duration != other.duration) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = pitch.hashCode()
|
||||
result = 31 * result + octave
|
||||
result = 31 * result + duration.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
65
api/src/main/kotlin/io/smnp/math/Fraction.kt
Normal file
65
api/src/main/kotlin/io/smnp/math/Fraction.kt
Normal file
@@ -0,0 +1,65 @@
|
||||
package io.smnp.math
|
||||
|
||||
class Fraction(val numerator: Int, val denominator: Int) : Comparable<Fraction> {
|
||||
init {
|
||||
if (denominator == 0) {
|
||||
throw RuntimeException("Invalid fraction: denominator can not be of 0")
|
||||
}
|
||||
}
|
||||
|
||||
operator fun component1() = numerator
|
||||
operator fun component2() = denominator
|
||||
|
||||
val decimal by lazy { numerator.toDouble() / denominator }
|
||||
val inversed by lazy { Fraction(denominator, numerator) }
|
||||
|
||||
val simplified by lazy {
|
||||
val gcd = greatestCommonDenominator(numerator, denominator)
|
||||
Fraction(numerator / gcd, denominator / gcd)
|
||||
}
|
||||
|
||||
private fun greatestCommonDenominator(a: Int, b: Int): Int = if (b == 0) a else greatestCommonDenominator(b, a % b)
|
||||
|
||||
operator fun unaryMinus() = Fraction(-this.numerator, this.denominator)
|
||||
|
||||
operator fun plus(second: Fraction) =
|
||||
if (this.denominator == second.denominator) Fraction(this.numerator + second.numerator, denominator)
|
||||
else {
|
||||
val numerator = numerator * second.denominator + second.numerator * denominator
|
||||
val denominator = denominator * second.denominator
|
||||
Fraction(numerator, denominator)
|
||||
}
|
||||
|
||||
operator fun minus(second: Fraction) = (this + (-second))
|
||||
|
||||
operator fun times(num: Int) = Fraction(numerator * num, denominator)
|
||||
|
||||
operator fun times(second: Fraction) =
|
||||
Fraction(numerator * second.numerator, denominator * second.denominator)
|
||||
|
||||
operator fun div(num: Int) = this.inversed * num
|
||||
|
||||
operator fun div(second: Fraction) = this * second.inversed
|
||||
|
||||
override fun compareTo(other: Fraction) = decimal.compareTo(other.decimal)
|
||||
|
||||
override fun toString() =
|
||||
this.simplified.let { if (it.denominator == 1) it.numerator.toString() else "${it.numerator}/${it.denominator}" }
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Fraction) {
|
||||
return false
|
||||
}
|
||||
|
||||
val (aNum, aDen) = this.simplified
|
||||
val (bNum, bDen) = other.simplified
|
||||
|
||||
return aNum == bNum && aDen == bDen
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(int: Int): Fraction {
|
||||
return Fraction(int, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,8 +101,7 @@ data class Value(val type: DataType, val value: Any, val properties: Map<String,
|
||||
DataType.NOTE, value, hashMapOf(
|
||||
Pair("pitch", string(value.pitch.toString())),
|
||||
Pair("octave", int(value.octave)),
|
||||
Pair("duration", int(value.duration)),
|
||||
Pair("dot", bool(value.dot))
|
||||
Pair("duration", float((value.duration.numerator / value.duration.denominator).toFloat()))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user