首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >音乐理论:基础--一枚戒指

音乐理论:基础--一枚戒指
EN

Code Review用户
提问于 2019-07-07 19:27:48
回答 2查看 163关注 0票数 4

我最近开始构建一个API,允许消费者创建和操作音乐实体,如音符、音程、音阶和和弦。

第一步是创建一个基础结构的基础,这些结构后来被音乐实体所使用。这样的结构之一就是Ring

A环在模算术

环是夹在0和环大小之间的整数的表示。它的Value是它表示的整数。它的Size是组大小。它的Class是固定在指定范围内的同余值。它的Group确定它远离引用组0的范围的数量。

--从音乐理论的角度看

我用戒指的方式是定义一个音符。音符由音高和度组成。音高是一个代表音符频率的数字。大小为12的环用于表示单个八度范围内的所有半色调.每个八度音阶都是一个环群。另一个大小为7的环用于定义音符的度数。学位的常见表示形式是{C, D, E, F, G, A, B}do-re-mi-fa-sol-la-tiC音符在5倍频程上被认为是基音0和度0的参考音。

我想要改变戒指的属性。下面的例子显示了音符的音高是如何用圆环来表示的。

代码语言:javascript
运行
复制
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)

下面的例子显示了如何用圆环来表示音符的程度。

代码语言:javascript
运行
复制
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)表示。

我选择了使用可变类和数据类。我想要一个可变的类,因为我需要很多操作来链接。我想要一个数据类,因为它隐藏锅炉板代码,并提供有趣的方法,如复制。

代码语言:javascript
运行
复制
val ring = someOtherRing.copy().setClass(2).setGroup(1)

我确实读过那个数据类应该是不可变的。

所以我的问题是:

  • 是否允许将数据类作为可变类使用?
  • 我把可变类和数据类组合在一起有意义吗?
  • 是否有更好的方法来创建API类?

任何关于Kotlin指南、一般指南、流利的方法和varia的补充反馈都是受欢迎的.

环码:

代码语言:javascript
运行
复制
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
    }
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2019-07-08 06:23:44

你的概念看上去完全被误导了。

环是一个数学概念,一个代数结构。它确实有一个大小,但它没有价值。

该值将是环元素的一部分。这样的环元素是一个元组( ring,value)。

音符的音高确实是一个环元素。环元素只能用来存储音符的音高,而不能用来存储八度音阶。如果是这样的话,它将不再符合一个环的数学概念。

要用西方的记谱法来代表一个音符,我的第一个想法是:

代码语言:javascript
运行
复制
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功能更强大。你只需说:

代码语言:javascript
运行
复制
val note = other.copy(octave = 3, pitch = 5)

这更容易掌握,编写的代码更少。如果你用Kotlin写了一个setter方法,向后倾,然后三思而后行。也许你在做一些不寻常的事。

顺便说一句,Kotlin的财产名称是用小写写的。Kotlin基于Java,而不是C#。

票数 3
EN

Code Review用户

发布于 2019-07-11 16:55:58

我决定不使用(ab-)使用数学结构Ring来创建音乐实体。正如罗兰所指出的那样,它只是不符合目的。相反,我已经创建了我自己的结构Coil,它代表了一个Value,它的Index在一个数量级的组Group Size中。我仍然使用setter,但仅用于派生属性。

Coil类:

代码语言:javascript
运行
复制
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用法:(给出线圈和备注之间相互作用的概念)

代码语言:javascript
运行
复制
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类:

代码语言:javascript
运行
复制
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()
        } 
}
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/223697

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档