OSC
Open Sound Control (OSC) - это стандартный сетевой протокол, якобы разработанный для музыки, но на самом деле это всего лишь простой способ передачи чисел и других данных по сети. с OSC совместим ряд систем лайф-кодинга и других систем, включая цифровые звуковые рабочие станции (DAW), визуализаторы и микшеры.
по сути, единственная задача Tidal Cycles - отправлять шаблонные сообщения OSC, чаще всего в аудиофреймворк SuperDirt. настроить Tidal для отправки OSC в другую систему довольно просто. для этого нужно указать, куда следует отправлять сообщения (цель), а также структуру отправляемых данных OSC (форму или формат сообщения).
расширенное руководство
сначала, нужно определить цель:
метод планирования для oSchedule может быть одним из следующих:
- Live: заставляет Tidal планировать сообщения таким образом, чтобы они отправлялись в «правильное» время за вычетом значения
oLatency. это самый простой подход, который будет работать хорошо в большинстве случаев. - Pre BundleStamp: отправляет каждое OSC-сообщение, упакованное в «bundle» OSC с временной меткой пакета. Объединенные сообщения будут отправляться один раз в кадр пакетами, но с опережением времени (в соответствии со значением конфигурации
oLatency). Tidal не пытается отправлять их с «правильным» временем, вместо этого от целевой системы ожидается точное планирование. это требует больше работы для целевой системы, но потенциально точнее, чем описанный выше вариант, поскольку позволяет избежать потенциального джиттера сети/обработки. - Pre MessageStamp: аналогично
BundleStamp, описанному выше, но временная метка добавляется к самому OSC-сообщению, заполняя два целочисленных поля «sec» и «usec».мнеобходимо явно включить их в список аргументов вашего формата osc (см. пример далее).
определение структуры сообщения OSC
далее определите структуру сообщения OSC. для начала стоит уделить немного времени ознакомлению со спецификацией OSC. существует два способа структурировать сообщения OSC, отправляемые Tidal: список аргументов или пары «имя-значение».
подход со списком аргументов наиболее распространён. он выглядит следующим образом:
чтобы определить структуру OSC, сначала укажите OSC, затем «адресс паттерна» OSC, в данном случае «/play». затем по порядку перечисляются аргументы сообщения. каждый аргумент представлен в виде «tuple», содержащего имя параметра и его значение по умолчанию.
в приведенном выше примере первый параметр «s» не имеет значения по умолчанию, на что указывает ключевое слово Nothing. это означает, что если параметр s не указан шаблоном, сообщение OSC не будет отправлено.
для остальных аргументов в примере значения по умолчанию указываются ключевым словом Just, за которым следует тип аргумента и его значение по умолчанию. VS задаёт значение по умолчанию как строку, VF — как число с плавающей точкой, а VI — как целое число. Другие доступные типы: VB для логических значений true/false (которые преобразуются в сообщении в целые числа 1/0) и VX для двоичных «blobs». если один или несколько из этих аргументов со значениями по умолчанию отсутствуют в паттерне, сообщение всё равно будет отправлено с этими значениями по умолчанию.
если вы используете Pre MessageStamp, вам необходимо добавить параметры сообщения sec и usec для их отправки:
помимо sec и usec, Tidal всегда заполняет ещё три параметра, если они есть: cps (циклы в секунду), cycle (начало события в циклах) и delta (продолжительность события в секундах). поэтому добавьте их, если хотите, чтобы эта информация была выведены:
названия параметров
вместо указания списка аргументов, как указано выше, вы можете указать названные параметры следующим образом:
при таком определении все параметры, указанные в паттерне, будут выведены. вместо фиксированных позиций в сообщении, как в случае со списком аргументов, параметры будут располагаться в произвольном порядке, но в виде пар «имя-значение». т.е., перед каждым параметром будет стоять дополнительный строковый параметр, задающий его имя. rак видно из примера, указан список «required» параметров - если все параметры, указанные в этом списке, не присутствуют в событии, заданном паттерне, оно не будет отправлено.
определение дополнительных параметров
многие параметры определены в Sound.Tidal.Params и доступны в сеансе Tidal. если вы хотите отправить параметры, которые ещё не определены, можно определить их самостоятельно. например, для параметра «intensity», использованного выше, необходимо задать следующее:
сопоставление структур сообщений с целями
последнее, что необходимо определить, - это соответствие между целями и принимаемыми ими структурами сообщений OSC. в данном случае есть только одна цель, принимающая только один тип сообщений OSC, поэтому всё просто:
стриминг и отправка паттернов
затем вы можете запустить 'stream' для преобразования шаблонов в OSC следующим образом:
и начать отправлять паттерн типо:
шорткаты
это очень похоже на определение d1, d2 и т. д. в BootTidal.hs.
вывод
несколько целей и сообщений
можно организовать несколько сообщений OSC и отправлять их в несколько выводов из одного и того же «stream». например, чтобы стримить данные, отправленные как указанному выше получателю, так и в SuperDirt, можно сделать следующее:
bd будет отправлен как в target, так и в superdirtTarget.
сложные цели с несколькими форматами сообщений
некоторые OSC-цели более сложны, поддерживают несколько форматов OSC и также указывают данные в «address pattern» OSC. возьмём в качестве примера ASCII-Simple-Video-Synth. вот спецификация Tidal для него:
это программное обеспечение принимает ряд паттернов адресов, некоторые из которых включают цвет, к которому обращается адрес. чтобы сделать этот цвет паттерном, ему присваивается имя в фигурных скобках: "{asccolour}". затем это имя можно задать с помощью параметра 'ascColour' в паттерне Tidal.
при назначении нескольких форматов сообщений OSC потоку рекомендуется убедиться, что каждый формат имеет хотя бы один уникальный аргумент, отличный от значения по умолчанию. это гарантирует, что сообщения будут отправляться только при задании в шаблоне аргументов, отличных от значения по умолчанию. в противном случае для каждого события, заданного шаблоном, будут отправляться все форматы.
контроль ввода
сonfiguration
если вам нужен ввод только MIDI в Tidal, проверьте страницу MIDI в боковой панели.
после установки и настройки новейшей версии Tidal по умолчанию будет автоматически прослушивать внешние управляющие сообщения по сетевому протоколу OSC на локальном хосте (127.0.0.1), порт 6010. это можно настроить - например, если вы предпочитаете прослушивать все сетевые интерфейсы и порт 6060, вы можете изменить конфигурацию (BootTidal.hs) следующим образом:
если вы предпочитаете полностью отключить прослушивание элементов управления, используйте следующее:
сообщение OSC, ожидаемое Tidal, содержит путь /ctrl и два значения: ключ и значение. Ключ может быть строкой или целым числом (tidal автоматически преобразует целое число в строку). Значение может быть строкой, целым числом или числом с плавающей точкой. Например, сообщение OSC /ctrl sf hello 0.4 - это сообщение об установке элемента управления hello в значение 0.4. в этом примере sf — это тег типа OSC. он указывает, что первое значение - строка (s), а второе — строка (f)loat.
затем вы можете использовать этот элемент управления в следующем паттерне:
cF - это то, что используется для управления числами с плавающей точкой. второй параметр, 1, - это значение по умолчанию, если Tidal ещё не получил этот параметр. также существует cS для строк и cI для целых чисел. для значений времени (например, для использования в качестве первого параметра быстро/медленно) используйте cT. для отношений добавьте cR. если вы хотите получать целые паттерны (записанные в виде строки мини-нотации), используйте cP.
дебаггинг
один из способов отладки OSC - использовать анализатор пакетов, например WireShark. чтобы отфильтровать всё, кроме пакетов OSC, можно ввести osc в поле фильтра. если щелкнуть OSC network packet и раскрыть поля, можно увидеть корректно отформатированное представление сообщения OSC.
OSC SuperDirt API
вот как выглядит сообщение-триггер OSC от Tidal Cycles в SuperDirt
Рассмотрим этот паттерн: sound "bd" # speed 2. вот какое сообщение вида OSC генерируется:
он состоит из одного сообщения, упакованного в пакет, который содержит временную метку, указывающую, когда событие должно быть запущено. поскольку для цели OSC для Superdirt параметр oSchedule установлен на Pre BundleStamp, сообщения будут отправляться пакетами, заранее, и получатель (например, Superdirt) должен точно запланировать их отправку.
сообщение внутри пакета имеет путь /dirt/play и содержит переменное количество пар «имя-значение». можно видеть пары s bd и «speed 2», но Tidal добавляет несколько дополнительных пар. текущий cps, положение события в циклах (с момента запуска Tidal), delta или длительность события в секундах и номер orbit.
контроллеры воспроизведения
в некст разделе опишу доступные функции управления воспроизведением, и приведу пример использования MIDI-управления в SuperCollider для управления несколькими паттернами.
открытые функции управления звуком
вы можете отключить или включить паттерн, отправив сообщение OSC пророутив его с /mute или /unmute и аргументом, указывающим паттерн. как и в обычном коде tidal, это может быть число (для d1, d2 и т.д.) или строка (для именованных паттернов).
:сообщение OSC /mute 3 отключит d3.
контроль над всеми паттернами
можно управлять всеми паттернами воспроизведения, используя пути OSC /muteAll, /unmuteAll, /unsoloAll и, конечно же, /hush. все эти сообщения не имеют аргументов.
вот полный пример SuperCollider для сопоставления кнопок MIDI-контроллера с паттернами, чтобы сообщения о включении/выключении нот от кнопок включали приглушение паттерна или запускали другие эффекты.
в этом примере используются MIDI-кнопки для нот C4 (значение MIDI 60), D4 и E4 для отключения звука на нотах d1, d2 и d3. ноты F4, G4 и A4 используются для включения solo на нотах d1, d2 и d3. также используется нота C5 для отключения звука всех нот, а нота D5 - для включения звука всех нот.
отредактируйте первый раздел кода (Mapping MIDI Controller), чтобы определить, какие кнопки контроллера вы хотите использовать для управления паттернами. остальной код должен работать с заданными вами назначениями и не требует редактирования, но может быть полезен для начала в проекте.
начните с уже запущенных Tidal (например, внутри Atom) и SuperDirt, а затем запустите следующий блок кода в SuperCollider:
// Оцените блок ниже, чтобы начать сопоставление MIDI -> OSC.
(
var mutes, solos, muteAll, unmuteAll, unsoloAll, hush;
var playbackControl, playbackState;
var osc;
/* — MIDI Controller Mapping ---------------------------- */
// Отредактируйте этот раздел, чтобы настроить свой MIDI-контроллер.
// "mutes" и "solos" представляют собой словарь номеров MIDI -> идентификаторы айди
// В этом кейсе, C4, D4 & E4 заглушаются паттерны d1, d2 & d3
mutes = Dictionary[
60 -> 1,
62 -> 2,
64 -> 3
];
// В этом кейсе, F4, G4 & A4 соло паттерны d1, d2 & d3
solos = Dictionary[
65 -> 1,
67 -> 2,
69 -> 3
];
// Тут MIDI note обращается к "muteAll"
// В этом кейсе, оно ставится C5
muteAll = 72;
// Тут MIDI обращается к "unmuteAll"
// В этом кейсе, оно ставится D5
unmuteAll = 74;
// Тут MIDI обращается к "unsoloAll"
// В этом кейсе, оно не используется
unsoloAll = nil;
// Тут MIDI обращается к "hush"
// В этом кейсе, оно не используется
hush = nil;
/* ------------------------------------------------------- */
union(mutes.values.asSet, solos.values.asSet).do({
arg item;
playbackState.put(item, Dictionary[\mute -> false, \solo -> false]);
});
osc = NetAddr.new("127.0.0.1", 6010);
MIDIClient.init;
MIDIIn.connectAll;
playbackControl = MIDIFunc.noteOn({ |val, num, chan, src|
var patID, patState;
if (mutes.at(num) !== nil, {
patID = mutes.at(num);
patState = playbackState.at(patID);
if (patState.trueAt(\mute), {
osc.sendMsg("/unmute", patID);
patState.put(\mute, false);
}, {
osc.sendMsg("/mute", patID);
patState.put(\mute, true);
});
});
if (solos.at(num) !== nil, {
patID = solos.at(num);
patState = playbackState.at(patID);
if (patState.trueAt(\solo), {
osc.sendMsg("/unsolo", patID);
patState.put(\solo, false);
}, {
osc.sendMsg("/solo", patID);
patState.put(\solo, true);
});
});
if (muteAll == num, {
osc.sendMsg("/muteAll");
playbackState.do({
arg patState;
patState.put(\mute, true);
});
});
if (unmuteAll == num, {
osc.sendMsg("/unmuteAll");
playbackState.do({
arg patState;
patState.put(\mute, false);
});
});
if (unsoloAll == num, {
osc.sendMsg("/unsoloAll");
playbackState.do({
arg patState;
patState.put(\solo, false);
});
});
if (hush == num, {
osc.sendMsg("/hush");
});
});
if (~stopMidiMuteControl != nil, {
~stopMidiMuteControl.value;
});
~stopMidiMuteControl = {
playbackControl.free;
};
)
// оцените строку ниже, чтобы остановить это.
~stopMidiMuteControl.value;