Вполне себе годный вариант для текстового ввода простеньких мелодий. Но не уверен, что хорошо подойдёт для профессиональных. Просто сложные музыкальные произведения, вводимые с помощью классической нотной грамоты крайне сложны в наборе - это хорошо знают классические композиторы, например, вводящие ноты в Finale (насколько я знаю - вводить классическим путём ноты в других, более привычных современным композиторам, редактора ещё куда сложнее). Оттого и стал так популярен Piano roll - но если нужны именно классические ноты - то он не особо годится (всё равно будет много правок - тут тогда уж проще на MIDI клавиатуре наиграть и потом долго корпеть в Finale - исправляя огрехи и расставляя правильную музыкальную "пунктуацию").
Но классические ноты редко нужны современным композиторам - если надо - они сбагрят это работу музыкальным "неграм" - пусть тем в Finale корячатся... Современным "не классически " композиторам вполне хватает Piano roll (в разных вариациях + drum machine) и мультидороженный семплер + Kontakt для подключения современных программ инструментов + DSP плагины для эффектов и некоторый доп. софт для создания/обработки семплов (но этим скорее уже саунд продюсеры руководят, без них композиторы обычно берут готовые библиотеки и программы Kontakt и иногда специальные плагины для эмуляции старых синтезаторов).
Поэтому я по умолчанию и не стал делать текстовый ввод в классическом нотном стиле (хотя такой формат не проблема реализовать и подключить, хотя вру - не проблема - это как у Вас - в примитивном виде, а если нужна точная нотная грамота - это это будет жесть - но в целом решаема - но сам не планирую - разработка формата ввода свободная - если кому надо будет - пусть делает транслятор во внутренние команды, и презентёр - для визуализации и графического вывода в нужном представлении).
Я правильно понял, что у Вас аккорды через { } объединение реализованы? Тогда как решаете такую игру - когда, скажем, зажимается одна клавиша (или несколько), и в это время играются другие? При этом ещё и зажатая клавиша так же может плавно варьироваться с другой клавишей! А ещё может быть начатый аккорд - переходящий в другой аккорд, и в процессе которого берутся оба аккорда с промежуточными легато, работой педалью и т.п. В современной музыке столько интересных и сложных техник... классикам и не снилось - хотя и они тоже порой сочиняли сложные произведения!
И это мы ещё сейчас мыслим скорее в терминах клавишных инструментов а-ля класс "фортепьяно". А у музыкальных инструментов других классов (видов) куча своих особенностей построения звукосочетаний.
Поэтому я и проектирую разные гибкие форматы ввода - чтобы подстроиться под технику разных инструментов. Я тут неспец по мультиинструменталке - но вот, хотя бы про гитары, или в общем случае - про класс "щипковые струнные" инструменты - там доступно столько хитрых техник исполнения (XX век был веком расцвета струнных инструментов и техник игры на них; впрочем клавишные в лице синтезаторов не шибко отставали по вариации звучания - но там техник не так много новых появилось), которые не доступны для исполнения на клавишных инструментах. И Piano roll тут вообще бессилен - а нам предлагают даже в Kontak писать мелодию для струнных через него... фу....
Так что проблема описания сложных аккордов и прочих безымянных техник гармонизации - это очень непростой вопрос! Он даже на нотном стане непростой...
Помимо аккордов продумывать и удобный ввод таких вещи как:
Музыкальные штрихи
Мелизмы
Сложные пассажи
И некоторые другие (о которых я не знаю)
Это техники для фортепиано - а у других инструментов свои хитры техники, которые тоже как-то надо вводить!
Обоснуете? Выше я тут уже приводил аргументы против графических ЯП! И привёл лишь капельку аргументов за! Некоторые из комментирующих так же привели капельку аргументов за и против. Но в целом - я тут пока не вижу значимых аргументов за графическое программирование, тем более, если говорить о далёком будущем - что там такого должно быть, чтобы визуальная разработка заметно перевесила иные способы (кстати, не обязательно текстовые - если уже говорите о далёком будущем, хотя тогда стоит хорошо подумать - какие ещё могут быть альтернативы)? А не просто визуальные системы станут лишь подспорьем для других способов
Замечу, что и визуальная разработка тоже может иметь много разных вариаций модели представления. Даже (чаще всего в комментариях рассматриваемые) диаграммы - могут иметь совершенно разные форматы. Вы за какой?
Для визуализации вариации piano roll - хорошая штука - а имею ввиду - вводить музыку с помощью него (тем боле как Вы показали в виде текстовой графики, или я Вас не правильно понял) - те же Black MIDI не создаются на piano roll - их создают сложные алгоритмы, генерирующие MIDI-события, обрабатывающие готовые музыкальные паттерны!
Вот так будет на "RANGE" в варианте ввода в проприетарном буквенно-цифровом коде (но, как уже сказал, кодированный формат ввода, в принципе, можно будет настроить любой, или использовать один из готовых вариантов):
//В более сложном примере с большим числом одинаковых партий могло бы быть ещё компактнее: use (octave=3, script="keys", len=600):{ var n1 = {D# C# -F#} var n2 = {D# C# -C#} var n3 = {-A#+-F#} var n4 = {B+-F} (n1 n3:r2):r2 n1 n3 -D# n3 B# n3:r2 (n2 n4:r2):r2 n2 n4 -D# n4 A# n3:r2}
Надеюсь нигде не ошибся. Если съедет ровность отображение - я не виноват - изначально всё было аккуратно выровнено
P.S.
Вот серьёзно думаю заменить символ "+" для формирования аккордов на что-то другое - чтобы не путать со смещением октав, например на "*" - просто "+" уже привычен для клавиатурных аккордов "сочетаний горячих клавиш"
MusicXML - он не для ручного ввода - а для автоматикой (в т.ч. кроссплатформенной) интеграции - как раз для копи/паста мог сгодиться...
Так же MusicXML априори очень гибкий и расширяемый формат. Например, в "ORANGE" помимо общих форматов музыкальных потоков WAVE и MIDI возможны и потоки в других форматах (пока сугубо теоретические - этот вопрос ещё не проработан, но заложен изначально), среди которых может быть и MusicXML; а так же запланирован (но может откажусь и буду расширять MusicXML) проприетарный открытый формат (кстати, рассматривалась база XML, JSON, Protobuf) - для более расширенного управления компонентами (как в качестве внутреннего протокола, так и для взаимодействия со сторонними компонентами). Просто у меня запланировано куда более сложное командное взаимодействие, чем набор MIDI-событий, в т.ч. с целью управления параметрами и техниками звукоизвлечения комплексных компонент (а-ля какие использует Kontakt)
мой пример было не про опции, а про работу с null значениями - я специально старался его абстрактно написать, а "CustomOptions["Allow"] " просто привёл как понятный источник null значения, но я не указывал его физическую природу - это может быть и карта "ключ-значение" (фиг его знает откуда полученная) - или обращение какому-либо хранилищу - хоть локальному, хоть БД, или какой-либо ещё комплексный сервис - который пытает получить значение из данных, собираемых их различных источников. ИМХО - алгоритму основной логики это всё должно быть сугубо "фиолетово" - у него есть источник "CustomOptions" - вот там он будет искать значение. А если его там нет - у него есть некий процесс иного поучения этого значения, которые тоже не гарантирует успех - и только затем значение берётся из кода - для случая, когда без него дальше никак не обойтись, но по какой-то непонятной причине оно так и не было получено!
Ну а в финале - передаём (во втором случае пытаемся) значение в нужные объекты - чтобы так больше не "мучиться" и для того, что это значение ещё где-то важно должно быть - если это "где-то" существует! Не вдаваясь в подробности того - как эти все вспомогательные объекты устроены!
Понятное дело, что вы будете топить за свой ЯП всеми правдами и неправдами.
Я не собираюсь топить за свой ЯП! Как и топить другие системы (ну кроме Native instruments Kontakt - уж больно она меня разочаровала (хотя шутка, бесспорно, полезная); как и разочаровало, например, и развитие Sony Acid - который теперь мультикомбайн Vega, Cakewalk - который теперь Sonar (и многие другие мультидорожечники) - но это для тех кто в курсе). И готов принять любую критику своих идей - главное чтобы было обоснование, даже если я его сочту спорным - обоснованная критика - всё равно полезная критика!. И я не пытаюсь необоснованно критиковать вашу разработку. Напротив - я всячески желаю Вам удачи с ней!
И сути это не меняет - нельзя ошибиться в названии переменной, если его нельзя напечатать, а можно только выбрать из списка. Нельзя ошибиться с пропущенной кавычкой, если кавычек тоже нету. Нельзя перепутать "=" или "==" в условии, если "=" тоже нету
Как я выше написал - всем есть своя цена - и всего есть плюсы и минусы. И где есть минусы - их стараются "пусть порой условно и костылями" эти минусы устранять! И таких наработок для текстовых процессоров уже уйма - так что если сравнивать кодинг лет пятьдесят назад и сейчас - это будет гигантский разрыв (даже в рамках одного текстового ЯП)! Даже за последние 10 лет IDE шагнули очень далеко вперёд. А сейчас, на волне, генеративных лингвистических алгоритмов - шагнут ещё сильнее!
(Кстати, у Вас есть имена модулей, и да - вы позволяете их выбирать из списка (как, впрочем, это позволяют и быстрые всплывающие подсказки ввода) - но решаете ли Вы проблему, когда - это имя потом меняется у модуля - везде где используется переназначаться? Для визуальных систем это, правда, плёвое дело - а текстовые процессоры пришли к решениям не так давно - рефакторинг изменения и подсчёт с отображением ссылок - но 100% надёжности это не даёт... впрочем наврядлли сейчас хоть где-то есть 100% надёжность при активном повторном использовании кода и библиотек)
А, вот, куда сейчас развиваются музыкальные редакторы (априори визуальные) - мне категорически не нравится - это навороченные визуальны монстры с красивой графической обёрткой - и скудным сложным содержимым (я не буду говорить про плагины - среди которых есть и не скудные но от того не менее простые приложения)! Вот тут действительно назрела революция! Когда-то такой был Трекерный редактор, Piano roll, драмм-машина, , затем технология VST-плагинов, затем виртуальных DSP-процессоров и мультидорожечных редакторов и наконец музыкальных приложений для платформы Kontakt (ну я не все знаковые технологии в музыко-строении перечислил). Пришла пора двигаться дальше...
Нельзя ошибиться с разыменованием NULL в рантайме, если NULL-ов тоже нету (а вместо них есть значения/объекты по умолчанию).
Вот за это всецелое УРА! Учли опыт величайшей ошибки Дейкстры! Я тоже против NULL но....
для обхода NULL придумали и целе семейство null-операторов (?. ?? ??=) и отделили nullable определения переменных/свойств от не-nullable - и это отличное подспорье - появились очень интересные и удобные техники их применения!
С NULL очень удобно и красиво решается - без NULL приводит к старом заскорузлому некрасивому (хоть и понятному) коду!
Bool? Enabled = CustomOptions["Allow"] ?? (Object as ICheckable)?.Checked; Enabled ??= A?.B?.C?.Checked ?? true; CustomOptions["Allow"] = Enabled; //A?.B?.C?.Checked ??= Enabled; //Вот это, правда, в C# не прокатит
Так оставляйте в стороне и вспомогательные средства IDE для быстрого ввода в виде автодополнения, подсветки синтаксиса и проверки орфографии, а то как-то нечестно получается
Если внимательно читали - то я при оценке времени так и сделал. И указал - что с данными средствами будет и ещё быстрее и надёжнее
Просто Ваш аргумент он не имеет значимой силы
Интересно - кто минус моему комментарию выше поставил - за что (если это вы просто в отместку - то я не минусовал ваш комментарий)
Тут всё зависит от подходов к разработке и среды. Чем жёстче подходы - тем меньше комфорта и универсальность - но выше контроль.
А подобные опечатки текстовые IDE в 99.98% случае сами находят ещё на стадии окончания ввода слова! А некоторые продвинутые IDE ещё и сразу могут исправлять! Не говоря уже о быстрых подсказках автоподстановки - которые в умелых руках (и умелом текстовом процессоре) в более чем 80% случае просто позволяют избегать таких ошибок!
(не ключевые слова, уже определённых идентификаторов или литералов - так же будут выделяться цветом; а при мало-мальски грамотном назначении имён идентификаторов - вероятность ошибиться и из-за опечатки ввести другое имя существующего идентификатора - практически равна нулю - и современные IDE могут сразу автоматически исправлять такие опечатки)!
У визуальных сред куча своих проблем - часть я в комментариях к этой статье уже описал - но это далеко не полный список!
То же зарезервированное слово для определения сущности "instrument" - в текстовом редакторе вводится за 1.5-2 секунды (кото-то и быстрее введёт, я уж не говорю о шаблонах и копипасте, и быстрых подсказках ввода) - ошибка в тестовых процессорах будет видна сразу (это же ключевое слово - оно не будет выделено соответствующим образом) - и на её осознание и исправление (требующееся очень редко) уйдёт ещё секунды 2-3.
А сколько времени уйдёт в визуальном редакторе размещение такого компонента на поле (так оставим в стороне вспомогательные средства быстрого ввода): надо где-то нажать кнопку с командой добавления компонента, затем выбрать его из списка, или воспользоваться текстовым поиском, кликнуть по кнопке добавления, перенести его в нужное место на плоскости (а то ещё и другие компоненты подёргать) - это десятки секунд.... а потом окажется что это не тот компонент, что нужен (например, промахнулись - не туда кликнули)...
То же и про пропущенную кавычку - обычно это всё легко видно - небольшие сложности только в интерполяционных строках бывают - но были бы такие у Вас - были бы такие же проблемы, скорее всего - но это всё мелкие проблемы - и быстро исправляются.
P.S.
Вот опечатка с MIDI там где нужна WAVE именно в моём ЯП всплыла- бы только при компиляции - когда драйвер устройства WASAPI не смог бы по типам данных спрячься с типом данных, передаваемых из канала! Но это уже нюансы ЯП гибкой статической типизации - точные типы появляются только при компиляции!
Упущенный параметр "Input=InputChanel2" для "Track2" (без специально настроенных анализаторов) всплыл бы только при отладке - параметр не обязательный (как и то, что у трека может не быть входного канала данных - когда он сами их статически описывает или генерирует, или откуда-то загружает) в своём теле - внутри { }; или в трек могут напрямую отправляться инструкции из другого трека (трек сначала может динамически наполняться, а потом только воспроизводиться, или не обязательно воспроизводиться, а, скажем, выгружаться на диск в файл/БД, или передаваться в другую программу, в т.ч. по сети)!
Пропущенное указание треков, которые надо стартовать при старте проекта - это скорее нюансы дизайна ЯП - может действительно не надо стартовать никаких треков - а при старте проекта нужно выполнить сначала подготовительные алгоритмы... но можно, конечно ввести какую-то конструкцию - указывающую на это - и когда ничего нет - хотя бы выдавать предупреждение при компиляции
Для составления инструкций алгоритма работы в среде требуются знания не столько в программировании, сколько в знаниях внешней аппаратной части бинарной логики управления.
Скажу так - в этом конкретном случае лишь три проблемы:
Отсутствие правильно архитектурно подготовленных библиотек и документации к ним
Отсутствие настроенных кодогенератор, отладчиков и прочего суппорт-софта для данной среды
Я же выше написал - что системное программирование ещё долго будет с нами - и там будет править классика. Но, объёмы такого программирования будут год от года снижаться. А вот объёмы кодогенерации, в т.ч. и в области системного программирования будут год от года расти. А визуальное и декларативное программирование будут как раз стоять на фундаменте низкоуровневой кодогенерации и некоторых системных готовых библиотеках!
Я ни разу не говорил, что Ваше решение - не рабочее и не может иметь практической ценности или быть популярным в определённых кругах. А так да - только практическое применение и отзывы могут показать эффективность того или иного подхода. У меня пока нет рабочего решения :-(
Про повторное использование кода, кодогенерацию и шаблоны, и разбиение по файлам/модулям - я уже написал выше. И уверен - даже незнакомые с построением DSP-плагинов люди, сходу поймут что да как тут происходит и настраивается! Не говоря уж про строки комментариев - и прочее самодокументированние!
Да и ЯП "Orange" - задуман как куда более мощная "штука", чем просто DSP-фильтр - так что тут есть "место" для боллеплейт-кода - но всё это можно вынести в повторноиспользуемый код -или применять шаблоны! Да и с некоторым "синтаксическим сахаром" это всё можно несколько было упростить - для простых случаев
P.S. Хотя компиляция проекта в DSP-фильтр или VST-плагин - тоже вполне возможна
Была проращена открывающая кавычка: Driver = WASAPI("Динамики (Realtek High Definition Audio)"
Опечатка "instrumnet"->"instrument"
В обоих OutputChanel ошибочно указал формат MIDI - там, конечно же WAVE - но может быть и MIDI - но тогда VSTi семплер будет проигнорирован (если он не сможет выдавать MIDI поток)
В "Track2" забыл указать параметр "Input=InputChanel2" для настройки канала, по которому в трек будут поступать MIDI события!
При старте проекта хорошо было бы указать и стартуемые треки, ну или аннотировать их как автотстартуемые вместе с проектом (а вообще треки могу запускаться (и останавливаться) в произвольный момент времени - мануально, по расписанию, по событию и другими способами - тут полная гибкость): run MyProject with track(Auto) [MyLayer.Track1, MyLayer.Track2]
У значений перечислений без указания полного пути идентификатора - по-хорошему надо бы поставить точку перед идентификатором значения - чтобы синтаксический разбор понимал, что надо проанализировать тип приёмника для идентификации полного имени значения - но, будем считать, что можно и без точки, когда нет пересечений идентификаторов в текущем контексте - опция настройки проекта)
Примечания:
Маппинг каналов скорее лучше делать через отдельный компонент Mixer - но для простоты не стал его указывать - а вообще - в мультиканальном проекте с пространственным 3D0звучанием такой компонент может активно применяться!
Вместо указания параметра инструмента "ProgramFIxed" (что скорее для инструментов, которые не поддерживают разные программы звучания) лучше настроить у InputChanel компонент фильтра - для отбрасывания MIDI инструкций смены инструмента:
Пример фильтрации в каналах
chanel InputChanel1 {
Direct = Input Format = MIDI Source = InputDevice1 Filter = MIDIBoolFilter {
return MIDIEvent.Command != ProgramChange)
} //Задали произвольный алгоритм фильтра MIDI сообщений - пропускаются только "истинные" MIDI события
}
chanel InputChanel2 {
Direct = Input Format = MIDI Source = InputDevice2 Filter = MIDIEventFilter {
} //Задали произвольный алгоритм фильтра MIDI сообщений - подменяющий сообщения по смене программы на пустую команду (в условии нет секции "иначе" - она генерируется автоматически - исходным значением событий); а, в принципе, такой фильтр может и другие преобразования делать (для WAVE-формата канала тоже свои фильтры могут быть - но там, их лучше подключать как DSP-процессоры - между каналами)
} //Задали произвольный алгоритм фильтра MIDI сообщений - подменяющий сообщения по смене программы на пустую команду (в условии нет секции "иначе" - она генерируется автоматически - исходным значением событий)
Музыкальный трек организован куда сложнее - чем показано в данном примере - он может обслуживать мульти-инструменты, мульти-эффекты, полифонию, мульти-семплирование, мульти-каналы и т.д. и т.п. По сути - трек - это просто некоторая агрегация сервиса воспроизведения (как и слой, как и, не указанный здесь, score - для разделения партитур) - все они определяют свой контекст воспроизведения
Главное преимущество визуального программирования - отсутствие необходимости именования переменных/объектов и ещё больше возможностей для инкапсуляции
Я бы назвал это преимущество весьма сомнительным. Да - можно - но много ли от этого толку... а сколько это в итоге порождает проблем в сложных схемах... Насколько это поддаётся эффективной и продвинутой отладке... Насколько это всё само документируемо, и поддаётся созданию отдельной документации...
Поэтому в своё примере выше - у меня все узлы именованы (кроме узлов проекций/фильтров - это порты, принадлежащие своим узлам, но - в принципе им, как и линиям тоже можно было бы вешать лейблы)! А так - как, в другом комментарии выше, я показывал - можно и анонимную лямбду создать - и передать её в другой блок!
И обратите внимание - графической связи между клавиатурами и синтезатором нет, она задаётся в свойствах
Да, при визуализации вполне допустимо - но, опять же, очень спорное решение - усложняет понимание схемы!
А насчёт примера.... ну я тут выше в комментарии уже приводил примеры своего ЯП для музыки "ORANGE" - он текстовый и на нём ваш пример (в котором, правда большая часть информации отсутствует в обоих вариантах представления) выглядел бы так:
пишу целиком, включая шапку, без особых синт. сокращений:
project MyProject {
device OutputDevice1 {
Direct = Output //Не обязательно - т.к. это по умолчанию так Format = MIDI Driver = WASAPI(Динамики (Realtek High Definition Audio)" ChanelsMap = {Left->"L", Right->"R", BackLeft->"BL", BackRight->"BR"}
}
device OutputDevice2 {
Direct = Output //Не обязательно - т.к. это по умолчанию так Format = MIDI Driver = WASAPIOut("LG ULTRAGEAR (Аудио Intel(R) для дисплеев)") ChanelsMap = {Left->"L", Right->"R"} //Не обязательно - т.к. это по умолчанию так (когда у устройства нет других каналов)
}
device InputDevice1 { Direct = Input Format = MIDI RAWFormat = Int RAWConverter = System.Converters.ToMIDI.KeyboardScanCodeToMIDI Driver = KeyboardInput //Если я правильно понял – что источников является букво-цифровая клавиатура RAWInternalConverter += @AnotherFunction //Если нужен какой-то свой обработчик конвертации потока кодов клавиатуры в MIDI инструкции ChanelsMap = {Mono->"1"}
}
device InputDevice2 {
Direct = Input Format = MIDI Driver = MIDIInput("LoopBe Intenral") //Какой-то MIDI input - из примера не ясно как он настроен ChanelsMap = {Mono->"1"}
}
chanel OutputChanel1 {
Direct = Output //Не обязательно - т.к. это по умолчанию так Format = MIDI Space = STEREO //Не обязательно - т.к. это по умолчанию так Destination = OutputDevice1 ChanelsMap = {Left->Left, Right->Right} //Маппинг только в два канала устройства воспроизведения
}
chanel OutputChanel2 { Format = MIDI Destination = OutputDevice2 //ChanelsMap = {Left->Left, Right->Right} //Не требуется, т.к. тут маппинг по умолчанию в два доступных у устройства канала
}
chanel InputChanel1 {
Direct = Input Format = MIDI Source = InputDevice1
}
chanel InputChanel2 {
Direct = Input Format = MIDI Source = InputDevice2
}
sampler S_yxg50_Sampler { Format = VSTi Path = FilePath("c:\vst\yamaha\syxg50.dll":raw) Space = STEREO //Не обязательно - т.к. это по умолчанию так ChanelsMap = {Left->"01", Right->"02"}
}
instrument S_yxg50 {
Destination = S_yxg50_Sampler Program = 8 //Вообще инструмент подразумевает фиксацию программы синтезатора здесь (т.к. именно инструмент определяет настройки звучания), но в примере она фиксируется только для одного Midi input (вероятно у другого - другая программа) - такое тоже возможно (здесь только значение по умолчанию)
}
layer MainLayer {
track Track1
{
useinstrumnet S_yxg50(ProgramFixed=true) useinput InputChanel1 useoutput [OutputChanel1,OutputChanel2] //В моей схеме не семплер подключается к каналам вывода, а музыкальный трек (здесь подключение сразу к двум каналам runtimeinput:loop //трек активен только в runtime режиме в бесконечном цикле воспроизведения
}
track Track2 ( Instrumnet=S_yxg50(Program=3, ProgramFixed=true) //Сменили программу (тут будет клон инструмента) Output = [OutputChanel1,OutputChanel2]
) @RuntimeInput(lnfinity) //аннотация режима исполнения трека
}
Layers=[MainLayer]
}
run MyProject
Надеюсь ничего не забыл, и правильно понял исходный пример. Да - может код несколько многословен - но тут много базового конфигурирования идёт. У Вас же, в визуальной части, настройки делаются в узлах - и не отображаются, но их всё-равно надо делать (текст тоже можно скрыть в сворачиваемых блоках, распихать по файлам и подключаемым модулям библиотек, в т.ч. для повторного использования), ну и текст легко комплексно просматривается, и легко рефакторится, и легко публикуется и передаётся куда-либо, да хоnть в Git
И да - всё-таки ЯП ORANGE хоть и спроектирован для реализации runtime MIDI-ввода - но, всё же, создан больше для кодирования воспроизведения в виде инструкций к семплерам (что можно делать и в runtime; или направлять MIDI инструкции из источника в генератор кода в инструкциях ЯП Orange)!
потому что для каждого объекта предусмотрено клавиатурное сокращение, например "kbd" или "midiin", после чего он появляется в позиции курсора.
Ну - для этого возможны:
Шаблоны быстрого ввода в текстовом процессоре IDE (в т.ч. привязанные к macros и static instument generators, которые могут читать конфигурацию из внешнего config-файла, а так же иметь доступ к AST-дереву выражений проекта - чтобы генерировать код максимально гибко и адаптивно; но я не говорю - что такое в принципе нельзя сделать при визуальной разработке - но инструментарий IDE должен быть очень продвинутым, в отличии от текстовых процессоров - где это либо уже реализовано, либо легко подключается плагинами, либо вообще решается на уровне компилятора и/или внешнего текстового препроцессора)
Просто готовые преданстроенные функции внутри проекта или во модулях библиотек - которые просто можно вызвать (в т.ч. с параметрами) - а они уже проведут всю общую настройку - в этом преимущество классического подхода кодирования исходников - в гибком повторном использовании кода (но я не говорю - что такое в принципе нельзя сделать при визуальной разработке)
Есть одно пересечение от функции "Process" к "B" (в идеале там должно быть "мостик" - рисовал в ""Miro" - не поддерживает, видимо) - но, замечу, что как раз в этой схеме его можно избежать, нарисовав линию стрелки в другую сторону (обогнув функцию "Process2")! Кстати, IDE может иметь средство перераспределения схемы/части схемы - для минимизации пересечений.
В более сложных схемах могут применяться разные способы избежание пересечений или решения проблем их читаемости. Например, можно клонировать состояние системы в узле "А" (переменной/источнике/примёнике) - и нарисовать его ниже отдельно - так чтобы его связи не мешали другим. Или размещать их на разных слоях (в т.ч. с настроенной прозрачностью или цветами линий - чтобы даже в неотображаемом или полуотображаемом случае видеть намёки, что тут есть и другие связи). Можно и сами линии группировать через промежуточные узлы - и начинать из них движения тоже где-то ниже. Можно просто часть линий выносить в узел субпрограммы, который в основном режиме отображается как просто узел, а при детализации (как написал выше - это легко делать просто масштабированием схемы, ну или просто кликом по лупе в этом узле) - уже отображать отдельно его структуру. И так далее и тому подобное - вариантов как синтаксических так и средств IDE может быть много! Не думаю, что это такая уж проблема. Тем более, что я, ту не требую целостности графа, а в большинстве случаев такого большого числа переходов из одного узла вряд ли будет - на картинке уж очень специфическая ситуация - чаще всего инструкции достаточно линейны (ветвления условий и матчей - скорее всего не будут шибко обширными или хорошо сворачиваются, по указанным выше, или иным, методикам), а распараллеливания - вполне можно достаточно эффективно разнести друг от друга.
Кстати, IDE может даже представлять схему в разных вариантах компоновки - просто меняя способы её отображения (фактически не переставляя сами элементы) - что позволяет легко подстраивать её анализ под разные ситуации
У визуального программирования есть пара интересных архитектурных преимуществ - перед текстовым:
Очень удобно управлять множественными потоками данных - в т.ч. ветвлениями или дублированием (см. пример ниже). От одного источника легко может исходить несколько переходов. И на эти переходы можно удобно накалывать разные условия или метки аннотаций - задающие логику работы. Более того, на источник и на приёмник можно вешать как бы специальные порты - проекции/фильтрации - так что все исходящие или, соответственно, входящие линии переходов, из/в эти порты, будут подвержены зависимы от действий, заданных в этих портах - и на один источник/приёмник можно повесить сразу несколько разных портов! Так же, множественные переходы могут удобно определять и параллельное выполнение этих операций (в т.ч. в режиме по умолчанию, когда компилятор сам решает как лучше выполнять эти действия - в отдельном потоке или последовательно - в зависимости от настроек контекста выполнения и специальных хинтов-аннотаций со стороны программиста)
В отличии от представления инструкций/деклараций в текстов - которых по сути линейны - графические схемы размещаются на плоскости - а, в принципе, в 3D и даже в большем числе измерений - что позволяет более наглядно размещать связи между элементами и вложенные блоки. Тут же можно сказать и про уровень масштабирования - когда можно приближая масштаб легко переходить от общего узла некоторого блока - к его внутренней реализации, оставаясь в текущем контексте, что позволяет (при необходимости), например, вместо внутренней логики подпрограммы (читай функции) подставлять идентификаторы текущего контекста - чтобы лучше понимать как внешняя среда влияет на результат внутренней
В схеме выше много действий. Условно начало можно считать получение фильтра FilterList (хотел сначала сделать точку входа Request - но передумал - и так сложно вышло)
Ключевым тут является источник переменной "A" (тип который является перечислимым) - у неё есть ряд исходящих портов разного типа - какие-то передают все данные переменной "A" (но только если сущность переменной "A" удовлетворяет условиям); какие-то передают только часть её содержимого - поэлементно удовлетворяющие условию. Так же тут осуществляются операции фильтрации данных по типам и контрактам (забыл добавить и просто операции преобразования)
Есть даже паттерн переназначения фильтра по умолчанию (он не изменяет передачу типа данных, но все условия на него, накладывается согласно заданного шаблона)
Короче - схема, безусловно очень абстрактная, но я просто хотел показать возможную глубину фишек визуального программирования на графических схемах
Вполне себе годный вариант для текстового ввода простеньких мелодий. Но не уверен, что хорошо подойдёт для профессиональных. Просто сложные музыкальные произведения, вводимые с помощью классической нотной грамоты крайне сложны в наборе - это хорошо знают классические композиторы, например, вводящие ноты в Finale (насколько я знаю - вводить классическим путём ноты в других, более привычных современным композиторам, редактора ещё куда сложнее). Оттого и стал так популярен Piano roll - но если нужны именно классические ноты - то он не особо годится (всё равно будет много правок - тут тогда уж проще на MIDI клавиатуре наиграть и потом долго корпеть в Finale - исправляя огрехи и расставляя правильную музыкальную "пунктуацию").
Но классические ноты редко нужны современным композиторам - если надо - они сбагрят это работу музыкальным "неграм" - пусть тем в Finale корячатся... Современным "не классически " композиторам вполне хватает Piano roll (в разных вариациях + drum machine) и мультидороженный семплер + Kontakt для подключения современных программ инструментов + DSP плагины для эффектов и некоторый доп. софт для создания/обработки семплов (но этим скорее уже саунд продюсеры руководят, без них композиторы обычно берут готовые библиотеки и программы Kontakt и иногда специальные плагины для эмуляции старых синтезаторов).
Поэтому я по умолчанию и не стал делать текстовый ввод в классическом нотном стиле (хотя такой формат не проблема реализовать и подключить, хотя вру - не проблема - это как у Вас - в примитивном виде, а если нужна точная нотная грамота - это это будет жесть - но в целом решаема - но сам не планирую - разработка формата ввода свободная - если кому надо будет - пусть делает транслятор во внутренние команды, и презентёр - для визуализации и графического вывода в нужном представлении).
Я правильно понял, что у Вас аккорды через { } объединение реализованы? Тогда как решаете такую игру - когда, скажем, зажимается одна клавиша (или несколько), и в это время играются другие? При этом ещё и зажатая клавиша так же может плавно варьироваться с другой клавишей! А ещё может быть начатый аккорд - переходящий в другой аккорд, и в процессе которого берутся оба аккорда с промежуточными легато, работой педалью и т.п. В современной музыке столько интересных и сложных техник... классикам и не снилось - хотя и они тоже порой сочиняли сложные произведения!
И это мы ещё сейчас мыслим скорее в терминах клавишных инструментов а-ля класс "фортепьяно". А у музыкальных инструментов других классов (видов) куча своих особенностей построения звукосочетаний.
Поэтому я и проектирую разные гибкие форматы ввода - чтобы подстроиться под технику разных инструментов. Я тут неспец по мультиинструменталке - но вот, хотя бы про гитары, или в общем случае - про класс "щипковые струнные" инструменты - там доступно столько хитрых техник исполнения (XX век был веком расцвета струнных инструментов и техник игры на них; впрочем клавишные в лице синтезаторов не шибко отставали по вариации звучания - но там техник не так много новых появилось), которые не доступны для исполнения на клавишных инструментах. И Piano roll тут вообще бессилен - а нам предлагают даже в Kontak писать мелодию для струнных через него... фу....
Так что проблема описания сложных аккордов и прочих безымянных техник гармонизации - это очень непростой вопрос! Он даже на нотном стане непростой...
Помимо аккордов продумывать и удобный ввод таких вещи как:
Музыкальные штрихи
Мелизмы
Сложные пассажи
И некоторые другие (о которых я не знаю)
Это техники для фортепиано - а у других инструментов свои хитры техники, которые тоже как-то надо вводить!
Обоснуете? Выше я тут уже приводил аргументы против графических ЯП! И привёл лишь капельку аргументов за! Некоторые из комментирующих так же привели капельку аргументов за и против. Но в целом - я тут пока не вижу значимых аргументов за графическое программирование, тем более, если говорить о далёком будущем - что там такого должно быть, чтобы визуальная разработка заметно перевесила иные способы (кстати, не обязательно текстовые - если уже говорите о далёком будущем, хотя тогда стоит хорошо подумать - какие ещё могут быть альтернативы)? А не просто визуальные системы станут лишь подспорьем для других способов
Замечу, что и визуальная разработка тоже может иметь много разных вариаций модели представления. Даже (чаще всего в комментариях рассматриваемые) диаграммы - могут иметь совершенно разные форматы. Вы за какой?
Для визуализации вариации piano roll - хорошая штука - а имею ввиду - вводить музыку с помощью него (тем боле как Вы показали в виде текстовой графики, или я Вас не правильно понял) - те же Black MIDI не создаются на piano roll - их создают сложные алгоритмы, генерирующие MIDI-события, обрабатывающие готовые музыкальные паттерны!
Вот так будет на "RANGE" в варианте ввода в проприетарном буквенно-цифровом коде (но, как уже сказал, кодированный формат ввода, в принципе, можно будет настроить любой, или использовать один из готовых вариантов):
//Первая часть партитуры
{octave:3 keys: D#600 C#600 -F#600 -A#600+-F#600 -A#600+-F#600}:repeat(2)
{octave:3 keys: D#600 C#600 -F#600 -A#600+-F#600 -D#600 -A#600+-F#600 B#600 B600+-F600 B600+-F600}
{octave:3 keys: D#600 C#600 -C#600 B600+-F600 B#600+-F600}:repeat(2)
{octave:3 keys: D#600 C#600 -C#600 B600+-F600 -D#600 B600+-F600 A#600 -A#600+-F#600 A#600+-F#600}
//Или компактнее
use (octave=3, script="keys", len=600):{
(D# C# -F# (-A#+-F#):r2):r2
D# C# -F# -A#+-F# -D# -A#+-F# B# ( B+-F):r2
(D# C# -C# ( B+-F):r2):r2
D# C# -C# B+-F -D# B+-F A# (-A#+-F#):r2}
//В более сложном примере с большим числом одинаковых партий могло бы быть ещё компактнее:
use (octave=3, script="keys", len=600):{
var n1 = {D# C# -F#}
var n2 = {D# C# -C#}
var n3 = {-A#+-F#}
var n4 = {B+-F}
(n1 n3:r2):r2
n1 n3 -D# n3 B# n3:r2
(n2 n4:r2):r2
n2 n4 -D# n4 A# n3:r2}
Надеюсь нигде не ошибся. Если съедет ровность отображение - я не виноват - изначально всё было аккуратно выровнено
P.S.
Вот серьёзно думаю заменить символ "+" для формирования аккордов на что-то другое - чтобы не путать со смещением октав, например на "*" - просто "+" уже привычен для клавиатурных аккордов "сочетаний горячих клавиш"
Не кажется Вам, что это слишком громоздко? Время Piano roll прошло...
MusicXML - он не для ручного ввода - а для автоматикой (в т.ч. кроссплатформенной) интеграции - как раз для копи/паста мог сгодиться...
Так же MusicXML априори очень гибкий и расширяемый формат. Например, в "ORANGE" помимо общих форматов музыкальных потоков WAVE и MIDI возможны и потоки в других форматах (пока сугубо теоретические - этот вопрос ещё не проработан, но заложен изначально), среди которых может быть и MusicXML; а так же запланирован (но может откажусь и буду расширять MusicXML) проприетарный открытый формат (кстати, рассматривалась база XML, JSON, Protobuf) - для более расширенного управления компонентами (как в качестве внутреннего протокола, так и для взаимодействия со сторонними компонентами). Просто у меня запланировано куда более сложное командное взаимодействие, чем набор MIDI-событий, в т.ч. с целью управления параметрами и техниками звукоизвлечения комплексных компонент (а-ля какие использует Kontakt)
мой пример было не про опции, а про работу с null значениями - я специально старался его абстрактно написать, а "CustomOptions["Allow"] " просто привёл как понятный источник null значения, но я не указывал его физическую природу - это может быть и карта "ключ-значение" (фиг его знает откуда полученная) - или обращение какому-либо хранилищу - хоть локальному, хоть БД, или какой-либо ещё комплексный сервис - который пытает получить значение из данных, собираемых их различных источников. ИМХО - алгоритму основной логики это всё должно быть сугубо "фиолетово" - у него есть источник "CustomOptions" - вот там он будет искать значение. А если его там нет - у него есть некий процесс иного поучения этого значения, которые тоже не гарантирует успех - и только затем значение берётся из кода - для случая, когда без него дальше никак не обойтись, но по какой-то непонятной причине оно так и не было получено!
Ну а в финале - передаём (во втором случае пытаемся) значение в нужные объекты - чтобы так больше не "мучиться" и для того, что это значение ещё где-то важно должно быть - если это "где-то" существует! Не вдаваясь в подробности того - как эти все вспомогательные объекты устроены!
//A?.B?.C?.Checked ??= Enabled;
но можно написать вот так (тоже вполне коротко, красиво и читаемо):
if (A?.B?.C.Checked is not null) A.B.C.Checked = Enabled;
P.S.
Введение NULL - "Ошибка на миллиард долларов" не Дейкстры, а Хоара (впервые появился NULL в ЯП ALGOL W ) - всё время путаю
Вопрос скорее про интеграцию с платформой
Я не собираюсь топить за свой ЯП! Как и топить другие системы (ну кроме Native instruments Kontakt - уж больно она меня разочаровала (хотя шутка, бесспорно, полезная); как и разочаровало, например, и развитие Sony Acid - который теперь мультикомбайн Vega, Cakewalk - который теперь Sonar (и многие другие мультидорожечники) - но это для тех кто в курсе). И готов принять любую критику своих идей - главное чтобы было обоснование, даже если я его сочту спорным - обоснованная критика - всё равно полезная критика!. И я не пытаюсь необоснованно критиковать вашу разработку. Напротив - я всячески желаю Вам удачи с ней!
Как я выше написал - всем есть своя цена - и всего есть плюсы и минусы. И где есть минусы - их стараются "пусть порой условно и костылями" эти минусы устранять! И таких наработок для текстовых процессоров уже уйма - так что если сравнивать кодинг лет пятьдесят назад и сейчас - это будет гигантский разрыв (даже в рамках одного текстового ЯП)! Даже за последние 10 лет IDE шагнули очень далеко вперёд. А сейчас, на волне, генеративных лингвистических алгоритмов - шагнут ещё сильнее!
(Кстати, у Вас есть имена модулей, и да - вы позволяете их выбирать из списка (как, впрочем, это позволяют и быстрые всплывающие подсказки ввода) - но решаете ли Вы проблему, когда - это имя потом меняется у модуля - везде где используется переназначаться? Для визуальных систем это, правда, плёвое дело - а текстовые процессоры пришли к решениям не так давно - рефакторинг изменения и подсчёт с отображением ссылок - но 100% надёжности это не даёт... впрочем наврядлли сейчас хоть где-то есть 100% надёжность при активном повторном использовании кода и библиотек)
А, вот, куда сейчас развиваются музыкальные редакторы (априори визуальные) - мне категорически не нравится - это навороченные визуальны монстры с красивой графической обёрткой - и скудным сложным содержимым (я не буду говорить про плагины - среди которых есть и не скудные но от того не менее простые приложения)! Вот тут действительно назрела революция! Когда-то такой был Трекерный редактор, Piano roll, драмм-машина, , затем технология VST-плагинов, затем виртуальных DSP-процессоров и мультидорожечных редакторов и наконец музыкальных приложений для платформы Kontakt (ну я не все знаковые технологии в музыко-строении перечислил). Пришла пора двигаться дальше...
Вот за это всецелое УРА! Учли опыт величайшей ошибки Дейкстры! Я тоже против NULL но....
для обхода NULL придумали и целе семейство null-операторов (?. ?? ??=) и отделили nullable определения переменных/свойств от не-nullable - и это отличное подспорье - появились очень интересные и удобные техники их применения!
С NULL очень удобно и красиво решается - без NULL приводит к старом заскорузлому некрасивому (хоть и понятному) коду!
Bool? Enabled = CustomOptions["Allow"] ?? (Object as ICheckable)?.Checked;
Enabled ??= A?.B?.C?.Checked ?? true;
CustomOptions["Allow"] = Enabled;
//A?.B?.C?.Checked ??= Enabled; //Вот это, правда, в C# не прокатит
Вот попробует без поддержки NULL такое написать
Если внимательно читали - то я при оценке времени так и сделал. И указал - что с данными средствами будет и ещё быстрее и надёжнее
Просто Ваш аргумент он не имеет значимой силы
Интересно - кто минус моему комментарию выше поставил - за что (если это вы просто в отместку - то я не минусовал ваш комментарий)
Тут всё зависит от подходов к разработке и среды. Чем жёстче подходы - тем меньше комфорта и универсальность - но выше контроль.
А подобные опечатки текстовые IDE в 99.98% случае сами находят ещё на стадии окончания ввода слова! А некоторые продвинутые IDE ещё и сразу могут исправлять! Не говоря уже о быстрых подсказках автоподстановки - которые в умелых руках (и умелом текстовом процессоре) в более чем 80% случае просто позволяют избегать таких ошибок!
(не ключевые слова, уже определённых идентификаторов или литералов - так же будут выделяться цветом; а при мало-мальски грамотном назначении имён идентификаторов - вероятность ошибиться и из-за опечатки ввести другое имя существующего идентификатора - практически равна нулю - и современные IDE могут сразу автоматически исправлять такие опечатки)!
У визуальных сред куча своих проблем - часть я в комментариях к этой статье уже описал - но это далеко не полный список!
То же зарезервированное слово для определения сущности "instrument" - в текстовом редакторе вводится за 1.5-2 секунды (кото-то и быстрее введёт, я уж не говорю о шаблонах и копипасте, и быстрых подсказках ввода) - ошибка в тестовых процессорах будет видна сразу (это же ключевое слово - оно не будет выделено соответствующим образом) - и на её осознание и исправление (требующееся очень редко) уйдёт ещё секунды 2-3.
А сколько времени уйдёт в визуальном редакторе размещение такого компонента на поле (так оставим в стороне вспомогательные средства быстрого ввода): надо где-то нажать кнопку с командой добавления компонента, затем выбрать его из списка, или воспользоваться текстовым поиском, кликнуть по кнопке добавления, перенести его в нужное место на плоскости (а то ещё и другие компоненты подёргать) - это десятки секунд.... а потом окажется что это не тот компонент, что нужен (например, промахнулись - не туда кликнули)...
То же и про пропущенную кавычку - обычно это всё легко видно - небольшие сложности только в интерполяционных строках бывают - но были бы такие у Вас - были бы такие же проблемы, скорее всего - но это всё мелкие проблемы - и быстро исправляются.
P.S.
Вот опечатка с MIDI там где нужна WAVE именно в моём ЯП всплыла- бы только при компиляции - когда драйвер устройства WASAPI не смог бы по типам данных спрячься с типом данных, передаваемых из канала! Но это уже нюансы ЯП гибкой статической типизации - точные типы появляются только при компиляции!
Упущенный параметр "Input=InputChanel2" для "Track2" (без специально настроенных анализаторов) всплыл бы только при отладке - параметр не обязательный (как и то, что у трека может не быть входного канала данных - когда он сами их статически описывает или генерирует, или откуда-то загружает) в своём теле - внутри { }; или в трек могут напрямую отправляться инструкции из другого трека (трек сначала может динамически наполняться, а потом только воспроизводиться, или не обязательно воспроизводиться, а, скажем, выгружаться на диск в файл/БД, или передаваться в другую программу, в т.ч. по сети)!
Пропущенное указание треков, которые надо стартовать при старте проекта - это скорее нюансы дизайна ЯП - может действительно не надо стартовать никаких треков - а при старте проекта нужно выполнить сначала подготовительные алгоритмы... но можно, конечно ввести какую-то конструкцию - указывающую на это - и когда ничего нет - хотя бы выдавать предупреждение при компиляции
Скажу так - в этом конкретном случае лишь три проблемы:
Отсутствие правильно архитектурно подготовленных библиотек и документации к ним
Отсутствие настроенных кодогенератор, отладчиков и прочего суппорт-софта для данной среды
Закостенелость мышления технического руководства
Я же выше написал - что системное программирование ещё долго будет с нами - и там будет править классика. Но, объёмы такого программирования будут год от года снижаться. А вот объёмы кодогенерации, в т.ч. и в области системного программирования будут год от года расти. А визуальное и декларативное программирование будут как раз стоять на фундаменте низкоуровневой кодогенерации и некоторых системных готовых библиотеках!
Я ни разу не говорил, что Ваше решение - не рабочее и не может иметь практической ценности или быть популярным в определённых кругах. А так да - только практическое применение и отзывы могут показать эффективность того или иного подхода. У меня пока нет рабочего решения :-(
Про повторное использование кода, кодогенерацию и шаблоны, и разбиение по файлам/модулям - я уже написал выше. И уверен - даже незнакомые с построением DSP-плагинов люди, сходу поймут что да как тут происходит и настраивается! Не говоря уж про строки комментариев - и прочее самодокументированние!
Да и ЯП "Orange" - задуман как куда более мощная "штука", чем просто DSP-фильтр - так что тут есть "место" для боллеплейт-кода - но всё это можно вынести в повторноиспользуемый код -или применять шаблоны! Да и с некоторым "синтаксическим сахаром" это всё можно несколько было упростить - для простых случаев
P.S. Хотя компиляция проекта в DSP-фильтр или VST-плагин - тоже вполне возможна
Поспешил - ошибки допустил - что сам уже заметил:
Была проращена открывающая кавычка:
Driver = WASAPI("Динамики (Realtek High Definition Audio)"
Опечатка "instrumnet"->"instrument"
В обоих OutputChanel ошибочно указал формат MIDI - там, конечно же WAVE - но может быть и MIDI - но тогда VSTi семплер будет проигнорирован (если он не сможет выдавать MIDI поток)
В "Track2" забыл указать параметр "Input=InputChanel2" для настройки канала, по которому в трек будут поступать MIDI события!
При старте проекта хорошо было бы указать и стартуемые треки, ну или аннотировать их как автотстартуемые вместе с проектом (а вообще треки могу запускаться (и останавливаться) в произвольный момент времени - мануально, по расписанию, по событию и другими способами - тут полная гибкость):
run MyProject with track(Auto) [MyLayer.Track1, MyLayer.Track2]
У значений перечислений без указания полного пути идентификатора - по-хорошему надо бы поставить точку перед идентификатором значения - чтобы синтаксический разбор понимал, что надо проанализировать тип приёмника для идентификации полного имени значения - но, будем считать, что можно и без точки, когда нет пересечений идентификаторов в текущем контексте - опция настройки проекта)
Примечания:
Маппинг каналов скорее лучше делать через отдельный компонент Mixer - но для простоты не стал его указывать - а вообще - в мультиканальном проекте с пространственным 3D0звучанием такой компонент может активно применяться!
Вместо указания параметра инструмента "ProgramFIxed" (что скорее для инструментов, которые не поддерживают разные программы звучания) лучше настроить у InputChanel компонент фильтра - для отбрасывания MIDI инструкций смены инструмента:
Пример фильтрации в каналах
chanel InputChanel1
{
Direct = Input
Format = MIDI
Source = InputDevice1
Filter = MIDIBoolFilter
{
return MIDIEvent.Command != ProgramChange)
} //Задали произвольный алгоритм фильтра MIDI сообщений - пропускаются только "истинные" MIDI события
}
chanel InputChanel2
{
Direct = Input
Format = MIDI
Source = InputDevice2
Filter = MIDIEventFilter
{
return ? (MIDIEvent.Command == ProgramChange) MIDIEvent:new{Command=None}
} //Задали произвольный алгоритм фильтра MIDI сообщений - подменяющий сообщения по смене программы на пустую команду (в условии нет секции "иначе" - она генерируется автоматически - исходным значением событий); а, в принципе, такой фильтр может и другие преобразования делать (для WAVE-формата канала тоже свои фильтры могут быть - но там, их лучше подключать как DSP-процессоры - между каналами)
}
Filter = MIDIBoolFilter
{
return ? (MIDIEvent.Command==ProgramChange) MIDIEvent:{Command=None}
} //Задали произвольный алгоритм фильтра MIDI сообщений - подменяющий сообщения по смене программы на пустую команду (в условии нет секции "иначе" - она генерируется автоматически - исходным значением событий)
Музыкальный трек организован куда сложнее - чем показано в данном примере - он может обслуживать мульти-инструменты, мульти-эффекты, полифонию, мульти-семплирование, мульти-каналы и т.д. и т.п. По сути - трек - это просто некоторая агрегация сервиса воспроизведения (как и слой, как и, не указанный здесь, score - для разделения партитур) - все они определяют свой контекст воспроизведения
Я бы назвал это преимущество весьма сомнительным. Да - можно - но много ли от этого толку... а сколько это в итоге порождает проблем в сложных схемах... Насколько это поддаётся эффективной и продвинутой отладке... Насколько это всё само документируемо, и поддаётся созданию отдельной документации...
Поэтому в своё примере выше - у меня все узлы именованы (кроме узлов проекций/фильтров - это порты, принадлежащие своим узлам, но - в принципе им, как и линиям тоже можно было бы вешать лейблы)! А так - как, в другом комментарии выше, я показывал - можно и анонимную лямбду создать - и передать её в другой блок!
Да, при визуализации вполне допустимо - но, опять же, очень спорное решение - усложняет понимание схемы!
А насчёт примера.... ну я тут выше в комментарии уже приводил примеры своего ЯП для музыки "ORANGE" - он текстовый и на нём ваш пример (в котором, правда большая часть информации отсутствует в обоих вариантах представления) выглядел бы так:
пишу целиком, включая шапку, без особых синт. сокращений:
project MyProject
{
device OutputDevice1
{
Direct = Output //Не обязательно - т.к. это по умолчанию так
Format = MIDI
Driver = WASAPI(Динамики (Realtek High Definition Audio)"
ChanelsMap = {Left->"L", Right->"R", BackLeft->"BL", BackRight->"BR"}
}
device OutputDevice2
{
Direct = Output //Не обязательно - т.к. это по умолчанию так
Format = MIDI
Driver = WASAPIOut(
"LG ULTRAGEAR (Аудио Intel(R) для дисплеев)")
ChanelsMap = {Left->"L", Right->"R"} //Не обязательно - т.к. это по умолчанию так (когда у устройства нет других каналов)}
device InputDevice1
{
Direct = Input
Format = MIDI
RAWFormat = Int
RAWConverter = System.Converters.ToMIDI.KeyboardScanCodeToMIDI Driver = KeyboardInput //Если я правильно понял – что источников является букво-цифровая клавиатура
RAWInternalConverter += @AnotherFunction //Если нужен какой-то свой обработчик конвертации потока кодов клавиатуры в MIDI инструкции
ChanelsMap = {Mono->"1"}
}
device InputDevice2
{
Direct = Input
Format = MIDI
Driver = MIDIInput("LoopBe Intenral") //Какой-то MIDI input - из примера не ясно как он настроен
ChanelsMap = {Mono->"1"}
}
chanel OutputChanel1
{
Direct = Output //Не обязательно - т.к. это по умолчанию так
Format = MIDI
Space = STEREO //Не обязательно - т.к. это по умолчанию так
Destination = OutputDevice1
ChanelsMap = {Left->Left, Right->Right} //Маппинг только в два канала устройства воспроизведения
}
chanel OutputChanel2
{
Format = MIDI
Destination = OutputDevice2
//ChanelsMap = {Left->Left, Right->Right} //Не требуется, т.к. тут маппинг по умолчанию в два доступных у устройства канала
}
chanel InputChanel1
{
Direct = Input
Format = MIDI
Source = InputDevice1
}
chanel InputChanel2
{
Direct = Input
Format = MIDI
Source = InputDevice2
}
sampler S_yxg50_Sampler
{
Format = VSTi
Path = FilePath("c:\vst\yamaha\syxg50.dll":raw)
Space = STEREO //Не обязательно - т.к. это по умолчанию так
ChanelsMap = {Left->"01", Right->"02"}
}
instrument S_yxg50
{
Destination = S_yxg50_Sampler
Program = 8 //Вообще инструмент подразумевает фиксацию программы синтезатора здесь (т.к. именно инструмент определяет настройки звучания), но в примере она фиксируется только для одного Midi input (вероятно у другого - другая программа) - такое тоже возможно (здесь только значение по умолчанию)
}
layer MainLayer
{
track Track1
{
use instrumnet S_yxg50(ProgramFixed=true)
use input InputChanel1
use output [OutputChanel1,OutputChanel2] //В моей схеме не семплер подключается к каналам вывода, а музыкальный трек (здесь подключение сразу к двум каналам
runtime input:loop //трек активен только в runtime режиме в бесконечном цикле воспроизведения
}
track Track2
(
Instrumnet=S_yxg50(Program=3, ProgramFixed=true) //Сменили программу (тут будет клон инструмента)
Output = [OutputChanel1,OutputChanel2]
) @RuntimeInput(lnfinity) //аннотация режима исполнения трека
}
Layers=[MainLayer]
}
run MyProject
Надеюсь ничего не забыл, и правильно понял исходный пример. Да - может код несколько многословен - но тут много базового конфигурирования идёт. У Вас же, в визуальной части, настройки делаются в узлах - и не отображаются, но их всё-равно надо делать (текст тоже можно скрыть в сворачиваемых блоках, распихать по файлам и подключаемым модулям библиотек, в т.ч. для повторного использования), ну и текст легко комплексно просматривается, и легко рефакторится, и легко публикуется и передаётся куда-либо, да хоnть в Git
И да - всё-таки ЯП ORANGE хоть и спроектирован для реализации runtime MIDI-ввода - но, всё же, создан больше для кодирования воспроизведения в виде инструкций к семплерам (что можно делать и в runtime; или направлять MIDI инструкции из источника в генератор кода в инструкциях ЯП Orange)!
Ну - для этого возможны:
Шаблоны быстрого ввода в текстовом процессоре IDE (в т.ч. привязанные к macros и static instument generators, которые могут читать конфигурацию из внешнего config-файла, а так же иметь доступ к AST-дереву выражений проекта - чтобы генерировать код максимально гибко и адаптивно; но я не говорю - что такое в принципе нельзя сделать при визуальной разработке - но инструментарий IDE должен быть очень продвинутым, в отличии от текстовых процессоров - где это либо уже реализовано, либо легко подключается плагинами, либо вообще решается на уровне компилятора и/или внешнего текстового препроцессора)
Просто готовые преданстроенные функции внутри проекта или во модулях библиотек - которые просто можно вызвать (в т.ч. с параметрами) - а они уже проведут всю общую настройку - в этом преимущество классического подхода кодирования исходников - в гибком повторном использовании кода (но я не говорю - что такое в принципе нельзя сделать при визуальной разработке)
del
Есть одно пересечение от функции "Process" к "B" (в идеале там должно быть "мостик" - рисовал в ""Miro" - не поддерживает, видимо) - но, замечу, что как раз в этой схеме его можно избежать, нарисовав линию стрелки в другую сторону (обогнув функцию "Process2")! Кстати, IDE может иметь средство перераспределения схемы/части схемы - для минимизации пересечений.
В более сложных схемах могут применяться разные способы избежание пересечений или решения проблем их читаемости. Например, можно клонировать состояние системы в узле "А" (переменной/источнике/примёнике) - и нарисовать его ниже отдельно - так чтобы его связи не мешали другим. Или размещать их на разных слоях (в т.ч. с настроенной прозрачностью или цветами линий - чтобы даже в неотображаемом или полуотображаемом случае видеть намёки, что тут есть и другие связи). Можно и сами линии группировать через промежуточные узлы - и начинать из них движения тоже где-то ниже. Можно просто часть линий выносить в узел субпрограммы, который в основном режиме отображается как просто узел, а при детализации (как написал выше - это легко делать просто масштабированием схемы, ну или просто кликом по лупе в этом узле) - уже отображать отдельно его структуру. И так далее и тому подобное - вариантов как синтаксических так и средств IDE может быть много! Не думаю, что это такая уж проблема. Тем более, что я, ту не требую целостности графа, а в большинстве случаев такого большого числа переходов из одного узла вряд ли будет - на картинке уж очень специфическая ситуация - чаще всего инструкции достаточно линейны (ветвления условий и матчей - скорее всего не будут шибко обширными или хорошо сворачиваются, по указанным выше, или иным, методикам), а распараллеливания - вполне можно достаточно эффективно разнести друг от друга.
Кстати, IDE может даже представлять схему в разных вариантах компоновки - просто меняя способы её отображения (фактически не переставляя сами элементы) - что позволяет легко подстраивать её анализ под разные ситуации
У визуального программирования есть пара интересных архитектурных преимуществ - перед текстовым:
Очень удобно управлять множественными потоками данных - в т.ч. ветвлениями или дублированием (см. пример ниже). От одного источника легко может исходить несколько переходов. И на эти переходы можно удобно накалывать разные условия или метки аннотаций - задающие логику работы. Более того, на источник и на приёмник можно вешать как бы специальные порты - проекции/фильтрации - так что все исходящие или, соответственно, входящие линии переходов, из/в эти порты, будут подвержены зависимы от действий, заданных в этих портах - и на один источник/приёмник можно повесить сразу несколько разных портов! Так же, множественные переходы могут удобно определять и параллельное выполнение этих операций (в т.ч. в режиме по умолчанию, когда компилятор сам решает как лучше выполнять эти действия - в отдельном потоке или последовательно - в зависимости от настроек контекста выполнения и специальных хинтов-аннотаций со стороны программиста)
В отличии от представления инструкций/деклараций в текстов - которых по сути линейны - графические схемы размещаются на плоскости - а, в принципе, в 3D и даже в большем числе измерений - что позволяет более наглядно размещать связи между элементами и вложенные блоки. Тут же можно сказать и про уровень масштабирования - когда можно приближая масштаб легко переходить от общего узла некоторого блока - к его внутренней реализации, оставаясь в текущем контексте, что позволяет (при необходимости), например, вместо внутренней логики подпрограммы (читай функции) подставлять идентификаторы текущего контекста - чтобы лучше понимать как внешняя среда влияет на результат внутренней
В схеме выше много действий. Условно начало можно считать получение фильтра FilterList (хотел сначала сделать точку входа Request - но передумал - и так сложно вышло)
Ключевым тут является источник переменной "A" (тип который является перечислимым) - у неё есть ряд исходящих портов разного типа - какие-то передают все данные переменной "A" (но только если сущность переменной "A" удовлетворяет условиям); какие-то передают только часть её содержимого - поэлементно удовлетворяющие условию. Так же тут осуществляются операции фильтрации данных по типам и контрактам (забыл добавить и просто операции преобразования)
Есть даже паттерн переназначения фильтра по умолчанию (он не изменяет передачу типа данных, но все условия на него, накладывается согласно заданного шаблона)
Короче - схема, безусловно очень абстрактная, но я просто хотел показать возможную глубину фишек визуального программирования на графических схемах