JFugue 5 音符问题
JFugue 5 note problems
我正在尝试在我的 Java 项目中使用 JFugue 5.0.9 来创建 *.midi 文件。在我使用 24 tune makam piano 频率的项目中实现 JFugue 的 MIDI 功能时,我意识到它有一些调音问题。
例如这段代码:
ChordProgression cp = new ChordProgression("I-III-IV-iv").setKey("E");
System.out.println(cp);
Player player = new Player();
player.play(cp);
应该打印
E4MAJ G#4MAJ A4MAJ A4MIN
在控制台上,据说 here。
但打印
E4MAJ E4MAJ E4MAJ A4MIN
在我的项目中。是的,前三个和弦出于某种原因是相同的。当然,它们听起来也一样。
此外,在使用像 "m390" 这样的微音时,它听起来并不完全符合那个频率。
在另一个文件中,我在主要方法中这样写:
Player player = new Player();
player.play("A4 m440 m400 m390 m380 m370 m360");
我知道 A4 和 m440 是一样的,但正如所说 here,A5 和 m440 听起来应该是一样的。但在我的项目中,A4 和 m440 听起来一样,但不完全是 440Hz。当我意识到不对劲时,我决定使用调谐应用程序,这是它分别计算出的频率:
221.5 221.5 197.5 186.6 186.6 186.6 176.2
如您所见,它播放的内容非常接近 A3,而不是清晰的 A4。但这并不是全部。 m390、m380 和 m370 听起来也完全一样。
这里到底出了什么问题?任何帮助将不胜感激。
小提示:我很确定这与我的项目无关。我在一个全新的项目中尝试 运行 上面的代码,出现了同样的问题。而且我的系统没有问题,因为我的主要项目和任何其他软件,如 SunVox,它们实际上听起来都非常好。
好的,我查看了 JFugue 5.0.9 的源代码,这是我在 ChordProgression.java:
中得到的
/**
* Only converts Roman numerals I through VII, because that's all we need in music theory...
* VIII would be the octave and equal I!
*/
private int romanNumeralToIndex(String romanNumeral) {
String s = romanNumeral.toLowerCase();
if (s.startsWith("vii")) { return 6; }
else if (s.startsWith("vi")) { return 5; }
else if (s.startsWith("v")) { return 4; }
else if (s.startsWith("iv")) { return 3; }
else if (s.startsWith("iii")) { return 2; }
else if (s.startsWith("ii")) { return 1; }
else if (s.startsWith("i")) { return 0; }
else { return 0; }
}
有点懒惰...:D 问题之一就在这里。如果您在未指定语言环境的情况下使用 toLowerCase() 方法,则会在运行时导致一些问题。在我现在使用的土耳其字母表中,I 的小写字母是“ı”,而不是 "i"。因此,该程序将我的和弦从 "I-II-III" 转换为 "i-i-i",因为如您所见,土耳其字母 (ı) 的小写 I 没有 if 语句,这导致它变为 return 0与 "i".
的情况一样
为了解决这个问题,我们要么删除小写转换并为大写 I's 编写所有 if 语句,要么将默认语言环境设置为 "en" 以确保它将(大写)I's 转换为(小写)我的。所以,这应该在源代码中使用:
Locale.setDefault(new Locale("en"));
幸运的是,JFugue 是一款在 Apache 2.0 下发布的开源软件。
这仍然没有解释为什么曲调听起来不对但现在至少我们知道这些完全是不同的问题。又或许它看起来如此……我不知道。如果我找到其余的解释或解决方案,我将编辑此答案。
编辑
终于无意中弄明白了微调的问题。我决定再看一遍源码中的微调计算函数。
在 MicrotonePreprocessor class(在 org.staccato 包中)有一个名为 convertFrequencyToStaccato() 的函数。此功能的作用是将频率转换为 MIDI 音符编号和弯音值。在第 107 行,如果计算出的音高值非常接近下一个音符,则该代码会舍入半音、八度和音高值:
// If we're close enough to the next note, just use the next note.
if (pitches >= 16380)
{
pitches = 0;
semitone += 1;
if (semitone == 12)
{
octave += 1;
semitone = 0;
}
}
重设音高的那一行应该改成:
pitches = 8192;
因为,您知道,中性音高值为 8192。0(零)是最小音高值,16384 是最大音高值。一开始我和开发者的想法是一样的:"After 16384, it should be 0. That is okay. No problem here."。然后我说"What if I change pitch-reset value from 0 to 8192?"。有效。这是我们俩都有的一个美丽的感知错误。 :D 我现在真的笑得很开心。
这解决了微调问题。我现在可以听到完美的音程了!我感到幸福和满足。
编辑2
我只是想分享我进一步的改变,这些改变导致了更好的微音调谐:
if (pitches >= 12288)
{
int diff = 16384-pitches;
int applieddiff = 8192-diff;
pitches = applieddiff;
semitone += 1;
if (semitone == 12)
{
octave += 1;
semitone = 0;
}
}
这也有助于在 MIDI 板上使用半音(黑色)键,而不是仅使用具有高音高值的音调(白色)键。因此,如果您将数据发送到另一个软件,它将单独检测所有密钥。比如之前没有G和G#,只有G和G-with-high-pitch。当同时发送音符消息时,这些导致键相互停止。
我正在尝试在我的 Java 项目中使用 JFugue 5.0.9 来创建 *.midi 文件。在我使用 24 tune makam piano 频率的项目中实现 JFugue 的 MIDI 功能时,我意识到它有一些调音问题。
例如这段代码:
ChordProgression cp = new ChordProgression("I-III-IV-iv").setKey("E");
System.out.println(cp);
Player player = new Player();
player.play(cp);
应该打印
E4MAJ G#4MAJ A4MAJ A4MIN
在控制台上,据说 here。
但打印
E4MAJ E4MAJ E4MAJ A4MIN
在我的项目中。是的,前三个和弦出于某种原因是相同的。当然,它们听起来也一样。
此外,在使用像 "m390" 这样的微音时,它听起来并不完全符合那个频率。
在另一个文件中,我在主要方法中这样写:
Player player = new Player();
player.play("A4 m440 m400 m390 m380 m370 m360");
我知道 A4 和 m440 是一样的,但正如所说 here,A5 和 m440 听起来应该是一样的。但在我的项目中,A4 和 m440 听起来一样,但不完全是 440Hz。当我意识到不对劲时,我决定使用调谐应用程序,这是它分别计算出的频率:
221.5 221.5 197.5 186.6 186.6 186.6 176.2
如您所见,它播放的内容非常接近 A3,而不是清晰的 A4。但这并不是全部。 m390、m380 和 m370 听起来也完全一样。
这里到底出了什么问题?任何帮助将不胜感激。
小提示:我很确定这与我的项目无关。我在一个全新的项目中尝试 运行 上面的代码,出现了同样的问题。而且我的系统没有问题,因为我的主要项目和任何其他软件,如 SunVox,它们实际上听起来都非常好。
好的,我查看了 JFugue 5.0.9 的源代码,这是我在 ChordProgression.java:
中得到的 /**
* Only converts Roman numerals I through VII, because that's all we need in music theory...
* VIII would be the octave and equal I!
*/
private int romanNumeralToIndex(String romanNumeral) {
String s = romanNumeral.toLowerCase();
if (s.startsWith("vii")) { return 6; }
else if (s.startsWith("vi")) { return 5; }
else if (s.startsWith("v")) { return 4; }
else if (s.startsWith("iv")) { return 3; }
else if (s.startsWith("iii")) { return 2; }
else if (s.startsWith("ii")) { return 1; }
else if (s.startsWith("i")) { return 0; }
else { return 0; }
}
有点懒惰...:D 问题之一就在这里。如果您在未指定语言环境的情况下使用 toLowerCase() 方法,则会在运行时导致一些问题。在我现在使用的土耳其字母表中,I 的小写字母是“ı”,而不是 "i"。因此,该程序将我的和弦从 "I-II-III" 转换为 "i-i-i",因为如您所见,土耳其字母 (ı) 的小写 I 没有 if 语句,这导致它变为 return 0与 "i".
的情况一样为了解决这个问题,我们要么删除小写转换并为大写 I's 编写所有 if 语句,要么将默认语言环境设置为 "en" 以确保它将(大写)I's 转换为(小写)我的。所以,这应该在源代码中使用:
Locale.setDefault(new Locale("en"));
幸运的是,JFugue 是一款在 Apache 2.0 下发布的开源软件。
这仍然没有解释为什么曲调听起来不对但现在至少我们知道这些完全是不同的问题。又或许它看起来如此……我不知道。如果我找到其余的解释或解决方案,我将编辑此答案。
编辑
终于无意中弄明白了微调的问题。我决定再看一遍源码中的微调计算函数。
在 MicrotonePreprocessor class(在 org.staccato 包中)有一个名为 convertFrequencyToStaccato() 的函数。此功能的作用是将频率转换为 MIDI 音符编号和弯音值。在第 107 行,如果计算出的音高值非常接近下一个音符,则该代码会舍入半音、八度和音高值:
// If we're close enough to the next note, just use the next note.
if (pitches >= 16380)
{
pitches = 0;
semitone += 1;
if (semitone == 12)
{
octave += 1;
semitone = 0;
}
}
重设音高的那一行应该改成:
pitches = 8192;
因为,您知道,中性音高值为 8192。0(零)是最小音高值,16384 是最大音高值。一开始我和开发者的想法是一样的:"After 16384, it should be 0. That is okay. No problem here."。然后我说"What if I change pitch-reset value from 0 to 8192?"。有效。这是我们俩都有的一个美丽的感知错误。 :D 我现在真的笑得很开心。
这解决了微调问题。我现在可以听到完美的音程了!我感到幸福和满足。
编辑2
我只是想分享我进一步的改变,这些改变导致了更好的微音调谐:
if (pitches >= 12288)
{
int diff = 16384-pitches;
int applieddiff = 8192-diff;
pitches = applieddiff;
semitone += 1;
if (semitone == 12)
{
octave += 1;
semitone = 0;
}
}
这也有助于在 MIDI 板上使用半音(黑色)键,而不是仅使用具有高音高值的音调(白色)键。因此,如果您将数据发送到另一个软件,它将单独检测所有密钥。比如之前没有G和G#,只有G和G-with-high-pitch。当同时发送音符消息时,这些导致键相互停止。