Реализация функциональности типа enum с пользовательскими типами

Я работаю над USB MIDI-контроллером, используя Teensy. Контроллер представляет собой ряд из 7 кнопок, каждая кнопка представляет собой степень прогрессии, а 7 кнопок составляют последовательность аккордов. При нажатии устройство отправляет сообщение о включении / выключении MIDI-ноты для воспроизведения аккорда.

В моем коде у меня есть интервалы, хранящиеся в 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;

У меня также есть массив chords, вот так:

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 }
  }
};

Я хотел бы получить доступ к аккордам аналогично enum интервалов, чтобы я мог сделать что-то вроде этого (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);

Я знаю, что невозможно иметь перечисление пользовательских типов, но есть ли способ, которым я мог бы приблизиться к этому? Я подумал об использовании HashTable, но мне пришлось бы реализовать его с нуля, и я не думаю, что если смогу помочь.

1 ответ

Решение

Смысл перечисления в том, что вы создаете новый тип, который может принимать только фиксированный набор значений. Для ваших интервалов целесообразно использовать enum, потому что в действительности используется только очень много интервалов, и потому что создание нового типа удобнее, чем использование здесь целочисленных констант.

История отличается для ваших аккордов. У вас уже есть тип для ваших аккордов, поэтому перенос их в другой тип enum не поможет. Кроме того, количество аккордов гораздо менее ограничено. Имеющаяся у меня диаграмма аккордов показывает 22 фигуры, но они не включают инверсии. Ваша структура аккордов гораздо более подходящая, чем искусственное ограничение аккордов с помощью enum.

В 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++ имеет пространства имен, но это не влияет на замечания, сделанные в этом ответе.

Другие вопросы по тегам