Как стать автором
Обновить

Комментарии 17

Разметка октав не совсем верная — первые две ноты слева — нулевая октава. С первой ноты До (обозначена как C) начинается первая октава.

На самом деле, нет. Вы путаете midi-октавы и октавы из музыкальной теории. В русскоязычной музыкальной теории октава всегда начинается с «до» (здесь вы правы), но «первая» октава — это не первая полная слева на фортепиано, а та, в которой ля — 440Гц (предполагая, что мы используем стандартную современную темперацию). Соответственно, октавы вверх («вправо») от нее нумеруются по возрастанию (вторая, третья...), октавы вниз («влево») нумеруются отдельно: малая, большая, контроктава, субконтроктава. Что характерно, вы дальше эти названия упоминаете, но не объясняете.

В зарубежной литературе применяется другая, научная нотация, которой мы в итоге и будем пользоваться:
ноты обозначаются латинскими буквами С (До), D (Ре), E (Ми), F (Фа), G (Соль), A (Ля), B (Си).

Это не научная нотация. В научной (академической) нотации буквой B обозначается си-бемоль, а си обозначается буквой H. То, о чем вы говорите — это упрощенная нотация, появившаяся в записи популярной музыки (и тема продолжительных дискуссий «B ли H» в ru.guitar).

Нота в нотной записи может иметь полый или закрашенный кружок (головку), иметь вертикальную палку (штиль) и флажок. Это определяет, какую часть такта по времени занимает нота — весь такт, полтакта и так далее. [...] Например, 1/8 с точкой звучит на протяжении 1/8 + 1/16 = 3/16 доли такта.

А это просто неверно (точнее, это было верно во времена мензуральной нотации). В современной академической нотации длительности относительны сами себя: целая равна х, половина в два раза короче, четверть в четыре раза короче — ну, вы поняли. Размер такта задается… размером — это специальная помета в первой строчке (и дальше, если он меняется). В данном случае размер ₵ — это абревиатура для 2/2, что означает, что длина такта равна целой ноте (при этом он двухдольный, это важно для метрической структуры). Если кому интересно, то собственно абсолютная длина x определяется по указанию темпа (Adagio Sostenuto), метрической доле (половина), принятым соглашениям, ну и собственно личному вкусу и настроению исполнителя.
Благодарю за подробное и понятное объяснение.
Поправил про нотацию, октавы и длительности.

С размерностями и длительностями было непросто, так как нот 12, и каждая — 1/8. Но на самом деле размер — не 12/8, а 2/2. Кроме того, в пятом и шестом тактах после двенадцати восьмых нот (четырех триолей) еще идет 1/16, «не влезающая» в такт. Что несколько затрудняло стройную математическую модель, выстроившуюся у меня в голове. В итоге и вылетело совершенно из головы про сложные размеры, где такт != целая нота.

Если кому интересно, то собственно абсолютная длина x определяется по указанию темпа (Adagio Sostenuto), метрической доле (половина), принятым соглашениям, ну и собственно личному вкусу и настроению исполнителя.


В интернете мнения об абсолютной длительности расходятся от «нельзя определить, но стоит ориентироваться на темп» до «считается через BPM/метроном по Мальтеру». И хотя я понимаю, что на заре развития нотной грамоты сложно было задать метроном ровно на m ударов в минуту и сыграть ноту длительностью n/m, сегодня в музыкальном ПО совсем не хочется ссылаться на «вкус и настроение исполнителя».
Тот же guitar pro ориентируется на BPM, насколько помню.
С размерностями и длительностями было непросто, так как нот 12, и каждая — 1/8.

Потому что каждая нота — не 1/8. Видите, там группировка «крышами» по три, и еще и троечка снизу пририсована на двух первых группах? Это триоли. А триоли (и вообще дробные длительности) — это весьма специфическая конструкция. Так, триоль восьмыми — это три равные ноты суммарной длительностью в 1/4, т.е… те самые 1/12.

Кроме того, в пятом и шестом тактах после двенадцати восьмых нот (четырех триолей) еще идет 1/16, «не влезающая» в такт.

Она не «после», она одновременно. Это двухголосие (в рамках правой руки), где нижний голос ровно играет триоли, а верхний — играет в двоичном делении пунктирный ритм: три четверти паузы, затем «нормальную» восьмую с точкой (3/16), затем шестнадцатую (1/16). Если вы будете пробовать изложить это все строго линейно, вы немножечко сойдете с ума.

сегодня в музыкальном ПО совсем не хочется ссылаться на «вкус и настроение исполнителя».

Это зависит только от задач «музыкального ПО».

(метроном Мельцеля, а не Мальтера)
На хроме 44.0.2398.0 dev-m (64-bit) ваш пример падает с:
plugin.webmidi.js:69

Uncaught (in promise) TypeError: plugin.outputs is not a function
at navigator.requestMIDIAccess.then.opts.api
Только что открыл с Chrome 42.0.2311.135 m в Win7 — работает…
Попробуйте стабильные версии браузера FF/Chrome.
Насколько знаю, в dev версии хрома сейчас включено экспериментальное midi api, которое умеет выполнять ввод с миди-устройств и вывод на миди-синтезатор ОС или внешний. Чтобы не валилось, надо или отключить это в chrome://flags или автору в коде указать явно, чтобы MIDI.js не использовало этот апи.
Круто, не хватает только возможности визульного редактирования как здесь , например
А объясните пожалуйста, в примере на гитхабе
// first bar
100 player.play('C2', 1);


и
// second bar
116 player.play('B1', 1);


Это два разных инструмента звучащих одновременно? Потому что я слышу в демо две клавиши нажатых одновременно, а где это в коде задается понять не могу (разъясните с учетом нулевого уровня как в музыке так и в javascript)
Инструмент один и канал — тоже один, что бы это ни значило.
В самом начале одновременно должно звучать по три ноты(клавиши). Может быть небольшое наложение вначале из-за тормозов и загрузки.

Алгоритм виден в первой реализации. Он, упрощенно, такой:
1 время = 0; задаем координату времени
2 нота = номерНоты; номер ноты, начинающий играть в момент «время» (0)
3 начатьИгратьНоту(нота, время);
4 закончитьИгратьНоту(нота, время + длительностьНоты) звукоизвлечение происходит «в отдельном потоке», то есть не влияет на возможность обрабатывать алгоритм далее.
5 если есть другие непроигранные ноты, начинающие звучать в момент «время» — вернуться к шагу 2 с одной из этой нот.
6 время = время + расстояние до начала ближайшей по времени следующей ноты
7 перейти к шагу 2

    // линия времени
    var delay = 0; 
    ...
    // длительность ноты
    var gap = 0.6;
    MIDI.setVolume(0, 80);
    // первый такт
    // начинаем играть ноту 49 в момент времени 0
    MIDI.noteOn(0, 49, velocity, delay);
    // перестаем играть ноту 49 в момент времени (0 + 4 длительности ноты)
    MIDI.noteOff(0, 49, delay + 4 * gap);
    // так как процесс происходит в "отдельном потоке", сразу пишем другую ноту
    // начинаем играть ноту 37 так же в момент времени 0
    MIDI.noteOn(0, 37, velocity, delay);
    // перестаем играть ноту 37 в момент времени (0 + 4 длительности ноты)
    MIDI.noteOff(0, 37, delay + 4 * gap);

    // опять же, т.к. поток другой, запускаем уже третью ноту №56 в момент времени 0
    MIDI.noteOn(0, 56, velocity, delay);
    // перестаем играть ее в момент времени (0 + 1 длительность ноты)
    MIDI.noteOff(0, 56, delay +gap);
    // сдвигаем координату времени до начала звучания следующей по времени ноты
    // время = 0 + 1 длительность ноты
    delay += gap;
    
    // нота 56 перестала звучать, ноты  37 и 49 продолжают играть

    // начинаем играть ноту 61 в момент времени (1 длительность ноты)
    MIDI.noteOn(0, 61, velocity, delay);
    // престаем играть ноту в момент времени (1 длительность ноты + 1 длительность ноты)
    MIDI.noteOff(0, 61, delay + duration);
    // сдвигаем координату времени до начала звучания следующей по времени ноты
    // время = 1 длительность ноты + 1 длительность ноты
    delay += gap;

    // нота 61 тоже перестала звучать, а ноты 37 и 49 будут играть еще два такта

    // first bar
    // тоже самое, только время является полем player'а
    //начинаем играть C2 в время=0 и прекращаем в время=1
    player.play('C2', 1);
    // начинаем играть C1 в время=0 и прекращаем время=1
    player.play('C1', 1);
    // начинаем играть G3 в время=0 и прекращаем время=1/12
    // true здесь приведет к время = время + 1/12
    player.play('G3', 1/12, true);
    // начинаем играть C4 в время=1/12 и прекращаем время=2/12
    // true здесь тоже подвинет временную координату внутри player'а
    player.play('C4', 1/12, true);
начинаем играть в время=0 и прекращаем в время=1

ОК, а как дальше не перемещая время? player.play принимает 3 аргумента (нота, начало, конец)?
player.play принимает 3 аргумента (нота, длительность, надоЛиСдвигатьВремя).
Если время сдвигать надо — время = время + длительность (та самая, которая второй аргумент).
Можно также сдвинуть в ручном, так сказать, режиме.
    player.play('G3', 1/12, true);
    player.play('C4', 1/12, true);

аналогично
    player.play('G3', 1/12);
    player.move(1/12);
    player.play('C4', 1/12);
    player.move(1/12);
понял, мерси
Спасибо за статью! Всё работает :)

Для визализации интересно прикрутить вот этот проект: www.vexflow.com

MIT лицензия, open-source: github.com/0xfe/vexflow

Можно использовать текстовую нотацию: www.vexflow.com/vextab

Пример: www.vexflow.com/vextab/playground/?foo

Кстати, здесь пример online проигрывания my.vexflow.com/articles/82?source=enabled
И осталось дело за малым — перевести все public domain партиты с этого ресурса imslp.org :)
НЛО прилетело и опубликовало эту надпись здесь
Я использовал MIDI.js и VexTab/VexFlow в своём проекте, и могу сказать, что сейчас в MIDI.js очень не хватает нормального сэмлера.
В частности в нашем проекте был инструмент гобой, и в midi были ноты которые проигрывались по 6 секунд и более… Чтобы MIDI.js нормально его воспроизводил, нужно либо делать очень длинные сэмплы, что сожрёт память, либо делать Loop, но Loop правильный.
Правильный сэмпл, по-хорошему, должен иметь три участка — attack, loop и fade. То есть при noteOn должно сначала воспроизводиться участок атаки, потом зацикливать воспроизведение loop, причём он должен быть бесшовным, а при noteOff воспроизводить fade.
Это конечно существенно потребует доработать библиотеку, но без этого нормального воспроизведения духовых и струнных (скрипка) инструментов не получится.
Не хватает конроля ADSR и лупатых точек:)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации