Комментарии 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, «не влезающая» в такт. Что несколько затрудняло стройную математическую модель, выстроившуюся у меня в голове. В итоге и вылетело совершенно из головы про сложные размеры, где такт != целая нота.
В интернете мнения об абсолютной длительности расходятся от «нельзя определить, но стоит ориентироваться на темп» до «считается через BPM/метроном по Мальтеру». И хотя я понимаю, что на заре развития нотной грамоты сложно было задать метроном ровно на m ударов в минуту и сыграть ноту длительностью n/m, сегодня в музыкальном ПО совсем не хочется ссылаться на «вкус и настроение исполнителя».
Тот же guitar pro ориентируется на BPM, насколько помню.
Поправил про нотацию, октавы и длительности.
С размерностями и длительностями было непросто, так как нот 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.
Попробуйте стабильные версии браузера FF/Chrome.
Насколько знаю, в dev версии хрома сейчас включено экспериментальное midi api, которое умеет выполнять ввод с миди-устройств и вывод на миди-синтезатор ОС или внешний. Чтобы не валилось, надо или отключить это в chrome://flags или автору в коде указать явно, чтобы MIDI.js не использовало этот апи.
А объясните пожалуйста, в примере на гитхабе
…
и
Это два разных инструмента звучащих одновременно? Потому что я слышу в демо две клавиши нажатых одновременно, а где это в коде задается понять не могу (разъясните с учетом нулевого уровня как в музыке так и в javascript)
// 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
В самом начале одновременно должно звучать по три ноты(клавиши). Может быть небольшое наложение вначале из-за тормозов и загрузки.
Алгоритм виден в первой реализации. Он, упрощенно, такой:
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 :)
Для визализации интересно прикрутить вот этот проект: 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 :)
Здесь, кстати, пример online проигрывания my.vexflow.com/articles/82?source=enabled
НЛО прилетело и опубликовало эту надпись здесь
Я использовал MIDI.js и VexTab/VexFlow в своём проекте, и могу сказать, что сейчас в MIDI.js очень не хватает нормального сэмлера.
В частности в нашем проекте был инструмент гобой, и в midi были ноты которые проигрывались по 6 секунд и более… Чтобы MIDI.js нормально его воспроизводил, нужно либо делать очень длинные сэмплы, что сожрёт память, либо делать Loop, но Loop правильный.
Правильный сэмпл, по-хорошему, должен иметь три участка — attack, loop и fade. То есть при noteOn должно сначала воспроизводиться участок атаки, потом зацикливать воспроизведение loop, причём он должен быть бесшовным, а при noteOff воспроизводить fade.
Это конечно существенно потребует доработать библиотеку, но без этого нормального воспроизведения духовых и струнных (скрипка) инструментов не получится.
В частности в нашем проекте был инструмент гобой, и в midi были ноты которые проигрывались по 6 секунд и более… Чтобы MIDI.js нормально его воспроизводил, нужно либо делать очень длинные сэмплы, что сожрёт память, либо делать Loop, но Loop правильный.
Правильный сэмпл, по-хорошему, должен иметь три участка — attack, loop и fade. То есть при noteOn должно сначала воспроизводиться участок атаки, потом зацикливать воспроизведение loop, причём он должен быть бесшовным, а при noteOff воспроизводить fade.
Это конечно существенно потребует доработать библиотеку, но без этого нормального воспроизведения духовых и струнных (скрипка) инструментов не получится.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Пишем Бетховена на Javascript или немного о MIDI.js