1

I'm working on a USB MIDI controller using a Teensy. The controller is a row of 7 buttons, each button is a progression degree and the 7 buttons make up a chord progression. When pressed the device sends a MIDI note on/off message to play a chord.

In my code I have intervals stored in an enum:

/*
 * Intervals
 */
 typedef enum {
   ROOT = 0,
   UNISON = 0,
   DIMINISHED_SECOND = 0,
   MINOR_SECOND = 1,
   AUGMENTED_UNISON = 1,
   HALFSTEP = 1,
   MAJOR_SECOND = 2,
   DIMINISHED_THIRD = 2,
   WHOLESTEP = 2,
   MINOR_THIRD = 3,
   AUGMENTED_SECOND = 3,
   MAJOR_THIRD = 4,
   DIMINISHED_FOURTH = 4,
   PERFECT_FOURTH = 5,
   AUGMENTED_THIRD = 5,
   DIMINISHED_FIFTH = 6,
   AUGMENTED_FOURTH = 6,
   PERFECT_FIFTH = 7,
   DIMINISHED_SIXTH = 7,
   MINOR_SIXTH = 8,
   AUGMENTED_FIFTH = 8,
   MAJOR_SIXTH = 9,
   DIMINISHED_SEVENTH = 9,
   MINOR_SEVENTH = 10,
   AUGMENTED_SIXTH = 10,
   MAJOR_SEVENTH = 11,
   DIMINISHED_OCTAVE = 11,
   PERFECT_OCTAVE = 12,
   AUGMENTED_SEVENTH = 12,
   DIMISHED_NINTH = 12,
   MINOR_NINTH = 13,
   AUGMENTED_OCTAVE = 13,
   MAJOR_NINTH = 14,
   DIMINISHED_TENTH = 14,
   MINOR_TENTH = 15,
   AUGMENTED_NINTH = 15,
   MAJOR_TENTH = 16,
   DIMINISHED_ELEVENTH = 16,
   PERFECT_ELEVENTH = 17,
   AUGMENTED_TENTH = 17,
   DIMINISHED_TWELFTH = 18,
   AUGMENTED_ELEVENTH = 18,
   PERFECT_TWELFTH = 19,
   DIMINISHED_THIRTEENTH = 19,
   MINOR_THIRTEENTH = 20,
   AUGMENTED_TWELFTH = 20,
   MAJOR_THIRTEENTH = 21,
   DIMINISHED_FOURTEENTH = 21,
   MINOR_FOURTEENTH = 22,
   AUGMENTED_THIRTEENTH = 22,
   MAJOR_FOURTEENTH = 23,
   DIMINISHED_FIFTEENTH = 23,
   PERFECT_FIFTEENTH = 24,
   AUGMENTED_FOURTEENTH = 24,
   AUGMENTED_FIFTEENTH = 25
 } INTERVAL;

I also have an array of chords, like so:

struct Chord {
   String name;
   int tones[7];
 };

Chord chords[6] = {
  { "maj", {
    INTERVAL::UNISON,
    INTERVAL::MAJOR_THIRD,
    INTERVAL::PERFECT_FIFTH }
  },
  { "min", {
    INTERVAL::UNISON,
    INTERVAL::MINOR_THIRD,
    INTERVAL::PERFECT_FIFTH }
  },
  { "maj7", {
    INTERVAL::UNISON,
    INTERVAL::MAJOR_THIRD,
    INTERVAL::PERFECT_FIFTH,
    INTERVAL::MAJOR_SEVENTH }
  },
  { "min7", {
    INTERVAL::UNISON,
    INTERVAL::MINOR_THIRD,
    INTERVAL::PERFECT_FIFTH,
    INTERVAL::MINOR_SEVENTH }
  },
  { "maj9", {
    INTERVAL::UNISON,
    INTERVAL::MAJOR_THIRD,
    INTERVAL::PERFECT_FIFTH,
    INTERVAL::MAJOR_SEVENTH,
    INTERVAL::MAJOR_NINTH }
  },
  { "min9", {
    INTERVAL::UNISON,
    INTERVAL::MINOR_THIRD,
    INTERVAL::PERFECT_FIFTH,
    INTERVAL::MINOR_SEVENTH,
    INTERVAL::MINOR_NINTH }
  }
};

I'd like to access the chords in a similar way to the enum of intervals so I can do something like this (psudeocode):

void playChord(Chord chord, int velocity, int channel) {
    int i;
    for(i=0; i<chord.length; i++) {
        usbMIDI.sendNoteOn(chord[i], velocity, channel);
    }
}

playChord(Chord::MAJOR, 127, 1);

I know it's not possible to have an enum of custom types, but is there any way I could get close to this? I've considered using a HashTable, but I'd have to implement it from scratch and I don't fancy that if I can help it.

4

1 回答 1

5

枚举的意义在于您创建了一个只能采用一组固定值的新类型。为您的区间使用枚举是合适的,因为实际使用的区间只有这么多,而且在这里创建新类型比使用整数常量更方便。

故事因您的和弦而异。您已经有了和弦类型,因此将它们包装在另一个枚举类型中没有帮助。此外,和弦的数量要少得多。我手头的和弦图显示了 22 种形状,但不包括倒转。您的和弦结构比使用枚举人为地限制和弦更合适。

除了枚举之外,C 还有另外两种创建“常量”的机制:预处理器定义和静态变量。

使用预处理器指令,我们可以定义一个 Chord 文字。IIRC 结构文字是 C99 的东西,以前只能有初始化文字。

#define CHORD_MAJOR ((Chord){"maj", {ROOT, MAJOR_THIRD, PERFECT_FIFTH}})

使用静态变量,您可以在标头中声明一个对象:

static const Chord chord_major = {"maj", {ROOT, MAJOR_THIRD, PERFECT_FIFTH}};

请注意,C 没有像::. 相反,您必须自己为任何可能冲突的标识符添加前缀。C++ 确实有命名空间,但这并不影响这个答案中的观点。

于 2017-08-13T13:16:46.247 回答