Часто задаваемые вопросы 🌀 Bulka

Часто задаваемые вопросы

Здесь собраны часто задаваемые вопросы и ответы на них. Обычно тема разбирается подробнее в разделе, ссылка на который есть в ответе.

Bulka, Strudel и Tidal — бесплатны?

Да — плата не взимается. Это коллективный проект с открытым исходным кодом, а музыка, которую вы с его помощью создаёте, принадлежит вам. Bulka — форк Strudel, поэтому, если у вас есть возможность, поддержите разовым или регулярным пожертвованием наш фонд на opencollective: он идёт на разработку и культурное развитие Strudel и других языков Uzu.

Хотя плата и не взимается, есть несколько оговорок, например:

  • исходный код должен оставаться свободным, то есть вы не можете распространять Strudel или Tidal в составе проектов с несовместимыми лицензиями — подробности см. в лицензии.
  • добавленные сообществом примеры и треки лицензируются отдельно, и их, например, нельзя использовать для обучения ИИ-моделей без разрешения.

Как попробовать самые свежие возможности?

Основной, стабильный сайт — bulka.app. Bulka основана на Strudel, а новейшие возможности в разработке появляются в апстриме: есть warm.strudel.cc, так называемый «warm strudel», где собраны последние наработки. В warm strudel могут быть исправления и функции, которых ещё нет на основном сайте, но он обычно менее стабилен и вряд ли подойдёт для важных выступлений.

Как вариант, можно запустить Strudel локально, чтобы попробовать новейшие возможности. Инструкции для разработчиков есть здесь.

Посмотреть последние изменения можно здесь — в виде «пул-реквестов».

Как записать или экспортировать звук?

Bulka — не цифровая звуковая рабочая станция (DAW) и работает не по тем принципам, что большинство традиционных аудиопрограмм. Тем не менее записать звук — и видео — из Bulka можно несколькими способами:

  • Воспользуйтесь вкладкой «export», чтобы отрендерить результат и скачать его как аудиофайл.
  • Захватите «сырой» стереосигнал, выходящий из браузера. Понадобится внешний аудиоредактор или DAW — Reaper, Audacity, Ardour и т. п.
  • Используйте альтернативный аудиодвижок SuperDirt. Подробнее об этом — на этой странице.
  • Захватите аудио/видеопоток программой для захвата, например OBS, которая создана для стриминга, но отлично подходит и для записи.
  • Ничего не записывайте, а просто снова напишите код на глазах у друзей.

Можно ли пользоваться Bulka в своём IDE?

Да, можно. Участники сообщества сделали экспериментальные режимы для нескольких IDE:

  • VS Code: Strudel VS — экспериментальный режим для Microsoft VSCode. Возрождённая версия TidalStrudel, которая больше не поддерживается.
  • nvim: strudel.nvim

Как использовать собственные сэмплы?

Загрузить свою коллекцию сэмплов можно несколькими способами. Одни удобны для быстрых экспериментов, другие — чтобы делиться коллекцией с другими музыкантами:

Можно ли создать новый проект на основе Strudel?

Strudel — свободное ПО с открытым исходным кодом (сама Bulka — как раз такой проект на его основе), и мы всегда рады, когда им пользуются, соблюдая следующие условия:

  • Пожалуйста, не используйте слово «strudel» в названии своего проекта (например, strudel2000 или foo-strudel), чтобы его не приняли за официальный проект Strudel. (Если вы хотите, чтобы он стал официальным проектом Strudel, свяжитесь с сообществом — например, в чате в Telegram.)
  • Пожалуйста, соблюдайте нашу лицензию AGPL: она, в частности, требует, чтобы вы делились исходным кодом Strudel (или ссылались на него), любыми внесёнными в него изменениями, а также исходным кодом остальной части вашего проекта, если он интегрируется со Strudel. Вы также обязаны сохранять уведомления об авторских правах Strudel в исходном коде и указывать уведомление об авторских правах Strudel в своём пользовательском интерфейсе. Это лишь краткое неофициальное изложение — полные подробности см. в тексте лицензии.

Также советуем связаться с сообществом, чтобы лучше понять наши цели и ценности.

Можно ли использовать Bulka с инструментами ИИ и LLM?

Вы вольны делать с Bulka что угодно в рамках свободной лицензии с открытым кодом AGPLv3. Но как сообщество мы заинтересованы в исследовании человеческого творчества. ИИ сейчас чрезмерно распиарен, в том числе людьми с весьма сомнительными мотивами. Многие в сообществе очень настороженно относятся к тому, что кто-то обучает модели на их композициях, в которые они вложили душу. Поэтому, пожалуйста, ведите обсуждения и задавайте вопросы про ИИ и LLM в специально отведённых для этого местах и с полным уважением к чужой работе.

Кроме того, инструменты вроде ChatGPT обычно дают неверные ответы. Пожалуйста, не просите сообщество исправлять эти ответы за вас — как правило, это будет бессмысленной тратой времени.

Живым, человеческим вопросам мы всегда рады!

Где скачать кучу паттернов, чтобы обучить свою LLM?

Нигде — такого места не существует. Подробнее о нашем отношении к ИИ/LLM см. выше.

Как работать офлайн?

Bulka прекрасно работает офлайн! Способов несколько — см. это объяснение.

Как менять темп? Как перевести BPM в cpm?

Bulka оперирует циклами, а не долями, но если задать определённое число долей на цикл, между ними можно переводить.

Например, если ваш темп задан в ударах в минуту и на цикл приходится 4 доли (скажем, трек в размере 4/4), можно написать setcpm(BPM/4), где BPM — число ударов в минуту.

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

Где посмотреть все функции?

Если открыть боковую панель на bulka.app (маленькая белая «<» справа), там есть вкладка «reference» со списком всех функций Bulka.

Где посмотреть все сэмплы и синтезаторы?

Если открыть боковую панель на bulka.app (маленькая белая «<» справа), там есть вкладка «sounds» со списком всех загруженных на данный момент драм-машин, сэмплов и синтезаторов.

Как пользоваться этим в точности как DAW?

У Bulka иные цели, чем у DAW, поэтому относиться к ней как к DAW, скорее всего, будет разочаровывающе. DAW заточены под предсказуемое выстраивание нот во времени, тогда как Bulka и похожие языки Uzu — под комбинирование и преобразование паттернов способами, которые бывает трудно предсказать.

Если хочется воспроизвести функциональность DAW в Bulka, придётся выделить операции, которые выполняет DAW (выстраивание последовательностей, повторение, применение фильтров и огибающих), и написать код, эквивалентный этим операциям. Например, в Bulka методы «arrange» и «pick» удобны для выстраивания паттернов во времени (см. вопрос о них далее в этом документе).

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

Почему бы всем просто не пользоваться DAW?

Простого ответа на этот вопрос нет. Вот несколько мыслей:

  • Инструменты лайв-кодинга, такие как Bulka, отлично подходят для импровизации с музыкой и визуалом на компьютере. DAW — ценные и надёжные помощники для других задач: продюсирования, мастеринга, сведения звука и не только. Использование одного инструмента не исключает использования другого — просто соберите себе набор инструментов.

  • Лайв-кодинг десятилетиями развивался как самостоятельная творческая практика. Например, лайв-кодеры любят показывать свои экраны, выступая перед публикой. Это важная часть того, чем они занимаются, и того, как они делятся своим занятием со всеми.

  • Код — это человеческий язык, он создан для того, чтобы его читали другие люди. Можно читать код и одновременно наслаждаться музыкой. У него есть смысл, ценность, а может, даже что-то поэтичное и важное! - Bulka — свободное ПО с открытым кодом: код можно изучать, переделывать, вносить в него свой вклад, если можете и хотите. Он не непрозрачен, и для многих это важно. Никакого чёрного ящика, тёмных абстракций, бизнес-модели, слежки за пользователями или скрытых функций. Искусству нужны открытые инструменты! - Не все лайв-кодеры чураются DAW. Многие пользуются ими постоянно — особенно когда это облегчает им жизнь ради… лайв-кодинга!

  • Код — такой же художественный материал, как и любой другой. В процессе создания музыки через код есть своя ценность. Если говорить шире, приятно решать творческие задачи с помощью языка программирования: творческое мышление, выстраивание собственных решений, DIY-подход к созданию музыки, неожиданные результаты работы алгоритмов, забавные человеческие ошибки и т. д.

  • В вашей DAW есть пианино и трубы — так почему люди продолжают играть на пианино или трубе? Считайте инструменты лайв-кодинга инструментами, на которых вы играете через акт программирования.

Как связать Bulka с моим любимым музыкальным софтом? Что с этим можно делать?

Bulka умеет отправлять MIDI и OSC — это протоколы для передачи музыкальной информации.

Другой музыкальный софт (или железо!) может слушать эти сообщения и обрабатывать их в меру своих возможностей.

Простой пример — отправить полученный в лайв-кодинге звук в DAW вроде Ardour по разным дорожкам, а затем использовать её для их сведения.

Ещё можно отправить MIDI из секвенированного паттерна в Musescore и получить нотную запись вашей лайв-кодинг-работы.

А можно отправлять MIDI на аппаратные синтезаторы, если вам нравится их звучание.

Как использовать это в моей закрытой (проприетарной) веб-игре или другом ПО?

Никак. Вам придётся распространять свою игру под свободной лицензией с открытым кодом, чтобы соблюсти лицензию AGPLv3, под которой распространяется Strudel.

Как проигрывать несколько паттернов одновременно?

С помощью оператора $ можно проигрывать сразу несколько паттернов:

$: s("bd*4").bank("tr707")
$: s("- sd").bank("tr909")

См. также stack

Можно ли заглушить паттерн?

С помощью дополнительного подчёркивания паттерн можно заглушить.

$: s("bd*4").bank("tr707")
_$: s("- sd").bank("tr909")

См. также hush

Как выстраивать аранжировку в Bulka с помощью mask?

В мини-нотации, используя операторы <> и !, можно попробовать что-то вроде:

.mask("<0!24 1!40>")

Это заглушает паттерн на 24 цикла и проигрывает его 40. Всего получится 64 цикла — число, кратное 2/4/8, что часто встречается в западной музыке.

Если каждый цикл — это такт, для начала можно написать такую маску для любого паттерна:

.mask("<0!16 0!16 0!16 0!16 0!16 0!10>")

Она глушит его целиком.

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

Если, применяя .mask() к разным паттернам, вы собьётесь со счёта, паттерны перестанут совпадать. С другой стороны, если делать это намеренно — это одна из тех вещей, которые можно считать сильной стороной tidalcycles и Strudel. Немного (контролируемой) интерференции — и всё становится живее и органичнее, на ваш собственный вкус. И вы вольны аранжировать циклами по 3, 6 или 9 тоже.

Чтобы менять всё сразу, можно попробовать all и when, например:

all(x=>x.when("<0!7 1>", x=>x.lpf(saw.range(200, 2000))))

Так каждые 8 циклов по всему будет проходить свип фильтра низких частот.

Как выстраивать аранжировку в Bulka с помощью arrange или pick?

Возьмём для примера Канон ре мажор Пахельбеля — в нём 4 голоса (виолончель и 3 скрипки) с повторяющимися паттернами, как видно по ссылке выше.

Следующий фрагмент задаёт паттерны как константы, которые затем можно использовать для разных голосов. arrange принимает несколько аргументов, каждый из которых — это число циклов и паттерн, который проигрывается это число циклов, обёрнутые в []. Если паттерн короче указанного числа, он повторяется.

const cello = note(
"<[d3 a2 b2 f#2] [g2 d3 g2 a2]>")
.color("grey").sound("gm_tremolo_strings:3")
const violin_p1 = note(
"<[f#5 e5 d5 c#5] [b4 a4 b4 c#5]>")
.color("blue")
const violin_p2 = note(
"<[d5 c#5 b4 a4] [ g4 f#4 g4 f#4]>")
.color("green")
const violin_p3 = note(
"<[d4 f#4 a4 g4 f#4 d4 f#4 e4] [d4 b3 d4 a4 g4 b4 a4 g4]>")
.color("purple")
const violin_p4 = note(
"<[f#4 d4 e4 c#5 d5 f#5 a5 a4] [b4 g4 a4 f#4 d4 d5 [d5@3 c#5]@2]>")
.color("red")

cello$: arrange(
[2, silence],
[18,cello])
violin1$: arrange(
[4,silence],
[2,violin_p1], [2,violin_p2],
[2,violin_p3], [2,violin_p4],
[2,violin_p1], [2,violin_p2],
[2,violin_p3], [2,violin_p4]
).sound("gm_tremolo_strings:0")
violin2$: arrange(
[6,silence], [2,violin_p1], 
[2,violin_p2], [2,violin_p3], 
[2,violin_p4], [2,violin_p1], 
[2,violin_p2], [2,violin_p3] 
).sound("gm_tremolo_strings:1")
violin3$: arrange(
[8,silence],
[2,violin_p1], [2,violin_p2],
[2,violin_p3], [2,violin_p4],
[2,violin_p1], [2,violin_p2]
).sound("gm_tremolo_strings:2")

  all(x => x.release(.2))

Как вариант, можно поместить разные паттерны скрипок в один массив (const violins = [violin_p1, violin_p2, violin_p3, violin_p4]) и использовать паттерн в качестве индекса, чтобы через pick выбирать n-й элемент этого массива. Это заменяет голоса, определённые выше. Здесь 0@2 означает, что первый элемент (то есть с индексом 0) проигрывается 2 цикла.

У pick подсветка лучше, чем у arrange:

const cello = note(
"<[d3 a2 b2 f#2] [g2 d3 g2 a2]>")
.color("grey").sound("gm_tremolo_strings:3")
const violin_p1 = note(
"<[f#5 e5 d5 c#5] [b4 a4 b4 c#5]>")
.color("blue")
const violin_p2 = note(
"<[d5 c#5 b4 a4] [ g4 f#4 g4 f#4]>")
.color("green")
const violin_p3 = note(
"<[d4 f#4 a4 g4 f#4 d4 f#4 e4] [d4 b3 d4 a4 g4 b4 a4 g4]>")
.color("purple")
const violin_p4 = note(
"<[f#4 d4 e4 c#5 d5 f#5 a5 a4] [b4 g4 a4 f#4 d4 d5 [d5@3 c#5]@2]>")
.color("red")

const violins = [violin_p1, violin_p2, violin_p3, violin_p4]

cello$: "<~@2 0@18>".pick([cello])
violin1$: "<~@4 0@2 1@2 2@2 3@2 0@2 1@2 2@2 3@2>".pick(violins)
.sound("gm_tremolo_strings:0")
violin2$: "<~@6 0@2 1@2 2@2 3@2 0@2 1@2 2@2>".pick(violins)
.sound("gm_tremolo_strings:1")
violin3$: "<~@8 0@2 1@2 2@2 3@2 0@2 1@2 >".pick(violins)
.sound("gm_tremolo_strings:2")
all(x => x.release(.2))

Метод pick также работает с json-объектами, у которых есть именованные элементы, что делает код читабельнее — см. здесь. pickRestart перезапускает паттерн при его выборе, что может иметь значение, если длительность индексов выбора не совпадает с выбираемыми паттернами — здесь это не так.

Попробуйте добавить .punchcard() после release(.2) для визуализации.

Я видел, как Switch Angel использует функции, которых нет в справочнике (например, trancegate). Как заставить их работать?

Методы вроде trancegate(), rlpf() и acidenv() сейчас не входят в число штатных методов паттернов Bulka.

Они входят в скрипт/prebake для Strudel, написанный Switch Angel и опубликованный здесь.

Инструкции по использованию этого скрипта есть там, в файле readme.md.

Есть ли разница между n и note?

Это не псевдонимы друг друга, в отличие от s и sound.

Метод note используется для указания конкретной ноты (либо по имени, например c или b2, либо по MIDI-номеру 69, например note("c3 e3 g3")).

А n — это способ обратиться к n-му индексу чего-либо. Этим «чем-либо» может быть гамма (например, n("0 2 4").scale("C:major")), а может быть и конкретная нота в аккорде (пример см. на https://bulka.app/recipes/recipes/#arpeggios).

Метод n можно использовать и для чего-то совершенно не связанного с нотами — в частности, для выбора n-го сэмпла из sample map: s("hh*8").bank("RolandTR909").n("0 1 2 3").


      n("<[0 1 2 3@3 -@2] [3 2 1 0@3 -@2] >")
      .scale("A:minor:pentatonic")
      .s("gm_acoustic_guitar_steel").n("<0 1 2 3>/2")

Обратите внимание: n — не единственный способ, которым функции используют индексы; некоторые вместо этого принимают пронумерованные паттерны.

Есть ли шпаргалка по всем символам?

Да!

'   marks start and end of strings, is different from "
"   marks start and end of single line patterns in mini notation, is different from '
`   marks start and end of patterns with line breaks in mini notation, is different from '
[]  used for patterns in mini notation, each item in it has the same length
<>  used for patterns, alternates between items each cycle
{}  historically used for polyrhythmic patterns. {a b c}%4 is the same as <a b c>*4.
@3  elongates the item by a factor of 3 (other numbers work too, even non-integer, but for numbers between 0 and 1 you need a leading zero like this: @0.5)
@   after an item: elongates the item once (multiple @ work too c @ @ is the same as c@3)
_   after an item: also elongates an item once (multiple _ work too c _ _ is the same as c@3), see below for a different usage.
.   this divides equal parts of a pattern and is called a foot. Can be used instead of [] like this: "1 6 7 8 . 2 . 3 . 4" is the same as "[1 6 7 8] 2 3 4"
-   silence
~   also silence
x   not silence (for the use in struct, any non-silence symbol works there)
b   decrease by one semitone, i.e. flat, works for steps of scales, note names (but not midi numbers) and chord names
s   increase by one semitone, i.e. sharp, works for steps of scales, note names (but not midi numbers) but not chord names
#   increase by one semitone, i.e. sharp works for steps of scales, note names (but not midi numbers) and chord names
#   also used in mondo notation
*3  play the sample or pattern at thrice the speed, fast(3)
!3  play the sample or pattern three times
/2  play the sample or pattern at half speed, slow(2)
?   play the pattern sometimes
|   once per cycle, choose randomly a pattern of those separated by i.e. chooseCycles()
,   play all items separated by it at the same time, i.e. stack()
:   is used to separate multiple parameters, such as adsr(".1:.1:.5:.2"), this is is an operator which creates a list of these objects.
$:  at the start of a line, defines a member of the stack. is the only stack name that should occur multiple names
_   before a stack name: mutes the stack, i.e. hush(), for example _$: s("bd"), see above for a different usage.