All streams
Search
Write a publication
Pull to refresh
-30
@LordDarklightread⁠-⁠only

Пользователь

Send message

Хотя, вот - кнопки на спинке, уже есть!

Но есть ли умная блокировка этих кнопок?

Безусловный ОФФтопик (хоть и контекстный) - но наглядный пример Black midi

Все 4-ре примера выше приведены в синтаксисе разрабатываемого мною ЯП 5-го поколения под рабочим названием "ODIN") - надеюсь разберётесь - всё же просто:

  1. -> и <- это операторы направления потока данных

  2. (источник) это операция начала извлечения (перечисления) элементов источника (может быть ещё так: (elm<-array) если нужен свой идентификатор для элементов источника - но это нужно при вложенных перечислениях

  3. Filter это команда - принимает на вход источник и возвращает обработанный источник (it - зарезервированное идентификатор - ссылающийся на внутреннюю реализацию команды - по сути там лямбда)

  4. Двоеточие - это операция преобразования - в данном случае с вложенным в скобки выражением фильтрации с обращением череp item - к содержимому источника)

  5. array:(item>threshold) -> filtredValues
    можно написать и так короче (но, на мой взгляд, менее наглядно):
    filtredValues <- array:>threshold
    или так (как аналог и для (array) -> if(item>threshold) -> filtredValues):

    filtredValues <- (e<-array, e>threshold)

    или (тут цепочка преобразований может быть ещё продолен с доп ":" справа, и можно обращаться к "e" - исходное значение; просто сами выражения перечислений могут быть куда более сложными):
    filtredValues <- (e<-array):(e>threshold)

Я это всё как раз к тому - что я сильно настаиваю, что текстом писать можно и компактнее эффективнее - и именно этот подход и надо развивать в будущих ЯП

$filteredValues = array_filter($array, fn(Item $item) => $item->getValue() > $threshold);

Чисто такой пример мог бы выглядеть так

Грифельного цвета блок - это анонимная лямбда с одним аргументов (автоопределяемый по контексту тела), и одной переменной в "замыкании"

Но выглядит то всё не очень красиво - поэтому ниже картинка с 4-мя вариантами более эффективного визуального представления данной операции:

Слева направо:

  1. Значение переменно "array" передаётся в переменную с условием, наложенным через зарезервированное слово "item" - что говорит, что его надо наложить на элементы внутри потока "array", вторым аргументом сравнения идёт другая переменная - т.к. условие написано на стрелочек, здесь нет стрелочки передачи сюда этой переменной из блока

  2. Зато в этом примере передаются обе переменные - здесь применятся отдельная операция сравнения в блоке условия, так же через обращение к элементам массива "array" через зарезервированное слово "item" (в принципе, наверное можно сделать и отдельный блок сравнения - который всегда оперирует только внутренними элементами передаваемого в него потока, тогда применять "item" будет не нужно)

  3. Вот, примерно как тут, только тут мы сразу определяем обращение к переменной "array" как блоку развёртывания потока - на выходе уже будет идти только поток элементов "array" (т.е. сами "item"), поэтому тут на стрелочке условие уже упрощено - только оператор сравнения и второй аргумент сравнения

  4. Здесь аналогичный подход - только применён блок преобразования (в данном случае фильтрации), с условием прям в самом блоке, поэтому на выходе уже будет отфильтрованный массив, который и передаётся далее в переменную-приёмник

Лично мне нравится 4-ый (правый) вариант - но в нём нужно определять блок источника; а если нужно переходить из другого, уже имеющегося, блока (без определения нового - хотя это тоже возможно) - то нравится первый (левый) вариант (здесь источник, в лице блока "array" может быть определён как угодно - это не влияет на решение, главное - его тип данных должен быть перечислимым).


Безусловно, это далеко не все возможные эффективные визуализации фильтрации одной переменной по условию для помещения результата в другую переменную.

Применение ссылок на источники (переменные) внутри выражений блоков или стрелок достаточно компактно и легко читается, но тут есть недостаток, в отличии от ссылки с другого блока - ссылка более гибкий механизм, т.к. в этом случае зависимый блок не зависит напрямую от выражения исходного блока - и его можно легко менять, не трогая зависимые блоки! Но насколько это принципиальное преимуществ - я не знаю! Но - все варианты записи должны быть иметь возможность лёгкого применения авторе факторинга друг в друга - так что это вообще не проблема - а ли8ь разные представления одного и того же!

А теперь посмотрите у кого представление получилось более красивым, понятным, компактным и эффективно воспринимаемым и видоизменяемым?

Впрочем... я и текстом могу написать такие варианты тоже весьма эффективно и компактно:

  1. filtredValues <- From array Filter (item>threshold)

  2. array -> Filter (it>threshold) -> filtredValues

  3. (array) -> if(item>threshold) -> filtredValues

  4. array:(item>threshold) -> filtredValues

Про MVCC PostgreSQL  знаю в общи чертах - каждое изменение записи - это новая запись - новая версия

A в ACID - это атомарность - но то что с ней беда в PostgreSQL никуда не девается - потому что есть много операций которые с ACID требуют выполнения в составе транзакции с излишним уровнем изоляции - что снижает эффективность когерентности из-за лишних продолжительных блокировок

Вот даже в примере визуализация занимает в разы большую площадь, если сравнивать со строкой.

Конечно - поэтому я и не адепт визуализации - изложил свои аргументы выше в комментариях - но это не значит что, относительно эффективно, визуализировать вообще нельзя - Вы спросили как будет выглядеть - я показал один из вариантов. Остальное дело привычки и дело вкуса

Очень много условностей, ИМХО. Вы добавили ещё одно измерение - цвет. И это тоже надо будет запоминать программисту

Во-первых, я не добавлял измерение цвет - он здесь, скорее, как подспорье - ведь и в современных IDE текст тоже же не чёрно-белые!

Во-вторых - мы же переходим от текста к графике - а тут уже активно в ход идут разные средства визуализации - иначе - зачем нам графика?

Но при этом, ИМХО, теряется вся фишка визуализации.

Наверное Вы слишком зашорены в понимании что есть визуализация при программировании - а я выше сразу дал замечание - что представлений может быть много. И много представлений могут быть реализованы в одном решении.

В тех же текстах ведь тоже, скажем, есть группировки - сворачивания блоков - и ничего - не шибко люди возмущаются "что теряется всё преимущество текстов" - да и настраивается это гибко...

 то куда, будет идти стрелка из предыдущего блока?

В примере выше входящая стрелочка должна указывать на левый блок "b" - это же очевидно!

Кроме того, примеры с $b и $c+1 это были просто примеры выражения. В реальном мире там может быть, например, результат выполнения какой-нить функции

Как я считаю - всё-таки писать выражения при визуализации можно несколькими путями - для удобства:

  1. Как в примере выше - прямо внутри блока - удобно для простых выражений, например математических вычислений

  2. Писать через соединения блоков стрелочками-операциями (показано в примере ниже для "+")

  3. Писать через блоки-операции (показано в примере ниже как вызов функции или приведение типа)

  4. Может ещё какие варианты могут быт...

При этом - все варианты должны быть взаимозаменяемы автоматическим рефакторингом- по необходимости. А сложные вычисления ещё должны быть и реализуемыми в виде подблока (подпрограммы) - по умолчанию свёрнутой, открываемой отдельно как функция (ниже в примере это может быть внутри нижнего блока справа - свёрнутого второго вызова функции "functionA").

Ваш же пример ниже:

$a = functionA($param1) ?? functionB($param2);

Опять же - это проcто один из вариантов. И я не претендую на эффективность и красоту - я не адепт визуального программирования

Примечание:

  1. +/- сворачиваемый блок

  2. Лупа - открывает детали реализации (вообще-то - предполагается открытие в отельном попап (или, скорее, плавающем) блоке, или даже окне - но тут, для наглядности (и упрощения моего рисования), рисую сбоку) - точечная линия от лупы - появляется только при раскрытии - или ещё может не быть вовсе

  3. В этом примере некоторые операции представлены внутри блоков (например приведение типов), некоторые на стрелочках перехода

  4. Пунктирная стрелочка справа - автоматическая - визуализация возврата значения функции

  5. Возможно, в блоках вызова функций тоже стоит показывать фактические типы (но это всё может быть настраиваемым) - раз это типизированный ЯП

  6. По-хорошему, в реализации функции "functionB" на стрелочке передачи результата сложения Arg2 нужно отобразить в какой параметр передаётся это значение, хотя бы автоматически - если у функции только один обязательный параметр (однозначное соответствие) - для наглядности визуализации

  7. Аналогично могут передаваться какие-то аргументы и в исходные вызовы функций "functionA", "functionB" - но это необязательно - т.к. логика переходов может быть совсем от других источников (как переход от функции "functionA" к функции "functionB)" - но всё-равно можно был бы сделать вторичные, "висящие" блоки передачи аргументов. Но, данный синтаксис визуализации этого не требует - хотя это можно создать автоматически рефакторингом для наглядности

  8. Ошибся в обязательных параметров функции "functinonB" - пусть Arg2 тоже имеет какое-то значение по умолчанию - лень перерисовывать! Прошу прощение - это не существенный огрех

  9. Кстати, очень интересной фишкой может стать раскрытие реализации вфункции "functinonB" сразу в переданных аргументах а не в параметрах

Так в чём сложность создания или восприятия?

Тут только некоторые трудности у платформы по эффективной визуализации будут!

Вопрос то был не про техническую часть - а как раз пофантазировать о том, как это визуально т.е. с практической стороны восприятия могло бы быть - ну и сделать оценки эффективности такого решения!

А насчёт технической части - да в мозг лезу уже сейчас - и, возможно, когда-то научатся очень умело им манипулировать (так что будущее "Матрица" покажется Вам раем, а вот будущее "Континуум", или" 2035: Город-призрак" - очень пугает, так что может не надо?) - но, думаю, это ещё очень и очень не скоро... ту скорее программы сами себя быстрее начнут писать - и человек тогда вообще не понадобится!

Про проблемы с транзакциями не понял

А с атомарными операциями в СУБД вообще беда

Не адепт визуального программирования. Но из чистого любопытства

$a = $b ?? $c + 1;

Хотя подвариантов представления операций тут много разных может быть (например +1 может быть операцией на стрелке)

Зелёная стрелка - передача значения (операция более приоритетна чем чем просто переход)

Фиолетовая - переход к другому блоку без передачи значения

Синий идентификатор с - случайно - должен быть как b - красный - как просто источник данных

Надписи на строках- условия (! - отрицание, ? - базовое сравнение)

Позже и про маппинг придумаю

В каком виде?

Это только на словах звучит эпично

Конечно. Да - это отдельное направление - и да такие партитуры я тоже хочу поддерживать в виде текста - честно ни в одном трекерном редакторе я не нашёл комфорта в написании трекерной музыки ;-)

Честно говоря, как-то сильно заморочено. Напоминает другой проект, правда не про музыку а про трехмерное моделирование - OpenScad. Там тоже все операции делаются исключительно кодом. Но это же жутко неудобно, имхо.

Как написал Выше - у меня идея достаточно комплексная и многофункциональная! Но тут только практика может показать - удачная она или нет - лично я вижу за этим будущее - а пока я представил лишь отрывки из очень раннего черновика! и даже если мне не удастся придумать удобное решение, которое могло бы стать популярным хотя бы в кругах с определённым мышлением, то это не значит, что это невозможно в принципе!

А всё остальное - это просто ломка восприятия - сейчас музыканты привыкли к другому. А насчёт Piano Roll - знаете - есть серьёзные композиторы, которые до сих пор его не признают такой подход - просо их академически учили обратному!

У меня те же аргументы как и при противостоянии визуального программирования и текстового (см выше) - разве что визуальны партитуры музыки вряд ли когда-то будут иметь очень высокую визуальную сложность (хотя это Вы её Black midi не видели (с одноимённой группой не путайте - хотя их музыка и вышла из данного стиля) - вот микро кавер на известный мотив 26 сек и 230k нот, подлиннее и пожёстче - не для всех ушей 5.5 мин и 5М нот; ну или попроще, а это вообще шедевр или вот известная мелодия с постепенной раскруткой - такое только программировать... God mode reblacked

The Convergence - шикарно).

Создавать сложные мелодии, с кучей инструментов, с разнесением в 3D пространстве - это Вам не партитуру в несколько сот тысяч знаков накалякать - тут число генерируемых команд будет исчисляться ярдами на минуту контента! Да - это уже очень далёкая от классики музыка (но классику тоже можно - осовременить) - это музыка будущего (я не про Black midi если что) - сложная, объёмная, непостоянная (ведь тут ещё и случайные числа можно применять - так чтобы, условно, каждый семпл звучал несколько иначе - а не просто дублировался - что добавляет натуральности); Не говоря уже о том, что музыка может просто генерироваться на лету (причём вполне нормальная музыка - но тут, конечно надо глубоко настраивать, готовые части партитуры с вариациями исполнения и совмещения; эх... вспомнил как генерировала музыка в древней, не сыскавшей популярность, игре 7th legion - если начало шокирует - промотайте вперёд или смените трек)... А уж какие огромные возможности по программированию эффектов обработки звука...

Так же программирование музыки - это отличное подспорье для математических направлений в музыки - например для Math Rock, и везде, где нужна высокая точность гармонии

Вероятно, я что-то не понял. Тогда объясните, пожалуйста, проблему

Очень любопытно! Я выше поделился отрывками из своих черновиков. Поделитесь тоже?

MIDI (Musical Instrument Digital Interface) как формат данных - это бинарное представление просто последовательности инструкций (команд). Дефакто стандарт цифрового сопряжения любого музыкального оборудования или программных плагинов (кроме прямой обработки звука в виде потока декретированной волны, хотя и WAVE тоже через MIDI обрабатывать можно). В каком-то смысле - можно считать это аналогом IR в контексте управляемых ЯП. Только туго с управляемыми ЯП для MIDI. Про текстовую нотацию для MIDI не слышал - но можно глянуть LilyPond

Про различные музыкальные нотации вот тут wiki (английская версия полнее). Но распространены сейчас табулатуры и ABC-разметка - текстовый программный подход к музыки позволяет применять разные нотации - как будет удобно! У меня есть о свой расширение для табулатур!

А любителям графической визуализации предлагаю заглянуть сюда

А адептам моделирования разметкой - сюда

А есть какие-то статьи или видео про ваши наработки?

Статья, как и сам ЯП пока в разработке ;-)

Но для удовлетворения любопытства - вот несколько отрывков из зеро-драфт:

Пролог ORANGE Music creator studio

OMC Language - это спецификация скриптовой языка программирования, разработанного для написания музыкальных (или иных других) звуковых композиций, да и вообще для звукоизвлечения и обработке звука.  Это специфический ЯП для эффективной работы со звуком, в первую очередь с его молодикой – т.е. главная поставленная задача – лёгкость аранжировки (от того и название orange – arrange) музыкального полотна плюс наложение эффектов и мультидорожечный (мультиканальный) мастеринг, с глубокой автоматизацией всех процессов. С поддержкой применения сторонних плагинов (в т.ч. VST) и внешнего звукового процессинга. С полным описанием всего проекта в удобочитаемом и набираемом текстовом виде. Впрочем, обработка звука тут не 100% обязательна, и на данном ЯП вполне себе можно писать практически любые программы (подключив необходимые библиотеки).

Данный ЯП является высокоуровневым, практически строго императивным, языком с элементами функционального программирования, и в своей базе является интерпретируемым.

Спецификация прицельно разработана для удобного программирования многопоточного потока команд исполнения (каждый трек – это отдельный параллельный поток) и их синхронизации друг с другом; причём во многих случаях операторы неявно (или явно) порождают отдельный поток (трэк) исполнения (но этим можно управлять по контексту использования).  Так же ЯП является контекстно-зависимым - в каждый момент есть некое состояние текущего контекста исполнения, им можно гибко управлять, настраивая общие контекстные параметры, на которые могут опираться исполняемые в нём команды, контекст можно клонировать и дробить (в подблоках и подпрограммах), делать частично наследуемым, сохранять, создавать новый и восстанавливать для текущего потока, и передавать в другие потоки и подпрограммы.

ЯП может эффективно применяться как для статического написания музыки (или иного процесса, но деле всё-таки будет идти речь о музыке), так и для динамического создания (или сведения) музыки в реальном времени, в т.ч. через внешнюю генерацию другими алгоритмами и управления воспроизведением, например, для программ-автогенератором музыки, как полностью автономных (нейросети, самостоятельно сочиняющие музыку), так и более простые алгоритмы – которые просто генерируют динамически меняющийся звуковой поток по заданным правилам реакции на какие-либо состояния их собственных процессов (или отслеживаемых ими внешних процессов). В принципе, на OMC тоже можно было бы реализовать в будущем полностью автономную генерацию музыки – у ЯП нет каких-либо ограничений для этого.

Введение в команду sample

Для помещения в текущий трек содержимого нужно просто выполнять разные встроенные, библиотечные, кастомные команды и паттерны. С точки зрения программирования всё это будет очень похоже просто на императивный (с элементами функционального стиля) код, где каждый трек – это просто параллельный поток исполнения программы, но с некоторыми специфическими нюансами.

Например, чтобы воспроизвести какой-то семпл (считаем, что он заранее уже был загружен программно, или входит в часть ресурсов проекта) нужно выполнить команду

sample(“путь\имя семпла”)

Можно создать переменную-псевдоним для этой команды, чтобы получить более короткое имя

val a = @sample(“путь\имя семпла”)

И далее просто обращаться к ней

a a a a //Семпла прозвучит подряд 4 раза друг за другом

Такие алиасы можно создавать для любых выражений Языка (в т.ч. параметрических и с открытым концом – об этом всё будет подробнее позже рассказано), но выражение должно иметь полностью закрытые скобки, которые были открыты в нём.

Существует много путей управления воспроизведением семплов и их последовательности, например, если надо вставить задержку между ними – то есть команда delay

a delay(100ms) a delay(100ms) a delay(100ms) a delay(100ms)

команда sample имеет параметр для установки начальной и конечной задержки

var a = @sample(“путь\имя семпла”, BeginDelay=40ms, EndDelay=60ms)

a a a a

или так

var a = @{ delay(40) sample(“путь\имя семпла”) delay(60) }

a a a a

Команда sample  имеет очень много параметров, и вообще – для воспроизведения сложных семплов могут быть определены отдельные специальные функции. Сейчас я не буду это всё рассматривать. Но несколько важных рассмотрю

Одним из главных параметров является тон

sample(“путь\имя семпла”, .D) //Формально тон «Ре» - если семпл откалиброван по тону «До»=C); в действительности – это изменение тона семпла от  базового на 3 ноты вверх; формально – понятие ноты тут тоже условно (семпл может быть сконфигурирован произвольным образом) – и это просто изменение на 3 тона вверх

sample(“путь\имя семпла”, .+D) //Но 3 тона и октаву вверх (октавы формально тоже условны)

sample(“путь\имя семпла”, .--D) //Опустить тон на 2 октавы и увеличить на 2 тона

sample(“путь\имя семпла”, .-<D) //Опустить тон на одну октаву и 3 тона

sample(“путь\имя семпла”, 800) //Числами тоже можно задавать: положительные – увеличение тона, отрицательные – уменьшения относительно базы

sample(“путь\имя семпла”, .D, BaseToneOffset=800) //Можно так же дополнительно смещать базовый тон, относительно которого будет воспроизводиться заданный основной тон

sample[“путь\имя семпла”].BaseToneOffset=800  //А вот так можно изменить значение по умолчанию для семпла (только в рамках текущего трека); замечу, что имя ресурса задано в квадратных скобках

Помимо тона важным параметром является длительность звучания:

sample(“путь\имя семпла”, .A, 500ms) //Базовый тон, звучит полсекунды (важно, чтобы он сэмпл не был короче 500 мс или был зациклен)

Тут для разных форматов семплов очень много нюансов определения длительности их звучания – и у команды sample есть много параметров для корректного использования длительности. Например, у семпла может быть часть затухания (после основного звучания) и в примере выше длительность будет общая т.е. вместе с затуханием. Если же нужно не учитывать длительность затухания то можно написать так:

sample(“путь\имя семпла”, .A, 500ms, FadePlay=.Always) //Если звучание не уложится в 500 мс, то затухание всё-равно будет проиграно

или так

sample(“путь\имя семпла”, .A, MainLen=500ms) //аналогично – лимитируется только длина основной части семпла

sample(“путь\имя семпла”, .A, 750ms MainLen=500ms) //Основная часть семпла лимитируется 500 мс, а весь семпл (с затуханием) лимитируется 750 мс

sample(“путь\имя семпла”, .A, 500ms, FadePlay=.Continue) //Затухание будет полностью звучать, если успеет начаться за 500 мс проигрывания всего семпла

И так далее – тут возможностей очень много. Особенно для циклических семплов. Для удобства проигрывания сложных семплов луче пользоваться специальными библиотечными командами, более узко специализированными для воспроизведения семпла в том или ином режиме. Плюс применять псевдонимы для перенастроенных вызовов команд.

Но ещё одним важным параметром длины семпла является DelayLen – это длина задержки семпла

sample(“путь\имя семпла”, .A, 750ms DelayLen=500ms) //Формально семпл будет звучать 750 мс, но выполнение sample завершится уже через 500 мс и начнётся выполнение следующей команды, до завершения звучания семпла. Важно – если будет задан параметр EndDelay – всё-равно сделает задержку после прошествии времени длины семпла

Очень важным компаньоном команды sample является команда specimen – она запускает семпл и не делает задержку для следующих команд, которые параллельно проигрываемому семплу могут выполнять ещё какие-то действия – например спецэффекты для семпла, или даже запускать другие семплы, которые начнут прерываться параллельно с текущим в рамках единого трека.

Введение в клавишную нотацию

Помимо прямой вставки семпла в партитуру трека, есть возможность зафиксировать его за всем треком

use instrument sample(“путь\имя семпла”) //Тут можно зафиксировать все необходимые параметры по умолчанию

Передача нот удобнее всего через паттерн клавиш

{keys: -A500+(D#200 E200 D#200 E200 D#200 E200) } //Нота «Ля», октавой ниже, звучит 500 мс, поэтому в начале получается аккорд (-А+D) нота Ре звучит 200 мс, после чего сменяется на «Ми», которая чередуется несколько раз с «До#» каждые 200 мс (но первые «До#», «Ми» и вторя «До#» буду сыграны вместе с «Ля», но за 100 мс, до конца звучания второй «До#», нота «Ля» перестанет звучать (истекут 500 мс)

Если не задать у ноты «Ля» время звучания, оно автоматически будет как у всего аккорда; при этом можно задать время в сего аккорда (даже такого сложного, с чередованием нот)

{keys: -A+(D# E D# E D# E):MainLen(1000) } //Тут вся последовательность звучит 1000 мс –время звучания каждой ноты будет одинаковым и вычислено из общего времени звучания (отдельным нотам можно задать их персональное время звучания – тогда оно будет вычтено из общего при расчёте звучания остальных нот)

Все ноты буду воспроизводиться текущим инструментом, заданным как семпл.

Число справа от ноты – это длительность звучания (без учета затухания). Символ «#» это диез (смещение на полтона вверх); есть и бимоль «&» - пример “D&200” – но применяется редко.

Если при воспроизведении ноты нужно указать какие-то особые параметры семпла – их можно указать в скобках вызова ноты

{keys: D#200(EndDelay=100) G(FullLen=200) D#200(EndDelay=100) G(FullLen=200)} //Число справа от имени ноты длительность звучания (без учета затухания) на это время будет пауза перед запуском следующей ноты; G(FullLen=200) задаёт длительность с учётом затухания; EndDelay=100 задаёт дополнительную паузу конце звучания ноты (от конца основного звучания) прежде чем управление перейдёт дальше

Можно и так записать

{keys: D#200:EndDelay:100 G:FullLen:200 D#200:EndDelay:100 G:FullLen:200}

Всё это в базовой октаве семпла. Октаву можно понижать и повышать – символы «-» и «+» строго перед именем ноты (ну или передать в ноту аргумент Octave=номер октавы – 0-я субконтр-октава –, 1-я контр-октава и т.д.) – сколько будет символов на столько октава будет сдвинута (а в примере выше  -A25 – это «Ля» на одну октаву ниже базовой – т.е. «Ля» в малой октаве).

{keys: ---D200 --D200 -D200 D200 +D200 ++D200 +++D200} //Игра ноты «До» во всех основных октавах

Кстати, октаву можно зафиксировать для текущего контекста, например, для нотного паттерна – тогда относительные изменения октавы будут уже от неё, а «абсолютные» (числовые) останутся от базовой

{octave:3 keys: D200 +D200 ++D200 +++D200 ++++D200 +++++D200 +++++++D200}

Ещё одним способом явно указать октаву – её номер перед нотой

{keys: 3D200 2D200 1D200 0D200 1D200 2D200 3D200}

Замечу, что такой способ отличается от общепринятого, где октава пишется справа от ноты (так как, в данном случае, там располагается длительность), но такой вариант формата тоже возможен:

{classic: D1-200 D2-200 D3-200 D4-200 D5-200 D6-200 D7-200} //октава всегда должна быть указана

По умолчанию за нулевую берётся смещение на 4 октавы вниз  до субконтр-октавы (но в настройках или в параметрах текущего контекста это можно изменить – но цифрами справа можно будет только прибавить октаву, но не убавить)

{octave:1 classic: D1-200 D2-200 D3-200 D4-200 D5-200 D6-200 D7-200} //задали нулевое смещение октавы

Замечу, что есть ещё один вариант записи нот (вообще их может быть много – если установить дополнительные интерпретаторы формата), из мира трекерной музыки – важное отличие: тут не задаётся напрямую длительность ноты – она звучит, пока явно не будет сброшена или не начнётся другая (ну если, нота зациклена – иначе звучит до своего конца и всё) – и тут каждая позиция имеет фиксированную длину тактов звучания (это всё настраивается), пропуск тактов «--» (обязательно отделено от нот) или «NP», заглушить ноту «00»

{ticklen:200ms tracker: D1 -- D2 -- -- D3 D4 D5 -- D6 00 NP NP D7}

Этот формат ещё много поддерживает фишек трекерной записи, но о них сейчас говорить не буду.

Нотные паттерны скрывают ещё много нюансов (например, можно задавать эффекты, силу/скорость нажатия и отпускания (или какие-то ещё техники, если речь не идёт про клавишный инструмент – это уже нюансы текущего инструмента), управлять громкостью и пространственным позиционированием и т.п.), но пока я вновь вернусь к описанию команд трека. Последнее, что добавлю – нотный паттерн так же поддерживает переменные (обычно речь о внешних, хотя внутри можно тоже объявить) и функции (в т.ч. генераторы) – ссылаться на них внутри паттерна можно обычным способом (по имени), но внутри паттерна есть свои ключевые слова – они переопределяют пользовательские идентификаторы (кстати, внутри паттерна можно переопределять существующие и вводить свои ключевые идентификаторы и переменные (в т.ч. внешние) через создание псевдонимов, сослаться на внешнюю переменную или функцию так же можно через префикс $ - за ним всегда подразумеваете внешний идентификатор (при необходимости получить адрес – последовательность спецсимволов такая «@$» далее идентификатор).

var len=800 {keys: D#(len+10) E(len+20) D#(len+40) E(len+80) D#(len+100) E(len+random(100)) }

аналогично (но переменная len существует только внутри паттерна; и фунукция даёт случайное число от 1, а не от 0)

def rnd() = random(1...100)

{ var len=800 keys: D#(len+10) E(len+20) D#(len+40) E(len+80) D#(len+100) E(len+rnd()) }

Введение в лупы

И так. Если нам нужно несколько раз подряд воспроизвести один и тот же семпл мы можем его написать несколько раз

sample(“путь\имя”) sample(“путь\имя”) sample(“путь\имя”)

А можно просто запустить цикл

repeat(3) sample(“путь\имя”) //можно ещё так  sample(“путь\имя”):repeat(3)

Такими же способами можно зациклить и блок кода

repeat(3) { sample(“путь\имя1”) delay (200) sample(“путь\имя2”) Delay(200) }

или

{ sample(“путь\имя1”) delay (200) sample(“путь\имя2”) delay (200) }:repeat(3) //кстати repeat(0) не выполнит ни разу

Код, следующий за блоком цикла будет ждать пока не выполнится цикл, но если написать так

loop(3) sample(“путь\имя1”)  sample(“путь\имя2”)

Выполнение семплов 1 и 2 начнётся одновременно, но на втором цикле будет запущен 1-ый семпл, независимо от звучания 2-го или какие команды там будут ещё вне цикла. То есть, loop цикл запускается параллельно текущему треку (можно считать, и по факту так оно и есть, что цикл запускается в отдельном субтреке, но с копией текущего контекста, и связанный с ним, и с его каналом; это похоже на оператор by, который тоже порождал параллельное выполнение – и внутри цикла он тоже может быть – и это будет ещё один поток параллельного выполнения).

Можно запустить и бесконечный цикл (если с repeat это не очень полезно, хотя – это как посмотреть), то с loop – будет куда полезнее (тем более что есть команда break – для прерывания цикла, а её параметр – это сколько вложенных циклов надо прервать (или можно указать конкретный цикл – об этом ниже)

loop { sample(“путь\имя1”) delay(300)  sample(“путь\имя2”) } //Бесконечный цикл, пока существует трек, в котором он запущен

По сути loop – это отдельный поток исполнения (отдельный трек) и ему присуще всё, что касается управления параллельными потоками. Например, их можно прерывать извне

loop { sample(“путь\имя1”, FullLen=300) delay(300)  sample(“путь\имя2»,  FullLen=300) ? checksignal(1) break }

repeat { delay(300) ? (random(4) == 0) -> signal(1); sample(“путь\имя3”, FullLen=300) }

//Здесь семрплы 1 и 2 могут звучать одновременно с семплом 3, или он звучит между ними, но случайным образом  внешний код подаёт сигнал и цикл завершается (сигнал – не звуковой, а логический)

Но каждый цикл – это объект – поэтому можно и так

var myLoop =  loop { sample(“путь\имя1”, FullLen=300) delay(300)  sample(“путь\имя2”,  FullLen=300) }

repeat { delay(300) ? (random(4) == 0) ->myLoop.Break(.End); sample(“путь\имя3»,  FullLen=300) }

Вместо Break можно вызывать Abort() – тогда цикл прервётся в любой момент времени (даже не доиграв семпл) или Stop() – тогда цикл остановится перед началом очередной итерации

Ещё вариант

var myLoop =  loop { sample(“путь\имя1”, FullLen=300) delay(300)  sample(“путь\имя2”,  FullLen=300) break(.Check) }

repeat { delay(300) ? (random(4) == 0) ->myLoop.Break(.Manual); sample(“путь\имя3”,  FullLen=300) }

Здесь «break(.Check)» сработает только если будет вызван «Break(.Manual)»

Наряду с break есть и команда continue – перезапускающая итерацию цикла (continue(.Restart) – перезапустит цикл полностью (если у него был лимит на число итераций или какая-то позиция), как будто цикл запустили снова.

Сигналы, удобны не столько для прерывания потоков (как выше можно заметить – они лишь маркер), но больше для синхронизации потоков

var myLoop1 =  loop { sample(“путь\имя1”, FullLen=300) waitsignal(1)  sample(“путь\имя2”,  FullLen=300) waitsignal(2)}

var myLoop2 =  loop { sample(“путь\имя3”, FullLen=300) waitsignal(1) sample(“путь\имя4”,  FullLen=300) waitsignal(2)}

repeat(4) { delay(300) signal(1,10ms) sample(“путь\имя5”,  FullLen=300) signal(2,10ms)} //10ms – вторым аргументом указывает длительность сигнала (после чего он автоматически сбросится, либо надо сбрасывать самостоятельно (signaloff(…)), либо сигнал без длительности автоматически сбрасывается при выходе исполнения из блока (блок в цикле считается всем циклом)

Есть схожая альтернатива сигналам – mark

loop { sample(“путь\имя1”, FullLen=300) mark(1,3)  sample(“путь\имя2”,  FullLen=300) mark(2,3)}

loop { sample(“путь\имя3”, FullLen=300) mark(1,3) sample(“путь\имя4”,  FullLen=300) mark(2,3)}

sample(“путь\имя5”, FullLen=300) mark(1,3) sample(“путь\имя6”,  FullLen=300) mark(2,3)

mark – это «сигнал» (не пересекается с обычным сигналом – это именно марк-сигнал) и ожидание в одном флаконе – тут два аргумента – первый идентификатор сигнала, второй счетчик – как только зарегистрируются все 3  марк-сигнала произойдёт продолжение выполнения и сигнал будет сброшен.

Но можно вызывать и так (причём тут опущен и идентификатор сигнала (будет общий) и счетчик (ведётся автоматически):

loop { mark(.Context) sample(“путь\имя1”, FullLen=300) mark sample(“путь\имя2”,  FullLen=300) mark}

loop { mark(.Context) sample(“путь\имя3”, FullLen=300) mark sample(“путь\имя4”,  FullLen=300) mark}

mark(.Context) sample(“путь\имя5”, FullLen=300) mark sample(“путь\имя6”,  FullLen=300) mark

здесь mark(.Context) – автоматически определяет количество различных независимых контекстов, нуждающихся в синхронизации и настраивает макс значения текущего счётчика единым образом

Альтернативная запись через блок mark { }

loop { mark { sample(“путь\имя1”, FullLen=300) } mark { sample(“путь\имя2”,  FullLen=300) }}

loop { mark { sample(“путь\имя3”, FullLen=300) } mark { sample(“путь\имя4”,  FullLen=300) }}

mark { sample(“путь\имя5”, FullLen=300) } mark { sample(“путь\имя6”,  FullLen=300) }

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

 

Каждый цикл имеет возвращаемое значение и его можно присвоить переменной

var a = loop(3) { sample(“путь\имя1”)  sample(“путь\имя2”) }  //или var a = { sample(“путь\имя1”)  sample(“путь\имя2”) }:loop(3)

В этой переменной содержится объект управления циклом. В  примере выше применён цикл loop – по умолчанию он стартует сразу же в параллельном потоке, а код в текущем продолжает выполняться сразу за ним. Соответственно, через переменную цикла можно управлять этим циклом – в основном это команды потока: Pause, Resume, Break и т.п., но есть и более специфичные команды именно для цикла: Start, Restart, Continue. И некоторые команды управления контекстом выполнения.

Можно и такой цикл присвоить переменной

var a = repeat { sample(“путь\имя1”)  sample(“путь\имя2”) }

Цикл repeat не выполняется параллельно, но в выражении присвоения он не стартует сразу (иначе бы присвоение не произошло, пока цикл не завершится и в таком присвоении не было бы смысла). Поэтому, чтобы цикл стартовать нужно написать так: a.Start(3) но можно и просто обратиться к переменной, как к функции a(3) – при этом цикл вполне можно вызвать несколько раз (в любом месте, где доступна переменная цикла):

a(3) a(4) a(3) //в аргументах вызова задаются значения выполнения цикла – в данном случае число повторов и это всё равносильно repeat(10) { sample(“путь\имя1”)  sample(“путь\имя2”) }

Но это всё просто синтаксический сахар внутри переменной находится объект, описывающий цикл. И этот значение можно даже передавать в другие функции как аргумент.

Все циклы являются подпрограммами и автоматически захватывают переменные контекста (где цикл был определён). Но есть важное замечание – параллельный цикл loop обычные переменные захватывает по значению – в его потоке исполнения это будут копии переменных, хотя ссылки на объекты будут одинаковые) – изменение копии переменной не будет менять другую её копию (но изменения внутри объекта значения переменной будет единым).  Такое поведение не является единственно возможным, тут много нюансов по синхронизации значений – про это будет отдельно в части параллельного программирования.

Отвечу Вам так - все смотрят на свои (редко - чужие) проблемы со "своей колокольни"! И, когда Вы увидели как звукорежиссёр управлять звуком с планшета - так - лет так 30-35 назад (ещё со школьной скамьи), увлекаясь и программированием и сочинение музыки, чертыхался от музыкальных редакторов от Cakewalk, Cubase, Sound forge до только набирающих тогда популярность мультидорожечников, от Sony Acid (который нынче Vega) до... (уже не помню какие тогда уже были, вроде тоже Sound Forge и Audition - но он он тогда по-другому назывался). Нет - в те времена я тащился от Cakewalk и Acid, а затем и от Fruty Loops - но считал это всё слишком неудобным и примитивным! И именно тогда мне пришла в голову мысль - что писать музыку куда удобнее было бы текстом (а тогда среди программирования также были крайне популярны визуальные среды разработки как Delphi, Vusual C, Borland C++ builder, или, скажем, Dream viewer; даже азы данных в СУБД стремились рисовать в визуальных конструкторах)!

И я стал думать - как это всё лучше организовать - перебрав несколько отличных концепций! Считая себя 100; оригинальным новатором этой темы! Особенно на фоне того - как в XXI веке музыка стали сочинять генеративные AI сети - генерировать музыку текстом для них самое то!

И только спустя 30 лет - я узнал о том, что программирование музыки придумали на заре самого программирования 3-го поколения - т.к. в 70-х когда прошлого века! Кстати, там начинали, кажется, именно с графического программирования (сейчас не помню, на вскидку какой это был ЯП - поищу)! Но... ознакомившись с ними всеми... в т.ч. с недавнопояшившишься новым ЯП для создания музыки, базирующемся на JavaScript - счёл это всё примитивным и не удобным!

И сейчас проектирую свой собственный ЯП для создания музыки под рабочим названием "Orange". Это будет весь продвинутый ЯП - не только для композирования и аранжировки - но и для сложной звукорежиссуры и звуковой обработки, и даже аконкурирования с системами библиотек цифровых музыкальных инструментов - а-ка Kontakt (который я тоже счёл крайне примитивным) и реализации гипер-мультисемплирования! Но всё делает текстом (само собой работа с привычными для музыкантов и звукорежиссёров средствами ввода тоже подразумевается - даже с, условно с электрогитарами и аналоговыми источниками звука") - и всё через кодогенерацию (само собой информации - это цифровые RAW данные).

И не только статичное программирование - но и динамичное - когда музыка меняется прям в реальном времени!

И управление этим всем подразумевается не только специфической адаптированной структурой текстовых скриптов! Но и реализацией адаптированной студии IDE (для повышения удобства) - с продвинутой частью визуализации! И конечно же поддержкой плагинов и библиотек! И... само собой версионирование а-ля Git, и продвинутого текстового поиска (в т.ч. по регулярным выражениям), анализа, маккро/мето-программирования, кодогенерации и рефакторинга!

И да.... моя идея не в том, чтобы управлять этим с планшета - это достаточно сложная система (особенно при управлении в реальном времени) - для работы с которой могут потребоваться несколько мониторов и лаже клавиатур... не считая специфичного музыкального оборудования! Но... в относительно простых статичных случаях можно обойтись и, условно, рядовыми текстовыми процессорами с несколькими специальными плагинами для комфорта! Да и при желании - частично можно было бы и с планшета управлять!

Так что вот даже так - музыка - это тоже код - и его вполне себе можно писать программой! Как и всё в нашем мире - есть программный кол! Таков мой девиз!

А системы искусственного интеллекта тут станут отличными помощниками с их гибким восприятием и оперированием текстовыми данными!

Какие варианты?

Лично я считаю что тут не может быть какого-то приоритетного визуального представления - всё это очень специфично и индивидуально - то есть визуальный ЯП просто должен иметь несколько визуальных представлений (выбор между которыми будет зависеть от самого кода/задачи, от предпочтений разработчика, инструментальной базы IDE). И само собой, обязательно, должен иметь и текстовое представление! И да - так же будет прямая зависимость и от текстового синтаксиса и общей семантики такого ЯП. Здесь нет какого-то одного верного решения!

Но подчеркну - вряд такая визуализация как средство ввода алгоритма станет маломальский популярна - от того и развиваться она будет очень медленно!

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity