首页 > 解决方案 > JFugue 5 笔记问题

问题描述

我正在尝试在我的 Java 项目中使用 JFugue 5.0.9 来创建 *.midi 文件。在将 JFugue 的 midi 功能应用到我使用24 音调 makam 钢琴频率的项目中时,我意识到它存在一些音调问题。

例如这段代码:

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

如这里所说,在控制台上。

但是打印

E4MAJ E4MAJ E4MAJ A4MIN

在我的项目中。是的,由于某种原因,前三个和弦是相同的。当然,它们听起来也一样。

此外,在使用像“m390”这样的微音时,它听起来并不完全在那个频率上。

在另一个文件中,在主要方法中我写了这个:

Player player = new Player();
player.play("A4 m440 m400 m390 m380 m370 m360");

我知道 A4 和 m440 是一样的,但正如这里所说的,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,它们实际上听起来都非常好。

标签: javajfugue

解决方案


好的,我查看了 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”转换为“iii”,因为如您所见,土耳其字母 (ı) 的小写 I 没有 if 语句,这导致它返回 0,如案例的“我”。

为了解决这个问题,我们必须删除小写转换并为大写 I 编写所有 if 语句,或者将默认语言环境设置为“en”以确保它将(大写)I 转换为(小写)i。所以,这应该在源代码中使用:

Locale.setDefault(new Locale("en"));

幸运的是,JFugue 是在 Apache 2.0 下发布的开源软件。

仍然不能解释为什么曲调听起来不对,但现在至少我们知道这些是完全不同的问题。或者也许看起来是这样……我不知道。如果我找到其余的解释或解决方案,我将编辑此答案。

编辑

最后我偶然发现了微音的问题。我决定再看一次源代码中的微音计算函数。

在 MicrotonePreprocessor 类(在 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 是最大音高值。一开始我和开发者的想法是一样的:“16384之后应该是0,没关系,这里没问题。”。然后我说“如果我将音高重置值从 0 更改为 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。当同时发送音符消息时,这些会导致键彼此停止。


推荐阅读