Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在java中创建一个简单但结构良好的乐谱类(Music score)?_Java_Oop_Music Notation - Fatal编程技术网

如何在java中创建一个简单但结构良好的乐谱类(Music score)?

如何在java中创建一个简单但结构良好的乐谱类(Music score)?,java,oop,music-notation,Java,Oop,Music Notation,我正在用非常基本的声音合成在我的游戏中创建音频和特效。基本上,我有一些方法可以播放给定频率、振幅和持续时间的声音 对于简短的短语和旋律,我想提出一个基本的符号,这样我就可以很容易地重写或在代码中添加新的旋律(最后我可能可以从文件中读取,但这可能是多余的) 然而,我不知道如何实现这一点 我首先创建了一个enum EqualTemperamentTuning,它包含所有88个基本钢琴音符,带有一个MIDI#字段和一个频率字段。这至少意味着我可以处理音符名称而不是频率 public enum Equa

我正在用非常基本的声音合成在我的游戏中创建音频和特效。基本上,我有一些方法可以播放给定频率、振幅和持续时间的声音

对于简短的短语和旋律,我想提出一个基本的符号,这样我就可以很容易地重写或在代码中添加新的旋律(最后我可能可以从文件中读取,但这可能是多余的)

然而,我不知道如何实现这一点

我首先创建了一个enum EqualTemperamentTuning,它包含所有88个基本钢琴音符,带有一个MIDI#字段和一个频率字段。这至少意味着我可以处理音符名称而不是频率

public enum EqualTemperamentTuning {
    A_0         (1, 27.5),
    A_SHARP_0   (2, 29.1352),
        ...
    C_8         (88, 4186.01);

    private int num;
    private double frequency;

    EqualTemperamentTuning(int num, double frequency){

        this.num = num;
        this.frequency = frequency;
    }

    public double getFrequency(){
        return frequency;
    }

    public double getNum(){
        return num;
    }
}
然后我开始创建更多的对象,首先是一个音符,它包含一个相等的温度、振幅和长度:

public class Note {

    /** Note frequency */
    private double frequency;
    /** Note Amplitude */
    private double amplitude;
    /** Note length */
    private int length;     

    public Note(EqualTemperamentTuning tuning, double amplitude, int length){

        this.frequency = tuning.getFrequency();
        this.amplitude = amplitude;
        this.length = length;   
    }

    public double getFrequency(){
        return frequency;
    }

    public double getAmplitude(){
        return amplitude;
    }

    public int getLength(){
        return length;  
    }
}
最后,为了定义我要播放的旋律,我创建了一个NotePhrase类:

public class NotePhrase {

    /** The arrayList of notes*/
    private Note[] notes;

    public NotePhrase(Note[] notes){

        this.notes = notes;
    }

    public double getFrequency(int counter){

        // for each note in the array
        for (int i = 0; i< notes.length; i++){

            // for each unit of length per note
            for (int j=0; j<notes[i].getLength(); j++){

                counter--;

                // return the frequency at this point
                if (counter <= 0) return notes[i].getFrequency();

            }
        }
        return -1;  
    }
}

然后在运行时将其加载到NotePhrase对象中。这不是很好,因为我必须为不同音符量的旋律创建一个新的构造函数。如果我用另一种方法,用一系列音符来构造枚举,那么我只是在别处“写”旋律,分为两部分,这似乎更令人困惑

所以我一直在思考如何正确地构建这个?ie创建什么类以及它们应该包含什么信息。。。我想让这个“正确”是因为,我可能会在将来扩展符号以包括效果(echo等),我已经发现,凭借我很少的经验,正确的类、结构和关系(甚至名称)可以使我的程序非常容易或难以理解

为这篇文章道歉,这可能不是一个措辞很好的问题,但作为java&OOP初学者,任何提示都是非常受欢迎的

编辑**


谢谢你的回答和建议,非常有用。考虑到本例中给出的答案,我重新思考了我的一般音频实现,这一点现在相当不稳定。但是,我不确定我应该将谁标记为正确的,因为我真的要采纳所有的建议,并尝试从那里开始。

不要对旋律使用枚举,因为旋律并不表示真正的常量,而是音符数据的集合。我建议不要使用数组,而是使用更灵活的
ArrayList

在Melody中,我将音符绑定到一个小节和一个节拍,并给Melody类一个addNote(Note,int-Measure,int-beatFraction),对于remove-Note也是如此

考虑将12个核心注释作为枚举,然后使用它们加上一个倍频程int来创建注释对象


。。。有很多方法可以玩这个练习。“关键”是要继续试验。

我们想到的是实现一个简单的内部函数,它将尽可能接近您用来记录音符的符号。 在java中,它可能看起来像

MelodyTrack  track1 = MelodyTrack().create().setTiming(TimeSignature.C)
.eighth(Notes.E5)
.bar().half(Notes.B5).quarter(Notes.A5).quarter(Notes.B5).bar().half(Notes.C6)
有一个

这只是一个例子,我并不坚持这样或那样的形式——基本思想是你的DSL必须是程序员和领域专家(音乐家)都可以阅读的。在音序器中表示数据的方式不应该影响你写旋律的方式,也就是说,让它更难

另外,如果您正在做一些严肃的事情,您应该尝试使用外部DSL a.k.a.midi文件或类似文件以及某种序列库,并将音乐存储在应用程序的单独资源文件中。

也许您可以将与结合使用。然后你可以写:

new NotePhraseBuilder()
     .beat(120)
     .measure(fourQuarters)
     .E_5()
     .quarter()
     .D_5()
     .half()
     ...;
NotePhraseBuilder包含像
beat()
measure()
E_5()
,但没有像
quarter()
half()这样的方法:

您可以将此模式中的提示合并


注意:方法
NotePhraseBuilder.E_5()
和其他类似方法调用
NoteBuilder
-构造函数,其
EqualTemperamentTuning
与其名称相等。写88种方法似乎是一种浪费,这能做得更漂亮吗?

记谱法可以被认为是一系列事件,无论是音符还是键和节奏的变化


对你来说,考虑一个包含事件的时间表并从中构建可能会更有成效。MIDI协议可能会给您提供更多的想法。

“这不太好,因为我必须为不同音符量的旋律创建一个新的构造函数。”您可以在此处使用varargs,varargs正是我需要的。它可能有助于创建语音和测量类,所以你的Melody课程只想做一件事。谢谢你的提示。我想我把Melody作为一个枚举,因为我认为一个旋律就像一张乐谱。它是固定的,只能在自身内部创建。我得把你的建议记在心里修改代码。@kirman:不客气,但不要把代码和数据混淆了。我认为旋律是一组数据。枚举更好地用于原子构建块或状态常量。类似于的方法。两个答案几乎同时发布:-),而且两个答案都比我的好。两者都是1+。是的,时间线是有意义的。我想我还没有完全弄清楚我真正需要从我正在制作的这个音频实现中得到什么,这使得设计很困难!MIDI看起来很复杂,但我想我可能会更好地理解它,因为它与我正在做的事情非常相关……MIDI在本质上相当简单——对于一首旋律,你有一个音符和一个音符,它在时间线上有一个音高值和一个位置。播放旋律是在正确的时间将事件发送到音源的过程。你可能还想看看mod跟踪器。
new NotePhraseBuilder()
     .beat(120)
     .measure(fourQuarters)
     .E_5()
     .quarter()
     .D_5()
     .half()
     ...;
class NotePhraseBuilder {
    ...
    public NoteBuilder E_5() {
        return new NoteBuilder(this, E_5);
    }
    ...
}

class NoteBuilder {
    private final NotePhraseBuilder parent;
    private final EqualTemperamentTuning tuning;

    public NoteBuilder(NotePhraseBuilder parent_, EqualTemperamentTuning tuning_) {
        this.parent = parent_;
        this.tuning = tuning_;
    }

    public NotePhraseBuilder quarter() {
        parent.add(new Note(tuning_, parent.getAmplitude(), parent.getTempo() / 4));
        return parent;
    }

    ...
}