我最近开始构建一个API,允许消费者创建和操作音乐实体,如音符、音程、音阶和和弦。
第一步是创建一个基础结构的基础,这些结构后来被音乐实体所使用。这样的结构之一就是Ring。
中
环是夹在0和环大小之间的整数的表示。它的Value是它表示的整数。它的Size是组大小。它的Class是固定在指定范围内的同余值。它的Group确定它远离引用组0的范围的数量。
我用戒指的方式是定义一个音符。音符由音高和度组成。音高是一个代表音符频率的数字。大小为
12的环用于表示单个八度范围内的所有半色调.每个八度音阶都是一个环群。另一个大小为7的环用于定义音符的度数。学位的常见表示形式是{C, D, E, F, G, A, B}或do-re-mi-fa-sol-la-ti。C音符在5倍频程上被认为是基音0和度0的参考音。
我想要改变戒指的属性。下面的例子显示了音符的音高是如何用圆环来表示的。
val pitch = Ring(0, 12) // the pitch of note 'C5'
pitch.Group = 1 // the pitch one octave higher 'C6'
pitch.Class = 2 // the pitch class changed to that of note 'D6'
println(pitch) // Ring(Value=14, Size=12)下面的例子显示了如何用圆环来表示音符的程度。
val degree = Ring(0, 7) // the degree of note 'C5'
degree.Group = 1 // the degree one octave higher 'C6'
degree.Class = 1 // the degree class changed to that of note 'D6'
println(degree) // Ring(Value=8, Size=7)这意味着音符D6可以用音调Ring(Value=14, Size=12)和度Ring(Value=8, Size=7)表示。
我选择了使用可变类和数据类。我想要一个可变的类,因为我需要很多操作来链接。我想要一个数据类,因为它隐藏锅炉板代码,并提供有趣的方法,如复制。
val ring = someOtherRing.copy().setClass(2).setGroup(1)我确实读过那个数据类应该是不可变的。。
所以我的问题是:
任何关于Kotlin指南、一般指南、流利的方法和varia的补充反馈都是受欢迎的.
环码:
import kotlin.math.*
data class Ring(var Value: Int, val Size: Int) {
var Class: Int
get() = modulo(Value)
set(value) {
Value = Group * Size + modulo(value)
}
var Group: Int
get() = Value / Size
set(value) {
Value = value * Size + Class
}
fun setValue(value: Int): Ring {
Value = value
return this
}
fun setClass(clazz: Int): Ring {
Class = clazz
return this
}
fun setGroup(group: Int): Ring {
Group = group
return this
}
private fun modulo(x: Int): Int {
return (x % Size + Size) % Size
}
}发布于 2019-07-08 06:23:44
你的概念看上去完全被误导了。
环是一个数学概念,一个代数结构。它确实有一个大小,但它没有价值。
该值将是环元素的一部分。这样的环元素是一个元组( ring,value)。
音符的音高确实是一个环元素。环元素只能用来存储音符的音高,而不能用来存储八度音阶。如果是这样的话,它将不再符合一个环的数学概念。
要用西方的记谱法来代表一个音符,我的第一个想法是:
data class Note(
val octave: Int,
val name: NoteName,
val mod: NoteModifier,
val duration: NoteDuration
)
enum class NoteName {
C, D, E, F, G, A, B
}
enum class NoteModifier {
Natural, Sharp, Flat, TwoSharp, TwoFlat
}
enum class NoteDuration {
Full, Half, Quarter, Eighth, Sixteenth
}上述定义非常粗略和有限。要了解排版音乐的实际复杂性,请看一下LilyPond,它应该在代码的某个地方有一个注释的定义。
如果您只想重放音乐,就没有必要区分c#和d\flat,这将使上述定义更加简单。
在Kotlin中,您不需要设置器,因为copy函数比Java功能更强大。你只需说:
val note = other.copy(octave = 3, pitch = 5)这更容易掌握,编写的代码更少。如果你用Kotlin写了一个setter方法,向后倾,然后三思而后行。也许你在做一些不寻常的事。
顺便说一句,Kotlin的财产名称是用小写写的。Kotlin基于Java,而不是C#。
发布于 2019-07-11 16:55:58
我决定不使用(ab-)使用数学结构Ring来创建音乐实体。正如罗兰所指出的那样,它只是不符合目的。相反,我已经创建了我自己的结构Coil,它代表了一个Value,它的Index在一个数量级的组Group Size中。我仍然使用setter,但仅用于派生属性。
Coil类:
import kotlin.math.*
data class Coil(var value: Int, val size: Int) {
var index: Int
get() = modulo(value)
set(n) {
value = group * size + modulo(n)
}
var group: Int
get() = value / size
set(n) {
value = n * size + index
}
val delta: Int
get() {
val d1 = index
val d2 = size - d1
return if (d1 <= d2) d1 else -d2
}
private fun modulo(n: Int): Int {
return (n % size + size) % size
}
}为了让您知道我将如何使用Coil,我创建了一个简化的Note版本。我想使用音符的方式是操纵它们和它们在和弦和音阶中的作用。我不打算创作乐谱,所以我不需要一个职位或持续时间。一个音符使用一个线圈来存储它的音高,另一个用来存储它的度。这两个值都是绝对值,这意味着八度包括在值中。他们的设置者重新同步了八度音阶。设置pitchClass或degreeClass不会改变八度音阶。一个音符的名字包括它的pitchClass在科学的音高表示法,它的意外(平的,尖锐的,自然的)和八度。
Note用法:(给出线圈和备注之间相互作用的概念)
fun main() {
val note = Note(0, 0, 5) // C5
note.degreeClass = 1 // Dbb5
note.pitchClass = 1 // Db5
note.octave = 6 // Db6
note.accidentals = 1 // D#6
println(note.name)
}Note类:
import kotlin.math.*
class Note(var _pitch: Int, var _degree: Int, var _octave: Int) {
private val PITCH_COUNT = 12
private val DEGREE_COUNT = 7
private val FLAT = 'b'
private val SHARP = '#'
private val DIATONIC_PITCH_CLASS_SET
: IntArray = intArrayOf(0, 2, 4, 5, 7, 9, 11)
private val SCIENTIFIC_PITCH_CLASS_SET
: CharArray = charArrayOf('C', 'D', 'E', 'F', 'G', 'A', 'B')
private val p = Coil(_pitch, PITCH_COUNT)
private val d = Coil(_degree, DEGREE_COUNT)
init {
p.group = _octave
d.group = _octave
}
var pitch: Int
get() = p.value
set(n) {
p.value = n
octave = p.group
}
var degree: Int
get() = d.value
set(n) {
d.value = n
octave = d.group
}
var octave: Int
get() = p.group
set(n) {
p.group = n
d.group = n
}
var pitchClass: Int
get() = p.index
set(n) {
p.index = n
}
var degreeClass: Int
get() = d.index
set(n) {
d.index = n
}
var accidentals: Int
get() {
val delta = pitchClass - DIATONIC_PITCH_CLASS_SET[degreeClass]
return Coil(delta, PITCH_COUNT).delta
}
set(n) {
pitchClass = DIATONIC_PITCH_CLASS_SET[degreeClass] + n
}
val name: String
get() {
val sb = StringBuilder()
val d = accidentals
sb.append(SCIENTIFIC_PITCH_CLASS_SET[degreeClass])
if (d != 0) {
sb.append(Character.toString((if (d > 0) SHARP else FLAT)).repeat(abs(d)))
}
sb.append(octave)
return sb.toString()
}
}https://codereview.stackexchange.com/questions/223697
复制相似问题