Chords Builder: Часть 2 - Добавление к EXPRESS схеме CHORD_BUILDER возможности описания табулатур аккордов.

Опубликовано crsm_admin - вс, 05/14/2023 - 22:43

Модернизируем исходную схему CHORD_BUILDER так, чтобы с её помощью на грифе струнного инструмента можно было задавать табулатуры аккордов:

Сначала зададим новые типы для описания индекса струны, для гитары нумерация будет идти от единицы (верхняя Ми) до шести (нижняя Ми) - CBStringIndex, тип CBFretIndex будет описывать индекс лада, на котором зажимается струна (начиная с 1), а тип CBFingerIndex будет использоваться для определения пальца, которым струна зажимается на указанном ладу (от 1 до 5).

В класс, описывающий накладку на гриф добавим инверсный атрибут в котором будут собранны все доступные в модели табулатуры аккордов для данного инструмента:

CBFretboard;
  Id: OPTIONAL STRING(64);
  Name: STRING(64);
  NFrets: CBPositiveInteger;
  Strings: LIST [1:?] OF CBString;
 INVERSE
  OfInstrument: CBInstrument FOR Fretboard;
  HasChords: SET [0:?] OF CBChord FOR Fretboard;

Следующим шагом добавим в схему возможность описания места, на котором будет зажата струна. Класс CPBressure будет иметь три атрибута для указания индеска струны, индекса лада и индекса пальца руки, которым струна зажимается на данном ладу:

ENTITY CBPressure;
  StringIndex: CBStringIndex;
  Fret: OPTIONAL CBFretIndex;
  Finger: OPTIONAL CBFingerIndex;
END_ENTITY;

EXPRESS-описание для табулатуры аккорда зададим определением CBChord, который кроме названия символа аккорда содержит ссылку на накладу на гриф (CBFretboard), не обязательный атрибут Barre которым задаётся индекс лада для зажатия баррэ и контейнер SET ссылок на объекты CBPressure, которые формируют табулатуру аккорда.

ENTITY CBChord;
  Symbol: STRING(64);
  Fretboard: CBFretboard;
  Barre: OPTIONAL CBFretIndex;
  Pressures: SET [0:?] OF CBPressure;
END_ENTITY;

Наличие опциональных аттрибутов для указания индекса лада (Fret) и индекса пальца (Finger) позволит определять струну, как не используемую в данном аккорде (т.е. для струны достаточно не указывать Fret и Finger для того чтобы она считалась не звучащей).

Полная схема в результате изменений будет такой:

SCHEMA CHORD_BUILDER;

TYPE CBPositiveInteger = INTEGER;
 WHERE
  IsPositive: SELF > 0;
END_TYPE;

TYPE CBStringIndex = INTEGER;
 WHERE
  IsPositive: SELF >= 1;
END_TYPE;

TYPE CBFretIndex = INTEGER;
 WHERE
  IsPositive: SELF >= 1;
END_TYPE;

TYPE CBFingerIndex = INTEGER;
 WHERE
  IsAppropriate: ((SELF >= 1) AND (SELF <= 5));
END_TYPE;

ENTITY CBInstrument;
  Name: STRING(64);
  Fretboard: CBFretboard;
END_ENTITY;

ENTITY CBFretboard;
  Id: OPTIONAL STRING(64);
  Name: STRING(64);
  NFrets: CBPositiveInteger;
  Strings: LIST [1:?] OF CBString;
 INVERSE
  OfInstrument: CBInstrument FOR Fretboard;
  HasChords: SET [0:?] OF CBChord FOR Fretboard;
END_ENTITY;

ENTITY CBString;
  Name: OPTIONAL STRING(64);
  Symbol: STRING(2);
 INVERSE
  OfFretboard: CBFretboard FOR Strings;
END_ENTITY;

ENTITY CBPressure;
  StringIndex: CBStringIndex;
  Fret: OPTIONAL CBFretIndex;
  Finger: OPTIONAL CBFingerIndex;
END_ENTITY;

ENTITY CBChord;
  Symbol: STRING(64);
  Fretboard: CBFretboard;
  Barre: OPTIONAL CBFretIndex;
  Pressures: SET [0:?] OF CBPressure;
END_ENTITY;

END_SCHEMA;

СИДД (SDAI) код для Smorodina STEP который добавляет аккорд в модель в соответствии с текущей схемой будет выглядеть следующим образом

// Функция добавляет в модель объект CBPressure для описания зажатия струны пальцем на определённом ладу:
SdaiAppInstance appendPressure(SdaiModel model, SdaiSet pressures, SdaiInteger stringIndex, SdaiInteger fretIndex = SDAI::Const::Integer_unset, SdaiInteger fingerIndex = SDAI::Const::Integer_unset)
{
  SdaiAppInstance string = sdaiCreateInstanceBN(model, "cbpressure");
  sdaiPutAttrBN(string, "stringindex", sdaiINTEGER, stringIndex);
  sdaiPutAttrBN(string, "fret", sdaiINTEGER, fretIndex);
  sdaiPutAttrBN(string, "finger", sdaiINTEGER, fingerIndex);
  sdaiAdd(pressures, sdaiINSTANCE, string);
  return string;
}

...

SdaiAppInstance chord_F = sdaiCreateInstanceBN(model, "cbchord");
sdaiPutAttrBN(chord_F, "symbol", sdaiSTRING, "F");
sdaiPutAttrBN(chord_F, "fretboard", sdaiINSTANCE, fretboard);
sdaiPutAttrBN(chord_F, "barre", sdaiINTEGER, 1);

SdaiSet pressures_F = sdaiCreateAggrBN(chord_F, "pressures");
appendPressure(model, pressures_F, 3, 2, 2);
appendPressure(model, pressures_F, 4, 3, 4);
appendPressure(model, pressures_F, 5, 3, 3);

...

Фрагмент Step Physical File, содержащий данный объект выглядит следующим образом:

...
#49=CBCHORD('F',#1,1,(#50,#51,#52));
#50=CBPRESSURE(3,2,2);
#51=CBPRESSURE(4,3,4);
#52=CBPRESSURE(5,3,3);
...

В приложении, отображающем табулатуры аккордов, аккорд F (Фа-Мажор) может выглядеть так:

Изображение аккорда F по его описанию в SDAI-модели в соответствии со схемой CHORD_BUILDER

 

Аккорд Am (Ля-Минор) может выглядеть следующим образом:

Аккорд Am в соответствии с описанием по схеме CHORD_BUILDER

 

Исходный код на Smorodina STEP СИДД (SDAI) для аккорда Am:

SdaiSet createCBChord(SdaiModel model, SdaiString symbol, SdaiAppInstance fretboard, SdaiInteger barreIndex = SDAI::Const::Integer_unset)
{
  SdaiAppInstance chord = sdaiCreateInstanceBN(model, "cbchord");
  sdaiPutAttrBN(chord, "symbol", sdaiSTRING, symbol);
  sdaiPutAttrBN(chord, "fretboard", sdaiINSTANCE, fretboard);
  if (!SDAI::Unset::is(barreIndex))
    sdaiPutAttrBN(chord, "barre", sdaiINTEGER, barreIndex);
  return sdaiCreateAggrBN(chord, "pressures");
}

...

SdaiSet pressures_Am = createCBChord(model, "Am", fretboard);

SdaiAppInstance Am_String_2 = sdaiCreateInstanceBN(model, "cbpressure");
sdaiPutAttrBN(Am_String_2, "stringindex", sdaiINTEGER, 2);
sdaiPutAttrBN(Am_String_2, "fret", sdaiINTEGER, 1);
sdaiPutAttrBN(Am_String_2, "finger", sdaiINTEGER, 1);
sdaiAdd(pressures_Am, sdaiINSTANCE, Am_String_2);

SdaiAppInstance Am_String_3 = sdaiCreateInstanceBN(model, "cbpressure");
sdaiPutAttrBN(Am_String_3, "stringindex", sdaiINTEGER, 3);
sdaiPutAttrBN(Am_String_3, "fret", sdaiINTEGER, 2);
sdaiPutAttrBN(Am_String_3, "finger", sdaiINTEGER, 3);
sdaiAdd(pressures_Am, sdaiINSTANCE, Am_String_3);

SdaiAppInstance Am_String_4 = sdaiCreateInstanceBN(model, "cbpressure");
sdaiPutAttrBN(Am_String_4, "stringindex", sdaiINTEGER, 4);
sdaiPutAttrBN(Am_String_4, "fret", sdaiINTEGER, 2);
sdaiPutAttrBN(Am_String_4, "finger", sdaiINTEGER, 4);
sdaiAdd(pressures_Am, sdaiINSTANCE, Am_String_4);

SdaiAppInstance Am_String_6 = sdaiCreateInstanceBN(model, "cbpressure");
sdaiPutAttrBN(Am_String_6, "stringindex", sdaiINTEGER, 6);
// Не задаём значения Fret и Finger для того чтобы обозначить, что 6 струна не используется в данном аккорде
sdaiAdd(pressures_Am, sdaiINSTANCE, Am_String_6);

Аккорд Bm - комбинация баррэ и не звучащей струны (его описание с использованием Smorodina STEP СИДД может быть выполнено в качестве упражнения):

Изображение табулатуры аккорда Си-Минор