我正在使用非常基本的声音合成在我的游戏中创建音频和效果。基本上,我有一些方法可以在给定频率、幅度和持续时间的情况下播放声音。
对于简短的短语和旋律,我想提出一个基本的符号,以便我可以轻松地重写或在代码中添加新的旋律(最后也许我可以从文件中读取,但这可能是矫枉过正)。
但是,我不确定如何实现这一点。
我首先创建了一个枚举 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;
}
}
然后我开始创建更多对象,首先是一个包含 EqualTemperamentTuning、振幅和长度的 Note:
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.getFrequency(int counter) 方法设置波形的频率。这应该(我还没有实际测试过!)只是根据频率和幅度(待添加)播放NotePhrase旋律。
问题是,它看起来不是很优雅,更具体地说,很难以任何清晰的方式“写”出旋律。我必须编写一大堆新的 Note 对象,然后用它们的数组构造一个 NotePhrase 对象……对我来说,如何硬编码这些旋律然后在它们之间轻松切换对我来说并不明显。
真的我想创建一个旋律的枚举或类似的东西,我可以很容易地为每个不同的旋律硬编码一个人类可读的配置,然后当我想使用它们时,只需将枚举类型传递给音频播放器.. .
我得到的最好的是:
private static enum Melody {
NOKIA_RINGTONE ( new Note(EqualTemperamentTuning.E_5, 0.5, 1), new Note(EqualTemperamentTuning.D_5, 0.5, 1))
;
private Note[] notes = new Note[2];
Melody (Note note1, Note note2){
this.notes[0] = note1;
this.notes[1] = note2;
}
}
然后我会在运行时将其加载到 NotePhrase 对象中。这不太好,因为我必须为具有不同数量音符的旋律创建一个新的构造函数。如果我反过来用一系列音符构建枚举,那么我只是在别处和两部分“写”旋律,这似乎更令人困惑......
所以我不知道如何正确地构建它?即要创建什么类以及它们应该保存什么信息......我想得到这个“正确”,因为,我可能想在未来扩展符号以包括效果(回声等),我已经用我的非常很少的经验,正确的类、结构和关系(甚至名称)可以使我的程序非常容易或难以理解。
为这篇文章道歉,这可能不是一个很好的措辞(咳咳)问题,但作为一个 java 和 OOP 初学者,任何提示都会非常受欢迎!
编辑* *
感谢您的回答和建议,非常有帮助。考虑在这种情况下给出的答案让我重新考虑了我的一般音频实现,这现在非常不稳定。不确定我应该将谁标记为正确,因为我真的只是将所有建议都纳入其中,然后尝试从那里开始。