首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >按音阶索引转换音符

按音阶索引转换音符
EN

Stack Overflow用户
提问于 2015-02-21 19:15:37
回答 4查看 2K关注 0票数 4

我试图创建一种算法,根据它们给定的音阶和转换因子,将音符向上或向下转换。

电流算法:

代码语言:javascript
复制
public Note getNoteByScale(Scale scale, int transposeFactor) {
    int newPitch = scale.getScaleIndex(this.pitch) + transposeFactor;
    int newOctave = this.octave;

    if (newPitch > 6) {
        newPitch -= 7;
        newOctave++;
    }
    if (newPitch < 0) {
        newPitch += 7;
    }

    return Note.createNote(scale.getNotesInScale().get(newPitch),
            newOctave, this.duration);
}

每个比额表(表示为这一目的的主要比额表)有7个音符。例如,C-主要比额表有以下说明:

代码语言:javascript
复制
C, D, E, F, G, A, and B

如果要将这个音阶与上面的算法一起使用,来转换音符,例如,在C-主音阶上的一个5倍音阶上的'B‘音符,该算法将按预期的方式工作,并将音符'C’(音阶的索引0)返回为6的八度音阶。

然而,假设我们使用了由音符组成的D-专业音阶。

代码语言:javascript
复制
D, E, F♯, G, A, B, and C♯

对于这个音阶,最后一个音符'C♯‘被认为是一个比音阶中其他音符更高的八度音阶。例如,如果我们使用上面的算法将音符'B‘(索引6)转到5倍频程上,那么算法实际上会在5倍频程上给出音符'C♯’,这是完全错误的:它应该是在6倍频程上。

代码语言:javascript
复制
Note original = Note.createNote(Pitch.B, 5, Duration.WHOLE);
// Prints: [Note: [pitch: C#],[octave: 5]]
System.out.println(original.getNoteByScale(Scale.D_MAJOR, 1)); 

是否有任何方法来修正上述算法,使其能够支持上述情况?

我为NotePitchScale类使用的Java类可以在这里找到:杰米·克雷恩·梅洛迪世代- GoogleCode

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2015-02-21 20:11:36

您目前正在测试pitch (即scale中的索引),以查看是否需要更改八度。

但是单个音阶可以包含一个以上的八度指数,这取决于音符本身相对于C的索引,我这里没有使用那个库的术语,因为我对它不太熟悉。因此,您需要知道便笺的索引相对于C是否已包装(向下或向上),以改变八度(上或下)。

首先,假设您从未移动超过transposeFactor值的6。接下来,在编写任何代码之前定义一些测试(见下面)。然后,我认为您只需要进行一个小的更改就可以使用Pitch.getNoteNumber(),它返回相对于C的注释索引,我还假设您的getNoteByScale方法是Note上的一个方法,而getScaleIndex是您在Scale上创建的一个方法,如下所示:

代码语言:javascript
复制
// this is a method on Scale
public int getScaleIndex(Pitch pitch) {
    return notesInScale.indexOf(pitch);
}
...
public Note getNoteByScale(Scale scale, int transposeFactor) {
    // rename to "newPitchIndex" to distinguish from the actual new Pitch object
    int newPitchIndex = scale.getScaleIndex(this.pitch) + transposeFactor;
    // only use pitch indexes for a scale in the range 0-6
    if (newPitchIndex > 6) newPitchIndex -=7;
    else if (newPitchIndex < 0) newPitchIndex += 7;
    // create the new Pitch object
    Pitch newPitch = scale.getNotesInScale().get(newPitchIndex);

    //  Get the note numbers (relative to C)
    int noteNumber = this.pitch.getNoteNumber();
    int newNoteNumber = newPitch.getNoteNumber();
    int newOctave = this.octave;
    if      (transposeFactor > 0 && newNoteNumber < noteNumber) newOctave++;
    else if (transposeFactor < 0 && newNoteNumber > noteNumber) newOctave--;

    return Note.createNote(newPitch, newOctave, this.duration);
}

现在我们有了这个,我们可以很容易地把它扩展到一次超过八度的移动。如果没有测试,这一切都不会那么容易:

代码语言:javascript
复制
public Note getNoteByScale(Scale scale, int transposeFactor) {
    // move not by more than 7
    int pitchIndex = scale.getScaleIndex(this.pitch);
    int newPitchIndex = (pitchIndex + transposeFactor) % 7;
    // and adjust negative values down from the maximum index
    if (newPitchIndex < 0) newPitchIndex += 7;
    // create the new Pitch object
    Pitch newPitch = scale.getNotesInScale().get(newPitchIndex);

    //  Get the note numbers (relative to C)
    int noteNumber = this.pitch.getNoteNumber();
    int newNoteNumber = newPitch.getNoteNumber();
    //  Get the number of whole octave changes
    int octaveChanges = transposeFactor / 7;
    int newOctave = this.octave + octaveChanges;
    //  Adjust the octave based on a larger/smaller note index relative to C
    if      (transposeFactor > 0 && newNoteNumber < noteNumber) newOctave++;
    else if (transposeFactor < 0 && newNoteNumber > noteNumber) newOctave--;

    return Note.createNote(newPitch, newOctave, this.duration);
}

需要进行更多的测试,但这是一个很好的开始,所有这些都通过了:

代码语言:javascript
复制
@Test
public void transposeUpGivesCorrectPitch() {
    //                 ┌~1▼
    // |D  E  F♯ G  A  B ║C♯|D  E  F♯ G  A  B ║C♯ |
    Note original = Note.createNote(Pitch.B, 5, Duration.WHOLE);
    Note actual = original.getNoteByScale(Scale.D_MAJOR, 1);
    Note expected = Note.createNote(Pitch.C_SHARP, 6, Duration.WHOLE);
    assertEquals(expected.getPitch(), actual.getPitch());
}

@Test
public void transposeDownGivesCorrectPitch() {
    //                 ▼1~┐                     
    // |D  E  F♯ G  A  B ║C♯|D  E  F♯ G  A  B ║C♯ |
    Note original = Note.createNote(Pitch.C_SHARP, 6, Duration.WHOLE);
    Note actual = original.getNoteByScale(Scale.D_MAJOR, -1);
    Note expected = Note.createNote(Pitch.B, 5, Duration.WHOLE);
    assertEquals(expected.getPitch(), actual.getPitch());
}

@Test
public void transposeUpOutsideScaleGivesCorrectPitch() {
    //                 ┌‒1‒~2‒‒3‒‒4▼         
    // ║C  D  E  F  G  A  B ║C  D  E  F  G  A  B |
    Note original = Note.createNote(Pitch.A, 5, Duration.WHOLE);
    Note actual = original.getNoteByScale(Scale.C_MAJOR, 4);
    Note expected = Note.createNote(Pitch.E, 6, Duration.WHOLE);
    assertEquals(expected.getPitch(), actual.getPitch());
}

@Test
public void transposeDownOutsideScaleGivesCorrectPitch() {
    //           ▼4‒‒3‒‒2‒‒1~┐                        
    // ║C  D  E  F  G  A  B ║C  D  E  F  G  A  B |
    Note original = Note.createNote(Pitch.C, 6, Duration.WHOLE);
    Note actual = original.getNoteByScale(Scale.C_MAJOR, -4);
    Note expected = Note.createNote(Pitch.F, 5, Duration.WHOLE);
    assertEquals(expected.getPitch(), actual.getPitch());
}

@Test
public void transposeUpGivesCorrectOctave() {
    //                 ┌~1▼
    // |D  E  F♯ G  A  B ║C♯|D  E  F♯ G  A  B ║C♯ |
    Note original = Note.createNote(Pitch.B, 5, Duration.WHOLE);
    Note actual = original.getNoteByScale(Scale.D_MAJOR, 1);
    Note expected = Note.createNote(Pitch.C_SHARP, 6, Duration.WHOLE);
    assertEquals(expected.getOctave(), actual.getOctave());
}

@Test
public void transposeUp2GivesCorrectOctave() {
    //                 ┌~1‒‒2‒‒3‒‒4‒‒5‒‒6‒‒7‒~1▼                     
    // |D  E  F♯ G  A  B ║C♯|D  E  F♯ G  A  B ║C♯ |
    Note original = Note.createNote(Pitch.B, 5, Duration.WHOLE);
    Note actual = original.getNoteByScale(Scale.D_MAJOR, 1 + 7);
    Note expected = Note.createNote(Pitch.C_SHARP, 7, Duration.WHOLE);
    assertEquals(expected.getOctave(), actual.getOctave());
}

@Test
public void transposeDownGivesCorrectOctave() {
    //                 ▼1~┐                     
    // |D  E  F♯ G  A  B ║C♯|D  E  F♯ G  A  B ║C♯ |
    Note original = Note.createNote(Pitch.C_SHARP, 6, Duration.WHOLE);
    Note actual = original.getNoteByScale(Scale.D_MAJOR, -1);
    Note expected = Note.createNote(Pitch.B, 5, Duration.WHOLE);
    assertEquals(expected.getOctave(), actual.getOctave());
}

@Test
public void transposeDown2GivesCorrectOctave() {
    //                 ▼1~‒7‒‒6‒‒5‒‒4‒‒3‒‒2‒‒1~┐                     
    // |D  E  F♯ G  A  B ║C♯|D  E  F♯ G  A  B ║C♯ |
    Note original = Note.createNote(Pitch.C_SHARP, 6, Duration.WHOLE);
    Note actual = original.getNoteByScale(Scale.D_MAJOR, -1 - 7);
    Note expected = Note.createNote(Pitch.B, 4, Duration.WHOLE);
    assertEquals(expected.getOctave(), actual.getOctave());
}

// ... and more tests are needed ...
票数 5
EN

Stack Overflow用户

发布于 2015-02-21 19:29:44

我想当你通过音阶结束时,你不想要+1倍频程(在这个例子中是ScaleIndex 7)。当你通过C时,你想要+1倍频程。

编辑:

我已经很久没有用java编程了,我没有为它安装IDE,所以我会编写一些伪代码(没有类型)。另外,这只适用于正转置因子,它应该很容易调整负片,但我不想再模糊它了。我们的想法是首先知道我们上升了多少倍频度,这意味着我们至少要增加那么多的八度音阶。然后添加其余部分(必须小于一个完整的刻度),并在添加该值时查看是否通过了C。我们通过查看新编号来实现这一点,如果新的notenumber比旧的小,我们肯定已经超过了C。

代码语言:javascript
复制
public Note getNoteByScale(Scale scale, int transposeFactor) {
    numberOfNotesInScale = scale.getNotesInScale().size();
    numberOfScalesToTraverseAtLeast = transposeFactor / number_of_notes_in_scale;  // this should be integer division
    newOctave = this.octave + numberOfScalesToTraverseAtLeast;
    restAfterDivide = transposeFactor % numberOfNotesInScale;
    newScaleIndex = (scale.getScaleIndex(this.pitch) + restAfterDivide) % numberOfNotesInScale;  // we are not gonna do +1 when we pass the end of the scale, so just do a modulo on the number of notes of the scale
    newPitch = scale.getNotesInScale().get(newScaleIndex)
    if (newPitch.getNoteNumber < this.pitch.getNoteNumber)  // then we have passed C, so another octave
        newOctave++;

    return Note.createNote(scale.getNotesInScale().get(newPitch), newOctave, this.duration);

}

对于一些测试驱动的开发,这将是一个理想的候选函数,我至少会围绕它编写一堆单元测试;)

票数 2
EN

Stack Overflow用户

发布于 2018-05-28 19:00:37

是的,我知道这是个老问题,但我需要类似的东西。基本上,这个问题可以分解为:给定一个注释("D")和一些正或负的半步(1),返回一个新的注,即原注转置(D#)。

这里的答案太复杂了。

试试这个:

代码语言:javascript
复制
public class Transposer {

    enum TARGET { FLAT, SHARP };
    static String[] SHARPS = { "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#" };
    static String[] FLATS  = { "A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab" };

    static String transpose(String note, int halfSteps, TARGET target) {
        if (target == null) {
            // By default we like flats
            target = TARGET.FLAT;
        }

        String[] scale = FLATS;
        if (target == TARGET.SHARP) {
            scale = SHARPS;
        }

        int index = findIndex(scale, note);
        if (index < 0) return note; // Not found, just return note


        String everythingelse = "";
        if (note.length() > scale[index].length()) {
           everythingelse = note.substring(scale[index].length());
        }


        index = index + halfSteps;
        while (index < 0) {
            index += scale.length;  // Make the index positive
        }
        index = index % scale.length;
        return scale[index]+everythingelse;
    }

    public static int findIndex(String[] scale, String note) {
        int r = -1;
        String root = note.substring(0,1);
        if (note.charAt(1) == '#' || note.charAt(1) == 'b') {
            root = note.substring(0,2);
        }
        for (int i=0; i<scale.length; ++i) {
            if (scale[i].equalsIgnoreCase(root)) {
                // Match.
                return i;
            }
        }
        return r;
    }

    public static void main(String args[]) {
        String note = args[0];
        int halfsteps = Integer.parseInt(args[1]);
        System.out.println(note+" transposed "+halfsteps+" halfsteps is "+transpose(note, halfsteps, TARGET.FLAT));
    }
}

主要()是测试:

代码语言:javascript
复制
java -cp . Transposer A 1
A transposed 1 halfsteps is Bb

java -cp . Transposer Eb -3
Eb transposed -3 halfsteps is C

java -cp Transposer C7 2
C7 transposed 2 halfsteps is D7

采用一个比额表是微不足道的:

代码语言:javascript
复制
    String[] scale = { "C", "D", "E", "F", "G", "A", "B" };
    for (String snote: scale) {
      System.out.print(Transposer.transpose(snote,1,null));
    }
    System.out.println();

=> Db Eb F Gb Ab Bb C
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28650156

复制
相关文章

相似问题

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