1 - Добро пожаловать, друг :-)

Добро пожаловать в мир Sonic Pi. Надеюсь, что вам настолько же не терпится начать создавать безумные звуки, насколько мне не терпится показать вам, как это делается. Это будет веселое путешествие, в котором ты узнаешь много нового о музыке, синтезе, программировании и производительности программ, композиции и о много другом.

Постойте, как же я мог быть таким невоспитанным! Позвольте мне представиться - я Sam Aaron - тот самый парень, который создал Sonic Pi. Меня можно найти по имени @samaaron в Твиттере, и я был бы очень счастлив обменяться там с вами приветствиями. Вам, возможно, также было бы интересно узнать больше о моих лайв-кодинг выступлениях, где я сам пишу на Sonic Pi перед зрителями.

Если у вас появятся какие-либо мысли или идеи о том, как улучшить Sonic Pi, то, пожалуйста, передайте мне их. Обратная связь - это очень полезно. Никогда не знаешь, вдруг ваша идея может вырасти в нечто действительно большое!

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

Наконец, наблюдать за чужим кодингом в режиме реального времени это отличный способ учиться. Я регулярно вещаю в прямом эфире на http://youtube.com/samaaron, так что не забывайте появляться там, чтобы поприветствовать всех и задать все свои многочисленные вопросы :-)

Что ж, давайте начнём…


1.1 - Лайв-кодинг

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

Расширьте своё сознание

Прежде, чем мы перейдем к деталям того, как Sonic Pi работает, я бы хотел поделиться с вами личным опытом того, что значит “лайв-кодинг”. Не переживайте, если вы пока чего-то из этого (или ничего) не понимаете. Просто держитесь крепче за стул и получайте удовольствие…

Живой цикл

Для начала скопируйте следующий код в пустой буфер:

live_loop :flibble do
  sample :bd_haus, rate: 1
  sleep 0.5
end

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

Убедитесь, что барабаны по-прежнему звучат Измените значение sleep с 0.5 на значение побольше, например 1. Еще раз нажмите кнопку Выполнить Можно заметить, что темп барабанов изменился. И, наконец, запомните этот момент - это первый, и, наверняка, не последний ваш лайв-кодинг на Sonic Pi…

Ладно, это было довольно просто. Давайте добавим что-нибудь ещё в наш микс. Сверху от sample :bd_haus добавьте строку sample :ambi_choir, rate: 0.3. Ваш код должен выглядеть так:

live_loop :flibble do
  sample :ambi_choir, rate: 0.3
  sample :bd_haus, rate: 1
  sleep 1
end

Время поиграться. Поменяйте частоту. Что происходит, когда вы используете высокие, малые, или отрицательные значения? Посмотрите, что произойдет, когда вы поменяете значение rate: для сэмпла :ambi_choir на незначительную величину (например до 0.29). Что произойдет, если вы выберите очень маленькое значение для sleep? Проверьте, получится ли заставить его играть так быстро, что ваш компьютер прекратит свою работу и выдаст ошибку из-за того, что он не справится с этим (если это всё же произошло, то просто выберите значение побольше для sleep и снова нажмите Выполнить).

Попробуйте закомментировать одну из строк sample. Для этого добавьте # в ее начало:

live_loop :flibble do
  sample :ambi_choir, rate: 0.3
#  sample :bd_haus, rate: 1
  sleep 1
end

Обратите внимание, компьютер проигнорировал её, поэтому мы её не слышим. Это называется комментарием. В Sonic Pi мы можем использовать комментарии, чтобы убирать или добавлять штуки в микс.

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

Попробуйте изменить синее значение rate:, чтобы услышать, как изменится звук. Попробуйте изменить время для sleep и вы услышите, как оба цикла повторяются по кругу с разными темпами. Попробуйте раскомментировать строку примера (удалите знак #) и наслаждайтесь звуками гитары на заднем плане. Попробуйте поменять синее значение любого из mix: на число в промежутке от 0 (что соответствует исключению из общего микса) до 1 (полностью сочетается).

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

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

live_loop :guit do
  with_fx :echo, mix: 0.3, phase: 0.25 do
    sample :guit_em9, rate: 0.5
  end
#  sample :guit_em9, rate: -0.5
  sleep 8
end
live_loop :boom do
  with_fx :reverb, room: 1 do
    sample :bd_boom, amp: 10, rate: 1
  end
  sleep 8
end

Продолжайте играть и экспериментировать до тех пор, пока вам не станет любопытно, как это на самом деле работает, и вы не начнёте думать, что ещё можно сделать. Тогда вы будете готовы к чтению оставшейся части этого учебника.

Так чего же вы ждёте…


1.2 - Интерфейс Sonic Pi

У Sonic Pi очень простой интерфейс для кодирования музыки. Мы потратим совсем немного времени на его изучение.

Интерфейс Sonic Pi

A - Управление воспроизведением B - Управление редактором C - Помощь и справка D - Редактор кода E - Панель настроек F - Просмотр сообщений G - Справочная система F - Просмотр сообщений

A. Управление Воспроизведением

Эти розовые кнопки - главные элементы для запуска и остановки звука. Есть кнопка Выполнить для запуска кода из редактора, Остановить для остановки всего выполняющегося кода, Сохранить для сохранения кода в файл и Запись для создания записи того, что вы слышите в звуковой файл в формате WAV.

B. Управление Редактором

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

C. Помощь И Справка

Данные синие кнопки дают вам доступ к информации и настройкам. Кнопка Информация открывает справочное окно, в котором находится информация о самом Sonic Pi - главной команде разработчиков, истории, помощниках и сообществе. Кнопка Помощь включает систему помощи (F), а кнопка Параметры включает панель настроек, где можно менять некоторые базовые параметры приложения.

D. Редактор Кода

Это область, в которой вы будете писать свой код и сочинять/исполнять музыку. Это простой текстовый редактор, где можно написать код, удалить его, вырезать, вставить и так далее. Представьте, что это простая версия Word или Google Docs. Редактор автоматически раскрасит слова в зависимости от их значения в коде. Поначалу это может казаться странным, но скоро вы увидите, насколько это помогает. К примеру, глядя на что-то синее ты будешь знать, что это число.

E. Панель Настроек

В Sonic Pi есть множество настраиваемых параметров. Доступ к ним можно получить, нажав кнопку Параметры в наборе кнопок “Помощь и справка”. Она переключит видимость панели настроек, в которой представлено множество вариантов, доступных для изменения. Примерами являются режим моно, инвертированное стерео, включение вывода детальных сообщений, ползунок громкости и выбор аудиовыхода Raspberry Pi.

F. Просмотр Сообщений

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

G. Справочная Система

Наконец, одна из самых важных частей интерфейса Sonic Pi - это справочная система, которую можно увидеть в нижней части окна приложения. Её можно выводить и прятать, нажимая синюю кнопку Помощь. В справочную систему входит помощь и информация обо все аспектах Sonic Pi, включая этот учебник, список доступных синтезаторов, сэмплов, эффектов, примеры и полный перечень всех функций Sonic Pi для кодирования музыки.

H. Область видимости

В области видимости можно наблюдать за воспроизводимым звуком. Вы с лёгкостью заметите, что пилообразная волна действительно напоминает пилу, а простейшие сигналы выглядят как синусоидальные волны. Вы также можете увидеть разницу между громкими и тихими звуками, глядя на размер выводимых линий. Существует три области видимости, с которыми можно повозиться - стандартная (комбинация левого и правого каналов), стерео (каждый канал обособлен) и, наконец, область видимости фигур Лиссажу, которая показывает отношения фаз левого и правого каналов, а также позволяет создавать красивые изображения при помощи звука. (https://ru.wikipedia.org/wiki/%D0%A4%D0%B8%D0%B3%D1%83%D1%80%D1%8B_%D0%9B%D0%B8%D1%81%D1%81%D0%B0%D0%B6%D1%83).


1.3 - Обучение через игру

Sonic Pi стремится воодушевить вас изучать компьютеры и музыку через игру и экспериментирование. Самое главное, чтобы вам было весело, и тогда, прежде, чем вы это поймёте, вы случайно научитесь кодировать, сочинять и исполнять.

Ошибок не существует

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

Начните с простого

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

Но не забывайте делиться своими достижениями с другими!


2 - Синтезаторы

Довольно вступлений. Пора экспериментировать со звуком.

В этой главе мы будем рассматривать основы запуска и управления синтами. Синт - это сокращение от синтезатор, что само по себе означает модное словечко для чего-то, что издает звук. Обычно, синты довольно сложны в использовании (особенно аналоговые, в которых много соединений проводами и модулей). Sonic Pi предоставляет такие же возможности, но очень простым и доступным способом.

Не обманывайтесь кажущейся простотой интерфейса Sonic Pi. С ним, если захочется, можно добраться до самых потаенных глубин очень сложной манипуляции звуком. Итак, придержите свои шляпы…


2.1 - Ваши Первые Звуки

Посмотрите на этот код:

play 70

Отсюда всё берет свое начало. Давайте, скопируйте и вставьте его в окно кода вверху приложения (большое белое пространство под кнопкой Выполнить). Затем, нажмите Выполнить

Звук!

Впечатляет. Нажмите заново. Снова. И снова…

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

play 75

Слышите разницу? Попробуйте число поменьше:

play 60

Вот, меньшее число дает более низкий звук, а большее число дает более высокий звук. Прямо как на пианино. Клавиши в нижней части пианино (слева) звучат ниже, а клавиши в верхней части пианино (правая сторона) - выше. На самом деле, числа имеют прямое отношение к клавишам пианино. play 47 означает “сыграй сорок седьмую ноту на пианино”. То есть play 48 - это на одну ноту выше (следующая клавиша справа). Так сложилось, что нота “До” четвертой октавы имеет номер 60. Попробуйте сыграть её: play 60.

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

Аккорды

Играть одну ноту весьма забавно, но играть несколько нот сразу ещё лучше. Попробуйте:

play 72
play 75
play 79

Звучит джазово! Итак, когда вы пишете play много раз, все ноты играют в одно и то же время. Попробуйте самостоятельно - какие числа звучат вместе хорошо? Какие звучат ужасно? Экспериментируйте, исследуйте и вы выясните это сами.

Мелодия

Играть ноты и аккорды весело, а как насчет мелодий? Что, если вы хотите играть одну ноту за другой, а не все вместе? Что же, это легко. Просто нужно вставить sleep между ними:

play 72
sleep 1
play 75
sleep 1
play 79

Как мило, маленькое арпеджио. Что же значит 1 в sleep 1? Ну, это означает продолжительность паузы. Вообще-то, это означает задержку на время одной доли такта, но, пока что будем считать, что это значит спать одну секунду. А вдруг мы захотим, чтобы наше арпеджио стало немного быстрее? Тогда надо указать значение для паузы покороче. Как насчет половины, то есть 0.5:

play 72
sleep 0.5
play 75
sleep 0.5
play 79

Можно заметить: мелодия стала играть быстрее. Попробуйте сами поменять время и используйте разные продолжительности и ноты.

Интересно попробовать значения между нотами, например play 52.3 или play 52.63. Совсем не нужно держаться стандартных целых чисел. Развлекайтесь и получайте удовольствие.

Обычные названия нот

Для тех, кто уже немного знаком с нотной грамотой (не волнуйтесь, если вы не знаете о чём речь - для веселья она вам не нужна), может быть интересно записать мелодию, используя названия нот, такие как “До” (C) и “Фа-диез” (F#), а не числа. Sonic Pi предусмотрел это. Можно делать так:

play :C
sleep 0.5
play :D
sleep 0.5
play :E

Просто помните, что перед нотой должно стоять двоеточие :. Тогда она станет розовой. Ещё можно указывать октаву, добавляя её номер после названия ноты:

play :C3
sleep 0.5
play :D3
sleep 0.5
play :E4

Если хотите сделать ноту на полтона выше, поставьте s после её имени, то есть play :Fs3. А когда нужен бемоль, обозначьте это при помощи b, то есть play :Eb3.

А теперь, дайте себе волю, и как следует повеселитесь, придумывая собственные мелодии.


2.2 - Параметры: Amp и Pan

В дополнение к возможности выбрать какую ноту играть или какой сэмпл включать, Sonic Pi предоставляет целый арсенал настроек для создания и управления звуком. Многие из этих настроек описаны в этом учебнике, и есть обширная документация по каждой из них в справочной системе. Пока что, мы познакомимся всего лишь с двумя самыми полезными: амплитуда и панорамирование. Вначале посмотрим, что же такое эти настройки на самом деле.

Настройки

Sonic Pi поддерживает регулировки для своих синтезаторов. Они представляют собой элементы управления, для управления которыми вы передаёте атрибуты функции play. Эти атрибуты изменяют и контролируют выводимый звук. У каждого синтезатора свой собственный набор регулировок для тонкой настройки звука. Но, есть общие наборы регулировок, одинаковые для разных синтезаторов, такие как amp:, pan: и параметры огибающей (обсуждается в другой главе).

Каждая регулировка состоит из двух частей: название регулировки (элемента управления) и её значение (деление, на которое вы хотите установить ручку регулировки). Например, может быть настройка с названием cheese:, а ее желаемым значением будет 1.

Настройки передаются вызовам play после запятой , далее следует название, например amp: (не забудьте двоеточие :), а потом ее значение. Например:

play 50, cheese: 1

(Замечание: cheese: - не настоящая настройка, мы просто используем ее для примера).

Можно передавать много настроек, разделяя их запятыми:

play 50, cheese: 1, beans: 0.5

Порядок регулировок не важен, так что следующий пример ничем не отличается:

play 50, beans: 0.5, cheese: 1

Регуляторы, о которых синтезатор не знает, попросту пропускаются (такие как cheese и beans - очевидно нелепые названия для настроек!)

Если вы случайно укажите одну и ту же настройку с разными значениями, то победит последнее. Например, значением beans: будет 2, а не 0.5:

play 50, beans: 0.5, cheese: 3, eggs: 0.1, beans: 2

Множество вещей в Sonic Pi гибко регулируемо, потратьте немного времени на изучение того, как использовать их, и будете “настроены”! Давайте позабавимся с нашим первым регулятором - amp:.

Амплитуда

Амплитуда - компьютерное представление громкости звука. Высокая амплитуда даёт громкий звук, а низкая амплитуда даёт тихий звук. Так же, как Sonic Pi использует числа для обозначения нот, числами обозначается и амплитуда. При значении амплитуды 0 получается тишина (ничего не будет слышно), а амплитуда 1 - это нормальная громкость. Можно, также, увеличить амплитуду до 2, 10 или 100. Но стоит отметить, что когда общая амплитуда становится слишком высокой, то Sonic Pi включает так называемый компрессор, чтобы сжать ее и не дать звуку сделаться слишком громким для ваших ушей. Часто это может делать звук непонятным и странным. Так что постарайтесь использовать значения амплитуды в промежутке от 0 до 0.5, чтобы избегать компрессии.

Усиление

Чтобы изменить амплитуду звука, можно воспользоваться настройкой amp:. Например, передай 0.5, чтобы играть на половинной громкости:

play 60, amp: 0.5

Чтобы играть на удвоенной громкости, передай 2:

play 60, amp: 2

Регулировка amp: изменяет тот вызов play, с которым она идёт вместе. Поэтому в следующем примере первый вызов произойдет на половинной громкости, а второй вернется к значению по умолчанию (1):

play 60, amp: 0.5
sleep 0.5
play 65

Конечно, можно задавать разные значения amp: для каждого вызова play:

play 50, amp: 0.1
sleep 0.25
play 55, amp: 0.2
sleep 0.25
play 57, amp: 0.4
sleep 0.25
play 62, amp: 1

Панорамирование

Еще одной забавной настройкой, которую стоит использовать, является pan:. Она контролирует смещение звука в стереобазе. Смещение звука влево означает, что вы услышите его из левого динамика, а смещение вправо - что он будет выходить из правого динамика. Мы используем -1 для полного смещения влево, 0 для центра, а 1 - для полного смещения вправо в стереобазе. Безусловно, мы можем указывать любое значение в промежутке между -1 и 1 для точного позиционирования нашего звука.

Давай получим звук из левого динамика:

play 60, pan: -1

Теперь, повторим его из правого:

play 60, pan: 1

Наконец, выведем его снова по центру (позиция по умолчанию):

play 60, pan: 0

Попробуйте сами сделать что-нибудь весёлое, меняя амплитуду и панорамирование ваших звуков!


2.3 - Переключаем Синтезаторы

До сего момента мы, по большей части, развлекались с простыми звуками. Однако, простые тона уже начали вас утомлять. Разве это все, что Sonic Pi может предложить? Определенно, у лайвкодинга есть больше возможностей, чем просто гудение. Да, есть. И в этой главе мы познакомимся с захватывающим набором звуков, доступных в Sonic Pi.

Синтезаторы

В Sonic Pi есть ряд инструментов, называемых синтами, что есть сокращение от термина “синтезатор”. В то время, как сэмплы представляют собой предварительно записанные звуки, синты могут генерировать новые звуки в зависимости от того, как вы ими управляете (это мы изучим далее в этом учебнике). Синты Sonic Pi - очень могущественные и яркие инструменты. Играть с ними очень весело. Вначале, давайте научимся выбирать какой синтезатор мы будем использовать.

Жужжащие Пилы И Профеты

Пилообразная волна - забавный звук. Давайте попробуем его:

use_synth :saw
play 38
sleep 0.25
play 50
sleep 0.25
play 62
sleep 0.25

Попробуйте другой - prophet:

use_synth :prophet
play 38
sleep 0.25
play 50
sleep 0.25
play 62
sleep 0.25

Как насчет смешивания обоих. Сначала один, а потом другой:

use_synth :saw
play 38
sleep 0.25
play 50
sleep 0.25
use_synth :prophet
play 57
sleep 0.25

А теперь вместе:

use_synth :tb303
play 38
sleep 0.25
use_synth :dsaw
play 50
sleep 0.25
use_synth :prophet
play 57
sleep 0.25

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

Изучаем синтезаторы

Чтобы увидеть, какие синты есть в Sonic Pi, загляните во вкладку Synths в меню слева внизу (рядом с Fx). Там их больше 20 на выбор. Вот несколько моих любимых:

:prophet :dsaw :fm :tb303 :pulse

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


2.4 - Управление длительностью при помощи огибающих

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

В качестве простого и мощного способа управления длительностью наших звуков Sonic Pi предоставляет нам понятие ADSR амплитудной огибающей (Дальше в этой главе - ADSR). Амплитудная огибающая предлагает контроль по двум параметрам:

управление длительностью звука управление амплитудой звука

Длительность

Длительность - это отрезок времени, в течении которого продолжается звук. Большая продолжительность означает, что вы слышите звук дольше. Все звуки в Sonic Pi имеют контролируемую продолжительность. Поэтому, управляя огибающей, вы управляете продолжительностью.

Амплитуда

ADSR-огибающая задает не только длительность, но еще и позволяет тонкую настройку амплитуды сигнала. Все слышимые звуки начинаются и заканчиваются тишиной, а в промежутке находится изменяющаяся часть звука. Огибающие дают вам возможность плавно изменять и удерживать амплитуду разных частей звука. Это похоже на написание инструкции о том, в каком порядке понижать и повышать громкость гитарного усилителя. Например, вы можете попросить “начать с тишины, потом медленно повышать до полной громкости, держать её на этой отметке короткое время, а потом быстро вернуть на тихий уровень”. Sonic Pi дает возможность запрограммировать такое поведение очень точно с помощью огибающих.

Итак, как мы видели раньше, амплитуда 0 - это тишина, а амплитуда 1 - нормальная громкость.

Теперь, пришла пора взглянуть на каждую часть огибающей по порядку.

Фаза затухания (Release Phase)

Единственная часть огибающей, установленная по умолчанию, - это время затухания. Оно обозначает продолжительность времени, за которое звук полностью исчезнет. У всех синтов затухание настроено на 1, что значит по умолчанию звук длится в течение одной доли такта (что при темпе по умолчанию в 60 составляет 1 секунду):

play 70

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

play 70, release: 1

Заметьте, что звук получился точно таким же (длился ровно 1 секунду). Очень легко изменять длительность, изменяя значение регулировки release::

play 60, release: 2

Можно cделать звучание синта крайне непродолжительным, указав очень маленькое значение затухания:

play 60, release: 0.2

Длительность затухания звука называется фазой затухания (release phase) и, по умолчанию, представляет из себя линейный переход (т.e. прямую линию). Следующая диаграмма иллюстрирует такой переход:

release envelope

Вертикальная линия на левом краю диаграммы показывает, что звук начинается при амплитуде 0, но сразу после этого начинает выводиться на полной громкости (это фаза атаки, которую мы рассмотрим далее). Как только достигнута максимальная амплитуда, она спадает прямой линией до нуля, занимая время, указанное для release:. Чем продолжительнее время затухания, тем дольше будет стихать синт

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

Фаза атаки (Attack Phase)

По умолчанию фаза атаки равна 0 для всех синтов, что значит их амплитуда меняется от 0 до 1 мгновенно. Поэтому начальный звук синта похож на удар. Но вам может понадобиться плавно вывести звук. Этого можно добиться при помощи регулировки attack:. Попробуйте плавно извлекать какие-нибудь звуки:

play 60, attack: 2
sleep 3
play 65, attack: 0.5

Можно пользоваться несколькими регуляторами в одно и то же время. Например, для короткой атаки и долгого затухания попробуйте:

play 60, attack: 0.7, release: 4

Эта краткая атака и длинное затухание иллюстрируются следующей диаграммой:

огибающая атаки и затухания

Конечно, можно и наоборот. Попробуйте долгую атаку и короткое затухание:

play 60, attack: 4, release: 0.7

огибающая с долгой атакой и быстрым стиханием

В конце-концов, для кратких звуков можно сделать короткими и атаку, и затухание.

play 60, attack: 0.5, release: 0.5

огибающая с короткой атакой и коротким стиханием

Фаза задержки (Sustain Phase)

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

play 60, attack: 0.3, sustain: 1, release: 1

ASR огибающая

Задержка полезна для важных звуков, которые вы хотели бы полностью выделить при сведении, прежде чем они перейдут в необязательную фазу затухания. Конечно же, совершенно допустимо устанавливать и attack: и release: в 0 и попросту использовать задержку, чтобы не иметь абсолютно никакого нарастания и затухания у звука. Но стоит заметить, что затухание 0 может давать щелчки в аудио, и часто лучше все-таки указывать очень маленькое значение, такое как 0.2.

Фаза спада (Decay Phase)

Для дополнительного уровня контроля можно указать время спада. Это фаза огибающей, которая помещается между фазами атаки и задержки. Она задает участок времени, когда амплитуда будет падать от уровня attack_level: до decay_level: (если явно не установить его, то он будет совпадать с sustain_level:). По умолчанию, уровни decay: и attack: установлена в 0, а уровень затухания в 1, поэтому надо указывать время спада, чтобы оно имело какой-либо эффект:

play 60, attack: 0.1, attack_level: 1, decay: 0.2, sustain_level: 0.4, sustain: 1, release: 0.5

ADSR огибающая

Уровень спада (Decay Level)

Еще один, последний трюк, состоит в том, что хотя настройки decay_level: изначально совпадают со значением sustain_level:, вы можете явно присвоить им разные значения для полного контроля над огибающей. Это позволяет создавать огибающие наподобие следующей:

play 60, attack: 0.1, attack_level: 1, decay: 0.2, decay_level: 0.3, sustain: 1, sustain_level: 0.4, release: 0.5

ASR огибающая

Можно, к тому же, устанавливать decay_level: выше, чем sustain_level::

play 60, attack: 0.1, attack_level: 0.1, decay: 0.2, decay_level: 1, sustain: 0.5, sustain_level: 0.8, release: 1.5

ASR огибающая

ADSR-огибающие

Итак, подведем итог. ADSR-огибающие в Sonic Pi состоят из следующих фаз:

Attack - атака - время, за которое амплитуда растет от 0 до attack_level, Decay - спад - время, когда амплитуда переходит от attack_level до decay_level, Sustain - задержка - время изменения амплитуды с decay_level на sustain_level, Release - затухание - время падения амплитуда с sustain_level до 0

Важно помнить, что длительность звука - это сумма продолжительностей всех фаз. Соответственно, такой звук будет длиться 0.5 + 1 + 2 + 0.5 = 4 доли такта:

play 60, attack: 0.5, attack_level: 1, decay: 1, sustain_level: 0.4, sustain: 2, release: 0.5

Теперь пришло время поиграть с огибающими, добавляя их в свои звуки…


3 - Сэмплы

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

В Sonic Pi можно делать много забавных вещей с сэмплами. Помимо 90 встроенных сэмплов со свободной лицензией, программа даёт вам работать и играть с собственными. Перейдем же к делу..


3.1 - Воспроизведение cэмплов

Простые звуки - это только начало. Кое-что весьма весёлое - это проигрывание сэмплов. Попробуйте:

sample :ambi_lunar_land

Sonic Pi включает множество сэмплов. Их можно использовать так же, как вы используете команду play. Чтобы проиграть несколько сэмплов и нот, просто запишите их друг под другом:

play 36
play 48
sample :ambi_lunar_land
sample :ambi_drone

Если их нужно разделить во времени, используйте команду sleep:

sample :ambi_lunar_land
sleep 1
play 48
sleep 0.5
play 36
sample :ambi_drone
sleep 1
play 36

Обратите внимание, что Sonic Pi не ждёт, пока закончится один звук, прежде чем начать играть следующий. Команда sleep всего лишь описывает разделение моментов, когда звуки начинаются. За счет этого их можно легко совмещать, создавая интересные эффекты наложения. Далее в этом учебнике мы рассмотрим управление длительностью звуков при помощи огибающих.

Знакомимся с сэмплами

Есть два способа познакомиться с набором сэмплов, включённых в Sonic Pi. Во-первых, вы можете воспользоваться системой помощи. Щелкните Samples в левом нижнем меню, выберите желаемую категорию, и после этого вы увидите список доступных звуков.

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

:ambi_ :bass_ :elec_ :perc_ :guit_ :drum_ :misc_ :bd_

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


3.2 - Параметры сэмплов: Амплитуда и Панорамирование

Также, как и с синтами, мы можем легко контролировать наши сэмплы при помощи параметров. Механизм передачи параметров сэмплам точно такой же. Вспомним наших друзей amp: и pan:.

Усиливаем сэмплы

Можно менять амплитуду сэмплов точно так же, как и синтов:

sample :ambi_lunar_land, amp: 0.5

Панорамируем сэмплы

Ещё один параметр сэмплов, доступный для использования - pan:. Например, вот так мы бы сыграли амен-брейк в левом канале, а потом на середине проиграли бы его снова, но в правом канале:

sample :loop_amen, pan: -1
sleep 0.877
sample :loop_amen, pan: 1

Обратите внимание, что 0.877 - это половина продолжительности сэмпла в секундах.

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


3.3 - Растягиваем сэмплы

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

Представление сэмпла

Сэмплы - это предварительно записанные звуки в виде чисел, описывающих, как колеблется мембрана динамика, чтобы воспроизвести звук. Мембрана движется внутрь и наружу, так что числами всего лишь нужно задать насколько втянутой или выпуклой она должна быть в каждый момент времени. Для правдоподобного воспроизведения звука, обычно сэмпл хранит много тысяч чисел для каждой секунды! Sonic Pi берет список этих чисел и передаёт их с нужной скоростью, чтобы двигать мембраной динамика наружу и внутрь так, чтобы получился нужный звук. Но можно неплохо поразвлечься, меняя звук за счет изменения скорости, с которой числа идут в динамик.

Меняем скорость

Попробуем проверить это на одном из природных звуков: :ambi_choir. Чтобы воспроизвести его с обычной скоростью, можно передавать команде sample настройку rate::

sample :ambi_choir, rate: 1

Этот пример воспроизводит сэмпл с нормальной скоростью (1). Пока что ничего особенного. Однако, мы можем свободно изменить это число на что-то другое. Как насчет 0.5:

sample :ambi_choir, rate: 0.5

Ого! Это что такое происходит? Ну, две вещи. Во-первых - сэмпл воспроизводится в два раза дольше. Во-вторых - звук стал ниже на октаву. Давайте разберём это более детально.

Давайте растянем

Сэмпл, который забавно растягивать и сжимать, - это амен-брейк. С нормальной скоростью мы можем легко себе представить его в составе трека драм-н-бейс:

sample :loop_amen

Но, задавая скорость, можно переключать и жанры. Попробуй половинное значение, чтобы получить старый-добрый хип-хоп:

sample :loop_amen, rate: 0.5

А если ускориться, то мы попадаем на территорию джангл:

sample :loop_amen, rate: 1.5

А теперь гвоздь программы - проверим, что произойдет, если мы зададим отрицательную скорость:

sample :loop_amen, rate: -1

Ничего себе! Он стал играть задом-наперёд! Попробуйте воспроизвести разные сэмплы на разных скоростях. Попробуйте очень быстрые скорости. Попробуйте несуразно медленные. Узнайте, какие интересные звуки можно создать.

Простое объяснение скорости сэмпла

Полезная мысленная аналогия сэмплам - пружина. Темп воспроизведения - это как сжатие и растягивание пружины. Если воспроизводить сэмпл со скоростью 2, то вы будете сжимать пружину до половины ее нормальной длины. Следовательно, воспроизведение сэмпла займёт половину времени, ведь он стал короче. Если играть сэмпл на половине обычной скорости, то вы растягиваете пружину в два раза. Поэтому сэмпл длится в два раза дольше, ведь он стал длиннее. Чем больше вы сдавливаете, тем короче он получается (скорость выше), а чем больше вы растягиваете, тем длиннее он становится (скорость ниже).

Сжатие пружины увеличивает ее плотность (число витков на сантиметр) - это совпадает с тем, почему сэмпл начинает звучать с высокой частотой. Растягивание пружины снижает ее плотность, и это похоже на то, как частота звука понижается.

Математика, объясняющая скорость сэмпла

(Эта часть приведена для тех, кому интересны детали. Всем остальным её можно пропустить…)

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

граф сэмплирования

Вы могли видеть похожие картинки до этого. Они называются волновой формой сэмпла. Это просто числовой график. Обычно подобные формы включают 44100 точек с данными на каждую секунду времени (из-за теоремы сэмплирования Найквиста-Шэннона). Итак, если сэмпл длится две секунды, то форма волны будет состоять из 88200 чисел, которые мы бы передавали динамической головке со скоростью 44100 значения в секунду. Конечно, мы могли бы удвоить темп передачи, тогда воспроизведение бы заняло всего одну секунду. А еще мы могли бы проигрывать его в два раза медленнее, что означало бы 22500 точек в секунду, и весь процесс бы занял четыре секунды.

На длительность сэмпла влияет скорость воспроизведения:

Удвоение темпа уполовинивает время воспроизведения, Сокращение темпа вдвое удваивает время воспроизведения, Если задать темп, равный четверти от нормального, то время учетверится, Если скорость воспроизведения равна 1/10 от нормальной, то время воспроизведения увеличится в 10 раз.

Мы можем представить это отношение формулой:

новая_длительность_сэмпла = (1 / скорость) * длительность_сэмпла 

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

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


3.4 - Огибающие сэмплов

Также возможно изменять длительность и амплитуду сэмпла, используя ADSR-огибающие. Но это работает немного по-другому, чем ADSR-огибающие синтов. Огибающие сэмплов позволяют уменьшать громкость и длительность, но не увеличивать их. Сэмпл остановится, либо когда его воспроизведение завершено, либо когда огибающая закончилась - в зависимости от того, что произошло раньше. Так что если задать очень долгое затухание, то звучание сэмпла не удлинится.

Огибающие амен

Вернемся к нашему верному другу амен-брейку:

sample :loop_amen

Без дополнительных регулировок мы слышим полный сэмпл на полной громкости. Если мы хотим его постепенно усиливать в течение одной секунды, то можно задейстовать параметр attack::

sample :loop_amen, attack: 1

Для более краткого нарастания, выберите значение атаки поменьше:

sample :loop_amen, attack: 0.3

Автоудержание

Отличие поведения ADSR-огибающей сэмпла от стандартной огибающей синта лежит в значении сустейна. В обычной огибающей синта сустейн сам по себе устанавливался в 0, если мы не указывали его вручную. Для сэмплов оно также настраивается “автомагически”, и равняется времени, необходимому для завершения сэмпла. Поэтому мы слышим полный сэмпл, когда никаких настроек не задаётся. Если бы значения атаки, спада, удержания и затухания равнялись 0, то мы бы и намёка на сэмпл не услышали. Так что Sonic Pi вычисляет сколько времени длится сэмпл, вычитает длительности атаки, спада и затухания, и присваивает результат фазе удержания. Если атака, спад и затухание вместе дают значение больше продолжительности сэмпла, то удержание устанавливается в 0.

Постепенное затухание

Для изучения этого явления рассмотрим наш амен-брейк более детально. Если спросить Sonic Pi сколько длится сэмпл:

print sample_duration :loop_amen

Он напечатает 1.753310657596372, что означает собственное время звучания сэмпла в секундах. Просто для удобства округлим его до 1.75. Затем, если установить затухание в 0.75, произойдет кое-что неожиданное:

sample :loop_amen, release: 0.75

Первая секунда сэмпла будет воспроизводиться на полной громкости, а потом последует постепенное затухание в течение 0.75 секунды. Это и есть пример автоудержания в действии. Само по себе затухание всегда работает с конца сэмпла. Если бы наш сэмпл длился 10.75 секунды, то первые 10 секунд он бы играл с полной амплитудой, а потом затухал оставшиеся 0.75 секунды.

Запомните: по умолчанию release: отсчитывает затухание от конца сэмпла.

Постепенное усиление и затухание

Можно использовать вместе attack:, release: и поведение автоудержания для постепенного усиления и затухания при воспроизведении сэмпла:

sample :loop_amen, attack: 0.75, release: 0.75

Так как полная продолжительность равняется 1.75 секунды, а наши фазы атаки и затухания суммарно дают 1.5 секунды, то удержание автоматически устанавливается в 0.25 секунды. Так мы легко можем плавно усиливать и гасить сэмпл.

Точное удержание

Вернуться к нормальному режиму ADSR-огибающей довольно просто: достаточно вручную задать 0 для sustain::

sample :loop_amen, sustain: 0, release: 0.75

После этого сэмпл звучит в общей сложности всего лишь 0.75 секунды. При обычных нулевых значениях attack: и decay: громкость сэмпла сразу же достигает полного значения, удерживается там в течение 0 секунд, а потом за 0.75 секунды полностью затихает во время фазы затухания.

Тарелки

Хороший эффект от данного поведения получается, если мы хотим превратить продолжительные сэмплы в более короткие. Это даст им более отрывистое ударное звучание. Рассмотрим сэмпл :drum_cymbal_open:

sample :drum_cymbal_open

Можно слышать, что тарелка продолжает звенеть некоторое время. С помощью огибающей можно сделать её более резкой:

sample :drum_cymbal_open, attack: 0.01, sustain: 0, release: 0.1

А еще можно изображать удар по тарелке и его глушение за счет увеличения периода удержания:

sample :drum_cymbal_open, attack: 0.01, sustain: 0.3, release: 0.1

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


3.5 - Фрагментированные сэмплы

Этот раздел станет заключительным в нашем исследовании проигрывателя сэмплов Sonic Pi. Давайте кратко повторим уже изученное. Пока мы только разбирались в том, как включать сэмплы:

sample :loop_amen

Потом мы рассмотрели то, как мы можем менять темп сэмплов, например для воспроизведения их на половинной скорости:

sample :loop_amen, rate: 0.5

Затем, мы увидели, как можно постепенно усиливать сэмпл (совместим это с уменьшённой вдвое скоростью):

sample :loop_amen, rate: 0.5, attack: 1

Еще мы узнали как сделать начало сэмпла более резким за счет задания sustain: явного значения и установки коротких атаки и затухания:

sample :loop_amen, rate: 2, attack: 0.01, sustain: 0, release: 0.35

Однако, вам бы понравилось, если бы не надо было всякий раз начинать сэмпл с начала? Разве не здорово было бы, если бы можно было играть сэмпл не до конца?

Выбираем точку старта

Возможно выбрать произвольную начальную точку для сэмпла, задавая ее в качестве значения в диапазоне от 0 до 1. 0 - начало сэмпла, 1 - его конец, а 0.5 - это середина. Попробуем сыграть только вторую половину амен-брейка:

sample :loop_amen, start: 0.5

А как насчет последней четверти:

sample :loop_amen, start: 0.75

Выбираем точку окончания

Похожим образом разрешается выбирать конечную точку сэмпла как значение между 0 и 1. Прервем амен-брейк на середине:

sample :loop_amen, finish: 0.5

Указываем начало и конец

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

sample :loop_amen, start: 0.4, finish: 0.6

Что случится, если установить старт после финиша?

sample :loop_amen, start: 0.6, finish: 0.4

Круто! Он играет задом-наперед!

Соединяем со скоростью

Можно объединить эту новую способность играть выбранные сегменты аудио с нашим знакомым rate:. Например, таким образом воспроизведется очень маленькая секция из середины амен-брейка очень медленно:

sample :loop_amen, start: 0.5, finish: 0.7, rate: 0.2

Соединяем с огибающими

Наконец, все это возможно соединить с известными нами ADSR-огибающими, чтобы получить интересный результат:

sample :loop_amen, start: 0.5, finish: 0.8, rate: -0.2, attack: 0.3, release: 1

Попробуйте также комбинировать сэмплы, не ограничивайте свою фантазию…


3.6 - Внешние сэмплы

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

Портативность

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

С другой стороны, если начинать использовать собственные записанные сэмплы, то такая переносимость теряется. Это происходит из-за того, что для воспроизведения твоей музыки другим людям не только нужен твой код, но также и сэмплы. Этот факт ограничивает других в их возможностях изменять и экспериментировать с твоими работами. Конечно, знание этого не должно тебя останавливать от использования собственных сэмплов. Просто помни об этом.

Локальные сэмплы

Так как все-таки проиграть любой WAV, AIFF или FLAC с твоего компьютера? Все что нужно, это передать путь к его файлу команде sample:

# Raspberry Pi, Mac, Linux
sample "/Users/sam/Desktop/my-sound.wav"
# Windows
sample "C:/Users/sam/Desktop/my-sound.wav"

Sonic Pi автоматически загрузит и воспроизведет сэмпл. Вместе с этим можно передавать все стандартные параметры, которые ты привык использовать с sample:

# Raspberry Pi, Mac, Linux
sample "/Users/sam/Desktop/my-sound.wav", rate: 0.5, amp: 0.3
# Windows
sample "C:/Users/sam/Desktop/my-sound.wav", rate: 0.5, amp: 0.3

3.7 - Наборы сэмплов

Замечание: этот раздел руководства рассказывает о расширенных возможностях работы с большими директориями ваших собственных сэмплов. Рекомендуем ознакомится с ним в тех случаях, если вы загрузили или купили набор сэмплов и хотите использовать их в Sonic Pi

Вы можете смело пропустить этот раздел, если вам хватает встроенных сэмплов

Работая с большими папками внешних сэмплов, вы можете ощутить неудобства при набирании полного пути к каждому отдельному сэмплу.

Например, предположим, что на вашем компьютере у вас есть папка:

/path/to/my/samples/

Когда мы посмотрим в директорию, то увидим следующие сэмплы:

100_A#_melody1.wav 100_A#_melody2.wav 100_A#_melody3.wav 120_A#_melody4.wav 120_Bb_guit1.wav 120_Bb_piano1.wav

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

sample "/path/to/my/samples/120_Bb_piano1.wav"

Если мы хотим проиграть гитарный сэмпл, мы также можем использовать его полный путь:

sample "/path/to/my/samples/120_Bb_guit.wav"

Тем не менее, оба этих вызова требуют от нас знания имён сэмплов внутри нашей директории. Что если мы просто хотим быстро прослушать каждый сэмпл по очереди?

Индексирование наборов сэмплов

Если мы хотим проиграть первый сэмпл в директории, мы просто должны указать имя директории в sample и установить индекс в значение 0 следующим образом:

sample "/path/to/my/samples/", 0

Мы также можем создать ярлык (shortcut) нашей директории, используя переменную:

samps = "/path/to/my/samples/"
sample samps, 0

Теперь, если мы хотим проиграть второй сэмпл в директории, нам нужно прибавить 1 к нашему индексу:

samps = "/path/to/my/samples/"
sample samps, 1

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

Фильтрация наборов сэмплов

Обычно, индексирования достаточно, но иногда нам необходимо сортировать и организовывать сэмплы. К счастью, большинство наборов сэмплов структурированы. Давайте ещё раз взглянем на имена сэмплов в нашей директории:

100_A#_melody1.wav 100_A#_melody2.wav 100_A#_melody3.wav 120_A#_melody4.wav 120_Bb_guit1.wav 120_Bb_piano1.wav

Заметьте, что в их именах содержится некоторая информация. Во-первых, у нас есть BMP сэмпла (удары в секунду) в начале имени. Таким образом, сэмпл пианино имеет BPM равный 120, а первые три мелодии - 100 BPM. Имена сэмплов также содержат тональность. Таким образом, гитарный сэмпл имеет тон Bb а мелодии A#. Эта информация очень полезна для сведения этих сэмплов в нашем коде. Например, мы знаем, что сэмпл пианино содержит 120 BPM и тональность Bb.

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

samps = "/path/to/my/samples/"
sample samps, "120"

Первое совпадение будет проиграно. Если мы хотим, чтобы заиграло второе совпадение, нам нужно просто изменить индекс:

samps = "/path/to/my/samples/"
sample samps, "120", 1

Более того, мы можем использовать несколько фильтров сразу. Например, если мы хотим сэмпл, имя которого содержит подстроки “120” и “A#”, мы можем легко добиться этого следующим кодом:

samps = "/path/to/my/samples/"
sample samps, "120", "A#"

Наконец, мы по-прежнему можем добавлять свойства к вызову нашего сэмпла:

samps = "/path/to/my/samples/"
sample samps, "120", "Bb", 1, lpf: 70, amp: 2

Источники

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

“/path/to/samples” - строка, представляющая валидный путь к директории “/path/to/samples/foo.wav” - строка, представляющая валидный путь к сэмплу

Функция sample сначала собирает все источники и использует их для создания большого списка совпадений. Этот список создается, сначала добавляя все допустимые пути, а затем добавляя все допустимые файлы .flac, .aif, .aiff, .wav, .wave, содержащиеся в каталогах.

Например, посмотрите на следующий код:

samps = "/path/to/my/samples/"
samps2 = "/path/to/my/samples2/"
path = "/path/to/my/samples3/foo.wav"
sample samps, samps2, path, 0

Например, здесь мы комбинируем сэмплы из двух каталогов и добавляем конкретный сэмпл. Если "/path/to/my/samples/"содержит 3 сэмпла и "/path/to/my/samples2/"содержит 12, в сумме мы получим 16 потенциальных сэмплов на индексирование и фильтрацию (3 + 12 + 1).

По умолчанию в список совпадений попадают только файлы примеров в каталоге. Иногда у вас может быть несколько вложенных папок с сэмплами, которые вы хотите искать и фильтровать внутри. Поэтому вы можете выполнить рекурсивный поиск всех образцов во всех подпапках конкретной папки, добавив ** в конец пути:

samps = "/path/to/nested/samples/**"
sample samps, 0

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

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

Фильтры

Как только вы получили список совпадений, вы можете использовать следующие фильтры, чтобы уменьшить выборку:

"foo" Строки будут фильтроваться при появлении подстроки в имени файла (минус путь к каталогу и расширение). /fo[oO]/Регулярное выражение будет фильтровать соответствие имени файла шаблону (минус путь к каталогу и расширение). :foo- Ключевое слово будет фильтровать совпадения по тому, является ли ключевое слово прямым соответствием имени файла (минус путь к каталогу и расширение). lambda{|a| ... } - Процесс с одним аргументом будет обрабатываться как фильтр списка совпадений или функция-генератор. Он принимает текущий и должен вернуть новый список совпадений (список действительных путей к сэмплам). 1 - Числа будут выбирать совпадение с этим индексом (зацикливаться, если необходимо).

Например, мы можем отфильтровать все образцы в каталоге, содержащем строку "foo", и воспроизвести первый совпадающий образец на половинной скорости:

sample "/path/to/samples", "foo", rate: 0.5

См. Справку по sample для множества подробных примеров использования. Обратите внимание, что порядок фильтров соблюдается.

Composites

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

sample "/path/to/dir", "100", "C#"
sample ["/path/to/dir", "100", "C#"]
sample "/path/to/dir", ["100", "C#"]
sample ["/path/to/dir", ["100", ["C#"]]]

Подведение итогов

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


4 - Рандомизация

Отличный способ сделать вашу музыку несколько более интересной - это использовать случайные числа. В Sonic Pi есть замечательная функция добавления случайности в музыку, но прежде, чем мы начнем, нужно усвоить неприятную истину: в Sonic Pi случайности не являются по-настоящему случайными. Что же, спрашивается, это значит? Давайте разберёмся.

Повторяемость

Очень полезная случайная функция называется rrand. Она вернет случайное значение в диапазоне двух чисел - минимума и максимума (rrand это сокращение от “ранжированный генератор случайных чисел”). Попробуем проиграть какую-нибудь случайную ноту:

play rrand(50, 95)

Ого, получился неожиданный звук. Он воспроизвел ноту 83.7527. Прекрасная случайная нота в промежутке между 50 и 95. Постойте-ка, я только что точно предсказал случайную ноту, которая и у вас получилась? Что-то тут нечисто. Попробуем снова. Что? Он снова выбрал 83.7527? Это явно не случайно!

Правда в том, что выбор не случаен, он псевдо-случаен. Sonic Pi выдаёт повторяющиеся псевдо-случайные числа, чтобы быть уверенным, что музыка, которую вы создаёте на своей машине, будет звучать точно так же на всех других машинах. Это очень полезное свойство, даже если вы добавляете совсем немного случайности в свои композиции.

Конечно же, в конкретном музыкальном произведении, если бы 83.7527 “случайно” выбиралась каждый раз, это было бы не очень интересно. Но все-таки этого не происходит. Попробуйте следующее:

loop do
  play rrand(50, 95)
  sleep 0.5
end 

Ура! Наконец-то это звучит случайно. В течение одного запуска, последующие вызовы функции генератора случайных чисел будут возвращать случайные значения. Однако, следующий прогон создаст точно такую же последовательность случайных чисел и будет звучать идентично. Это как если бы код Sonic Pi всякий раз возвращался назад во времени в тот момент, когда нажимается кнопка Run. Это День Сурка для музыкального синтеза!

Колокола с привидениями

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

loop do
  sample :perc_bell, rate: (rrand 0.125, 1.5)
  sleep rrand(0.2, 2)
end

Случайный Срез

Еще один забавный пример случайности - управление частотой среза фильтра синта случайным образом. Отличный синт, на котором можно это попробовать, - это эмулятор :tb303:

use_synth :tb303
loop do
  play 50, release: 0.1, cutoff: rrand(60, 120)
  sleep 0.125
end

Начальные Точки Случайных Чисел

Что, если вам не нравится последовательность случайных чисел, сгенерированная Sonic Pi? Ну, очень легко выбрать другую начальную точку, если использовать use_random_seed.Начальная точка по умолчанию равна 0, так что для другого случайного результата выбирайте другую точку!

Сравните следующее:

5.times do
  play rrand(50, 100)
  sleep 0.5
end

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

use_random_seed 40
5.times do
  play rrand(50, 100)
  sleep 0.5
end

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

Давайте познакомимся с некоторыми другими случайными функциями.

choose

Очень часто приходится выбирать вещь случайным образом из списка. Например, мне бы могло захотеться проиграть одну ноту из набора: 60, 65 и 72. Я могу достичь этого с помощью функции choose, которая позволяет мне выбрать предмет из списка. Вначале, надо добавить мои числа в список, что достигается заключением их в квадратные скобки и разделением запятыми: [60, 65, 72]. Затем, надо просто передать их choose:

choose([60, 65, 72])

Послушаем, на что это похоже:

loop do
  play choose([60, 65, 72])
  sleep 1
end

rrand

Мы уже видели rrand, но пробежимся по ней вновь. Она возвращает случайную величину в промежутке двух чисел, не включая границы. Это значит, что никогда не будет выбрано самое большое или самое маленькое значение - всегда нечто между ними. Число всегда будет дробным, то есть не целым. Примеры дробных чисел, возвращаемых rrand(20, 110):

87.5054931640625 86.05255126953125 61.77825927734375

rrand_i

Время от времени вам может понадобится получить целое случайное число, а не дробное. Тут-то и придёт на помощь rrand_i. Она работает так же, как rrand, но включает граничные значения (то есть она может выбирать минимальные и максимальные числа). Примеры, полученные из rrand_i(20, 110):

88 86 62

rand

Вернет случайное дробное число от 0 (включительно), до максимального указанного вами. По умолчанию числа лежат в промежутке от 0 до 1. Такая функция полезна для выбора случайного усиления:

loop do
  play 60, amp: rand
  sleep 0.25
end

rand_i

Относится к rrand_i, как rrand_i к rrand. То есть она выбирает случайное целое число в диапазоне от 0 до максимального значение, которое вы укажете.

dice

Иногда нужно изобразить бросание игральных костей - это частный случай rrand_i, когда минимальное значение всегда 1. В вызов dice требуется передать количество сторон кости. Стандартная игральная кость имеет 6 сторон, так что dice(6) будет работать очень похожим образом - возвращать 1, 2, 3, 4, 5 или 6. Однако, прямо как в ролевых играх в стиле фэнтези, пользу может принести и кость с 4 сторонами, и с 12 сторонами или с 20 сторонами. Может даже потребуется кость со 120 гранями!

one_in

Наконец, может потребоваться сымитировать вероятность выпадания наибольшей из граней игральной кости, например, 6 для обычной кости. Функция one_in возвращает истину с вероятностью один к числу сторон кости. Следовательно, для one_in(6) шанс получить истину составит 1 к 5. В остальных 5 из 6 случаев вернётся ложь. Истинные и ложные значения очень полезны для логических выражений if, которые мы рассмотрим в одной из следующих глав нашего учебника.

А теперь, пришла пора добавить немного неразберихи в вашу музыку за счет случайностей!


5 - Программирование структур

После того, как мы изучили основы создания звука при помощи play и sample, и сочинения простых мелодий и ритмов, расставляя задержки между ними, вам могло стать интересно, а что же ещё мир программирования может нам предложить…

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

Ограничимся лишь основами…


5.1 - Блоки

Структура, которая будет попадаться вам в Sonic Pi особенно часто, - это блок. Блоки позволяют нам делать разные полезные вещи с большими участками кода. Например, при помощи параметров синтов и сэмплов мы могли изменять что-то, что происходило в одной строке. Однако, иногда нам нужно сделать что-то значительное, что касается множества строк кода. Например, мы можем решить играть их по кругу, добавить эффект реверберации всего к одному из пяти повторов и так далее. Рассмотрим следующий код:

play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62

Чтобы сделать что-то с куском кода, нам надо сказать Sonic Pi, где этот блок кода начинается, и где он заканчивается. Для его начала мы используем do, а для обозначения конца - end. Пример:

do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Все же он ещё не является завершённым и не будет работать (попробуйте и получите сообщение об ошибке), так как мы не сообщили Sonic Pi, что мы хотим делать с этим блоком *do/end. Этого можно добиться, если написать некий специальный код перед do. Далее мы увидим много примеров этих специальных кусочков кода. Пока лишь важно понять, что оборачивание вашего кода в do и end сообщает Sonic Pi, что вы хочтите сделать что-то особенное с этим блоком.


5.2 - Итерации и циклы

До этого момента мы потратили немало времени, пробуя разные звуки, которые можно производить внутри блоков play и sample. Мы также изучили как разносить эти звуки во времени, используя sleep.

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

Повторение

Доводилось ли вам писать код, который вы бы хотели повторить несколько раз? Например, у вас могло получиться нечто подобное:

play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25

Что, если мы захотим повторить это три раза? Что ж, можно было бы выбрать простейший путь и попросту продублировать код трижды:

play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25

Этот код просто огромен! Как быть, если нам захочется поменять сэмпл на :elec_plip? Придется найти все места с изначальным :elec_blup и переписать их. Что ещё важнее, каким образом повторить кусок кода 50 раз или 1000? Это было бы и впрямь целая куча кода. Гигантское число строк пришлось бы изменять для каждой правки.

Итерации

На самом деле сделать повторение кода должно быть не сложнее, чем сказать сделай это три раза. В общем-то, так оно и есть. Помните ли вы о нашем старом знакомом, блоке кода? Его можно использовать для отметки начала и конца кода, который необходимо повторить три раза. Мы добавим специальную конструкцию 3.times. Так что, вместо записи делай это три раза, мы напишем 3.times do. Это не очень сложно. Просто не забывайте указать end в конце кода, который надо повторить:

3.times do
  play 50
  sleep 0.5
  sample :elec_blup
  sleep 0.5
  play 62
  sleep 0.25
end

Ну, разве это не лучше дублирования? Много красивых структур может быть создано с использованием этой инструкции:

4.times do
  play 50
  sleep 0.5
end
8.times do
  play 55, release: 0.2
  sleep 0.25
end
4.times do
  play 50
  sleep 0.5
end

Вложенные Итерации

Еще можно поместить итерации внутрь других итераций и получить интересный рисунок. Например:

4.times do
  sample :drum_heavy_kick
  2.times do
    sample :elec_blip2, rate: 2
    sleep 0.25
  end
  sample :elec_snare
  4.times do
    sample :drum_tom_mid_soft
    sleep 0.125
  end
end

Цикличность

Если потребуется повторить что-то много раз, придётся указывать очень большие значения, такие как 1000.times do. В этом случае, наверное, лучше попросить Sonic Pi повторять код до бесконечности (или, как минимум, до того момента, пока вы не нажмёте кнопку “Остановить”!). Давайте зациклим амен-брейк:

loop do
  sample :loop_amen
  sleep sample_duration :loop_amen
end

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

loop do
  play 50
  sleep 1
end
sample :drum_cymbal_open

Теперь, пришла пора создать интересные структуры из вашего кода при помощи итераций и циклов!


5.3 - Условные выражения

Часто, помимо игры произвольных нот (см. предыдущую главу), вы также будете хотеть принимать произвольные решения и выполнять либо одну, либо другую ветку кода, в зависимости от результата. Например, вы можете решить случайным образом сыграть звук барабана или звук тарелки. Мы можем достичь этого при помощи выражения if.

Подбрасывание монетки

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

loop do
  if one_in(2)
    sample :drum_heavy_kick
  else
    sample :drum_cymbal_closed
  end
  sleep 0.5
end

Отметим, что выражение if состоит из трёх частей:

Вопрос, на который нужно получить ответ Первый вариант исполняемого кода (выбирается, если ответ на вопрос “Да”) Второй вариант исполняемого кода (если ответ “Нет”)

Обычно, в программных языках ответ “Да” представлен термином true (истина), а false (ложь) означает “Нет”. То есть нам нужно задать вопрос, который даёт true или false ответ. Это именно то, что делает one_in.

Обратите внимание, что первый вариант действия размещается между if и else, а второй вариант находится между else и end. Как и в блоках do/end, допускается многострочный код. Например:

loop do
  if one_in(2)
    sample :drum_heavy_kick
    sleep 0.5
  else
    sample :drum_cymbal_closed
    sleep 0.25
  end
end

В этом случае мы добавляем задержку в течение разного количества времени в зависимости от сделанного выбора.

Простое Условие

Иногда вам требуется условно выполнить всего лишь одну строчку кода. Это возможно, если написать if и сразу же ваш вопрос. Например:

use_synth :dsaw
loop do
  play 50, amp: 0.3, release: 2
  play 53, amp: 0.3, release: 2 if one_in(2)
  play 57, amp: 0.3, release: 2 if one_in(3)
  play 60, amp: 0.3, release: 2 if one_in(4)
  sleep 1.5
end

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


5.4 - Потоки Выполнения

Итак, вы написали чумовую бас-партию и заводной ритм. Как сыграть обе эти части одновременно? Одно из решений - переплести их вместе вручную - играть небольшую чать баса, затем немного от ударных, потом еще бас… Однако, вскоре становится очень сложно даже думать о том, как разместить это все во времени, особенно если вы начнётк добавлять новые элементы.

Что если бы Sonic Pi мог соединять партии инструментов за вас автоматически? Вообще-то это возможно, для этого вам надо использовать специальную вещь, которая называется поток выполнения.

Бесконечные Циклы

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

loop do
  sample :drum_heavy_kick
  sleep 1
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end

Как мы уже обсуждали, циклы - это черные дыры в программе. Как только начинает выполняться цикл, выйти из него уже нельзя, не нажав кнопку “Остановить”. Как сыграть сразу оба цикла в одно и то же время? Надо сообщить Sonic Pi, что мы хотим выполнять что-то параллельно с остальной частью кода. Вот тут-то нам и придут на помощь потоки выполнения.

Спасительные Потоки

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end

Обернув первый цикл в do/end блок типа in_thread, мы заставляем Sonic Pi выполнять содержимое блока do/end точно в то же самое время, когда начинается следующее выражение после блока do/end. В нашем примере таковым является второй цикл. Попробуйте, и вы услышите, как ударные и бас переплетаются вместе!

Теперь, что если мы хотим добавить синт поверх. Что-то типа:

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end
loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end

Опять та же самая проблема. Первый цикл воспроизводится вместе со вторым из-за in_thread. Но третий цикл не достигается. То есть нам нужен ещё один поток:

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
in_thread do
  loop do
    use_synth :fm
    play 40, release: 0.2
    sleep 0.5
  end
end
loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end

Течёт, Как Поток

Вас может это удивить, но при нажатии кнопки “Выполнить” также создаётся поток выполнения кода. Вот почему её многократное нажатие наслаивает звуки друг на друга. Запуски автоматически наслаивают звуки, потому что они сами являются потоками.

Область Видимости

По мере овладевания секретами мастерства Sonic Pi вы узнаете, что потоки - это самые важные строительные блоки вашей музыки. Не последним их качеством является способность изолировать от других потоков то, что называется текущей установкой. Что это значит? Ну, когда вы переключаете синт при помощи use_synth, на самом деле вы изменяете синт в текущем потоке - ни в одном другом потоке он не изменится. Давайте посмотрим на это в действии:

play 50
sleep 1
in_thread do
  use_synth :tb303
  play 50
end
sleep 1
play 50

Обратите внимание, как звук в середине отличается от остальных. Команда use_synth применилась только к тому потоку, где она была выполнена, а не к окружающему главному потоку выполнения программы.

Наследование

Когда вы создаёте новый поток, вызывая in_thread, то он автоматически наследует все текущие настройки из родительского потока. Проверим это:

use_synth :tb303
play 50
sleep 1
in_thread do
  play 55
end

Заметили, что вторая нота воспроизводится на :tb303, несмотря на то, что это происходит в отдельном потоке? Любые регулировки, устанавливаемые различными use_* функциями, будут вести себя аналогично.

При создании, потоки наследуют все установки от своего родителя, но любые последующие изменения не имеют обратного эффекта.

Именование Потоков

Наконец, мы можем называть наши потоки:

in_thread(name: :bass) do
  loop do
    use_synth :prophet
    play chord(:e2, :m7).choose, release: 0.6
    sleep 0.5
  end
end
in_thread(name: :drums) do
  loop do
    sample :elec_snare
    sleep 1
  end
end

Посмотрите на панель сообщений, когда этот код выполняется. Видите, как появляются сообщения, в которые включены имена потоков?

[Run 36, Time 4.0, Thread :bass]
 |- synth :prophet, {release: 0.6, note: 47}

Каждый Поток Со Своим Именем

Последнее, что вам нужно узнать об именованных потоках, это то, что только один поток с определённым именем может выполняться одновременно. Рассмотрим следующий код:

in_thread do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end

Скопируйте этот фрагмент в пустой буфер редактора и нажмите кнопку “Выполнить”. Нажмите её еще пару раз. Получилась какафония от того, что много повторений амен-брейка смешались вместе. Ладно, нажимайте “Остановить”.

Это то самое поведение, которое нам не раз уже доводилось наблюдать. Если нажимать кнопку “Выполнить”, то новый звук накладывается поверх уже играющего. Поэтому, если у вас есть цикл, и вы нажмёте кнопку “Выполнить” три раза, то получится три слоя циклов, звучащих вместе.

Но ситуация меняется с именованными потоками:

in_thread(name: :amen) do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end

Теперь попробуте понажимать кнопку “Выполнить” с этим кодом. Вы услышите только один цикл амен-брейк. В сообщениях вы также увидите:

==> Skipping thread creation: thread with name :amen already exists.

Sonic Pi сообщает вам, что поток с именем :amen уже существует, так что он не создаст ещё одного такого же.

Сейчас такое поведение может не показаться вам таким уж полезным. Но в дальнейшем вы ещё вспомните о нём, когда мы начнем лайвкодинг…


5.5 - Функции

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

Определение Функций

define :foo do
  play 50
  sleep 1
  play 55
  sleep 2
end

Мы определили новую функцию, называющуюся foo. Мы сделали это при помощи нашего старого знакомого - блока do/end и магического слова define, за которым следует имя нашей функции. Необязательно было называть ее foo. Можно было придумать какое угодно название, к примеру bar или baz. Но, вообще-то, лучше всего, когда имя что-то означает, например main_section (главная секция) или lead_riff (солирующий рифф).

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

Вызов Функций

Когда наша функция определена, мы можем вызвать её, просто написав её имя:

define :foo do
  play 50
  sleep 1
  play 55
  sleep 0.5
end
foo
sleep 1
2.times do
  foo
end

Можно использовать foo внутри повторяющихся блоков, а также везде, где мы могли бы использовать play или sample. Это даёт нам отличный способ самовыражения и создания новых обозначений в наших композициях.

Функции Сохраняются Между Запусками

Пока что, всякий раз при нажатии кнопки “Выполнить”, Sonic Pi начинал с чистого листа. Ему не известно ничего, кроме того, что находится в буфере редактора. Нельзя ссылаться на код в другом буфере или другом потоке. Однако, функции меняют это. Когда вы определяете функцию, Sonic Pi запоминает ее. Попробуем. Удалите весь код в буфере и замените его вот на что:

foo

Нажмите кнопку “Выполнить”, и убедитесь, что ваша функция играет. Куда же пропал код? Откуда Sonic Pi знал, что играть? Он просто вспомнил вашу функцию даже после её удаления из буфера. Он запомнил что вы напечатали. Это работает только с функциями, которые создаются при помощи definedefonce)

Функции С Параметрами

Вам, возможно, будет интересно узнать, что вашу функцию можно научить принимать аргументы, прямо как rrand принимает значения минимума и максимума. Посмотрим как это выглядит:

define :my_player do |n|
  play n
end
my_player 80
sleep 0.5
my_player 90

Пример не очень интересный, но он показывает, что имеется в виду. Мы создали нашу собственную версию play с параметрами и назвали её my_player.

Параметры должны описываться после ключевого слова do блока define. Они должны быть окружены вертикальными стойками | и разделены запятыми ,. Любые слова могут служить именами параметров.

Магия происходит внутри блока define. Там можно использовать имена параметров, как если бы они были настоящими значениями. В этом примере я проигрываю ноту n. Вы можете думать о параметрах, как о неких обещаниях о том, что при выполнении кода они будут заменены их значениями. Это достигается передачей параметра функции при её вызове. Чтобы сыграть ноту 80, я пишу my_player 80. Тогда внутри определения функции n меняется на 80, так что play n превращается в play 80. В следующий раз, когда я вызываю my_player 90, вместо n появляется 90, поэтому play n становится play 90.

Рассмотрим что-нибудь поинтереснее:

define :chord_player do |root, repeats|
  repeats.times do
    play chord(root, :minor), release: 0.3
    sleep 0.5
  end
end
chord_player :e3, 2
sleep 0.5
chord_player :a3, 3
chord_player :g3, 4
sleep 0.5
chord_player :e3, 3

Я использовал переменную repeats, как если бы она была числом в строке repeats.times do. Ещё я использовал root в качестве названия ноты для play.

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


5.6 - Переменные

A useful thing to do in your code is to create names for things. Sonic Pi makes this very easy: you write the name you wish to use, an equal sign (=), then the thing you want to remember:

sample_name = :loop_amen

В этом примере мы “запомнили” обозначение :loop_amen в переменной sample_name. Теперь мы можем пользоваться sample_name везде, где мы могли бы указать :loop_amen. Например:

sample_name = :loop_amen
sample sample_name

Есть три главных причины для использования переменных в Sonic Pi: присвоение значения, управление повторами и захват результатов операций.

Присвоение Значения

Когда вы пишете код, вы говорите компьютеру сделать что-то. Пока компьютер это понимает - всё в порядке. Но всё же важно помнить, что код читает не только компьютер. Другие люди, возможно, захотят тоже прочесть его и попробовать разобраться, что в нём происходит. Скорее всего и вы сами будете читать и пытаться понять свой собственный код в будущем. Сейчас всё кажется очевидным, но для остальных, или же для вас самих в будущем, это может быть не так!

Одним из способов помочь другим понять, что делает ваш код, - это оставлять комментарии (как мы это видели в предыдущей главе). Ещё один способ - давать осмысленные имена переменным. Посмотрите на этот код:

sleep 1.7533

Зачем указано число 1.7533? Откуда оно взялось? Что оно означает? Теперь взгляните на этот пример:

loop_amen_duration = 1.7533
sleep loop_amen_duration

Вот теперь стало ясно, что значит 1.7533. Это же продолжительность сэмпла :loop_amen! Конечно, вы могли бы заметить, почему бы просто не написать так:

sleep sample_duration(:loop_amen)

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

Управление Повторами

Часто вы видите много повторений в своем коде. Когда необходимо что-то поменять, то делать это приходится во многих местах. Посмотрите на следующее:

sample :loop_amen
sleep sample_duration(:loop_amen)
sample :loop_amen, rate: 0.5
sleep sample_duration(:loop_amen, rate: 0.5)
sample :loop_amen
sleep sample_duration(:loop_amen)

Много чего мы делаем с :loop_amen! Но что если мы захотим послушать как это будет звучать с другим замкнутым сэмплом, таким как :loop_garzul? Пришлось бы найти и заменить все :loop_amen на :loop_garzul. Этим можно заняться, если больше нечего делать. А как насчет живого выступления? Иногда время - это непозволительная роскошь. Особенно, если вы хотите, чтобы люди продолжали танцевать.

Ну, а если бы вы записали свой код так:

sample_name = :loop_amen
sample sample_name
sleep sample_duration(sample_name)
sample sample_name, rate: 0.5
sleep sample_duration(sample_name, rate: 0.5)
sample sample_name
sleep sample_duration(sample_name)

Он делает совершенно то же самое (попробуйте сами). Зато тут есть возможность заменить строку sample_name = :loop_amen на sample_name = :loop_garzul, что приведёт к реальным изменениям во многих местах. Вот что даёт нам магия переменных.

Захват Результатов

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

sd = sample_duration(:loop_amen)

Теперь можно вставлять sd везде, где нам нужна длительность сэмпла :loop_amen.

Вероятно, ещё более важно то, что переменная разрешает нам сохранить результат вызова play или sample:

s = play 50, release: 8

Мы захватили и запомнили s как переменную, которая позволяет управлять синтом, пока он играет:

s = play 50, release: 8
sleep 2
control s, note: 62

В следующей главе мы рассмотрим подробнее управление синтами.

Warning: Variables and Threads

Whilst variables are great for giving things names and capturing the results of things, it is important to know that they should typically only be used locally within a thread. For example, don’t do this:

a = (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
  a = a.shuffle
  sleep 0.5
end
live_loop :sorted do
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end

In the above example we assign a ring of numbers to a variable a and then used it within two separate live_loops. In the first live loop every 0.5s we sort the ring (to (ring 1, 2, 3, 4, 5, 6)) and then print it out to the log. If you run the code, you’ll find that the printed list is not always sorted!. This may surprise you - especially that sometimes the list is printed as sorted, and sometimes it is not. This is called non-deterministic behaviour and is the result of a rather nasty problem called a race-condition. The problem is due to the fact that the second live loop is also manipulating the list (in this case shuffling it) and by the time the list is printed, sometimes it has just been sorted and sometimes it has just been shuffled. Both live loops are racing to do something different to the same variable and every time round a different loop ‘wins’.

There are two solutions to this. Firstly, don’t use the same variable in multiple live loops or threads. For example, the following code will always print a sorted list as each live loop has its own separate variable:

live_loop :shuffled do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.shuffle
  sleep 0.5
end
live_loop :sorted do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end

However, sometimes we do want to share things across threads. For example, the current key, BPM, synth etc. In these cases, the solution is to use Sonic Pi’s special thread-safe state system via the fns get and set. This is discussed later on in section 10.


5.7 - Синхронизация Потоков

Когда у вас начнёт достаточно хорошо получаться лайвкодинг с большим количеством потоков, выполняющихся одновременно, вы, скорее всего, заметите, что очень легко допустить ошибку, которая может прервать поток. Ничего страшного в этом нет, так как поток может быть перезапущен простым нажатием кнопки “Выполнить”. Однако, после перезапуска потока он будет играть невпопад с другими.

Унаследованное Время

Как мы обсуждали ранее, новые потоки, созданные при помощи in_thread наследуют все настройки от родительского потока. Они включают и текущую метку времени. Это означает, что потоки всегда синхронизированы друг с другом после одновременного их запуска.

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

Функции cue и sync

В Sonic Pi решением этой проблемы являются функции cue и sync.

cue позволяет нам отправлять сигнал пульса другим потокам. По умолчанию, другие потоки не слушают и пропускают эти сообщения. Однако, поток может легко заявить о своей заинтересованности, вызвав функцию sync.

Важно осознавать, что функция sync похожа на sleep, ведь она останавливает текущий поток, и тот некоторое время не выполняется. Для sleep время простоя указывается явно, но вызвав sync, вы не знаете сколько времени оно продлится, потому что sync ждёт следующий cue от другого потока. Это может случиться скоро, а может и нет.

Давайте выясним немного больше деталей:

in_thread do
  loop do
    cue :tick
    sleep 1
  end
end
in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end

Здесь у нас есть два потока. Один работает как метроном. Он не играет никаких звуков, только отправляет :tick сигнал каждую долю такта. Второй поток синхронизуется с сообщениями tick. Когда он получает :tick сообщение, то наследует временную отметку потока, вызвавшего cue, и продолжает выполнение.

В результате мы будем слышать сэмпл :drum_heavy_kick в тот самый момент, когда другой поток отправляет :tick сигнал. Даже если два этих потока стартовали не одновременно, это всё равно будет происходить:

in_thread do
  loop do
    cue :tick
    sleep 1
  end
end
sleep(0.3)
in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end

Результатом вызова sleep будет расхождение второго потока с первым. Однако, так как мы используем cue и sync, то мы автоматически синхронизируем потоки и избегаем любых случайных временных сдвигов.

Имена Сигналов

Для сигналов cue можно использовать любые названия, а не только :tick. Просто следите за тем, чтобы все остальные потоки вызывали sync с правильным именем. Иначе они остановятся навсегда (или, по крайней мере, пока вы не нажмёте кнопку “Остановить”).

Попробуем что-нибудь с несколькими названиями cue:

in_thread do
  loop do
    cue [:foo, :bar, :baz].choose
    sleep 0.5
  end
end
in_thread do
  loop do
    sync :foo
    sample :elec_beep
  end
end
in_thread do
  loop do
    sync :bar
    sample :elec_flip
  end
end
in_thread do
  loop do
    sync :baz
    sample :elec_blup
  end
end

Тут у нас есть цикл с функцией cue, отправляющей пульс. Случайным образом этот пульс может быть назван :foo, :bar или :baz. Ещё есть три цикла в потоках, которые синхронизируются с каждым из этих сигналов независимо и воспроизводят разные сэмплы. Чистый эффект от этого кода в том, что каждые полсекунды мы слышим звук, так как каждый из потоков sync случайным образом синхронизируется с потоком cue и проигрывает свой сэмпл.

Конечно же, код будет работать даже если расположить потоки в обратном порядке, поскольку потоки будут дожидаться следующего cue.


6 - Студийные Эффекты

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

Sonic Pi даёт очень простой, но вместе с тем очень мощный способ добавления эффектов. Можно даже строить цепочки из них (таким образом можно пропустить звук через дисторшн, потом эхо и в конце реверберацию) и настраивать каждый из них по отдельности при помощи регулировок (как синты и сэмплы). Можно даже менять параметры эффектов пока они используются. Так, к примеру, реверберация баса может усиливаться в течение трэка…

Гитарные Педали

Не переживайте, если это кажется вам слишком сложным. Всё станет намного понятнее после того, как вы попробуете это сами. Но пока полезной аналогией будет служить гитарный эффект. Есть много видов гитарных эффектов. Одни добавляют реверберацию, другие искажение и так далее. Гитарист подключает свой инструмент к педали, например дисторшн, затем другим кабелем добавляет в цепь педаль реверберации. После этого выход реверберации соединяется с усилителем:

Гитара -> Дисторшн -> Реверберация -> Усилитель

Это называется каскадом эффектов. И это поддерживается в Sonic Pi. Вдобавок, обычно на каждой педали есть ручки и ползунки для управления применяемым эффектом. В Sonic Pi есть то же самое. Представьте себе музыканта, который что-то играет, а кто-то другой управляет эффектами. Такой способ тоже реализован в Sonic Pi, но тут вместо кого-то другого вступает в дело компьютер.

Давайте исследовать эффекты!


6.1 - Добавление Эффектов

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

Система эффектов Sonic Pi использует блоки. Так что, если ты еще не прочел главу 5.1, было бы неплохо её просмотреть и вернуться назад.

Реверберация

Нужно написать специальный блок with_fx :reverb, если мы хотим использовать реверберацию. Примерно так:

with_fx :reverb do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Теперь запустите этот код, и вы услышите как он звучит с реверберацией. Неплохо, правда? Все что угодно будет звучать отлично с реверберацией.

Посмотрим, что произойдет, если поместить код вне блока do/end:

with_fx :reverb do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end
sleep 1
play 55

Обратите внимание, как последний play 55 играется без реверберации. Это происходит потому, что эта строка находится за пределами блока do/end, то есть она не пропускается через эффект.

Точно так же, если играть звуки перед блоком do/end, то и они не будут обрабатываться эффектом:

play 55
sleep 1
with_fx :reverb do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end
sleep 1
play 55

Эхо

Есть множество эффектов на выбор. Как насчет эхо?

with_fx :echo do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Одна из сильных сторон блоков эффектов Sonic Pi в том, что у них есть регулировки, похожие на те, что мы уже видели у play и sample. Например, рассмотрим забавный параметр phase:, который представляет собой длительность эхо в долях такта. Сделаем эхо помедленнее:

with_fx :echo, phase: 0.5 do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Или побыстрее:

with_fx :echo, phase: 0.125 do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Заставим эхо затихать дольше, установив время decay: на 8 долей такта:

with_fx :echo, phase: 0.5, decay: 8 do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Вкладывание Эффектов

Блоки эффектов можно вкладывать друг в друга, что позволяет очень легко собирать из них каскады эффектов. Например, что если вы захотите пропустить некоторый код через эхо, а потом через реверберацию? Легко! Просто поместите один эффект внутри другого:

with_fx :reverb do
  with_fx :echo, phase: 0.5, decay: 8 do
    play 50
    sleep 0.5
    sample :elec_blup
    sleep 0.5
    play 62
  end
end

Представьте как звук вытекает изнутри наружу. Весь звук из внутреннего блока do/end (такой как play 50) вначале попадает в эффект эхо, а затем звук с эффектом эхо в свою очередь отправляется в эффект реверберации.

Можно создавать очень глубокие вложения, чтобы получать непредсказуемые результаты. Однако, имейте в виду, что эффекты требуют больших ресурсов. При вложении они работают в одно и то же время. Так что используйте эффекты разумно, особенно на платформах с низким количеством ресурсов (например Raspberry Pi).

Исследование Эффектов

В Sonic Pi встроено большое количество эффектов на выбор. Чтобы увидеть доступные, щелкните мышью по FX в левом нижнем углу справочной системы. Вот некоторые из моих любимых:

wobble, reverb, echo, distortion, slicer

Теперь вы можете добавлять эффекты везде, где только захочется!


6.2 - Практика Использования Эффектов

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

Рассмотрим следующий код:

loop do
  with_fx :reverb do
    play 60, release: 0.1
    sleep 0.125
  end
end

В этом примере мы играем ноту 60 с очень коротким временем затухания. То есть это очень короткая нота. Нам нужна реверберация, поэтому мы обернули её в блок reverb. Пока что все хорошо. За исключением…

Посмотрим, что делает код. Во-первых, у нас есть цикл loop. Это означает, что всё, что внутри него, повторяется бесконечно. Во-вторых, есть блок with_fx. Его роль в том, чтобы создавать новый эффект реверберации каждый раз, когда он выполняется. Как будто для каждого щипка гитарной струны включается собственная педаль реверберации. Здорово, что так можно делать, но это не всегда то, что нам нужно. Например, такой код будет трудно выполнять на Raspberry Pi. Создание эффекта реверберации и ожидание момента, когда он должен быть остановлен и удален, - обо всем позаботится за тебя блок with_fx. Но на это требуются ресурсы процессора, а они могут быть ограничены.

Как изменить код так, чтобы он больше походил на обычное подключение, где у нашего гитариста всего одна педаль реверберации, через которую и пропускаются все звуки? Легко:

with_fx :reverb do
  loop do
    play 60, release: 0.1
    sleep 0.125
  end
end

Мы помещаем наш цикл внутрь блока with_fx. Таким образом создается всего один единственный эффект для всех нот, извлекаемых в цикле. Этот вариант намного более эффективен, и Raspberry Pi с ним хорошо справится.

В качестве компромисса можно предложить использовать with_fx на протяжении нескольких повторений внутри цикла:

loop do
  with_fx :reverb do
    16.times do
      play 60, release: 0.1
      sleep 0.125
    end
  end
end

То есть новый эффект реверберации будет создаваться через каждые 16 нот.

This is such a common pattern that with_fx supports an opt to do exactly this but without having to write the 16.times block:

loop do
  with_fx :reverb, reps: 16 do
    play 60, release: 0.1
    sleep 0.125
  end
end

Both the reps: 16 and 16.times do examples will behave identically. The reps: 16 essentially repeats the code in the do/end block 16 times so you can use them both interchangeably and choose the one that feels best for you.

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


7 - Управление Звуком Во Время Воспроизведения

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

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

Вам повезло - этот раздел покажет, как это можно сделаеть в Sonic Pi.


7.1 - Управление Играющим Синтезатором

Раньше мы изучали только как включать звуки и добавлять эффекты. Однако, в Sonic Pi есть возможность управлять воспроизводимыми звуками. Этого можно достичь, сохранив ссылку на синт в переменной:

s = play 60, release: 5

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

После того, как у нас появилась s, мы можем управлять ей с помощью функции control:

s = play 60, release: 5
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72

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

Можно передавать функции control любые из стандартных настроек, то есть доступен контроль над такими параметрами, как amp:, cutoff: или pan:.

Неконтролируемые Настройки

Некоторые регулировки нельзя менять после включения синта. Это относится ко всем параметрам ADSR-огибающей. Из документации в справочной системе можно узнать какие параметры разрешено менять, а какие нет. Если документация говорит Нельзя изменять после установки, это значит, что после запуска синта настройке нельзя задавать новое значение.


7.2 - Управление Эффектами

Также в Sonic Pi доступен контроль над эффектами, хотя он и достигается немного другим способом:

with_fx :reverb do |r|
  play 50
  sleep 0.5
  control r, mix: 0.7
  play 55
  sleep 1
  control r, mix: 0.9
  sleep 1
  play 62
end

Мы используем параметры блока do/end вместо переменных. Нам надо задать уникальное имя для выбранного эффекта, окружив его вертикальными чертами |. Затем мы можем ссылаться на него изнутри охватывающего блока do/end. Так же работают и параметризованные функции.

Пришла пора немного поуправлять синтами и эффектами!


7.3 - Плавные Настройки

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

Рассмотрим пример:

s = play 60, release: 5
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72

Вы можете слышать, как высота звука синта сразу же меняется, как только происходит вызов control. Однако, что если мы захотим, чтобы параметр менялся плавно. Поскольку мы управляем значением note:, то для добавления плавности надо задать параметр синтезатора note_slide:

s = play 60, release: 5, note_slide: 1
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72

Теперь мы слышим, как ноты подтягиваются в промежутках между вызовами control. Звучит неплохо, не так ли? Можно ускорить скольжение звука, если указать более короткий интервал времени, например note_slide: 0.2, или замедлить его, установив большее значение.

Для каждой настройки, которой можно управлять, есть соответствующий _slide параметр, с которым можно поиграть.

Скольжение Сохраняется

Если вы задали значение _slide для играющего синтезатора, то оно запомнится и будет использоваться всякий раз, когда соответствующая настройка меняется. Чтобы прекратить плавное изменение звука, надо установить значение _slide в 0 перед следующим вызовом control.

Плавные Эффекты

Можно делать плавное изменение эффектов:

with_fx :wobble, phase: 1, phase_slide: 5 do |e|
  use_synth :dsaw
  play 50, release: 5
  control e, phase: 0.025
end

Пришла пора добавить немного плавности к вашей музыке…


8 - Структуры Данных

Очень полезный инструмент в инструментарии программиста - это структуры данных.

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

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

Давайте рассмотрим его более подробно. Мы узнаем про его базовую форму, а так же как списки могут быть использованы для представления гамм и аккордов.


8.1 - Списки

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

play choose([50, 55, 62])

В этом разделе мы также изучим использование списков для представления аккордов и гамм. Сначала давайте вспомним, как мы можем сыграть аккорд. Помните, что если мы не используем sleep, все звуки воспроизводятся одновременно:

play 52
play 55
play 59

Давайте посмотрим на другой способ представления этого кода.

Воспроизведение Списка

Один из вариантов - разместить все ноы в виде списка: [52, 55, 59]. Наша дружественная функция play достаточно умна, чтобы понимать, как играть список нот. Попробуйте это:

play [52, 55, 59]

О, это уже приятнее читать. Воспроизведение списка нот не мешает вам использовать любые обычные параметры:

play [52, 55, 59], amp: 0.3

Конечно, мы также можем использовать традиционные имена нот вместо номеров MIDI:

play [:E3, :G3, :B3]

Теперь те из вас, кому повезло изучать теорию музыки, смогут подтвердить, что аккорд ми минор был сыгран в 3-й октаве.

Доступ К Списку

Еще одной полезной особенностью списка является возможность получать оттуда информацию. Это может звучать немного странно, но это не сложнее чьей-либо просьбы открыть книгу на странице 23. Применительно к списку, говорят - “Какой элемент находится по индексу 23”? Единственная странность заключается в том, что в программировании индексы начинаются с 0, а не с 1.

Мы не считаем индекы списка 1, 2, 3… вместо этого мы считаем 0, 1, 2…

Давайте рассмотрим это чуть более подробно. Взгляните на этот список:

[52, 55, 59]

Здесь нет ничего страшного. Теперь, какой второй элемент в этом списке? Конечно, это 55. Это было легко. Давайте посмотрим, что нам ответит компьютер на аналогичный вопрос:

puts [52, 55, 59][1]

Это выглядит немного странно, если вы никогда не видели ничего подобного раньше. Поверьте мне, это не очень сложно. Строка выше состоит из трёх частей: слово puts, наш список [52, 55, 59] и наш индекс [1]. Сначала мы говорим puts потому что хотим, чтобы Sonic Pi напечатал ответ для нас в журнал. Далее, мы даём ему наш список, и, наконец, наш индекс запрашивает второй элемент. Мы должны окружить индекс квадратными скобками и т.к. счёт начинается с 0, индекс второго элемента это 1. Посмотрите:

# indexes:  0   1   2
           [52, 55, 59]

Попробуйте выполнить код puts [52, 55, 59][1] и вы увидите, что 55 появится в журнале. Измените индекс 1 на другой, попробуйте более длинные списки и подумайте, как вы сможете использовать список в своём следующем сочинении. Например, какие музыкальные структуры могут быть представлены в виде ряда чисел…


8.2 - Аккорды

Sonic Pi имеет встроенную поддержку названий аккордов, которая возвращает списки. Попробуйте сами:

play chord(:E3, :minor)

Теперь мы действительно проникаем куда-то. Это выглядит намного симпатичнее, чем сырые списки (и это легче читать). А как на счёт других аккордов, Sonic Pi их поддерживает? Да, и много. Попробуйте некоторые из них:

chord(:E3, :m7) chord(:E3, :minor) chord(:E3, :dim7) chord(:E3, :dom7)

Арпеджио

Мы можем легко превратить аккорды в арпеджио с функцией play_pattern:

play_pattern chord(:E3, :m7)

Хорошо, но не очень весело - она играла очень медленно. play_pattern будет играть каждую ноту из списка, вызывая sleep 1 после каждого вызова play. Мы можем использовать другую функцию - play_pattern_timed, чтобы устанавливать наше собственное время и повысить скорость:

play_pattern_timed chord(:E3, :m7), 0.25

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

play_pattern_timed chord(:E3, :m13), [0.25, 0.5]

Это эквивалентно:

play 52
sleep 0.25
play 55
sleep 0.5
play 59
sleep 0.25
play 62
sleep 0.5
play 66
sleep 0.25
play 69
sleep 0.5
play 73

Что бы вы предпочли написать?


8.3 - Гаммы

Sonic Pi поддерживает широкий диапазон гамм. Как насчет сыграть мажорную гамму от ноты С3?

play_pattern_timed scale(:c3, :major), 0.125, release: 0.1

Мы даже можем запросить больше октав:

play_pattern_timed scale(:c3, :major, num_octaves: 3), 0.125, release: 0.1

Как насчёт пентатоники?

play_pattern_timed scale(:c3, :major_pentatonic, num_octaves: 3), 0.125, release: 0.1

Случайные Ноты

Использование аккордов и гамм - отличный способ ограничить случайный выбор для получения чего-то более осмысленного. Поиграйте с примером, который выбирает случайные ноты из минорного аккорда:

use_synth :tb303
loop do
  play choose(chord(:E3, :minor)), release: 0.3, cutoff: rrand(60, 120)
  sleep 0.25
end

Попробуйте другие названия аккордов и диапазоны частот среза фильтра.

Нахождение Аккордов И Гамм

Чтобы выяснить, какие гаммы и аккорды поддерживаются Sonic Pi, просто нажмите кнопку lang внизу слева от этого учебника и затем выберите аккорд или гамму в списке. Прокручивайте вниз информацию в главной панели, пока не увидите длинный список аккордов или гамм (в зависимости от того, что вы ищете).

Получайте удовольствие и помните: здесь нет ошибок, только возможности.


8.4 - Кольца

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

В предыдущем разделе о списках мы видели, как можно извлечь из них элементы, используя механизм индексирования:

puts [52, 55, 59][1]

Так, а что произойдет, если вы захотите извлечь индекс 100? Там явно нет элемента с индексом 100, так как у нашего списка только три элемента. Поэтому Sonic Pi вернет вам nil, что означает “ничто”.

Однако, представьте, что у вас есть счётчик, который, подобно номеру текущего такта, постоянно увеличивается. Давайте создадим наш счётчик и наш список:

counter = 0
notes = [52, 55, 59]

Теперь мы можем использовать счётчик, чтобы получить доступ к нотам в нашем списке:

puts notes[counter]

Здорово, мы получили 52. Теперь давайте увеличим наш счётчик и получим другую ноту:

counter = (inc counter)
puts notes[counter]

Супер, теперь мы получили 55 и если мы сделаем так снова, получим 59. Однако, если после этого мы увеличим счётчик ещё раз, мы выйдем за пределы нашего списка и получим nil. Но что, если нам нужно вернуться к началу списка и снова начинать извлекать элементы по кругу? Вот для этого и нужны кольца.

Создание Колец

Мы можем создавать кольца одним из двух способов. Либо используем функцию ring с элементами кольца в качестве параметров:

(ring 52, 55, 59)

Либо можем взять обычный список и преобразовать его в кольцо, передав ему .ring сообщение:

[52, 55, 59].ring

Индексирование Колец

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

(ring 52, 55, 59)[0] #=> 52
(ring 52, 55, 59)[1] #=> 55
(ring 52, 55, 59)[2] #=> 59
(ring 52, 55, 59)[3] #=> 52
(ring 52, 55, 59)[-1] #=> 59

Использование Колец

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

Гаммы И Аккорды Есть Кольца

Полезно знать, что списки, возвращаемые scale и chord также являются кольцами и позволяют получить к ним доступ с помощью произвольных индексов.

Конструкторы Колец

В дополнение к ring существует ряд других функций, которые будут создавать кольца для нас.

range предлагает вам указать начальную точку, конечную точку и размер шага. bools позволяет использовать 1 и 0, чтобы кратко представлять логические переменные. knit позволяет связывать в последовательности повторяющиеся значения. spread создает кольцо логических значений с Евклидовым распределением.

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


8.5 - Колцевые Связки

Помимо конструкторов, таких как range и spread, есть и другой способ создания новых колец - манипулирование существующими кольцами.

Связянные Методы

Чтобы исследовать это, возьмём простое кольцо:

(ring 10, 20, 30, 40, 50)

Что, если мы хотим перевернуть его задом наперёд? Тогда мы используем связанный метод .reverse, который разворачивает кольцо в обратную сторону:

(ring 10, 20, 30, 40, 50).reverse  #=> (ring 50, 40, 30, 20, 10)

Так, а что, если мы захотим получить первые три элемента кольца?

(ring 10, 20, 30, 40, 50).take(3)  #=> (ring 10, 20, 30)

И, наконец, что, если мы захотим перемешать элементы кольца?

(ring 10, 20, 30, 40, 50).shuffle  #=> (ring 40, 30, 10, 50, 20)

Множественные Связки

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

Как на счёт перемешать кольцо, отбросить 1 элемент, а затем получить первые 3 элемента?

Давайте сделаем это в несколько этапов:

(ring 10, 20, 30, 40, 50) - наше исходное кольцо (ring 10, 20, 30, 40, 50).shuffle - перемешиваем - (ring 40, 30, 10, 50, 20) (ring 10, 20, 30, 40, 50).shuffle.drop(1) - отбрасываем 1 элемент - (ring 30, 10, 50, 20) (ring 10, 20, 30, 40, 50).shuffle.drop(1).take(3) - берём 3 первых элемента - (ring 30, 10, 50)

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

Неизменяемость

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

Доступные Связанные Методы

Вот список доступных для вас связанных методов:

.reverse - возвращает отсортированную в обратном порядке версию кольца .sort - возвращает отсортированную версию кольца .shuffle - возвращает отсортированную в случайном порядке версию кольца .pick(3) - returns a ring with the results of calling .choose 3 times .pick - similar to .pick(3) only the size defaults to the same as the original ring .take(5) - возвращает кольцо, содержащее первые 5 элементов исходного кольца .drop(3) - возвращает кольцо, содержащее все элементы исходного кольца, кроме первых 3 элементов .butlast - возвращает новое кольцо с отсутствующим последним элементом .drop_last(3) - возвращает новое кольцо с отсутствующими 3 последними элементами .take_last(6)- возвращает кольцо, содержащее последние 6 элементов исходного кольца .stretch(2) - повторяет каждый элемент кольца дважды .repeat(3) - повторяет всё кольцо 3 раза .mirror - добавляет кольцо к перевёрнутой версии себя .reflect - то же, что и .mirror, но не дублирует средний элемент .scale(2) - returns a new ring with all elements multiplied by 2 (assumes ring contains numbers only)

Конечно, связанные методы, принимающие числовые аргументы, могут принимать и другие числа! Так что не стесняйтесь вызывать .drop(5) вместо .drop(3), если вы хотите отбросить первые 5 элементов кольца.


9 - Лайв-кодинг

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

В этом разделе мы рассмотрим основы превращения статического кода ваших сочинений в динамичное исполнение.

Держитесь за своё кресло…


9.1 - Лайв-кодинг

Теперь мы узнали достаточно, чтобы начать по-настоящему развлекаться. В этом разделе мы будем черпать из всех предыдущих разделов и узнаем, как вы сможете делать свои музыкальные композиции живыми и превращать их в выступления. Для этого нам понадобится 3 основных компонента:

Умение писать код, который создаёт звук - ПРОВЕРЬТЕ! Умение писать функции - ПРОВЕРЬТЕ! Умение использовать (именовать) потоки - ПРОВЕРЬТЕ!

Чудненько, давайте начнём. Будем вживую кодировать наши первые звуки. Начнем с простого. Сначала нам нужна функция, содержащая код, который мы хотим проиграть. Ещё мы хотим зациклить вызовы этой функции в отдельном потоке:

define :my_loop do
  play 50
  sleep 1
end
in_thread(name: :looper) do
  loop do
    my_loop
  end
end

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

То, что мы здесь имеем - это определение функции, которая всего лишь играет ноту 50 и немного ждёт. Затем мы определили именованную нить, названную :looper, которая бесконечно выполняет цикл с функцией my_loop.

Если вы выполните этот код, то услышите ноту 50 повторяющуюся снова и снова…

Изменяем Это

Итак, вот где начинается веселье. В то время, как код по-прежнему выполняется, изменим 50 на другое число, скажем, на 55, и снова нажмём кнопку “Выполнить”. Ого! Всё изменилось! Вживую!

Это не добавило новый звуковой слой, так как мы используем именованные потоки выполнения, которые допускают только один поток для каждого имени. Кроме того, звук изменился, потому что мы переопределили функцию. Мы дали :my_loop новое определение. Когда нить :looper повторяла цикл, она просто вызвала новое определение.

Попробуйте изменить это снова - измените ноту, измените время сна. Как на счёт добавления инструкции use_synth? Например, измените код на:

define :my_loop do
  use_synth :tb303
  play 50, release: 0.3
  sleep 0.25
end

Сейчас это звучит довольно интересно, но мы можем оживить это ещё больше. Вместо того, чтобы играть одну и ту же ноту снова и снова, попробуем сыграть аккорд:

define :my_loop do
  use_synth :tb303
  play chord(:e3, :minor), release: 0.3
  sleep 0.5
end

Как на счёт воспроизведения случайных нот из аккорда:

define :my_loop do
  use_synth :tb303
  play choose(chord(:e3, :minor)), release: 0.3
  sleep 0.25
end

Или использования случайных значений частоты среза:

define :my_loop do
  use_synth :tb303
  play choose(chord(:e3, :minor)), release: 0.2, cutoff: rrand(60, 130)
  sleep 0.25
end

Наконец, добавим немного барабанов:

define :my_loop do
  use_synth :tb303
  sample :drum_bass_hard, rate: rrand(0.5, 2)
  play choose(chord(:e3, :minor)), release: 0.2, cutoff: rrand(60, 130)
  sleep 0.25
end

Теперь это становится увлекательным!

Однако, прежде чем вы начнёте живое кодирование с функциями и потоками, остановите то, что вы сейчас делаете, и прочтите следующий раздел live_loop, который изменит манеру вашего кодирования в Sonic Pi навсегда…


9.2 - Живые циклы

Окей, этот раздел учебника - настоящая жемчужина. Если вы решите прочесть только один раздел, это должен быть именно этот раздел. Если вы прочли предыдущий раздел - основы лайвкодинга, live_loop - это простой способ сделать всё то же самое, но без необходимости писать так много кода.

Если вы не читали предыдущий раздел, live_loop - это лучший способ поджемовать с Sonic Pi.

Давайте поиграем. Наберите следующее в новом буфере:

live_loop :foo do
  play 60
  sleep 1
end

Теперь нажмите кнопку “Выполнить”. Вы услышите базовый звуковой сигнал на каждую долю такта. Здесь нет ничего весёлого. Однако, пока не нажимайте “Остановить”. Измените 60 на 65 и нажмите “Выполнить” снова.

Ого! Тон изменился автоматически без пропуска доли. Это и есть лайвкодинг.

Почему бы не изменить звук так, чтобы он больше походил на бас? Просто обновите свой код пока тот играет:

live_loop :foo do
  use_synth :prophet
  play :e1, release: 8
  sleep 8
end

Затем нажмите “Выполнить”.

Давайте добавим изменяющуюся частоту среза:

live_loop :foo do
  use_synth :prophet
  play :e1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end

Нажмите “Выполнить” снова.

Добавьте какие-нибудь барабаны:

live_loop :foo do
  sample :loop_garzul
  use_synth :prophet
  play :e1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end

Измените ноту с e1 на c1:

live_loop :foo do
  sample :loop_garzul
  use_synth :prophet
  play :c1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end

Теперь хватит слушать меня и начните играь сами! Веселись!


9.3 - Несколько Живых Циклов

Рассмотрим следующий живой цикл:

live_loop :foo do
  play 50
  sleep 1
end

Вы, наверное, заинтересовались, почему он называется :foo. Это имя имеет значение, поскольку оно показывает, что этот живой цикл отличается от всех других живых циклов.

Не может быть двух живых циклов, выполняющихся под одним именем.

Это значит, что если мы хотим иметь несколько одновременно выполняющихся циклов, нам просто нужно дать им разные имена:

live_loop :foo do
  use_synth :prophet
  play :c1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end
live_loop :bar do
  sample :bd_haus
  sleep 0.5
end

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

Синхронизация Живых Циклов

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

Рассмотрим этот плохо синхронизированный код:

live_loop :foo do
  play :e4, release: 0.5
  sleep 0.4
end
live_loop :bar do
  sample :bd_haus
  sleep 1
end

Давайте посмотрим, сможем ли мы исправить тайминг и синхронизировать его без остановки воспроизведения. Сначала исправим время сна в цикле :foo на что-то вроде 0.5:

live_loop :foo do
  play :e4, release: 0.5
  sleep 0.5
end
live_loop :bar do
  sample :bd_haus
  sleep 1
end

Но мы ещё не совсем закончили, ведь вы заметили, что такты пока не выстроились правильно. Это происходит потому, что циклы не в фазе. Давайте исправим это, синхронизировав один цикл с другим:

live_loop :foo do
  play :e4, release: 0.5
  sleep 0.5
end
live_loop :bar do
  sync :foo
  sample :bd_haus
  sleep 1
end

Ого, теперь всё прекрасно синхронизируется по времени и всё это без остановки.

А теперь вперёд! Кодируйте вживую с живыми петлями!


9.4 - Тики

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

Тики И Кольца

Sonic Pi предоставляет очень удобный инструмент для работы с кольцами внутри live_loop. Он называется системой тиков. В разделе о кольцах мы говорили о счётчике, который постоянно увеличиватся, подобно номеру текущего такта. Тик как раз и реализует эту идею. Давайте рассмотрим примеры:

counter = 0
live_loop :arp do
  play (scale :e3, :minor_pentatonic)[counter], release: 0.1
  counter += 1
  sleep 0.125
end

Это эквивалентно:

live_loop :arp do
  play (scale :e3, :minor_pentatonic).tick, release: 0.1
  sleep 0.125
end

Здесь мы просто берём минорную пентатонику, построенную от e3, и перебираем каждый элемент кольца. Это делается путём добавления .tick в конец объявления гаммы. Тик является локальным для живого цикла, поэтому каждый живой цикл может иметь свой собственный, независимый тик:

live_loop :arp do
  play (scale :e3, :minor_pentatonic).tick, release: 0.1
  sleep 0.125
end
live_loop :arp2 do
  use_synth :dsaw
  play (scale :e2, :minor_pentatonic, num_octaves: 3).tick, release: 0.25
  sleep 0.25
end

Функция tick

Вы также можете вызвать tick как стандартную функцию и использовать возвращаемое ею значение в качестве индекса:

live_loop :arp do
  idx = tick
  play (scale :e3, :minor_pentatonic)[idx], release: 0.1
  sleep 0.125
end

Однако, гораздо изящнее вызывать .tick в конце объявления кольцевого списка. Функция tick полезна, когда вы хотите сделать что-то необычное с величиной тика или, если нужно, использовать тики для чего-то отличного от индексирования в кольцах.

Функция Look

Магическая особенность тика заключается в том, что он не только возвращает новый индекс (или элемент кольцевого списка по этому индексу), он также гарантирует, что в следующий раз, когда вы вызовете тик, вы получите следующее значение. Посмотрите примеры для функции tick в разделе “Язык” документации, чтобы узнать о различных способах работы с ней. Впрочем, сейчас необходимо отметить, что иногда вам захочется просто посмотреть на текущее значение тика и не увеличивать его. Это доступно с помощью Функции look. Вы можете вызвать look как стандартную функцию путем добавления .look в конец кольцевого списка.

Именование Тиков

Наконец, иногда вам может потребоваться использовать более одного тика внутри живой петли. Это достигается путем предоставления вашему тику имени:

live_loop :arp do
  play (scale :e3, :minor_pentatonic).tick(:foo), release: 0.1
  sleep (ring 0.125, 0.25).tick(:bar)
end

Здесь мы используем два тика - один для игры нот, другой для времени сна. Так как они оба находятся в одной и той же живой петле, чтобы держать их раздельно, нам нужно дать им уникальные имена. Это тот же самое, что и именование live_loop - мы просто передаем функции аргумент с префиксом :. В примере выше мы назвали один тик :foo а другой :bar. Если мы хотим применить функцию look к одному из тиков, нам нужно передать имя тика функции look.

Не Делайте Это Слишком Сложным

Большая часть мощности системы тиков не будет полезной, пока вы ещё только начинаете осваивать Sonic Pi. Не пытайтесь сразу выучить всё в этом разделе. Просто сосредоточьтесь на одном тике для одного кольца. Это даст вам больше радости и простоты применения тиков для колец в твоих live_loop.

Взгляните на документацию для функции tick, там много полезных примеров. Счастливых тиков!


10 - Time State

Often it is useful to have information that is shared across multiple threads or live loops. For example, you might want to share a notion of the current key, BPM or even more abstract concepts such as the current ‘complexity’ (which you’d potentially interpret in different ways across different threads). We also don’t want to lose any of our existing determinism guarantees when doing this. In other words, we’d still like to be able to share code with others and know exactly what they’ll hear when they run it. At the end of Section 5.6 of this tutorial we briefly discussed why we should not use variables to share information across threads due to a loss of determinism (in turn due to race conditions).

Sonic Pi’s solution to the problem of easily working with global variables in a deterministic way is through a novel system it calls Time State. This might sound complex and difficult (in fact, in the UK, programming with multiple threads and shared memory is typically a university level subject). However, as you’ll see, just like playing your first note, Sonic Pi makes it incredibly simple to share state across threads whilst still keeping your programs thread-safe and deterministic..

Meet get and set


10.1 - Set and Get

Sonic Pi has a global memory store called Time State. The two main things you do with it are to set information and get information. Let’s dive deeper…

Set

To store information into the Time State we need two things:

the information we want to store, a unique name (key) for the information.

For example, we might want to store the number 3000 with the key :intensity. This is possible using the set function:

set :intensity, 3000

We can use any name for our key. If information has already been stored with that key, our new set will override it:

set :intensity, 1000
set :intensity, 3000

In the above example, as we stored both numbers under the same key, the last call to set ‘wins’, so the number associated with :intensity will be 3000 as the first call to set is effectively overridden.

Get

To fetch information from the Time State we just need the key we used to set it, which in our case is :intensity. We then just need to call get[:intensity] which we can see by printing out the result to the log:

print get[:intensity] #=> prints 3000

Notice that calls to get can return information that was set in a previous run. Once a piece of information has been set it is available until either the information is overridden (just like we clobbered the :intensity value of 1000 to 3000 above) or Sonic Pi is closed.

Multiple Threads

The main benefit of the Time State system is that it can be safely used across threads or live loops. For example, you could have one live loop setting information and another one getting it:

live_loop :setter do
  set :foo, rrand(70, 130)
  sleep 1
end
live_loop :getter do
  puts get[:foo]
  sleep 0.5
end

The nice thing about using get and set across threads like this is that it will always produce the same result every time you hit run. Go on, try it. See if you get the following in your log:

{run: 0, time: 0.0}
 └─ 125.72265625
{run: 0, time: 0.5}
 └─ 125.72265625
{run: 0, time: 1.0}
 └─ 76.26220703125
{run: 0, time: 1.5}
 └─ 76.26220703125
{run: 0, time: 2.0}
 └─ 114.93408203125
{run: 0, time: 2.5}
 └─ 114.93408203125
{run: 0, time: 3.0}
 └─ 75.6048583984375
{run: 0, time: 3.5}
 └─ 75.6048583984375
 ~~~~
 Try running it a few times - see, it's the same every time. This is
 what we call deterministic behaviour and it's really very important
 when we want to share our music as code and know that the person
 playing the code is hearing exactly what we wanted them to hear (just
 like playing an MP3 or internet stream sounds the same for all
 listeners).
## A Simple Deterministic State System
Back in Section 5.6 we discussed why using variables across threads can
lead to random behaviour. This stops us from being able to reliably
reproduce code such as this:

## An Example of Non-Deterministic Behaviour ## (due to race conditions caused by multiple ## live loops manipulating the same variable ## at the same time). ## ## If you run this code you’ll notice ## that the list that’s printed is ## not always sorted! a = (ring 6, 5, 4, 3, 2, 1)

live_loop :shuffled do a = a.shuffle sleep 0.5 end

live_loop :sorted do a = a.sort sleep 0.5 puts “sorted: “, a end ~~~~

Let’s take a look at how this might look using get and set:

## An Example of Deterministic Behaviour
## (despite concurrent access of shared state)
## using Sonic Pi's new Time State system.
##
## When this code is executed, the list that's
## printed is always sorted!
set :a, (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
  set :a, get[:a].shuffle
  sleep 0.5
end
live_loop :sorted do
  set :a, get[:a].sort
  sleep 0.5
  puts "sorted: ", get[:a]
end

Notice how this code is pretty much identical to the version using a variable before it. However when you run the code, it behaves as you would expect with any typical Sonic Pi code - it does the same thing every time in this case thanks to the Time State system.

Therefore, when sharing information across live loops and threads, use get and set instead of variables for deterministic, reproducible behaviour.


10.2 - Sync

Section 5.7 introduced the functions cue and sync when dealing with the issue of synchronising threads. What it didn’t explain was that it is the Time State system which provides this functionality. It just so happens that set is actually a variation of cue and is built on top of the same core functionality which is to insert information into the Time State system. Additionally, sync is also designed in such a way that it works seamlessly with Time State - any information that we plan to store in Time State we can sync on. In other words - we sync on events yet to be inserted into Time State.

Waiting for Events

Let’s take a quick look at how to use sync to wait for new events to be added to Time State:

in_thread do
  sync :foo
  sample :ambi_lunar_land
end
sleep 2
set :foo, 1

In this example first we create a thread which waits for a :foo event to be added to the Time State. After this thread declaration we sleep for 2 beats and then set :foo to be 1. This then releases the sync which then moves to the next line which is to trigger the :ambi_lunar_land sample.

Note that sync always waits for future events and that it will block the current thread waiting for a new event. Also, it will inherit the logical time of the thread which triggered it via set or cue so it may also be used to sync time.

Passing values into the Future

In the example above we set :foo to 1 which we did nothing with. We can actually get this value from the thread calling sync:

in_thread do
  amp = sync :foo
  sample :ambi_lunar_land, amp: amp
end
sleep 2
set :foo, 0.5

Note that values that are passed through set and cue must be thread safe - i.e. immutable rings, numbers, symbols or frozen strings. Sonic Pi will throw an error if the value you are attempting to store in the Time State is not valid.


10.3 - Pattern Matching

When getting and setting information into the Time State, it’s possible to use more complex keys than basic symbols such as :foo and :bar. You can also use URL style strings called paths such as "/foo/bar/baz". Once we start working with paths, we can then start taking advantage of Sonic Pi’s sophisticated pattern matching system to get and sync with ‘similar’ rather than ‘identical’ paths. Let’s take a look.

Match any path segment

Let’s assume we want to wait for the next event that has three path segments:

sync "/*/*/*"

This will match any Time State event with exactly three path segments, regardless of their names. For example:

cue "/foo/bar/baz" cue "/foo/baz/quux" cue "/eggs/beans/toast" cue "/moog/synths/rule"

However, it will not match paths with fewer or more path segments. The following will not match:

cue "/foo/bar" cue "/foo/baz/quux/quaax" cue "/eggs"

Each * means any content. So we could match paths with just one segment with /* or paths with five segments with /*/*/*/*/*

Matching partial segments

If we know what the segment is going to start or finish with, we can use a * in addition to a partial segment name. For example: "/foo/b*/baz" will match any path that has three segments, the first of which is foo, the last baz and the middle segment can be anything that starts with b. So, it would match:

cue "/foo/bar/baz" cue "/foo/baz/baz" cue "/foo/beans/baz"

However, it wouldn’t match the following:

cue "/foo/flibble/baz" cue "/foo/abaz/baz" cue "/foo/beans/baz/eggs"

You can also place the * at the start of the segment to specify the last characters of a segment: "/foo/*zz/baz" which will match any 3 segment cue or set where the first segment is foo, the last is baz and the middle segment ends with zz such as "cue "/foo/whizz/baz".

Matching Nested Path Segments

Sometimes you don’t know how many path segments you want to match. In these cases you can use the powerful double star: ** such as "/foo/**/baz" which will match:

cue "/foo/bar/baz" cue "/foo/bar/beans/baz" cue "/foo/baz" cue "/foo/a/b/c/d/e/f/baz"

Matching Single Letters

You can use the ? character to match against a single char such as "/?oo/bar/baz" which will match:

cue "/foo/bar/baz" cue "/goo/bar/baz" cue "/too/bar/baz" cue "/woo/bar/baz"

Matching Multiple Words

If you know that a segment may be one of a select number of words, you can use the { and } matchers to specify a list of choices such as "/foo/{bar,beans,eggs}/quux" which will only match the following:

cue "/foo/bar/quux" cue "/foo/beans/quux" cue "/foo/eggs/quux"

Matching Multiple Letters

Finally, you can match against a selection of letters if you use the [ and ] matchers to specify a list of choices such as "/foo/[abc]ux/baz" which will match only:

cue "/foo/aux/baz" cue "/foo/bux/baz" cue "/foo/cux/baz"

You can also use the - character to specify ranges of letters. For example "/foo/[a-e]ux/baz" which will match only:

cue "/foo/aux/baz" cue "/foo/bux/baz" cue "/foo/cux/baz" cue "/foo/dux/baz" cue "/foo/eux/baz"

Combining Matchers

When calling sync or get you are free to combine matchers in any order you see fit to powerfully match any Time State event created by cue or set. Let’s look at a crazy example:

in_thread do
  sync "/?oo/[a-z]*/**/ba*/{quux,quaax}/"
  sample :loop_amen
end
sleep 1
cue "/foo/beans/a/b/c/d/e/bark/quux/"

OSC Pattern Matching

For those curious, these matching rules are based on the Open Sound Control pattern matching specification which is explained in detail here: http://opensoundcontrol.org/spec-1_0


11 - MIDI

Once you’ve mastered converting code to music, you might wonder - what’s next? Sometimes the constraints of working purely within Sonic Pi’s syntax and sound system can be exciting and put you into a new creative position. However, sometimes it is essential to break out of the code into the real world. We want two extra things:

To be able to convert actions in the real world into Sonic Pi events to code with To be able to use Sonic Pi’s strong timing model and semantics to control and manipulate objects in the real world

Luckily there’s a protocol that’s been around since the 80s that enables exactly this kind of interaction - MIDI. There’s an incredible number of external devices including keyboards, controllers, sequencers, and pro audio software that all support MIDI. We can use MIDI to receive data and also use it to send data.

Sonic Pi provides full support for the MIDI protocol enabling you to connect your live code to the real world. Let’s explore it further…


11.1 - MIDI In

In this section we will learn how to connect a MIDI controller to send events into Sonic Pi to control our synths and sounds. Go and grab a MIDI controller such as a keyboard or control surface and let’s get physical!

Connecting a MIDI Controller

In order to get information from an external MIDI device into Sonic Pi we first need to connect it to our computer. Typically this will be via a USB connection, although older equipment will have a 5-pin DIN connector for which you’ll need hardware support for your computer (for example, some sound cards have MIDI DIN connectors). Once you’ve connected your device, launch Sonic Pi and take a look at the IO section of the Preferences panel. You should see your device listed there. If not, try hitting the ‘Reset MIDI’ button and see if it appears. If you’re still not seeing anything, the next thing to try is to consult your operating system’s MIDI config to see if it sees your device. Failing all that, feel free to ask questions in the public chat room: http://gitter.im/samaaron/sonic-pi

Receiving MIDI Events

Once your device is connected, Sonic Pi will automatically receive events. You can see for yourself by manipulating your MIDI device and looking at the cue logger in the bottom right of the application window below the log (if this isn’t visible go to Preferences->Editor->Show & Hide and enable the ‘Show cue log’ tickbox). You’ll see a stream of events such as:

/midi/nanokey2_keyboard/0/1/note_off  [55, 64]
/midi/nanokey2_keyboard/0/1/note_on   [53, 102]
/midi/nanokey2_keyboard/0/1/note_off  [57, 64]
/midi/nanokey2_keyboard/0/1/note_off  [53, 64]
/midi/nanokey2_keyboard/0/1/note_on   [57, 87]
/midi/nanokey2_keyboard/0/1/note_on   [55, 81]
/midi/nanokey2_keyboard/0/1/note_on   [53, 96]
/midi/nanokey2_keyboard/0/1/note_off  [55, 64]

Once you can see a stream of messages like this, you’ve successfully connected your MIDI device. Congratulations, let’s see what we can do with it!

MIDI Time State

These events are broken into two sections. Firstly there’s the name of the event such as /midi/nanokey2_keyboard/0/1/note_on and secondly there’s the values of the event such as [18, 62]. Interestingly, these are the two things we need to store information in Time State. Sonic Pi automatically inserts incoming MIDI events into Time State. This means you can get the latest MIDI value and also sync waiting for the next MIDI value using everything we learned in section 10 of this tutorial.

Controlling Code

Now we’ve connected a MIDI device, seen its events in the cue log and discovered that our knowledge of Time State is all we need to work with the events, we can now start having fun. Let’s build a simple MIDI piano:

live_loop :midi_piano do
  note, velocity = sync "/midi/nanokey2_keyboard/0/1/note_on"
  synth :piano, note: note
end

There’s a few things going on in the code above including some issues. Firstly, we have a simple live_loop which will repeat forever running the code between the do/end block. This was introduced in Section 9.2. Secondly, we’re calling sync to wait for the next matching Time State event. We use a string representing the MIDI message we’re looking for (which is the same as was displayed in the cue logger). Notice that this long string is provided to you by Sonic Pi’s autocompletion system, so you don’t have to type it all out by hand. In the log we saw that there were two values for each MIDI note on event, so we assign the result to two separate variables note and velocity. Finally we trigger the :piano synth passing our note.

Now, you try it. Type in the code above, replace the sync key with a string matching your specific MIDI device and hit Run. Hey presto, you have a working piano! However, you’ll probably notice a couple of problems: firstly all the notes are the same volume regardless of how hard you hit the keyboard. This can be easily fixed by using the velocity MIDI value and converting it to an amplitude. Given that MIDI has a range of 0->127, to convert this number to a value between 0->1 we just need to divide it by 127:

live_loop :midi_piano do
  note, velocity = sync "/midi/nanokey2_keyboard/0/1/note_on"
  synth :piano, note: note, amp: velocity / 127.0
end

Update the code and hit Run again. Now the velocity of the keyboard is honoured. Next, let’s get rid of that pesky pause.

Removing Latency

Before we can remove the pause, we need to know why it’s there. In order to keep all the synths and FX well-timed across a variety of differently capable CPUs, Sonic Pi schedules the audio in advance by 0.5s by default. (Note that this added latency can be configured via the fns set_sched_ahead_time! and use_sched_ahead_time). This 0.5s latency is being added to our :piano synth triggers as it is added to all synths triggered by Sonic Pi. Typically we really want this added latency as it means all synths will be well timed. However, this only makes sense for synths triggered by code using play and sleep. In this case, we’re actually triggering the :piano synth with our external MIDI device and therefore don’t want Sonic Pi to control the timing for us. We can turn off this latency with the command use_real_time which disables the latency for the current thread. This means you can use real time mode for live loops that have their timing controlled by syncing with external devices, and keep the default latency for all other live loops. Let’s see:

live_loop :midi_piano do
  use_real_time
  note, velocity = sync "/midi/nanokey2_keyboard/0/1/note_on"
  synth :piano, note: note, amp: velocity / 127.0
end

Update your code to match the code above and hit Run again. Now we have a low latency piano with variable velocity coded in just 5 lines. Wasn’t that easy!

Getting Values

Finally, as our MIDI events are going straight into the Time State, we can also use the get fn to retrieve the last seen value. This doesn’t block the current thread and returns nil if there’s no value to be found (which you can override by passing a default value - see the docs for get). Remember that you can call get in any thread at any time to see the latest matching Time State value. You can even use time_warp to jump back in time and call get to see past events…

Now You are in Control

The exciting thing now is that you can now use the same code structures to sync and get MIDI information from any MIDI device and do whatever you want with the values. You can now choose what your MIDI device will do!


11.2 - MIDI Out

In addition to receiving MIDI events we can also send out MIDI events to trigger and control external hardware synths, keyboards and other devices. Sonic Pi provides a full set of fns for sending various MIDI messages such as:

Note on - midi_note_on Note off - midi_note_off Control change - midi_cc Pitch bend - midi_pitch_bend Clock ticks - midi_clock_tick

There are many other supported MIDI messages too - check out the API documentation for all the other fns that start with midi_.

Connecting to a MIDI Device

In order to send a MIDI message to an external device, we must first have connected it. Check out the subsection ‘Connecting a MIDI Controller’ in section 11.1 for further details. Note that if you’re using USB, connecting to a device which you’re sending to (rather than receiving from) is the same procedure. However, if you’re using the classic DIN connectors, make sure you connect to the MIDI out port of your computer. You should see your MIDI device listed in the preferences pane.

Sending MIDI events

The many midi_* fns work just like play, sample and synth in that they send a message at the current (logical) time. For example, to spread out calls to the midi_* fns you need to use sleep just like you did with play. Let’s take a look:

midi_note_on :e3, 50

This will send a MIDI note on event to the connected MIDI device with velocity 50. (Note that Sonic Pi will automatically convert notes in the form :e3 to their corresponding MIDI number such as 52 in this case.)

If your connected MIDI device is a synthesiser, you should be able to hear it playing a note. To disable it use midi_note_off:

midi_note_off :e3

Selecting a MIDI device

By default, Sonic Pi will send each MIDI message to all connected devices on all MIDI channels. This is to make it easy to work with a single connected device without having to configure anything. However, sometimes a MIDI device will treat MIDI channels in a special way (perhaps each note has a separate channel) and also you may wish to connect more than one MIDI device at the same time. In more complicated setups, you may wish to be more selective about which MIDI device receives which message(s) and on which channel.

We can specify which device to send to using the port: opt, using the device name as displayed in the preferences:

midi_note_on :e3, port: "moog_minitaur"

We can also specify which channel to send to using the channel: opt (using a value in the range 1-16):

midi_note_on :e3, channel: 3

Of course we can also specify both at the same time to send to a specific device on a specific channel:

midi_note_on :e3, port: "moog_minitaur", channel: 5

MIDI Studio

Finally, a really fun thing to do is to connect the audio output of your MIDI synthesiser to one of the audio inputs of your soundcard. You can then control your synth with code using the midi_* fns and also manipulate the audio using live_audio and FX:

with_fx :reverb, room: 1 do
  live_audio :moog
end
live_audio :moog_trigger do
  use_real_time
  midi (octs :e1, 3).tick, sustain: 0.1
  sleep 0.125
end

(The fn midi is available as a handy shortcut to sending both note on and note off events with a single command. Check out its documentation for further information).


12 - OSC

In addition to MIDI, another way to get information in and out of Sonic Pi is via the network using a simple protocol called OSC - Open Sound Control. This will let you send messages to and from external programs (both running on your computer and on external computers) which opens up the potential for control way beyond MIDI which has limitations due to its 1980s design.

For example, you could write a program in another programming language which sends and receives OSC (there are OSC libraries for pretty much every common language) and work directly with Sonic Pi. What you can use this for is only limited by your imagination.


12.1 - Receiving OSC

By default when Sonic Pi is launched it listens to port 4559 for incoming OSC messages from programs on the same computer. This means that without any configuration, you can send Sonic Pi an OSC message and it will be displayed in the cue log just like incoming MIDI messages. This also means that any incoming OSC message is also automatically added to the Time State which means you can also use get and sync to work with the incoming data - just like with MIDI.

A Basic OSC Listener

Let’s build a basic OSC listener:

live_loop :foo do
  use_real_time
  a, b, c = sync "/osc/trigger/prophet"
  synth :prophet, note: a, cutoff: b, sustain: c
end

In this example we described an OSC path "/osc/trigger/prophet" which we’re syncing on. This can be any valid OSC path (all letters and numbers are supported and the / is used like in a URL to break up the path to multiple words). The /osc prefix is added by Sonic Pi to all incoming OSC messages, so we need to send an OSC message with the path /trigger/prophet for our sync to stop blocking and the prophet synth to be triggered.

Sending OSC to Sonic Pi

We can send OSC to Sonic Pi from any programming language that has an OSC library. For example, if we’re sending OSC from Python we might do something like this:

from pythonosc import osc_message_builder
from pythonosc import udp_client
sender = udp_client.SimpleUDPClient('127.0.0.1', 4559)
sender.send_message('/trigger/prophet', [70, 100, 8])

Or, if we’re sending OSC from Clojure we might do something like this from the REPL:

(use 'overtone.core)
(def c (osc-client "127.0.0.1" 4559))
(osc-send c "/trigger/prophet" 70 100 8)

Receiving from External Machines

For security reasons, by default Sonic Pi does not let remote machines send it OSC messages. However, you can enable support for remote machines in Preferences->IO->Network->Receive Remote OSC Messages. Once you’ve enabled this, you can receive OSC messages from any computer on your network. Typically the sending machine will need to know your IP address (a unique identifier for your computer on your network - kind of like a phone number or an email address). You can discover the IP address of your computer by looking at the IO section of the preferences pane. (If your machine happens to have more than one IP address, hovering the mouse over the listed address will pop up with a list of all known addresses).

Note, some programs such as TouchOSC for iPhone and Android support sending OSC as a standard feature. So, once you’re listening to remote machines and know your IP address you can instantly start sending messages from apps like TouchOSC which enable you to build your own custom touch controls with sliders, buttons, dials etc. This can provide you with an enormous range of input options.


12.2 - Sending OSC

In addition to receiving OSC and working with it using Time State, we can also send out OSC messages in time with our music (just like we can send out MIDI messages in time with our music). We just need to know which IP address and port we’re sending to. Let’s give it a try:

use_osc "localhost", 4559
osc "/hello/world"

If you run the code above, you’ll notice that Sonic Pi is sending itself an OSC message! This is because we set the IP address to the current machine and the port to the default OSC in port. This is essentially the same as posting a letter to yourself - the OSC packet is created, leaves Sonic Pi, gets to the network stack of the operating system which then routes the packed back to Sonic Pi and then it’s received as a standard OSC message and is visible in the cue logger as the incoming message "/osc/hello/world. (Notice how Sonic Pi automatically prefixes all incoming OSC messages with /osc.)

Sending OSC to other programs

Of course, sending OSC messages to ourselves may be fun but it’s not that useful. The real benefit starts when we send messages to other programs:

use_osc "localhost", 123456
osc "/hello/world"

In this case we’re assuming there’s another program on the same machine listening to port 123456. If there is, then it will receive a "/hello/world OSC message with which it can do what it wants.

If our program is running on another machine, we need to know its IP address which we use instead of "localhost":

use_osc "192.168.10.23", 123456
osc "/hello/world"

Now we can send OSC messages to any device reachable to us via our local networks and even the internet!


13 - Multichannel Audio

So far, in terms of sound production, we’ve explored triggering synths and recorded sounds via the fns play, synth and sample. These have then generated audio which has played through our stereo speaker system. However, many computers also have the ability to input sound, perhaps through a microphone, in addition to the ability to send sound out to more than two speakers. Often, this capability is made possible through the use of an external sound card - these are available for all platforms. In this section of the tutorial we’ll take a look at how we can take advantage of these external sound cards and effortlessly work with multiple channels of audio in and out of Sonic Pi.


13.1 - Sound In

One simple (and perhaps familiar) way of accessing sound inputs is using our friend synth by specifying the :sound_in synth:

synth :sound_in

This will operate just like any synth such as synth :dsaw with the exception that the audio generated will be read directly from the first input of your system’s sound card. On laptops, this is typically the built-in microphone, but if you have an external sound card, you can plug any audio input to the first input.

Increasing the Duration

One thing you might notice is that just like synth :dsaw the :sound_in synth only lasts for 1 beat as it has a standard envelope. If you’d like to keep it open for a little longer, change the ADSR envelope settings. For example the following will keep the synth open for 8 beats before closing the connection:

synth :sound_in, sustain: 8

Добавление Эффектов

Of course, just like any normal synth, you can easily layer on effects with the FX block:

with_fx :reverb do
  with_fx :distortion do
    synth :sound_in, sustain: 8
  end
end

If you have plugged in a guitar to your first input, you should be able to hear it with distortion and reverb until the synth terminates as expected.

You are free to use the :sound_in synth as many times as you like concurrently (just like you would do with any normal synth). For example, the following will play two :sound_in synths at the same time - one through distortion and one through reverb:

with_fx :distortion do
  synth :sound_in, sustain: 8
end
with_fx :reverb do  
  synth :sound_in, sustain: 8
end

Multiple Inputs

You can select which audio input you want to play with the input: opt. You can also specify a stereo input (two consecutive inputs) using the :sound_in_stereo synth. For example, if you have a sound card with at least three inputs, you can treat the first two as a stereo stream and add distortion and the third as a mono stream and add reverb with the following code:

with_fx :distortion do
  synth :sound_in_stereo, sustain: 8, input: 1
end
with_fx :reverb do  
  synth :sound_in, sustain: 8, input: 3
end

Potential Issues

However, although this is a useful technique, there are a couple of limitations to this approach. Firstly, it only works for a specific duration (due to it having an ADSR envelope) and secondly, there’s no way to switch the FX around once the synth has been triggered. Both of these things are typical requests when working with external audio feeds such as microphones, guitars and external synthesisers. We’ll therefore take a look at Sonic Pi’s solution to the problem of manipulating a (potentially) infinite stream of live audio input: live_audio.


13.2 - Live Audio

The :sound_in synth as described in the previous section provides a very flexible and familiar method for working with input audio. However, as also discussed it has a few issues when working with a single input of audio as a single instrument (such as a voice or guitar). By far the best approach to working with a single continuous stream of audio is to use live_audio.

A Named Audio Input

live_audio shares a couple of core design constraints with live_loop (hence the similar name). Firstly it must have a unique name and secondly only one live_audio stream with that name may exist at any one time. Let’s take a look:

live_audio :foo

This code will act in a similar fashion to synth :sound_in with some key differences: it runs forever (until you explicitly stop it) and you can move it to new FX contexts dynamically.

Working with FX

On initial triggering live_audio works exactly as you might expect it to work with FX. For example, to start a live audio stream with added reverb simply use a :reverb FX block:

with_fx :reverb do
  live_audio :foo
end

However, given that live_audio runs forever (at least until you stop it) it would be pretty limiting if, like typical synths, the live audio was bound within the :reverb FX for its entire existence. Luckily this is not the case and it was designed to be easy to move between different FX. Let’s try it. Run the code above to hear live audio coming directly from the first input of your sound card. Note, if you’re using a laptop, this will typically be out of your built-in microphone, so it’s recommended to use headphones to stop feedback.

Now, whilst you’re still hearing the audio live from the sound card with reverb, change the code to the following:

with_fx :echo do
  live_audio :foo
end

Now, hit Run, and you’ll immediately hear the audio played through the echo FX and no longer through reverb. If you wanted them both, just edit the code again and hit Run:

with_fx :reverb do
  with_fx :echo do
    live_audio :foo
  end
end

It’s important to point out that you can call live_audio :foo from any thread or live loop and it will move the live audio synth to that thread’s current FX context. You could therefore easily have multiple live loops calling live_audio :foo at different times resulting in the FX context being automatically swapped around for some interesting results.

Stopping live audio

Unlike standard synths, as live_audio has no envelope, it will continue running forever (even if you delete the code, just like a function is still defined in memory if you delete the code in the editor). To stop it, you need to use the :stop arg:

live_audio :foo, :stop

It can easily be restarted by calling it without the :stop arg again:

live_audio :foo

Additionally all running live audio synths are stopped when you hit the global Stop button (as with all other running synths and FX).

Stereo input

With respect to audio channels, by default live_audio acts similarly to the :sound_in synth in that it takes a single mono input stream of audio and converts it to a stereo stream using the specified panning. However, just like :sound_in_stereo it’s also possible to tell live_audio to read two consecutive audio inputs and treat them as the left and right channels directly. This is achieved via the :stereo opt. For example, to treat input 2 as the left signal and input 3 as the right signal, you need to configure the input: opt to 2 and enable stereo mode as follows:

live_audio :foo, stereo: true, input: 2

Note that once you have started a live audio stream in stereo mode, you cannot change it to mono without stopping and starting. Similarly, if you start it in the default mono mode, you can’t switch to stereo without starting and stopping the stream.


13.3 - Sound Out

So far in this section we’ve looked at how to get multiple streams of audio into Sonic Pi - either through the use of the :sound_in synth or via the powerful live_audio system. In addition to working with multiple streams of input audio, Sonic Pi can also output multiple streams of audio. This is achieved via the :sound_out FX.

Output contexts

Let’s quickly recap on how Sonic Pi’s synths and FX output their audio to their current FX context. For example, consider the following:

with_fx :reverb do    # C
  with_fx :echo do    # B
    sample :bd_haus   # A
  end
end

The simplest way to understand what’s happening with the audio stream is to start at the innermost audio context and work our way out. In this case, the innermost context is labelled A and is the :bd_haus sample being triggered. The audio for this goes directly into its context which is B - the :echo FX. This then adds echo to the incoming audio and outputs it to its context which is C - the :reverb FX. This then adds reverb to the incoming audio and outputs to its context which is the top level - the left and right speakers (outputs 1 and 2 in your audio card). The audio flows outwards with a stereo signal all the way through.

Sound Out FX

The above behaviour is true for all synths (including live_audio) and the majority of FX with the exception of :sound_out. The :sound_out FX does two things. Firstly it outputs its audio to its external context as described above. Secondly it also outputs its audio directly to an output on your sound card. Let’s take a look:

with_fx :reverb do      # C
  with_fx :sound_out, output: 3 do # B
    sample :bd_haus     # A
  end
end

In this example, our :bd_haus sample outputs its audio to its external context which is the :sound_out FX. This in turn outputs its audio to its external context the :reverb FX (as expected). However, it also outputs a mono mix to the 3rd output of the system’s soundcard. The audio generated within :sound_out therefore has two destinations - the :reverb FX and audio card output 3.

Mono and Stereo out

As we’ve seen, by default, the :sound_out FX outputs a mono mix of the stereo input to a specific channel in addition to passing the stereo feed to the outer context (as expected). If outputting a mono mix isn’t precisely what you want to do, there are a number of alternative options. Firstly, by using the mode: opt you can choose to output just the left or just the right input signal to the audio card. Or you can use the :sound_out_stereo FX to output to two consecutive sound card outputs. See the function documentation for more information and examples.

Direct Out

As we have also seen, the default behaviour for :sound_out and :sound_out_stereo is to send the audio both to their external context (as is typical of all FX) and to the specified output on your soundcard. However, occasionally you may wish to only send to the output on your soundcard and not to the external context (and therefore not have any chance of the sound being mixed and sent to the standard output channels 1 and 2). This is possible by using the standard FX opt amp: which operates on the audio after the FX has been able to manipulate the audio:

with_fx :sound_out, output: 3, amp: 0 do # B
  sample :loop_amen                      # A
end

In the above example, the :loop_amen sample is sent to its outer context, the :sound_out FX. This then sends a mono mix to audio card output 3 and then multiplies the audio by 0 which essentially silences it. It is this silenced signal which is then sent out to the :sound_out’s outer context which is the standard output. Therefore with this code, the default output channels will not receive any audio, and channel 3 will receive a mono mix of the amen drum break.


14 - Заключение

На этом и завершается вводное руководство по Sonic Pi. Надеюсь, вы узнали что-нибудь по пути. Не волнуйтесь, если чувствуете, что не всё поняли, просто играйте и веселитесь, и всё придёт в своё время. Не стесняйтесь снова погрузиться в чтение этого учебника, когда у вас возникнут вопросы, которые могут быть раскрыты в одной из глав.

Если у вас есть вопросы, которые в учебнике освещены не были, то, пожалуйста, зайдите на Sonic Pi forums и задайте свой вопрос там. Вы обязательно найдёте там кого-нибудь доброжелательного и готового протянуть руку помощи.

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

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

Здесь нет ошибок, только возможности.

Sam Aaron


- Статьи MagPi

В Приложении A собраны статьи о Sonic Pi, выходившие в журнале MagPi.

Погружение В Тему

Эти статьи не были задуманы для чтения в каком-либо определённом порядке и содержат много перекрёстных ссылок на материалы из разных глав этого учебника. Вместо того, чтобы пытаться рассказать вам обо всём в Sonic Pi, эти статьи фокусируются на определённом аспекте и подают это в весёлой и доступной форме.

Читать В MagPi

Вы можете прочесть эти статьи в великолепном, профессионально оформленном виде, бесплатно загрузив их в формате PDF из MagPi

Предложить Тему

Если вы не находите интересующую вас тему в этих статьях - почему бы не предложить свою? Самый простой способ сделать это - твитнуть в @Sonic_Pi. Кто знает, возможно предложенная вами тема станет предметом следующей статьи!


- Пять Главных Советов

1. Здесь Нет Ошибок

Самый главный урок в обучении работе с Sonic Pi заключается в том, что здесь действительно нет ошибок. Лучший способ узнать это - пробовать, пробовать и ещё раз пробовать. Перестаньте беспокоиться о том, звучит ли ваш код хорошо или нет, и начните экспериментировать с как можно большим количеством различных синтезаторов, нот, эффектов и параметров. Вы откроете для себя множество вещей, которые заставят вас смеяться, потому что они звучат просто ужасно, но некоторые - настоящие жемчужины, звучащие просто потрясающе. Просто отбросьте то, что не нравится, и продолжайте делать то, что делаете. Чем больше “ошибок” вы позволите себе совершить, тем быстрее вы научитесь и откроете ваш собственный уникальный звук.

2. Используйте Эффекты

Допустим, вы уже освоили основы создания звука в Sonic Pi с помощью sample и play. Что дальше? Знаете ли вы, что Sonic Pi поддерживает более 27 студийных эффектов для изменения звука вашего кода? Эффекты похожи на причудливые фильтры изображений в графических редакторах, за исключением того, что вместо размытия или замены чего-то цветного чёрно-белым, вы можете использовать в своём звуке такие вещи, как реверберация, искажения и эхо. Думайте об этом, как о подключении кабеля от вашей гитары к педали эффектов, которую вы выбрали, а затем к усилителью. К счастью, Sonic Pi делает использование эффектов очень простым и не требует никаких проводов! Всё что вам нужно сделать - это выбрать, какой именно фрагмент кода вы бы обработали эффектом, и обернуть его в FX код. Давайте рассмотрим пример. Скажем, у вас есть следующий код:

sample :loop_garzul
 
16.times do
  sample :bd_haus
  sleep 0.5
end

Если вы хотите добавить эффект в :loop_garzul сэмпл, просто поместите его внутрь with_fx блока, как здесь:

with_fx :flanger do
  sample :loop_garzul
end
 
16.times do
  sample :bd_haus
  sleep 0.5
end

Теперь, если вы захотите добавить эффект к басовому барабану, оберните его with_fx тоже:

with_fx :flanger do
  sample :loop_garzul
end
 
with_fx :echo do
  16.times do
    sample :bd_haus
    sleep 0.5
  end
end

Помните, вы можете обернуть любой код в with_fx, и любые созданные звуки будут проходить через выбранный эффект.

3. Параметризируйте Ваши Синтезаторы

Для того, чтобы создать действительно уникальный кодированный звук, вы, вероятно, захотите узнать как модифицировать и контролировать синтезаторы и эффекты. Например, вы можете захотеть изменить длительность ноты, добавить больше реверберации, или изменить время между повторами эхо. К счастью, Sonic Pi даёт вам удивительный уровень контроля с помощью специальных штук, называемых параметры. Давайте окинем их беглым взглядом. Скопируйте этот код в свободный буфер и нажмите “Выполнить”:

sample :guit_em9

О, прекрасный звук гитары! Теперь, давайте начнём играть с ним. Как насчёт изменения его скорости?

sample :guit_em9, rate: 0.5

Эй, что это за rate: 0.5 я добавил в конце? Это называется параметр. Все синтезаторы и эффекты в Sonic Pi поддерживают их, и их тут множество. Они также доступны и для эффектов. Попробуйте это:

with_fx :flanger, feedback: 0.6 do
  sample :guit_em9
end

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

4. Лайвкодинг

Лучший способ быстро экспериментировать и исследовать Sonic Pi - кодирование в реальном времени. Это позволяет вам начать выполнять какой-то код, постоянно изменяя и подправляя его в то время, как он звучит. Например, если вы не знаете, что делает с сэмплом параметр частоты среза фильтра, просто играйте его по кругу и экспериментируйте. Давайте пробовать! Скопируйте этот код в буфер вашего Sonic Pi:

live_loop :experiment do
  sample :loop_amen, cutoff: 70
  sleep 1.75
end

Теперь, нажмите “Выполнить” и вы будете слышать чуть приглушенный барабанный брейк. Измените значение cutoff: на 80 и нажмите “Выполнить” снова. Слышите разницу? Попробуйте 90, 100, 110

Как только вы зависните с live_loop, вы уже никогда не вернётесь обратно. Всякий раз, когда я делаю лайвкодинг концерт, я завишу от live_loop так, как ударник зависит от своих палочек. Для получения дополнительной информации о лайвкодинге, проверьте раздел 9, встроенного учебника.

5. Поток Случайных Нот

Наконец, ещё одна вещь, которую я люблю делать, это давать Sonic Pi сочинять за меня музыку. Действительно крутой способ сделать это - использовать рандомизацию. Это может показаться сложным, но на самом деле это не так. Давайте проверим. Скопируйте это в свободное рабочее пространство:

live_loop :rand_surfer do
  use_synth :dsaw
  notes = (scale :e2, :minor_pentatonic, num_octaves: 2)
  16.times do
    play notes.choose, release: 0.1, cutoff: rrand(70, 120)
    sleep 0.125
  end
end

Когда вы запустите это, вы услышите постоянный поток случайных нот гаммы :Е2 :minor_pentatonic, сыгранную :dsaw синтезатором. “Подожди, подожди! Это не мелодия”, слышу я ваш возглас! Ну, это первая часть магического трюка. Каждый раз, когда мы проходим круг цикла live_loop, мы можем сказать Sonic Pi сбросить случайный поток к известной начальной точке. Это как будто возвращаться во времени в ТАРДИС С ДОКТОРОМ к определённой точке во времени и пространстве. Давайте попробуем это - добавьте строку use_random_seed 1 к live_loop:

live_loop :rand_surfer do
  use_random_seed 1
  use_synth :dsaw
  notes = (scale :e2, :minor_pentatonic, num_octaves: 2)
  16.times do
    play notes.choose, release: 0.1, cutoff: rrand(70, 120)
    sleep 0.125
  end
end

Теперь каждый раз, как цикл live_loop повторяется, случайный поток будет сброшен. Это значит, что он выбирает одни и те же 16 нот каждый раз. Мгновенная мелодия. Теперь это действительно захватывающий бит. Изменитьте значение отправной точки с 1 на другое число. Скажем, 4923. Ого! Другая мелодия! Так, просто изменяя одну цифру (отправную точку), вы можете открыть столько мелодических комбинаций, сколько сможете себе представить! Это и есть магия кода.


- Лайв-кодинг

Лазерные лучи прорезали дым так же глубоко, как сабвуфер прокачивал низами танцующие тела. В атмосфере царила пьянящая смесь синтезаторов и танца. Однако, что-то в этом ночном клубе было не так… Яркими красками над кабиной диджея светился футуристический текст, движущийся, танцующий, вспыхивающий. Но это не были фантастические визуальные эффекты, это была лишь проекция Sonic Pi, запущенного на Raspberry Pi. Обитатель кабины диджея не крутил дисков, он писал, редактировал и запускал код. В реальном времени. Это был лайвкодинг.

Sam Aaron Live Coding

Это может звучать как притянутая за уши история из футуристичного ночного клуба, но кодирование музыки - это развивающийся тренд, часто называемый Лайвкодинг (http://toplap.org). Одним из новых направлений этого способа создания музыки является Алгорэйв (http://algorave.com) - мероприятие, где такие артисты как я, кодируют музыку для танцующих людей. Однако, вам не надо быть в ночном клубе, чтобы кодировать в реальном времени - с Sonic Pi v2.6+ вы можете делать это в любом месте, в которое сможете взять ваш Raspberry Pi и пару наушников или колонок. Как только вы дойдёте до конца этой статьи, вы будете программировать свой собственный бит и изменять его вживую. То, где вы окажетесь после этого, может быть ограничено только вашим воображением.

Живой Цикл

Ключ к лайвкодингу с Sonic Pi - овладение циклами live_loop. Давайте посмотрим на один из них:

live_loop :beats do
  sample :bd_haus
  sleep 0.5
end

Есть 4 основные составляющие циклов live_loop. Первая - это имя. Наш live_loop называется :beats. Вы вольны называть свой live_loop так, как вам угодно. С ума сойти. Подойдите к этому творчески. Обычно я использую имена циклов, что-то говорящие о музыке, которую они делают. Вторая состовляющая - это слово do, которое отмечает, где цикл начинается. Третья состовляющая - слово end, которое отмечает, где цикл заканчивается, и, наконец, есть тело цикла live_loop, которое описывает, что цикл повторяет - это часть кода между do и end. В данном случае мы многократно воспроизводим сэмпл бас бочки и ждём пол тактовой доли. Это производит приятный ровный ритм. Скопируйте этот код в пустой Sonic Pi буфер и нажмите “Выполнить”. Бум, Бум, Бум!.

Изменения На Лету

Итак, что такого особенного в цикле live_loop? Он отдалённо напоминает усовершенствованный цикл loop. Преимущество live_loop в том, что вы можете изменять его на лету. Это значит, что пока выполняется цикл, вы можете изменить то, что он делает. Это и есть секрет лайвкодинга с Sonic Pi. Давайте играть:

live_loop :choral_drone do
  sample :ambi_choir, rate: 0.4
  sleep 1
end

Теперь, нажмите кнопку “Выполнить” или Alt-r. Вы услышите великолепный звук хора. Теперь, пока он ещё играет, измените скорость с 0.4 на 0.38. Нажмите “Выполнить” снова. Ого! Вы слышите, как хор сменил ноту? Измените скорость обратно на 0.4, чтобы вернуться к тому, что было раньше. Теперь, снизьте скорость до 0.2, потом до 0.19, а затем обратно до 0.4. Видите, как изменение на лету одного лишь параметра, даёт вам реальный контроль над музыкой? Теперь поиграйте со скоростью самостоятельно - выберите свои собственные значения. Попробуйте отрицательные числа, попробуйте очень маленькие и очень большие числа. Повеселитесь!

Сон Важен

Один из самых важных аспектов цикла live_loop в том, что в нём должна быть задержка. Рассмотрим следующий код:

live_loop :infinite_impossibilities do
  sample :ambi_choir
end

Если вы попробуете запустить этот код, вы сразу увидите жалобу Sonic Pi на то, что в live_loop нет sleep. Это ругается система безопасности! Воспользуйтесь моментом, чтобы подумать о том, что этот код просит сделать компьютер. Правильно, он просит компьютер играть бесконечное количество хоровых сэмплов за нулевое время. Без системы безопасности бедный компьютер будет пытаться сделать это, что приведёт к аварии. Поэтому помните, ваш live_loop должен содержать sleep.

Сочетание Звуков

Музыка полна вещей, происходящих одновременно. Барабаны звучат в то же самое время, что и бас, вокал, гитары… В информатике это называется параллелизм и Sonic Pi обеспечивает нам удивительно простой способ играть разные вещи в одно и то же время. Просто используйте больше, чем один live_loop!

live_loop :beats do
  sample :bd_tek
  with_fx :echo, phase: 0.125, mix: 0.4 do
    sample  :drum_cymbal_soft, sustain: 0, release: 0.1
    sleep 0.5
  end
end
  
live_loop :bass do
  use_synth :tb303
  synth :tb303, note: :e1, release: 4, cutoff: 120, cutoff_attack: 1
  sleep 4
end

Здесь у нас есть два цикла live_loop, один цикл делает быстрый ритм, а другой - медленный сумасшедший басовый звук.

Одна из особенностей использования нескольких циклов live_loop состоит в том, что каждый из них обладает своим собственным независимым временем. Это значит, что с помощью этого легко создавать интересные полиритмические структуры и даже поиграть с фазировкой в стиле Стива Рейха (Steve Reich). Зацени:

# Steve Reich's Piano Phase
  
notes = (ring :E4, :Fs4, :B4, :Cs5, :D5, :Fs4, :E4, :Cs5, :B4, :Fs4, :D5, :Cs5)
  
live_loop :slow do
  play notes.tick, release: 0.1
  sleep 0.3
end
  
live_loop :faster do
  play notes.tick, release: 0.1
  sleep 0.295
end

Соединим Всё Вместе

Каждую из этих статей мы будем заканчивать примером в виде фрагмента музыкального произведения, который вырисовывается из всех представленных в статье идей. Прочтите этот код и посмотрите, сможете ли вы понять, что он делает. Затем скопируйте его в свежий буфер Sonic Pi, нажмите “Выполнить” и послушайте, как он самом деле звучит. Наконец, измените одно из чисел или закомментируйте/раскомментируйте какие-нибудь строки. Посмотрите, сможете ли вы использовать этот пример в качестве отправной точки для нового выступления, и самое главное, веселитесь! До встречи в следующий раз…

with_fx :reverb, room: 1 do
  live_loop :time do
    synth :prophet, release: 8, note: :e1, cutoff: 90, amp: 3
    sleep 8
  end
end
  
live_loop :machine do
  sample :loop_garzul, rate: 0.5, finish: 0.25
  sample :loop_industrial, beat_stretch: 4, amp: 1
  sleep 4
end
  
live_loop :kik do
  sample :bd_haus, amp: 2
  sleep 0.5
end
  
with_fx :echo do
  live_loop :vortex do
    # use_random_seed 800
    notes = (scale :e3, :minor_pentatonic, num_octaves: 3)
    16.times do
      play notes.choose, release: 0.1, amp: 1.5
      sleep 0.125
    end
  end
end

- Кодирование ритмов

Одной из самых захватывающих и разрушающих технических разработок современной музыки, стало изобретение сэмплеров. Это были коробки, которые позволяли записывать в них любой звук, а затем воспроизводить и управлять этим звуком множеством различных способов. Например, можно взять старую записать, найти барабанное соло (или брэйк), записать его в свой сэмплер и воспроизводить на половинной скорости, обеспечивая основу для своих собственных ритмов. Так зарождался ранний хип-хоп, и сегодня практически невозможно найти электронную музыку, в составе которой нет сэмплов в том или ином виде. Использование сэмплов - это действительно отличный способ легко внедрять новые интересные элементы в ваше лайвкодинг выступление.

Так где же можно получить сэмплер? Ну, у вас уже есть один - это ваш Raspberry Pi! Идущее в комплекте приложение для лайвкодинга Sonic Pi имеет чрезвычайно мощный сэмплер, встроенный в его ядро. Давайте с ним поиграем!

Амен-брейк

Один из самых узнаваемых сэмплов классических барабанных брейков называется Амен-брейк. Он впервые прозвучал в 1969 году в песне “Аминь, Брат”, исполнителя “Winstons”, как часть барабанного брейка. Тем не менее, когда он был обнаружен ранними хип-хоп музыкантами в 80-х годах и начал использоваться в сэмплерах, он стал активно внедряться и в другие стили, такие как драм-эн-бэйс, брейкбит, хардкор, техно и брейк-кор.

Я уверен, что вы будете взволнованы, узнав, что он также входит в состав Sonic Pi. Очистите буфер и поместите в него следующий код:

sample :loop_amen

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

Растяжение Ритмов

Давайте зациклим амен-брейк, используя наш старый добрый цикл live_loop, представленный в прошлом выпуске этого журнала:

live_loop :amen_break do
  sample :loop_amen
  sleep 2
end

ОК, мы его зациклили, но при каждом повторе есть неприятная пауза. Это потому, что мы сказали Sonic Pi ждать 2 тактовые доли, а с учётом того, что по умолчанию BPM (ударов в минуту) равно 60, сэмпл :loop_amen длится только 1.753 удара. Поэтому мы имеем тишину 2 - 1.753 = 0.247 удара. Даже не смотря на то, что она довольно короткая, она всё равно заметна.

Чтобы устранить эту проблему, мы можем воспользоваться параметром beat_stretch:, чтобы сказать Sonic Pi растянуть (или сжать) сэмпл согласно установленнму количеству ударов.

Функции Sonic Pi sample и synth дают вам контроль через необязательные параметры, такие как amp:, cutoff: и release:. Однако, термин “необязательный параметр” - это слишком длинное название, поэтому для простоты будем называть их просто параметры.

live_loop :amen_break do
  sample :loop_amen, beat_stretch: 2
  sleep 2
end

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

Играем Со Временем

ОК, но что, если мы хотим изменить стиль на олдскульный хип-хоп или брейк-кор? Один из простейших способов сделать это - играть со временем - или, другими словами, сдвинуть темп. В Sonic Pi это супер просто - добавьте use_bpm в живой цикл:

live_loop :amen_break do
  use_bpm 30
  sample :loop_amen, beat_stretch: 2
  sleep 2
end 

Пока вы будете читать рэп под этот медленный ритм, обратите внимание, что мы до сих пор ждём 2 удара, и хотя наш BPM 30, всё продолжает звучать пропорционально. Параметр beat_stretch работает с текущим BPM, чтобы быть уверенным, что всё будет работать исправно.

Теперь самое интересное. Пока цикл всё ещё исполняется, изменми 30 в строке use_bpm 30 на50. Ого, всё ускорилось, но продолжает звучать равномерно во времени! Попытайтесь разогнаться ещё быстрее - до 80, до 120, или, сойдите с ума, и дайте 200!

Фильтрация

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

live_loop :amen_break do
  use_bpm 50
  sample :loop_amen, beat_stretch: 2, cutoff: 70
  sleep 2
end

Давайте изменим параметр cutoff:. Например, увеличте его до 100, нажмите Выполнить и ждите, пока круг цикла завершится, чтобы услышать изменения в звуке. Обратите внимание, что низкие значения, такие как 50, дают сочный звук и басы, а высокие значения, типа 100 и 120, дают более полное звучание и больше высокочастотных составляющих. Это происходит потому, что параметр cutoff: срубает высокочастотную область звука так же, как газонокосилка срезает верхнюю часть травы. Параметр cutoff: определяет сколько “травы” оставлять.

Нарезка

Еще один отличный инструмент, с которым можно поиграть - это эффект slicer. Он позволяет “нарезать” звук сэмпла. Оберните строку sample кодом эффекта, как здесь:

live_loop :amen_break do
  use_bpm 50
  with_fx :slicer, phase: 0.25, wave: 0, mix: 1 do
    sample :loop_amen, beat_stretch: 2, cutoff: 100
  end
  sleep 2
end

Обратите внимание, как звук стал более упругим (вы можете услышать оригинальное звучание сэмпла без эффекта, установив параметр mix: в 0.) Теперь поиграйтесь с параметром phase:. Это размер (в ударах) нарезки. Меньшее значение, такое как 0.125, нарежет сэмпл более короткими кусками и в большем количестве, чем значение 0.5, при котором нарезка будет реже и толще. Обратите внимание, что удвоения значений phase, как правило, всегда звучат хорошо. Наконец, измените параметр wave:, выбрав из 0, 1 или 2, и послушайте, как меняется звук. Это три разные формы волны. 0 - это пилообразная волна, (резко начинается, плавно затухает), 1 - прямоугольная волна (резко начинается, резко обрывается), и 2 - треугольная волна (плавно нарастает, плавно затухает).

Соединим Всё Вместе

Наконец, давайте вернёмся назад во времени, и вновь посетим раннюю Бристольскую драм-эн-бэйс сцену. Не волнуйтесь о том, что всё это значит, просто введите этот код в пустой буфер, нажмите “Выполнить”, и начинайте лайвкодинг. Изменяйте значения параметров и подумайте, где бы вы смогли применить это. Пожалуйста, поделитесь тем, что у вас получилось! Увидимся в следующий раз…

use_bpm 100
  
live_loop :amen_break do
  p = [0.125, 0.25, 0.5].choose
  with_fx :slicer, phase: p, wave: 0, mix: rrand(0.7, 1) do
    r = [1, 1, 1, -1].choose
    sample :loop_amen, beat_stretch: 2, rate: r, amp: 2
  end
  sleep 2
end
  
live_loop :bass_drum do
  sample :bd_haus, cutoff: 70, amp: 1.5
  sleep 0.5
end
  
live_loop :landing do
  bass_line = (knit :e1, 3, [:c1, :c2].choose, 1)
  with_fx :slicer, phase: [0.25, 0.5].choose, invert_wave: 1, wave: 0 do
    s = synth :square, note: bass_line.tick, sustain: 4, cutoff: 60
    control s, cutoff_slide: 4, cutoff: 120
  end
  sleep 4
end

- Синтезаторные Риффы

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

Итак, подключите ваш Paspberry Pi, откройте Sonic Pi версии v2.6+ и давайте пошумим!

Тембральные Возможности

Неотъемлемой частью любого синтезаторного риффа является его тембр. Мы можем изменять тембр в Sonic Pi двумя способами - выбирая различные синты для резкой смены тембра, и изменяя различные синтовые параметры для более тонкой настройки. Мы также можем использовать эффекты, но об этом в другой раз…

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

live_loop :timbre do
  use_synth (ring :tb303, :blade, :prophet, :saw, :beep, :tri).tick
  play :e2, attack: 0, release: 0.5, cutoff: 100
  sleep 0.5
end

Взгляните на код. Мы просто перебираем по кругу элементы кольцевого списка, содержащего имена синтов (цикл переключает синт на следующий в списке снова и снова). Мы передаём имена этих синтов функции use_synth, которая и переключает текущий синтезатор цикла live_loop. Ещё мы играем ноту :Е2 (ми второй октавы), со временем затухания 0.5 удара (пол секунды с ВРМ по умолчанию 60) и с параметром cutoff:, равным 100.

Слышите, разные синтезаторы звучат совершенно по-разному, хотя играют одну и ту же ноту? Теперь давайте поэкспериментируем. Измените время затухания на большее или меньшее значение. Изменяйте оба параметра - attack: и release:, чтобы посмотреть, как сильно разные их значения изменяют звук. Наконец, измените параметр cutoff:, чтобы услышать, что разные значения среза фильтра также радикально изменяют тембр синта (значения между 60 и 130 обычно звучат хорошо). Посмотрите, сколько разнообразных звуков можно создать, изменяя всего лишь несколько параметров. Как только вы это освоите, откройте вкладку “Синтезаторы” справочной системы, и взгляните на полный список синтезаторов и их параметров, чтобы увидеть, какую мощь вы держите в руках.

Тембр

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

Мелодическая Структура

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

live_loop :riff do
  use_synth :prophet
  riff = (ring :e3, :e3, :r, :g3, :r, :r, :r, :a3)
  play riff.tick, release: 0.5, cutoff: 80
  sleep 0.25
end

Здесь мы поместили нашу мелодию в кольцевой список, содержащий ноты, такие как :е3 и паузы, обозначенные :r. Затем, мы использовали .tick, чтобы получить повторяющийся рифф.

Автомелодия

Не всегда бывает легко придумать хороший рифф с нуля. Вместо этого, иногда проще сказать Sonic Pi играть случайный рифф, и выбрать тот, который понравится вам больше всего. Для этого нам необходимо объединить три вещи: кольца, рандомизацю и отправную точку (зерно) рандомизаци. Взгляните на пример:

live_loop :random_riff do
  use_synth :dsaw
  use_random_seed 3
  notes = (scale :e3, :minor_pentatonic).shuffle
  play notes.tick, release: 0.25, cutoff: 80
  sleep 0.25
end

Здесь происходит несколько вещей - давайте рассмотрим их по очереди. Во-первых, мы указли, что используем зерно рандомизаци 3. Что это значит? Ну, смысл в том, что когда мы устанавливаем зерно, мы можем предсказать, какой будет рандомизаця в следующий раз. Той же самой, что и в тот раз, когда мы устанавливали зерно в 3! Ещё одна вещь, которую полезно знать, что перемешивание нот кольца будет произведено тем же способом. В примере выше, мы, по сути, вызываем “третье перемешивание” из стандартного списка перемешиваний, который остаётся неизменным всякий раз, когда мы устанавливаем зерно в то же значение перед перемешиванием. Наконец, мы просто вызываем по кругу наши перемешанные ноты, чтобы получить рифф.

Вот где начинается веселье. Если мы меняем значение зерно рандомизаци на другое, скажем 3000, мы получаем совершенно другое перемешивание нот. Так что теперь очень легко исследовать новые мелодии. Просто выберите список нот, которые вы хотите перемешать (гаммы представляют собой отличную отправную точку), а затем выберите зерно рандомизаци, с которого вы хотите начать перемешивание. Если вам не нравится мелодия, просто измените одну из этих двух вещей и попробуйте снова. Повторяйте до тех пор, пока вам понравится то, что вы слышите!

Псевдо Рандомизация

Случайный выбор в Sonic Pi на самом деле не является случайным, это то, что обычно называется псевдо рандомизация. Представьте, что вы бросали игральные кости 100 раз и записывали результат каждого броска на лист бумаги. Sonic Pi имеет эквивалент этого списка результатов, которые он использует, когда вы запрашиваете случайные значения. Только вместо бросков настоящих костей, он выбирает следующее значение из списка. Настройка зерна рандомизаци - это просто прыжок в определённое место этого списка.

Поиск Ритма

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

live_loop :random_riff do
  use_synth :dsaw
  use_random_seed 30
  notes = (scale :e3, :minor_pentatonic).shuffle
  16.times do
    play notes.tick, release: 0.2, cutoff: 90 if one_in(2)
    sleep 0.125
  end
end

Очень полезно знать функцию one_in, которая возвращает нам true или false с заданной вероятностью. Здесь мы используем значение 2, поэтому в среднем один раз каждые два вызова, one_in возвратит true. Другими словами, она будет возвращать true в 50% случаев. Если использовать более высокие значения, falseбудет возвращаться чаще, добавляя в рифф больше пауз.

Заметим, что мы добавили повторы 16.times. Это потому, что мы хотим сбрасывать наше зерно рандомизаци каждые 16 нот. Так наш ритм будет повторяться каждые 16 ударов. Это не влияет на перемешивание, т.к. оно выполняется сразу после того, как установлено зерно рандомизаци. Мы можем использовать разное количество повторов для изменения длины риффа. Попробуйте изменить 16 на 8 или даже на 4 или 3 и посмотрите, как это повлияет на ритм.

Соединим Всё Вместе

ОК, теперь давайте совместим всё, чему мы научились, в один финальный пример. Увидимся в следующий раз!

live_loop :random_riff do
  #  uncomment to bring in:
  #  synth :blade, note: :e4, release: 4, cutoff: 100, amp: 1.5
  use_synth :dsaw
  use_random_seed 43
  notes = (scale :e3, :minor_pentatonic, num_octaves: 2).shuffle.take(8)
  8.times do
    play notes.tick, release: rand(0.5), cutoff: rrand(60, 130) if one_in(2)
    sleep 0.125
  end
end
 
live_loop :drums do
  use_random_seed 500
  16.times do
    sample :bd_haus, rate: 2, cutoff: 110 if rand < 0.35
    sleep 0.125
  end
end
 
live_loop :bd do
  sample :bd_haus, cutoff: 100, amp: 3
  sleep 0.5
end

- Кислотный Бас

Невозможно исследовать историю электронной танцевальной музыки, и не заметить огромного влияния крошечного синтезатора Roland TB-303. Это секретный ингридиент оригинального кислотного баса. Классическое визжание и чавканье басовых риффов TB-303 можно услышать в ранней чикагской хаус сцене благодаря таким музыкантам, как Plastikman, Squarepusher и Aphex Twin.

Интересно, что инженеры Roland никогда не предполагали, что TB-303 будет использоваться в танцевальной музыке. Он изначально создавался для гитаристов. Они расчитывали, что гитаристы будут программировать TB-303 играть басовые линии в джемах. К сожалению, существовал ряд проблем: они были немного неудобными для программирования, не очень хорошо звучали в качестве замены бас-гитары и были довольно дорогими. Решив сократить убытки, Roland прекратили их выпуск после продажи 10 000 экземпляров, и после нескольких лет лежания на полках, эи синтезаторы можно было найти только в магазинах “секонд хенд”. Эти одинокие, отвергнутые TB-303 ждали, когда их обнаружит новое поколение экспериментаторов, которые начали применять их способом, который в Roland и представить не могли, создавая новые сумасшедшие звуки. Так зародился эйсид хаус.

Хотя получить на руки оригинал TB-303 не так уж и просто, вам будет приятно узнать, что вы можете превратить ваш Raspberry Pi в один из них, используя мощь Sonic Pi. Узрите свет Sonic Pi, забросив этот код в пустой буфер и нажав “Выполнить”:

use_synth :tb303
play :e1

Мгновенный кислотный бас! Давайте с ним поиграем…

- Изменяем Бас

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

use_synth :tb303
live_loop :squelch do
  n = (ring :e1, :e2, :e3).tick
  play n, release: 0.125, cutoff: 100, res: 0.8, wave: 0
  sleep 0.125
end

Для начала рассмотрим код построчно:

В первой строке мы задаём синтезатор по умолчанию tb303 с помощью функции use_synth. Во второй строке мы создаем живой цикл :squelch, который будет просто повторяться снова и снова. Третья строка - та, где мы создаём наш рифф - кольцо нот (ми в октавах 1, 2 и 3), которые мы извлекаем по кругу с помощью .tick. Мы определяем n для представления текущей ноты в риффе. Знак равенства присваивает значение справа, имени, находящемуся слева. Это значение будет отличаться каждый круг цикла. Первый круг n будет иметь значение :е1. Второй круг - :е2, затем :е3, потом снова :е1 и так круг за кругом. Четвёртая строка - место, где мы включаем наш :tb303. У нас здесь есть несколько интересных параметров: release:, cutoff:, res: и wave:, которые мы обсудим чуть позже. В пятой строке появляется sleep - мы просим цикл замыкать круг каждые 0.125 удара, или 8 раз в секунду с BPM по умолчанию 60. Строка шесть - это end живого цикла. Мы просто говорим Sonic Pi, что здесь конец живого цикла.

В то время, как вы всё ещё пытаетесь понять, что здесь происходит, поместите этот код в пустой буфер, и нажмите кнопку “Выполнить”. Вы должны услышать :tb303 в действии. Теперь пора начинать лайвкодинг.

В то время, как цикл исполняется, измените параметр cutoff: на 110. Затем нажмите кнопку “Выполнить” снова. Вы должны услышать, что звук стал немного жёстче и более “хлюпающим”. Наберите 120 и нажмите “Выполнить”. Затем 130. Слушайте, как более высокие значения делают звук более пронзительным и интенсивным. Наконец, понизьте значение до 80, если захотите отдохнуть. Затем повторите это столько раз, сколько захотите. Не волнуйтесь, я всё ещё буду здесь…

Ещё один параметр, с которым стоит поиграть, это res:. Он контролирует уровень резонанса фильтра. Для кислотных басовых партий характерен высокий резонанс. В настоящее время наш res: установлен в 0.8. Попробуйте изменить его на 0.85, затем на 0.9, и наконец, на 0.95. Вы можете обнаружить, что предельные значения cutoff:, такие как 110 или выше, помагают легче услышать различия. Наконец, сойдите с ума и установите 0.999, чтобы получить действительно безумный звук. С этим значением res: вы слышите так много резонанса, что он начинает звучать сам по себе!

Наконец, для резкого изменения тембра попробуйте изменить параметр wawe: на 1. Это выбор формы волны исходного генератора. Значение по умолчанию - 0 - пилообразная волна, 1 - прямоугольная волна, и 2 - треугольная форма волны.

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

- Деконструирование TB-303

Конструкция оригинальных TB-303 на самом деле довольно проста. Как вы можете видеть из следующей диаграммы, главных составляющих всего 4.

TB-303 Design

Первой является волновой герератор - сырая основа звука. В этом примере у нас прямоугольная волна. Дальше идёт генератор амплитудной огибающей, который контролирует амплитуду прямоугольной волны во времени. Он доступен в Sonic Pi в виде параметров attack:, decay:, sustain: и release:. Для получения более подробной информации об этих параметрах читайте раздел 2.4 “Длительность с помощью огибающих” встроенного учебника. Затем, мы пропускаем наш амплитудно модулированный сигнал через резонансный низкочастотный фильтр. Это срубает высокочастотную составляющую, а также добавляет этот приятный резонансный эффект. Теперь начинается самое весёлое. Значение среза этого фильтра может управляться своей собственной огибающей! Это значит, что мы имеем удивительный контроль над тембром звука, играя двумя этими огибающими. Давайте приступим:

use_synth :tb303
with_fx :reverb, room: 1 do
  live_loop :space_scanner do
    play :e1, cutoff: 100, release: 7, attack: 1, cutoff_attack: 4, cutoff_release: 4
    sleep 8
  end
end

В синтезаторе :tb303, для каждого стандартного параметра огибающей есть cutoff_ эквивалент. Так, для изменения времени атаки фильтра, мы можем использовать параметр cutoff_attack:. Скопируйте приведенный выше код в пустой буфер, и нажмите “Выполнить”. Вы услышите безумный плавающий звук. Теперь поиграем. Попробуйте изменять время cutoff_attack: на 1, а затем на 0.5. Потом попробуйте 8.

Заметьте, что я пропустил звук через :reverb эффект для создания дополнительной атмосферы - попробуйте другие эффекты, чтобы посмотреть, как это работает!

- Соединим Всё Вместе

Наконец, ниже расположен фрагмент кода, который я сочинил, используя идеи этой статьи. Скопируйте его в свой буфер, послушайте, а потом начните лайвкодинг, внося свои собственные изменения. Посмотрим, какие безумные звуки вы сможете создать с помощью этого! Увидимся в следующий раз…

use_synth :tb303
use_debug false
 
with_fx :reverb, room: 0.8 do
  live_loop :space_scanner do
    with_fx :slicer, phase: 0.25, amp: 1.5 do
      co = (line 70, 130, steps: 8).tick
      play :e1, cutoff: co, release: 7, attack: 1, cutoff_attack: 4, cutoff_release: 4
      sleep 8
    end
  end
 
  live_loop :squelch do
    use_random_seed 3000
    16.times do
      n = (ring :e1, :e2, :e3).tick
      play n, release: 0.125, cutoff: rrand(70, 130), res: 0.9, wave: 1, amp: 0.8
      sleep 0.125
    end
  end
end

- Музыкальный Minecraft

Привет, и с возвращением! В предыдущих упражнениях мы сосредотачивались исключительно на музыкальных возможностях Sonic Pi - (превращая ваш Raspberry Pi в готовый к выступлению музыкальный инструмент). К этому моменту мы научились:

Кодировать в реальном времени, Создавать различные ритмы, Генерировать мощные солирующие синты, Воссоздавать знаменитые кислотные басы TB-303.

Однако, есть ещё множество аспектов создания музыки, которые нужно показать вам (что я и сделаю в будущем). Но в этом месяце мы рассмотрим особенность Sonic Pi, о наличии которой вы, наверное, даже и не подозревали: управлять Minecraft.

- Привет, Мир Minecraft

ОК, давайте начнём. Подключите ваш Raspberry Pi, запустите Minecraft Pi и создайте новый мир. Теперь запустите Sonic Pi, измените размеры окон и разместите их так, чтобы вы могли видеть одновременно и Minecraft Pi, и Sonic Pi.

В пустом буфере наберите следующее:

mc_message "Hello Minecraft from Sonic Pi!"

Теперь, нажмите “Выполнить”. Бум! Ваше сообщение появилось в Minecraft! Ну как, это было легко? Теперь, прекратите на мгновение чтение этой статьи и поиграйте с вашими собственными сообщениями. Веселитесь!

Screen 0

- Sonic Телепортатор

Теперь давайте исследовать мир Minecraft. Стандартный вариант - дотянуться до мыши и клавиатуры и начать перемещаться в мире Minecraft. Это работает, но это довольно медленно и скучно. Было бы гораздо интереснее, если бы у нас было что-то вроде телепортатора. Благодаря Sonic Pi, мы имеем один. Попробуйте это:

mc_teleport 80, 40, 100

Боже мой! Это был долгий путь вверх. Если вы не находились в режиме полёта, то должны были упасть обратно на землю. Если вы дважды нажмёте пробел для входа в режим полёта и телепортируетесь снова, вы останетесь висеть на том месте, куда вы в телепортировались.

Итак, что же означают эти цифры? У нас есть три числа, которые описывают координаты места, куда мы хотим переместиться. Мы даём каждой цифре имя - х, y и z:

x - как далеко от левого края (в нашем примере - 80) y - как высоко (в нашем примере - 40) z - как далеко вглубь мира (в нашем примере - 100)

Выбирая различные значения для x, y и z, мы можем телепортироваться куда угодно. Попробуйте! Выбирайте различные цифры и смотрите, где вы окажетесь. Если экран стал чёрным, это произошло потому, что вы телепортировали себя под землю или внутрь скалы. Просто выбирите значение y выше, чтобы оказаться снова над землёй. Продолжайте исследования, пока не найдёте место, которое вам понравится…

Используя эти идеи, построим телепортатор, который издаёт весёлый звук телепортации, пока переносит нас сквозь мир Minecraft:

mc_message "Preparing to teleport...."
sample :ambi_lunar_land, rate: -1
sleep 1
mc_message "3"
sleep 1
mc_message "2"
sleep 1
mc_message "1"
sleep 1
mc_teleport 90, 20, 10
mc_message "Whoooosh!"

Screen 1

- Магические Блоки

Сейчас, когда вы нашли хорошее место, начнём строительство. Вы могли бы делать то, к чему привыкли, и начать яростно кликать мышкой, распологая блоки под курсором. Или вы могли бы использовать магию Sonic Pi. Попробуйте это:

x, y, z = mc_location
mc_set_block :melon, x, y + 5, z

Теперь посмотри вверх! В небе дынный блок! Найдите минутку, чтобы посмотреть на код. Что он делает? На первой строке мы определили текущее местоположение как переменные x, y и z. Они соответствуют нашим координатам, описанным выше. Мы использовали эти координаты в функции mc_set_block, которая поместила блок, который мы выбрали, по указанным координатам. Чтобы указать сделать что-то выше в небе, нам просто нужно увеличить значение y, вот почему мы добавляем 5 к нему. Сделаем длинный след из этих блоков:

live_loop :melon_trail do
  x, y, z = mc_location
  mc_set_block :melon, x, y-1, z
  sleep 0.125
end

Теперь переключитесь на Minecraft, убедитесь что вы находитесь в режиме полёта (двойное нажатие на пробел, если нет) и летайте по всему миру. Обернитесь, чтобы увидеть длинный след дынных блоков! Посмотрите, какие извилистые структуры вы можете сделать в небе.

- Лайвкодинг Minecraft

Тем из вас, кто следит за этими статьями в течение последних нескольких месяцев, наверное снесёт крышу к этому моменту. След дынных блоков это очень здорово, но самая захватывающая часть предыдущего пример заключается в том, что вы можете использовать live_loop с Minecraft! Для тех, кто не знает, цикл live_loop в Sonic Pi - особая магическая способность, которой нет других языках программирования. Он позволяет запускать несколько циклов в одно и то же время и изменять их, пока они выполняются. Они невероятно мощные и удивительно весёлые. Я использую live_loop, чтобы исполнять музыку в ночных клубах с Sonic Pi - диджеи используют диски, а я циклы live_loop:-) Тем не менее, сегодня мы собираемся кодировать в реальном времени и музыку, и Minecraft.

Давайте начнём. Запустите приведенный выше код и начните делать свой дынный след снова. Теперь, не останавливая код, просто измените :melon на :brick и нажмите “Выполнить”. Вуаля, вы теперь делаете кирпичную дорогу. Это было просто! Добавим немного музыки в это? Легко. Попробуйте это:

live_loop :bass_trail
  tick
  x, y, z = mc_location
  b = (ring :melon, :brick, :glass).look
  mc_set_block b, x, y -1, z
  note = (ring :e1, :e2, :e3).look
  use_synth :tb303
  play note, release: 0.1, cutoff: 70
  sleep 0.125
end

Теперь, пока это играет, начнём вносить изменения в наш код. Измените тип блока. Попробуйте :water, :grass или другой ваш любимый тип блока. Также, попробуйте изменть значение отсечки фильтра с 70 до 80 и затем до 100. Разве это не весело?

- Соединим Всё Вместе

Screen 2

Давайте объединим всё, что мы видели до сих пор, с небольшой дополнительной магией. Совместим наши способности к телепортации с размещением блоков и музыкой, сделав музыкальное видео Minecraft. Не волнуйтесь, если вы не всё здесь понимаете, просто введите код в буфер и играйте, изменяя значения, пока код выполняется. Повеселитесь и увидимся в следующий раз…

live_loop :note_blocks do
  mc_message "This is Sonic Minecraft"
  with_fx :reverb do
    with_fx :echo, phase: 0.125, reps: 32 do
      tick
      x = (range 30, 90, step: 0.1).look
      y = 20
      z = -10
      mc_teleport x, y, z
      ns = (scale :e3, :minor_pentatonic)
      n = ns.shuffle.choose
      bs = (knit :glass, 3, :sand, 1)
      b = bs.look
      synth :beep, note: n, release: 0.1
      mc_set_block b, x+20, n-60+y, z+10
      mc_set_block b, x+20, n-60+y, z-10
      sleep 0.25
    end
  end
end
live_loop :beats do
  sample :bd_haus, cutoff: 100
  sleep 0.5
end

- Ритмы Бизе

После нашего краткого экскурса в сказочный мир кодинга Minecraft в прошлом месяце, давайте снова помузицируем. Сегодня мы собираемся перенести классический оперный танцевальный ритм в 21-й век, используя грандиозную силу кода.

- Возмутительно И Дерзко

Переместимся на машине времени в 1875 год. Композитор Бизе как раз закончил свою последнюю оперу “Кармен”. К сожалению, как и множество других прогрессивных, ломающих стереотипы музыкальных произведений, публика поначалу её не приняла, ведь она была слишком дерзкой и непохожей на другие оперы того времени. Печально, но Бизе умер за десять лет до того, как эта опера заработала ошеломляющий международный успех и стала одной из самых известных и часто исполняемых опер всех времён. Из сочувствия к этой трагедии, возьмем одну из главных тем оперы “Кармен”, и преобразуем её в современный формат музыки, который также слишком возмутителен и чужд большинству людей нашего времени - кодированную музыку!

- Расшифровка Хабанеры

Пытаться кодировать всю оперу было бы сложноватой задачей для одной статьи, так что давайте сосредоточимся на одной из самых известных её частей - басовой партии Хабанеры:

Habanera Riff

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

- Ноты

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

Мы уже знаем, как изменить высоту ноты в Sonic Pi - либо используя цифры, такие как play 75 и play 80, либо используя названия нот: play :Е и play :F. К счастью, каждой вертикальной позиции музыкальной партитуры соответствует определенная нота. Взгляните на эту удобную таблицу:

Notes

- Паузы

Ноты представляют собой чрезвычайно богатый и выразительный вид кода, способного объединить сразу множество вещей. Поэтому дя вас не должно стать большим удивлением, что партитура может не только сказать вам, какие ноты играть, но также когда ноты играть не нужно . В программировании это эквивалентно nil или null - отсутствию значения. Другими словами, пауза - это отсутствие ноты.

Если вы внимательно посмотрите на партитуру, вы увидите, что это на самом деле сочетание горизонтальных линий с черными точками, которые представляют собой ноты, коротые нужно играть, и волнистых штук, которые представляют собой паузы. К счастью, Sonic Pi имеет очень удобное представление для пауз: :r, так, если мы выполним: play :r, он на самом деле сыграет тишину! Мы могли бы также написать play :rest, play nil или play false - это всё эквивалентные способы представления пауз.

- Ритм

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

Когда мы пытаемся расшифровать и изучить новые вещи, очень удобный приём - сделать всё как можно более однородным, чтобы увидеть соотношения и структуры. Например, если мы перепишем нашу партитуру одними только 16-ми длительностями, вы сможете увидеть, что она превращается в простую последовательность нот и пауз.

Habanera Riff 2

- Повторное кодирование Хабанеры

Теперь мы в состоянии начать переводить эту басовую партию для Sonic Pi. Давайте закодируем эти ноты и паузы в кольцо:

(ring :d, :r, :r, :a, :f5, :r, :a, :r)

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

live_loop :habanera do
  play (ring :d, :r, :r, :a, :f5, :r, :a, :r).tick
  sleep 0.25
end

Легендарный, мгновенно узнаваемый рифф возвращается к жизни сквозь ваши колонки. Потребовалось немало усилий, чтобы добраться сюда, но это того стоило!

- Мрачные Синты

Теперь у нас есть басовая партия. Давайте воссоздадим атмосферу оперной сцены. Попробуем осуществить это с :blade, мрачным солирующим синтом в стиле 80-х. Давайте испробуем его, извлекая ноту :d, пропущенную через эффекты нарезки и реверберации:

live_loop :habanera do
  use_synth :fm
  use_transpose -12
  play (ring :d, :r, :r, :a, :f5, :r, :a, :r).tick
  sleep 0.25
end
with_fx :reverb do
  live_loop :space_light do
    with_fx :slicer, phase: 0.25 do
      synth :blade, note: :d, release: 8, cutoff: 100, amp: 2
    end
    sleep 8
  end
end

Теперь, попробуйте подставить туда другие ноты басовой партии::a и :f5. Помните, вам не надо нажимать кнопку “Остановить”, просто измените код в то время, как музыка играет, и нажмите “Выполнить” снова. Также, попробуйте разные значения параметра phase: нарезки, такие как 0.5, 0.75 и 1.

- Соединим Всё Вместе

Наконец, давайте объединим все идеи этой статьи в новый ремикс Хабанеры. Вы можете заметить, что я включил ещё одну часть басовой партии как комментарий. Как только вы набрали всё это в буфере, нажмите “Выполнить”, чтобы прослушать композицию. Затем, не нажимая кнопку “Остановить”, раскомментируйте вторую строку, удалив знак #, и нажмите “Выполнить” снова - как это удивительно! Итак, начинайте лайвкодинг и веселитесь!

use_debug false
bizet_bass = (ring :d, :r, :r, :a, :f5, :r, :a, :r)
#bizet_bass = (ring :d, :r, :r, :Bb, :g5, :r, :Bb, :r)
 
with_fx :reverb, room: 1, mix: 0.3 do
  live_loop :bizet do
    with_fx :slicer, phase: 0.125 do
      synth :blade, note: :d4, release: 8,
        cutoff: 100, amp: 1.5
    end
    16.times do
      tick
      play bizet_bass.look, release: 0.1
      play bizet_bass.look - 12, release: 0.3
      sleep 0.125
    end
  end
end
 
live_loop :ind do
  sample :loop_industrial, beat_stretch: 1,
    cutoff: 100, rate: 1
  sleep 1
end
 
live_loop :drums do
  sample :bd_haus, cutoff: 110
  synth :beep, note: 49, attack: 0,
    release: 0.1
  sleep 0.5
end

- Становимся Minecraft Виджеями

Screen 0

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

Если вашим единственным способом игры с Minecraft была модификация игрового мира с помощью мышки, вам наверняка было бы трудно изменять его достаточно быстро. К счастью, ваш Raspberry Pi поставляется с версией Minecraft, управляемой с помощью кода. Он также содержит приложение Sonic Pi, которое делает кодирование Minecraft не только лёгким, но и невероятно весёлым.

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

Итак, начнём…

- Приступая К Работе

Давайте начнём с простого разминочного упражнения, дабы освежить в памяти основы. Во-первых, подключите ваш Raspberry Pi и запустите Sonic Pi вместе с Minecraft. В Minecraft создайте новый мир, а в Sonic Pi выберите пустой буфер и наберите там этот код:

mc_message "Let's get started..."

Нажмите кнопку “Выполнить”, и вы увидите сообщение в окне Minecraft. Хорошо, мы готовы начать, давайте повеселимся……

- Песчаные Бури

Когда мы используем Minecraft для создания визуальных эффектов, мы стараемся думать о том, чтобы это одновременно выглядело интересно и легко генерировалось с помощью кода. Интересный трюк - создать песчаную бурю, сбрасывая песчаные блоки с неба. Для этого нам необходимы несколько базовых функций:

sleep - для того, чтобы вставить задержку между действиями mc_location - для нахождения нашего текущего местоположения mc_set_block- чтобы поместить песчаный блок в заданное место rrand - для генерации случайных чисел в заданном диапазоне live_loop - позволяет сделать дождь из песка непрерывным

Если вы не знакомы с какой-либо из встроенных функций, например rrand, просто введите её название в свой буфер, кликните на нём, а затем используйте клавиатурное сочетание Ctrl-i, вызвающее встроенную документацию. В качестве альтернативы вы можете переместиться к вкладке язык справочной системы, и найти нужную функцию прямо там, как и другие интересные вещи.

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

x, y, z = mc_location
mc_set_block :sand, x, y + 20, z + 5
sleep 2
mc_set_block :sand, x, y + 20, z + 6
sleep 2
mc_set_block :sand, x, y + 20, z + 7
sleep 2
mc_set_block :sand, x, y + 20, z + 8

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

Давайте быстро посмотрим, что тут у нас происходит. На первой строке мы получили местоположение Стива в виде координат с помощью функции mc_location и присвоили их переменным х, у, и z. На последующих строках мы использовали функциию mc_set_block, чтобы поместить немного песка в тех же координатах, что и Стив но с некоторыми изменениями. Мы выбрали ту же координату x, но координата y стала на 20 блоков выше, а затем последовательно увеличивали z, чтобы песок падал в линию, удаляющуюся от Стива.

Почему бы вам самим не поиграть с этим кодом? Попробуйте добавить больше линий, изменените время задержки, попробуйте смешать :sand с :gravel и выбирать разные координаты. Просто экспериментируйте и веселитесь!

- Раскручиваем Живые Циклы

Хорошо, пришло время получить бушующую бурю, высвободив мощь live_loop - магической способности Sonic Pi, раскрывающей всю силу лайвкодинга - изменения кода в реальном времени!

live_loop :sand_storm do
  x, y, z = mc_location
  xd = rrand(-10, 10)
  zd = rrand(-10, 10)
  co = rrand(70, 130)
  synth :cnoise, attack: 0, release: 0.125, cutoff: co
  mc_set_block :sand, x + xd, y + 20, z + zd
  sleep 0.125
end

Это весело! Мы выполняем круг цикла довольно быстро (8 раз в секунду), и во время каждого круга мы находим местоположение Стива также, как и раньше, но в этот раз генерируем 3 случайных значения:

xd - разница для x, которая будет находиться между -10 и 10 zd - разница для z, которая также будет между -10 и 10 co - значение среза фильтра низких частот между 70 и 130

Затем мы используем эти случайные значения в функциях synth и mc_set_block, что даёт нам песок, падающий случайным образом вокруг Стива вместе с перкуссионным звуком, похожим на дождь, от синтезатора :cnoise.

Для тех из вас, кто ещё новичок в живых циклах - именно здесь и начинается самое интересное. Пока код выполняется и песок сыплется вниз, попробуйте изменить одно из значений. Например, время задержки на 0.25, или тип блока с :sand на :gravel. Теперь нажмите “Выполнить” снова. Эй! Всё изменилось без остановки кода. Это и есть та самая дверь к выступлениям в роли виджея. Продолжайте тренироваться и что-нибудь изменять. Насколько разнообразными можно сделать визуальные эффекты, не останавливая кода?

- Эпические Блочные Структуры

Screen 1

Наконец, еще один отличный способ создания интересных визуальных эффектов - генерировать огромные повторяющиеся стены, парящие рядом. Для этого эффекта нам нужно перейти от размещения блоков случайным образом, к размещению их в упорядоченном виде. Мы можем сделать это с помощью двух вложеных итераций (нажмите кнопку “Помощь”, чтобы перейти в раздел 5.2 учебника “Итерации И Циклы”, чтобы узнать больше об итерациях). Аргумент |xd| после do означает, что xd будет соответствовать номеру итерации. Поэтому сначала это будет 0, потом 1, потом 2… и т. д. Совмещая две итерации вместе, мы можем сгенерировать любые координаты. Затем мы произвольно выбираем типы блоков из кольцевого списка для создания интересного эффекта:

x, y, z = mc_location
bs = (ring :gold, :diamond, :glass)
10.times do |xd|
  10.times do |yd|
    mc_set_block bs.choose, x + xd, y + yd, z
  end
end

Довольно аккуратно. В то время как мы здесь веселимся, попробуйте изменить bs.choose, на bs.tick, чтобы перейти от случайного выбора к последовательному. Попробуйте изменять типы блоков более радикально. Вы можете захотеть делать это с live_loop, чтобы узоры продолжали изменяться автоматически.

Теперь, измените обе итерации с 10.times на 100.times и нажмите “Выполнить”. Бабах! Огромные исполинские стены с хаотично расположенными кирпичами. Представьте сколько времени потребуется вам, чтобы построить их вручную с помощью мыши! Дважды нажмите пробел, чтобы переключиться в режим полёта и начните парить для создания замечательных визуальных эффектов. Не останавливайтесь на этом, используйте своё воображение, чтобы придумать интересные идеи, а затем, использовав силу кода Sonic Pi, сделайте это реальностью. Когда вы достаточно попрактикуетесь, приглушите свет и покажите шоу своим друзьям!


- Surfing Random Streams

Back in episode 4 of this tutorial series we took a brief look at randomisation whilst coding up some sizzling synth riffs. Given that randomisation is such an important part of my live coding DJ sets I thought it would be useful to cover the fundamentals in much greater detail. So, get your lucky hat on and let’s surf some random streams!

- There is no random

The first thing to learn which might really surprise you when playing with Sonic Pi’s randomisation functions is that they’re not actually really random. What does this actually mean? Well, let’s try a couple of tests. First, imagine a number in your head between 0 and 1. Keep it there and don’t tell me. Now let me guess… was it 0.321567? No? Bah, I’m clearly no good at this. Let me have another go, but let’s ask Sonic Pi to choose a number this time. Fire up Sonic Pi v2.7+ and ask it for a random number but again don’t tell me:

print rand

Now for the reveal… was it 0.75006103515625? Yes! Ha, I can see you’re a little sceptical. Perhaps it was just a lucky guess. Let’s try again. Press the Run button again and see what we get… What? 0.75006103515625 again? This clearly can’t be random! You’re right, it’s not.

What’s going on here? The fancy computer science word here is determinism. This just means that nothing is by chance and everything is destined to be. Your version of Sonic Pi is destined to always return 0.75006103515625 in the program above. This might sound pretty useless, but let me assure you that it’s one of the most powerful parts of Sonic Pi. If you stick at it you’ll learn how to rely on the deterministic nature of Sonic Pi’s randomisation as a fundamental building block for your compositions and live coded DJ sets.

- A Random Melody

When Sonic Pi boots it actually loads into memory a sequence of 441,000 pre-generated random values. When you call a random function such as rand or rrand, this random stream is used to generate your result. Each call to a random function consumes a value from this stream. Therefore the 10th call to a random function will use the 10th value from the stream. Also, every time you press the Run button, the stream is reset for that run. This is why I could predict the result to rand and why the ‘random’ melody was the same every time. Everybody’s version of Sonic Pi uses the exact same random stream which is very important when we start sharing our pieces with each other.

Let’s use this knowledge to generate a repeatable random melody:

8.times do
 play rrand_i(50, 95)
 sleep 0.125
end

Type this into a spare buffer and hit Run. You’ll hear a melody consisting of ‘random’ notes between 50 and 95. When it’s finished, hit Run again to hear exactly the same melody again.

Handy Randomisation Functions

Sonic Pi comes with a number of useful functions for working with the random stream. Here’s a list of some of the most useful:

rand - Simply returns the next value in the random stream rrand - Returns a random value within a range rrand_i - Returns a random whole number within a range one_in - Returns true or false with the given probability dice - Imitates rolling a dice and returns a value between 1 and 6 choose - Chooses a random value from a list

Check out their documentation in the Help system for detailed information and examples.

- Resetting the Stream

Whilst the ability to repeat a sequence of chosen notes is essential to allow you to replay a riff on the dance floor, it might not be exactly the riff you want. Wouldn’t it be great if we could try a number of different riffs and choose the one we liked best? This is where the real magic starts.

We can manually set the stream with the fn use_random_seed. In Computer Science, a random seed is the starting point from which a new stream of random values can sprout out and blossom. Let’s try it:

use_random_seed 0
3.times do
  play rrand_i(50, 95)
  sleep 0.125
end

Great, we get the first three notes of our random melody above: 84, 83 and 71. However, we can now change the seed to something else. How about this:

use_random_seed 1
3.times do
  play rrand_i(50, 95)
  sleep 0.125
end

Interesting, we get 83, 71 and 61 . You might notice that the first two numbers here are the same as the last two numbers before - this isn’t a coincidence.

Remember that the random stream is just a giant list of ‘pre-rolled’ values. Using a random seed simply jumps us to a point in that list. Another way of thinking about it is to imagine a huge deck of pre-shuffled cards. Using a random seed is cutting the deck at a particular point. The fabulous part of this is that it’s precisely this ability to jump around the random stream which gives us huge power when making music.

Let’s revisit our random melody of 8 notes with this new stream resetting power, but let’s also throw in a live loop so we can experiment live whilst it’s playing:

live_loop :random_riff do    
  use_random_seed 0
  8.times do
    play rrand_i(50, 95), release: 0.1
    sleep 0.125
  end
end

Now, whilst it’s still playing, change the seed value from 0 to something else. Try 100, what about 999. Try your own values, experiment and play around - see which seed generates the riff you like best.

- Соединим Всё Вместе

This month’s tutorial has been quite a technical dive into the workings of Sonic Pi’s randomisation functionality. Hopefully it has given you some insight into how it works and how you can start using randomisation in a reliable way to create repeatable patterns within your music. It’s important to stress that you can use repeatable randomisation anywhere you want. For example, you can randomise the amplitude of notes, the timing of the rhythm, the amount of reverb, the current synth, the mix of an FX, etc. etc. In the future we’ll take a close look at some of these applications, but for now let me leave you with a short example.

Type the following into a spare buffer, hit Run, and then start changing the seeds around, hit Run again (whilst it’s still playing) and explore the different sounds, rhythms and melodies you can make. When you find a nice one, remember the seed number so you can get back to it. Finally, when you’ve found a few seeds you like, put on a live coded performance for your friends by simply switching between your favourite seeds to create a full piece.

live_loop :random_riff do
  use_random_seed 10300
  use_synth :prophet
  s = [0.125, 0.25, 0.5].choose
  8.times do
    r = [0.125, 0.25, 1, 2].choose
    n = (scale :e3, :minor).choose
    co = rrand(30, 100)
    play n, release: r, cutoff: co
    sleep s
  end
end
live_loop :drums do
  use_random_seed 2001
  16.times do
    r = rrand(0.5, 10)
    sample :drum_bass_hard, rate: r, amp: rand
    sleep 0.125
  end
end

- Controlling Your Sound

So far during this series we’ve focussed on triggering sounds. We’ve discovered that we can trigger the many synths built into Sonic Pi with play or synth and how to trigger pre-recorded samples with sample. We’ve also looked at how we can wrap these triggered sounds within studio FX such as reverb and distortion using the with_fx command. Combine this with Sonic Pi’s incredibly accurate timing system and you can produce a vast array of sounds, beats and riffs. However, once you’ve carefully selected a particular sound’s options and triggered it, there’s no ability to mess with it whilst it’s playing right? Wrong! Today you’re going to learn something very powerful - how to control running synths.

A Basic Sound

Let’s create a nice simple sound. Fire up Sonic Pi and in a fresh buffer type the following:

synth :prophet, note: :e1, release: 8, cutoff: 100

Now press the Run button at the top left to hear a lovely rumbling synth sound. Go ahead, press it again a few times to get a feel for it. OK, done? Let’s start controlling it!

Synth Nodes

A little known feature in Sonic Pi is that the fns play, synth and sample, return something called a SynthNode which represents a running sound. You can capture one of these SynthNodes using a standard variable and then control it at a later point in time. For example, let’s change the value of the cutoff: opt after 1 beat:

sn = synth :prophet, note: :e1, release: 8, cutoff: 100
sleep 1
control sn, cutoff: 130

Let’s look at each line in turn:

Firstly we trigger the :prophet synth using the synth fn as normal. However we also capture the result in a variable called sn. We could have called this variable something completely different such as synth_node or jane - the name doesn’t matter. However, it’s important to choose a name that’s meaningful to you for your performances and for people reading your code. I chose sn as it’s a nice short mnemonic for synth node.

On line 2 we have a standard sleep command. This does nothing special - it just asks the computer to wait for 1 beat before moving onto the next line.

Line 3 is where the control fun starts. Here, we use the control fn to tell our running SynthNode to change the cutoff value to 130. If you hit the Run button, you’ll hear the :prophet synth start playing as before, but after 1 beat it will shift to sound a lot brighter.

Modulatable Options

Most of Sonic Pi’s synths and FX opts may be changed after being triggered. However, this isn’t the case for all of them. For example, the envelope opts attack:, decay:, sustain: and release: can only be set when triggering the synth. Figuring out which opts can and can’t be changed is simple - just head to the documentation for a given synth or FX and then scroll down to the individual option documentation and look for the phrases “May be changed whilst playing” or “Can not be changed once set”. For example, the documentation for the :beep synth’s attack: opt makes it clear that it’s not possible to change it:

Default: 0 Must be zero or greater Can not be changed once set Scaled with current BPM value

Multiple Changes

Whilst a synth is running you’re not limited to changing it only once - you’re free to change it as many times as you like. For example, we can turn our :prophet into a mini arpeggiator with the following:

notes = (scale :e3, :minor_pentatonic)
sn = synth :prophet, note: :e1, release: 8, cutoff: 100
sleep 1
16.times do
  control sn, note: notes.tick
  sleep 0.125
end

In this snippet of code we just added a couple of extra things. First we defined a new variable called notes which contains the notes we’d like to cycle through (an arpeggiator is just a fancy name for something that cycles through a list of notes in order). Secondly we replaced our single call to control with an iteration calling it 16 times. In each call to control we .tick through our ring of notes which will automatically repeat once we get to the end (thanks to the fabulous power of Sonic Pi’s rings). For a bit of variety try replacing .tick with .choose and see if you can hear the difference.

Note that we can change multiple opts simultaneously. Try changing the control line to the following and listen for the difference:

control sn, note: notes.tick, cutoff: rrand(70, 130)

Sliding

When we control a SynthNode, it responds exactly on time and instantly changes the value of the opt to the new one as if you’d pressed a button or flicked a switch requesting the change. This can sound rhythmical and percussive - especially if the opt controls an aspect of the timbre such as cutoff:. However, sometimes you don’t want the change to happen instantaneously. Instead, you might want to smoothly move from the current value to the new one as if you’d moved a slider or dial. Of course, Sonic Pi can also do this too using the _slide: opts.

Each opt that can be modified also has a special corresponding _slide: opt that allows you to specify a slide time. For example, amp: has amp_slide: and cutoff: has cutoff_slide:. These slide opts work slightly differently than all the other opts in that they tell the synth note how to behave next time they are controlled. Let’s take a look:

sn = synth :prophet, note: :e1, release: 8, cutoff: 70, cutoff_slide: 2
sleep 1
control sn, cutoff: 130

Notice how this example is exactly the same as before except with the addition of cutoff_slide:. This is saying that next time this synth has its cutoff: opt controlled, it will take 2 beats to slide from the current value to the new one. Therefore, when we use control you can hear the cutoff slide from 70 to 130. It creates an interesting dynamic feel to the sound. Now, try changing the cutoff_slide: time to a shorter value such as 0.5 or a longer value such as 4 to see how it changes the sound. Remember, you can slide any of the modifiable opts in exactly this way and each _slide: value can be totally different so you can have the cutoff sliding slowly, the amp sliding fast and the pan sliding somewhere in between if that’s what you’re looking to create…

Соединим Всё Вместе

Let’s look at a short example which demonstrates the power of controlling synths after they’ve been triggered. Notice that you can also slide FX just like synths although with a slightly different syntax. Check out section 7.2 of the built-in tutorial for more information on controlling FX.

Copy the code into a spare buffer and take a listen. Don’t stop there though - play around with the code. Change the slide times, change the notes, the synth, the FX and the sleep times and see if you can turn it into something completely different!

live_loop :moon_rise do
  with_fx :echo, mix: 0, mix_slide: 8 do |fx|
    control fx, mix: 1
    notes = (scale :e3, :minor_pentatonic, num_octaves: 2).shuffle
    sn = synth :prophet , sustain: 8, note: :e1, cutoff: 70, cutoff_slide: 8
    control sn, cutoff: 130
    sleep 2
    32.times do
      control sn, note: notes.tick, pan: rrand(-1, 1)
      sleep 0.125
    end
  end
end

- Tracking the Beat

Last month in this series we took a deep technical dive into the randomisation system underpinning Sonic Pi. We explored how we can use it to deterministically add new levels of dynamic control over our code. This month we’re going to continue our technical dive and turn our attention to Sonic Pi’s unique tick system. By the end of this article you’ll be ticking your way through rhythms and riffs on your way to being a live coding DJ.

- Beat Counting

When making music we often want to do a different thing depending on which beat it is. Sonic Pi has a special beat counting system called tick to give you precise control over when a beat actually occurs and even supports multiple beats with their own tempos.

Let’s have a play - to advance the beat we just need to call tick. Open up a fresh buffer, type in the following and hit Run:

puts tick #=> 0

This will return the current beat: 0. Notice that even if you press the Run button a few times it will always return 0. This is because each run starts a fresh beat counting from 0. However, whilst the run is still active, we can advance the beat as many times as we want:

puts tick #=> 0
puts tick #=> 1
puts tick #=> 2

Whenever you see the symbol #=> at the end of a line of code it means that that line will log the text on the right-hand-side. For example, puts foo #=> 0 means the code puts foo prints 0 to the log at that point in the program.

- Checking the Beat

We’ve seen that tick does two things. It increments (adds one) and returns the current beat. Sometimes we just want to look at the current beat without having to increment it which we can do via look:

puts tick #=> 0
puts tick #=> 1
puts look #=> 1
puts look #=> 1

In this code we tick the beat up twice and then call look twice. We’ll see the following values in the log: 0, 1, 1, 1. The first two ticks returned 0, then 1 as expected, then the two looks just returned the last beat value twice which was 1.

- Кольца

So now we can advance the beat with tick and check the beat with look. What next? We need something to tick over. Sonic Pi uses rings for representing riffs, melodies and rhythms and the tick system has been specifically designed to work very closely with them. In fact, rings have their own dot version of tick which does two things. Firstly, it acts like a regular tick and increments the beat. Secondly it looks up the ring value using the beat as the index. Let’s take a look:

puts (ring :a, :b, :c).tick #=> :a

.tick is a special dot version of tick which will return the first value of the ring :a. We can grab each of the values in the ring by calling .tick multiple times:

puts (ring :a, :b, :c).tick #=> :a
puts (ring :a, :b, :c).tick #=> :b
puts (ring :a, :b, :c).tick #=> :c
puts (ring :a, :b, :c).tick #=> :a
puts look                   #=> 3

Take a look at the log and you’ll see :a, :b, :c and then :a again. Notice that look returns 3. Calls to .tick act just like they are regular calls to tick - they increment the local beat.

- A Live Loop Arpeggiator

The real power comes when you mix tick with rings and live_loops. When combined we have all the tools we need to both build and understand a simple arpegiator. We need just four things:

A ring containing the notes we want to loop over. A means of incrementing and obtaining the beat. The ability to play a note based on the current beat. A loop structure to keep the arpegiator repeating.

These concepts can all be found in the following code:

notes = (ring 57, 62, 55, 59, 64)
live_loop :arp do
  use_synth :dpulse
  play notes.tick, release: 0.2
  sleep 0.125
end

Let’s look at each of these lines. First we define our ring of notes which we’ll continually play. We then create a live_loop called :arp which loops round for us. Each time round the live_loop we set our synth to :dpulse and then play the next note in our ring using .tick. Remember that this will increment our beat counter and use the latest beat value as an index into our notes ring. Finally, we wait for an eighth of a beat before looping round again.

- Multiple Simultaneous Beats

A really important thing to know is that ticks are local to the live_loop. This means that each live_loop has its own independent beat counter. This is much more powerful than having a global metronome and beat. Let’s take a look at this in action:

notes = (ring 57, 62, 55, 59, 64)
with_fx :reverb do
  live_loop :arp do
    use_synth :dpulse
    play notes.tick + 12, release: 0.1
    sleep 0.125
  end
end
live_loop :arp2 do
  use_synth :dsaw
  play notes.tick - 12, release: 0.2
  sleep 0.75
end

- Clashing Beats

A big cause of confusion with Sonic Pi’s tick system is when people want to tick over multiple rings in the same live_loop:

use_bpm 300
use_synth :blade
live_loop :foo do
  play (ring :e1, :e2, :e3).tick
  play (scale :e3, :minor_pentatonic).tick
  sleep 1
end

Even though each live_loop has its own independent beat counter, we’re calling .tick twice within the same live_loop. This means that the beat will be incremented twice every time round. This can produce some interesting polyrhythms but is often not what you want. There are two solutions to this problem. One option is to manually call tick at the start of the live_loop and then use .look to look up the current beat in each live_loop. The second solution is to pass a unique name to each call to .tick such as .tick(:foo). Sonic Pi will then create and track a separate beat counter for each named tick you use. That way you can work with as many beats as you need! See the section on named ticks in 9.4 of the built-in tutorial for more information.

- Соединим Всё Вместе

Let’s bring all this knowledge of ticks, rings and live_loops together for a final fun example. As usual, don’t treat this as a finished piece. Start changing things and play around with it and see what you can turn it into. See you next time…

use_bpm 240
notes = (scale :e3, :minor_pentatonic).shuffle
live_loop :foo do
  use_synth :blade
  with_fx :reverb, reps: 8, room: 1 do
    tick
    co = (line 70, 130, steps: 32).tick(:cutoff)
    play (octs :e3, 3).look, cutoff: co, amp: 2
    play notes.look, amp: 4
    sleep 1
  end
end
live_loop :bar do
  tick
  sample :bd_ada if (spread 1, 4).look
  use_synth :tb303
  co = (line 70, 130, steps: 16).look
  r = (line 0.1, 0.5, steps: 64).mirror.look
  play notes.look, release: r, cutoff: co
  sleep 0.5
end

- Sample Slicing

Way back in episode 3 of this Sonic Pi series we looked at how to loop, stretch and filter one of the most famous drum breaks of all time - the Amen Break. In this tutorial we’re going to take this one step further and learn how to slice it up, shuffle the slices and glue it back together in a completely new order. If that sounds a bit crazy to you, don’t worry, it will all become clear and you’ll soon master a powerful new tool for your live coded sets.

- Sound as Data

Before we get started let’s just take a brief moment to understand how to work with samples. By now, you’ve all hopefully played with Sonic Pi’s powerful sampler. If not, there’s no time like the present! Boot up your Raspberry Pi, launch Sonic Pi from the Programming menu, type the following into a fresh buffer and then hit the Run button to hear a pre-recorded drum beat:

sample :loop_amen

A recording of a sound is simply represented as data - lots of numbers between -1 and 1 which represent the peaks and troughs of the sound wave. If we play those numbers back in order, we get the original sound. However, what’s to stop us from playing them back in a different order and creating a new sound?

How are samples actually recorded? It’s actually pretty simple once you understand the basic physics of sound. When you make a sound - for example by hitting a drum, the noise travels through the air in a similar fashion to how the surface of a lake ripples when you throw a pebble into it. When those ripples reach your ears, your eardrum moves sympathetically and converts those movements into the sound you hear. If we wish to record and play back the sound, we therefore need a way of capturing, storing and reproducing those ripples. One way is to use a microphone which acts like an eardrum and moves back and forth as the sound ripples hit it. The microphone then converts its position into a tiny electric signal which is then measured many times a second. These measurements are then represented as a series of numbers between -1 and 1.

If we were to plot a visualisation of the sound it would be a simple graph of data with time on the x axis and microphone/speaker position as a value between -1 and 1 on the y axis. You can see an example of such a graph at the top of the diagram.

- Playing Part of a Sample

So, how do we code Sonic Pi to play a sample back in a different order? To answer this question we need to take a look at the start: and finish: opts for sample. These let us control the start and finish positions of our playback of the numbers which represent the sound. The values for both of these opts are represented as a number between 0 and 1 where 0 represents the start of the sample and 1 is the end. So, to play the first half of the Amen Break, we just need to specify a finish: of 0.5:

sample :loop_amen, finish: 0.5

We can add in a start: value to play an even smaller section of the sample:

sample :loop_amen, start: 0.25, finish: 0.5

For fun, you can even have the finish: opt’s value be before start: and it will play the section backwards:

sample :loop_amen, start: 0.5, finish: 0.25

- Re-ordering Sample Playback

Now that we know that a sample is simply a list of numbers that can be played back in any order and also how to play a specific part of a sample we can now start having fun playing a sample back in the ‘wrong’ order.

Amen Slices

Let’s take our Amen Break and chop it up into 8 equally-sized slices and then shuffle the pieces around. Take a look at the diagram: at the top A) represents the graph of our original sample data. Chopping it into 8 slices gives us B) - notice that we’ve given each slice a different colour to help distinguish them. You can see each slice’s start and finish values at the top. Finally C) is one possible re-ordering of the slices. We can then play this back to create a new beat. Take a look at the code to do this:

live_loop :beat_slicer do
  slice_idx = rand_i(8)
  slice_size = 0.125
  s = slice_idx * slice_size
  f = s + slice_size
  sample :loop_amen, start: s, finish: f
  sleep sample_duration :loop_amen, start: s, finish: f
end

we choose a random slice to play which should be a random number between 0 and 7 (remember that we start counting at 0). Sonic Pi has a handy function for exactly this: rand_i(8). We then store this random slice index in the variable slice_idx. We define our slice_size which is 1/8 or 0.125. The slice_size is necessary for us to convert our slice_idx into a value between 0 and 1 so we can use it as our start: opt. We calculate the start position s by multiplying the slice_idx by the slice_size. We calculate the finish position f by adding the slice_size to the start position s. We can now play the sample slice by plugging in the s and f values into the start: and finish: opts for sample. Before we play the next slice we need to know how long to sleep which should be the duration of the sample slice. Luckily, Sonic Pi has us covered with sample_duration which accepts all the same opts as sample and simply returns the duration. Therefore, by passing sample_duration our start: and finish: opts, we can find out the duration of a single slice. We wrap all of this code in a live_loop so that we continue to pick new random slices to play.

Соединим Всё Вместе

Let’s combine everything we’ve seen so far into a final example which demonstrates how we can take a similar approach to combine randomly sliced beats with some bass to create the start of an interesting track. Now it’s your turn - take the code below as a starting point and see if you can take it in your own direction and create something new…

live_loop :sliced_amen do
  n = 8
  s =  line(0, 1, steps: n).choose
  f = s + (1.0 / n)
  sample :loop_amen, beat_stretch: 2, start: s, finish: f
  sleep 2.0  / n
end
live_loop :acid_bass do
  with_fx :reverb, room: 1, reps: 32, amp: 0.6 do
    tick
    n = (octs :e0, 3).look - (knit 0, 3 * 8, -4, 3 * 8).look
    co = rrand(70, 110)
    synth :beep, note: n + 36, release: 0.1, wave: 0, cutoff: co
    synth :tb303, note: n, release: 0.2, wave: 0, cutoff: co
    sleep (ring 0.125, 0.25).look
  end
end

- Code a Probabilistic Sequencer

In a previous episode of this Sonic Pi series we explored the power of randomisation to introduce variety, surprise and change into our live coded tracks and performances. For example, we randomly picked notes from a scale to create never-ending melodies. Today we’re going to learn a new technique which uses randomisation for rhythm - probabilistic beats!

- Переносимость

Before we can start making new beats and synth rhythms we need to take a quick dive into the basics of probability. This might sound daunting and complicated, but really it’s just as simple as rolling a dice - honestly! When you take a regular 6 sided board game dice and roll it what’s actually happening? Well, firstly you’ll roll either a 1, 2, 3, 4, 5 or 6 with exactly the same chance of getting any of the numbers. In fact, given that it’s a 6 sided dice, on average (if you roll lots and lots of times) you’ll throw a 1 every 6 throws. This means you have a 1 in 6 chance of throwing a 1. We can emulate dice rolls in Sonic Pi with the fn dice. Let’s roll one 8 times:

8.times do
  puts dice
  sleep 1
end

Notice how the log prints values between 1 and 6 just as if we’d rolled a real dice ourselves.

- Начальные Точки Случайных Чисел

Послушаем, на что это похоже:

live_loop :random_beat do
  sample :drum_snare_hard if dice == 1
  sleep 0.125
end

Let’s quickly go over each line to make sure everything is very clear. First we create a new live_loop called :random_beat which will continually repeat the two lines between do and end. The first of these lines is a call to sample which will play a pre-recorded sound (the :drum_snare_hard sound in this case). However, this line has a special conditional if ending. This means that the line will only be executed if the statement on the right hand side of the if is true. The statement in this case is dice == 1. This calls our dice function which, as we have seen, returns a value between 1 and 6. We then use the equality operator == to check to see if this value is 1. If it is 1, then the statement resolves to true and our snare drum sounds, if it isn’t 1 then the statement resolves to false and the snare is skipped. The second line simply waits for 0.125 seconds before rolling the dice again.

- Changing Probabilities

Those of you that have played role play games will be familiar with lots of strangely shaped dice with different ranges. For example there is the tetrahedron shaped dice which has 4 sides and even a 20 sided dice in the shape of a icosahedron. The number of sides on the dice changes the chance, or probability of rolling a 1. The fewer sides, the more likely you are to roll a 1 and the more sides the less likely. For example, with a 4 sided dice, there’s a one in 4 chance of rolling a 1 and with a 20 sided dice there’s a one in 20 chance. Luckily, Sonic Pi has the handy one_in fn for describing exactly this. Let’s play:

live_loop :different_probabilities do
  sample :drum_snare_hard if one_in(6)
  sleep 0.125
end

Start the live loop above and you’ll hear the familiar random rhythm. However, don’t stop the code running. Instead, change the 6 to a different value such as 2 or 20 and hit the Run button again. Notice that lower numbers mean the snare drum sounds more frequently and higher numbers mean the snare triggers fewer times. You’re making music with probabilities!

- Combining Probabilities

Things get really exciting when you combine multiple samples being triggered with different probabilities. For example:

live_loop :multi_beat do
  sample :elec_hi_snare if one_in(6)
  sample :drum_cymbal_closed if one_in(2)
  sample :drum_cymbal_pedal if one_in(3)
  sample :bd_haus if one_in(4)
  sleep 0.125
end

Again, run the code above and then start changing the probabilities to modify the rhythm. Also, try changing the samples to create an entirely new feel. For example try changing :drum_cymbal_closed to :bass_hit_c for extra bass!

- Ритм

Next, we can use our old friend use_random_seed to reset the random stream after 8 iterations to create a regular beat. Type the following code to hear a much more regular and repeating rhythm. Once you hear the beat, try changing the seed value from 1000 to another number. Notice how different numbers generate different beats.

live_loop :multi_beat do
  use_random_seed 1000
  8.times do
    sample :elec_hi_snare if one_in(6)
    sample :drum_cymbal_closed if one_in(2)
    sample :drum_cymbal_pedal if one_in(3)
    sample :bd_haus if one_in(4)
    sleep 0.125
  end
end

One thing I tend to do with this kind of structure is to remember which seeds sound good and make a note of them. That way I can easily re-create my rhythms in future practice sessions or performances.

- Соединим Всё Вместе

Finally, we can throw in some random bass to give it some nice melodic content. Notice that we can also use our newly discovered probabilistic sequencing method on synths just as well as samples. Don’t leave it at that though - tweak the numbers and make your own track with the power of probabilities!

live_loop :multi_beat do
  use_random_seed 2000
  8.times do
    c = rrand(70, 130)
    n = (scale :e1, :minor_pentatonic).take(3).choose
    synth :tb303, note: n, release: 0.1, cutoff: c if rand < 0.9
    sample :elec_hi_snare if one_in(6)
    sample :drum_cymbal_closed if one_in(2)
    sample :drum_cymbal_pedal if one_in(3)
    sample :bd_haus, amp: 1.5 if one_in(4)
    sleep 0.125
  end
end

- Амплитуда

This month we’re going to take a deep dive into one of Sonic Pi’s most powerful and flexible audio FX - the :slicer. By the end of this article you will have learned how to manipulate the overall volume of parts of our live coded sound in powerful new ways. This will allow you to create new rhythmic and timbral structures and broaden your sonic possibilities.

Slice that Amp

So, what does the :slicer FX actually do? One way to think about it is that it’s just like having someone play around with the volume control on your TV or home hi-fi. Let’s take a look but first, listen to the deep growl of the following code which triggers the :prophet synth:

synth :prophet, note: :e1, release: 8, cutoff: 70
synth :prophet, note: :e1 + 4, release: 8, cutoff: 80

Now, let’s pipe it through the :slicer FX:


with_fx :slicer do
  synth :prophet, note: :e1, release: 8, cutoff: 70
  synth :prophet, note: :e1 + 4, release: 8, cutoff: 80
end

Hear how the slicer acts like it’s muting and unmuting the audio with a regular beat. Also, notice how the :slicer affects all the audio generated between the do/end blocks. You can control the speed at which it turns the audio on and off with the phase: opt which is short for phase duration. Its default value is 0.25 which means 4 times a second at the default BPM of 60. Let’s make it faster:

with_fx :slicer, phase: 0.125 do
  synth :prophet, note: :e1, release: 8, cutoff: 70
  synth :prophet, note: :e1 + 4, release: 8, cutoff: 80
end

Now, play with different phase: durations yourself. Try longer and shorter values. See what happens when you choose a really short value. Also, try different synths such as :beep or :dsaw and different notes. Take a look at the following diagram to see how different phase: values change the number of amplitude changes per beat.

Длительность фазы

Phase duration is the length of time for one on/off cycle. Therefore smaller values will make the FX switch on and off much faster than larger values. Good values to start playing with are 0.125, 0.25, 0.5 and 1.

Контроль волн

By default, the :slicer FX uses a square wave to manipulate the amplitude through time. This is why we hear the amplitude on for a period, then immediately off for a period, then back on again. It turns out that the square wave is just one of 4 different control waves that are supported by :slicer. The others are saw, triangle and (co)sine. Take a look at the diagram below to see what these look like. We can also hear what they sound like. For example, the following code uses (co)sine as the control wave. Hear how the sound doesn’t turn on and off abruptly but instead smoothly fades in and out:

with_fx :slicer, phase: 0.5, wave: 3 do
  synth :dsaw, note: :e3, release: 8, cutoff: 120
  synth :dsaw, note: :e2, release: 8, cutoff: 100
end

Have a play with the different wave forms by changing the wave: opt to 0 for saw, 1 for square, 2 for triangle and 3 for sine. See how different waves sound with different phase: opts too.

Each of these waves can be inverted with the invert_wave: opt which flips it on the y axis. For example, in a single phase the saw wave typically starts high, and slowly goes down before jumping back to the top. With invert_wave: 1 it will start low and slowly go up before jumping back down again. Additionally, the control wave can be started at different points with the phase_offset: opt which should be a value between 0 and 1. By playing around with phase:, wave:, invert_wave: and phase_offset opts you can dramatically change how the amplitude is modified through time.

Длительность фаз

Устанавливаем собственные уровни

By default, :slicer switches between amplitude values 1 (fully loud) and 0 (silent). This can be changed with the amp_min: and amp_max: opts. You can use this alongside the sine wave setting to create a simple tremolo effect:

with_fx :slicer, amp_min: 0.25, amp_max: 0.75, wave: 3, phase: 0.25 do
  synth :saw, release: 8
end

This is just like grabbing the volume knob on your hi-fi and moving it up and down just a little so the sound ‘wobbles’ in and out.

Вероятности

One of :slicer’s powerful features is its ability to use probability to choose whether or not to turn the slicer on or off. Before the :slicer FX starts a new phase it rolls a dice and based on the result either uses the selected control wave or keeps the amplitude off. Let’s take a listen:

with_fx :slicer, phase: 0.125, probability: 0.6  do
  synth :tb303, note: :e1, cutoff_attack: 8, release: 8
  synth :tb303, note: :e2, cutoff_attack: 4, release: 8
  synth :tb303, note: :e3, cutoff_attack: 2, release: 8
end

Hear how we now have an interesting rhythm of pulses. Try changing the probability: opt to a different value between 0 and 1. Values closer to 0 will have more space between each sound due to the likelihood of the sound being triggered being much lower.

Another thing to notice is that the probability system in the FX is just like the randomisation system accessible via fns such as rand and shuffle. They are both completely deterministic. This means that each time you hit Run you’ll hear exactly the same rhythm of pulses for a given probability. If you would like to change things around you can use the seed: opt to select a different starting seed. This works exactly the same as use_random_seed but only affects that particular FX.

Finally, you can change the ‘resting’ position of the control wave when the probability test fails from 0 to any other position with the prob_pos: opt:

with_fx :slicer, phase: 0.125, probability: 0.6, prob_pos: 1  do
  synth :tb303, note: :e1, cutoff_attack: 8, release: 8
  synth :tb303, note: :e2, cutoff_attack: 4, release: 8
  synth :tb303, note: :e3, cutoff_attack: 2, release: 8
end

“Нарезаем” биты

One really fun thing to do is to use :slicer to chop a drum beat in and out:

with_fx :slicer, phase: 0.125 do
  sample :loop_mika
end

This allows us to take any sample and create new rhythmical possibilites which is a lot of fun. However, one thing to be careful about is to make sure that the tempo of the sample matches the current BPM in Sonic Pi otherwise the slicing will sound totally off. For example, try swapping :loop_mika with the loop_amen sample to hear how bad this can sound when the tempos don’t align.

Изменяем темп

As we have already seen, changing the default BPM with use_bpm will make all the sleep times and synth envelope durations grow or shrink to match the beat. The :slicer FX honours this too, as the phase: opt is actually measured in beats not seconds. We can therefore fix the issue with loop_amen above by changing the BPM to match the sample:

use_sample_bpm :loop_amen
with_fx :slicer, phase: 0.125 do
  sample :loop_amen
end

Соединим Всё Вместе

Let’s apply all these ideas into a final example that only uses the :slicer FX to create an interesting combination. Go ahead, start changing it and make it into your own piece!

live_loop :dark_mist do
  co = (line 70, 130, steps: 8).tick
  with_fx :slicer, probability: 0.7, prob_pos: 1 do
    synth :prophet, note: :e1, release: 8, cutoff: co
  end
  
  with_fx :slicer, phase: [0.125, 0.25].choose do
    sample :guit_em9, rate: 0.5
  end
  sleep 8
end
live_loop :crashing_waves do
  with_fx :slicer, wave: 0, phase: 0.25 do
    sample :loop_mika, rate: 0.5
  end
  sleep 16
end

- Пять техник лайв-кодинга

In this month’s Sonic Pi tutorial we’re going to take a look at how you can start treating Sonic Pi like a real instrument. We therefore need to start thinking of code in a completely different way. Live coders think of code in a similar way to how violinists think of their bow. In fact, just like a violinist can apply various bowing techniques to create different sounds (long slow motions vs short fast hits) we will explore five of the basic live coding techniques that Sonic Pi enables. By the end of this article you’ll be able to start practicing for your own live coded performances.

1. Memorise the Shortcuts

The first tip to live coding with Sonic Pi is to start using the shortcuts. For example, instead of wasting valuable time reaching for the mouse, moving it over to the Run button and clicking, you can simply press alt and r at the same time which is much faster and keeps your fingers at the keyboard ready for the next edit. You can find out the shortcuts for the main buttons at the top by hovering the mouse over them. See section 10.2 of the built-in tutorial for the full list of shortcuts.

When performing, one fun thing to do is to add a bit of flair with your arm motion when hitting shortcuts. For example, it’s often good to communicate to the audience when you’re about to make a change - so embellish your movement when hitting alt-r just like a guitarist would do when hitting a big power chord.

2. Manually Layer your Sounds

Now you can trigger code instantly with the keyboard, you can instantly apply this skill for our second technique which is to layer your sounds manually. Instead of ‘composing’ using lots of calls to play, and sample separated by calls to sleep we will have one call to play which we will manually trigger using alt-r. Let’s try it. Type the following code into a fresh buffer:

synth :prophet, note: :e1, release: 8, cutoff: 100

Now, hit Run and whilst the sound is playing, modify the code in order to drop down four notes by changing it to the following:

synth :prophet, note: :e1, release: 8, cutoff: 100

Now, hit Run again, to hear both sounds playing at the same time. This is because Sonic Pi’s Run button doesn’t wait for any previous code to finish, but instead starts the code running at the same time. This means you can easily layer lots of sounds manually with minor or major modifications between each trigger. For example, try changing both the note: and the cutoff: opts and then re-trigger.

Также вы можете опробовать эту технику с длинными абстрактными сэмплами:

sample :ambi_lunar_land

Try starting the sample off, and then progressively halving the rate: opt between hitting Run from 1 to 0.5 to 0.25 to 0.125 and then even try some negative values such as -0.5. Layer the sounds together and see where you can take it. Finally, try adding some FX.

When performing, working with simple lines of code in this way means that an audience new to Sonic Pi has a good chance to follow what you’re doing and relate the code that they can read to the sounds they are hearing.

Живые циклы

When working with more rhythmic music, it can often be hard to manually trigger everything and keep good time. Instead, it is often better to use a live_loop. This provides repetition for your code whilst also giving the ability to edit the code for the next time round the loop. They also will run at the same time as other live_loops which means you can layer them together both with each other and manual code triggers. Take a look at section 9.2 of the built-in tutorial for more information about working with live loops.

When performing, remember to make use of live_loop’s sync: opt to allow you to recover from accidental runtime mistakes which stop the live loop running due to an error. If you already have the sync: opt pointing to another valid live_loop, then you can quickly fix the error and re-run the code to re-start things without missing a beat.

4. Use the Master Mixer

One of Sonic Pi’s best kept secrets is that it has a master mixer through which all sound flows. This mixer has both a low pass filter and a high pass filter built-in, so you can easily perform global modifications to the sound. The master mixer’s functionality can be accessed via the fn set_mixer_control!. For example, whilst some code is running and making sound, enter this into a spare buffer and hit Run:

set_mixer_control! lpf: 50

After you run this code, all existing and new sounds will have a low pass filter applied to them and will therefore sound more muffled. Note that this means that the new mixer values stick until they are changed again. However, if you want, you can always reset the mixer back to its default state with reset_mixer!. Some of the currently supported opts are: pre_amp:, lpf: hpf:, and amp:. For the full list, see the built-in docs for set_mixer_control!.

Use the mixer’s *_slide opts to slide one or many opts values over time. For example, to slowly slide the mixer’s low pass filter down from the current value to 30, use the following:

set_mixer_control! lpf_slide: 16, lpf: 30

Затем вы можете быстро перескочить обратно к высокому значению:

set_mixer_control! lpf_slide: 1, lpf: 130

When performing, it’s often useful to keep a buffer free for working with the mixer like this.

5. Практика

The most important technique for live coding is practice. The most common attribute across professional musicians of all kinds is that they practice playing with their instruments - often for many hours a day. Practice is just as important for a live coder as a guitarist. Practice allows your fingers to memorise certain patterns and common edits so you can type and work with them more fluently. Practice also gives you opportunities to explore new sounds and code constructs.

When performing, you’ll find the more practice you do, the easier it will be for you to relax into the gig. Practice will also give you a wealth of experience to draw from. This can help you understand which kinds of modifications will be interesting and also work well with the current sounds.

Соединим Всё Вместе

This month, instead of giving you a final example that combines all the things discussed, let’s part by setting down a challenge. See if you can spend a week practicing one of these ideas every day. For example, one day practice manual triggers, the next do some basic live_loop work and the following day play around with the master mixer. Then repeat. Don’t worry if things feel slow and clunky at first - just keep practicing and before you know it you’ll be live coding for a real audience.


- 8 Советов, которые помогут вам при лайв-кодинге

Last month we took a look at five important techniques for mastering live coding - in other words, we explored how we could use Sonic Pi to approach code in the same way we would approach a musical instrument. One of the important concepts that we discussed was practice. This month we’re going to take a deeper dive into understanding why live coding practice is important and how you might start.

Регулярно практикуйтесь

The most important piece of advice is to make sure you practice regularly. As a rule I typically practice for 1-2 hours a day, but 20 mins is just fine when you’re starting out. Little but often is what you’re aiming for - so if you can only manage 10 minutes, that’s a great start.

Practice tip #1 - start to develop a practice routine. Find a nice time in the day that works for you and try and practice at that time as many days of the week as you can. Before long you’ll be looking forward to your regular session.

Научитесь слепому набору

If you watch a professional musician performing on stage you’ll likely notice a few things. Firstly, when they play they don’t stare at their instrument. Their fingers, arms and bodies know which keys to press, strings to pluck or drums to hit without them having to think about it too much. This is known as “muscle memory” and although it might sound like something only professionals can do - it’s just the same as when you first learned to walk or ride a bike - practicing through repetition. Live coders use muscle memory to free their minds from having to think about where to move their fingers so they can focus on the music. This is called touch-typing - typing without having to look at the keyboard.

Практический совет #2 - тренируйтесь слепой печати. Есть множество приложений, вебсайтов и даже игр, которые могут помочь вам в этом деле. Найдите то, которое вам нравится и поупражняйтесь в нём, пока у вас не получится набирать текст не глядя.

Программируйте стоя

The body of a musician is conditioned for playing their instrument. For example, a trumpet player needs to be able to blow hard, a guitar player needs to be able to grip the fretboard with strength and a drummer needs to be able to continually hit the drums for long periods of time. So, what’s physical about live coding? Just like DJs, live coders typically perform whilst standing up and some even dance whilst they code! If you practice live coding whilst sitting at a desk and then have to get up and stand at a gig, you’ll likely find the difference very difficult and frustrating.

Practice tip #3 - stand whilst you practice. The easiest way to do this is to use a standing height desk. However, if like me you don’t have one at home, there’s a couple of low-fi options. The approach I take is to use an ironing board which happens to work rather well. Another is to stack some boxes or large books on a normal desk and place your keyboard on top of that. Also, make sure you stretch before you start practicing and try and dance a little during the session. Remember, nobody is watching you, so have fun and you’ll feel much more natural on stage.

Практикуйтесь в развёртке оборудования

Most instruments require some assembly and tuning before they can be played. Unless you’re a rockstar with a bus full of roadies, you’ll have to set up your own instrument before your gig. This is often a stressful time and it is easy for problems to occur. One way to help with this is to incorporate the setup process into your practice sessions.

Practice tip #4 - treat setting up as an important part of your practice. For example, have a box or bag that you can keep your Raspberry Pi and keyboard in etc. Before each practice session, take out all the parts, connect everything, and work through the boot process until you have Sonic Pi running and can make sounds. Once you’ve finished practicing, take the time to carefully pack everything away afterwards. This may take some time at first, but before long you’ll be able to setup and pack everything away incredibly quickly without having to think about it.

Проводите множество музыкальных экспериментов

Once you’ve set up and are ready to start making music, you might find yourself struggling to know where to start. One problem many people face is that they might have a good idea of the kinds of sounds they want to make, but are frustrated that they can’t produce them. Some people don’t even know what kind of sounds they want to make! The first thing to do is not to worry - this is very common and happens to every musician - even if they’ve been practicing for a long time. It is much more important to be making sounds you don’t like than not making any sounds at all.

Practice tip #5 - spend time making sounds and music you don’t like. Try to make time to explore new sounds and ideas. Don’t worry that it might sound terrible if it’s not the style you’re looking for. When you’re experimenting like this you increase the chance of stumbling over a sound or combination of sounds which you love! Even if 99% of the sounds you make are bad, that 1% might be the riff or intro to your new track. Forget the things you don’t like and remember the parts you do. This is even easier when you’re making music with code - just hit save!

Учитесь слышать код

Many musicians can look at a musical score and hear the music in their head without having to play it. This is a very useful skill and it’s well worth incorporating into your live coding practice sessions. The imporant point is to be able to have some understanding of what the code is going to sound like. You don’t need to be able to hear it exactly in your head, but instead it’s useful to know if the code is going to be fast, slow, loud, rhythmic, melodic, random, etc. The final goal is then to be able to reverse this process - to be able to hear music in your head and know what code to write to make it. It may take you a long time to master this, but once you do, you’ll be able to improvise on stage and express your ideas fluently.

Practice tip #6 - write some code into Sonic Pi but don’t hit the Run button. Instead, try to imagine what sound it is going to produce. Then, hit Run, listen, and think about what you got right and what you didn’t. Keep repeating this until it become a natural part of your coding process. When I practice I normally have a good idea of what the code will sound like. However, I still am occasionally surprised, and then I’ll stop and spend some time thinking about why I was wrong. Each time this happens, I learn new tricks which allow me to express myself in new ways.

Устраните все отвлекающие факторы

A common problem when practicing is to become distracted with other things. Practicing is hard and requires real discipline regardless of the kind of music you’re making - from jazz to classical to EDM. If you’re struggling to get started or make progress, it’s often too easy to hop on social media, or look something up on the internet etc. If you’ve set yourself a target of 20 minutes of practice, it’s important to try and spend all that time being as productive as possible.

Practice tip #7 - before you start practicing remove as many distractions as possible. For example, disconnect from the internet, put your phone in another room and try to practice in a quiet place where you’re unlikely to be disturbed. Try to focus on coding music and you can return to your distractions when you’ve finished.

Ведите дневник практики

When you are practicing, you’ll often find your mind is full of new exciting ideas - new musical directions, new sounds to try out, new functions to write, etc. These ideas are often so interesting that you might stop what you’re doing and start working on the idea. This is another form of distraction!

Practice tip #8 - keep a practice diary by your keyboard. When you get an exciting new idea, temporarily pause your practice session, quickly jot the idea down, then forget about it and carry on practicing. You can then spend some quality time thinking about and working on your ideas after you’ve finished practicing.

Соединим Всё Вместе

Try to establish a practice routine which incorporates as many of these ideas as possible. Try to keep the sessions as fun as possible but be aware that some practice sessions will be hard and feel a little like work. However, it will all be worth it once you’ve created your first piece or given your first performance. Remember, practice is the key to success!


- Растяжение Ритмов

When people discover Sonic Pi, one of the first things they learn is how simple it is to play pre-recorded sounds using the sample function. For example, you can play an industrial drum loop, hear the sound of a choir or even listen to a vinyl scratch all via a single line of code. However, many people don’t realise that you can actually vary the speed that the sample is played back at for some powerful effects and a whole new level of control over your recorded sounds. So, fire up a copy of Sonic Pi and let’s get started stretching some samples!

- Сэмплы

To modify the playback rate of a sample we need to use the rate: opt:

sample :guit_em9, rate: 0.5

If we specify a rate: of 1 then the sample is played back at the normal rate. If we want to play it back at half speed we simply use a rate: of 0.5:

sample :guit_em9, rate: 0.5

choose

- Сэмплы

In addition to making the sound longer and lower using a small rate, we can use higher rates to make the sound shorter and higher. Let’s play with a drum loop this time. First, take a listen to how it sounds at the default rate of 1:

sample :loop_amen, rate: -1

Now, let’s speed it up a little:

sample :loop_amen, rate: 1.5

Ha! We just moved musical genres from old-skool techno to jungle. Notice how the pitch of each drum hit is higher as well as how the whole rhythm speeds up. Now, try even higher rates and see how high and short you can make the drum loop. For example, if you use a rate of 100, the drum loop turns into a click!

- Reverse Gear

Now, I’m sure many of you are thinking the same thing right now… “what if you use a negative number for the rate?”. Great question! Let’s think about this for a moment. If our rate: opt signifies the speed with which the sample is played back, 1 being normal speed, 2 being double speed, 0.5 being half speed, -1 must mean backwards! Let’s try it on a snare. First, play it back at the normal rate:

sample :elec_filt_snare, rate: 1

Круто! Он играет задом-наперед!

sample :elec_filt_snare, rate: -1

Of course, you can play it backwards twice as fast with a rate of -2 or backwards at half speed with a rate of -0.5. Now, play around with different negative rates and have fun. It’s particularly amusing with the :misc_burp sample!

Sample, Rate and Pitch [Sidebar]

One of the effects of rate modification on samples is that faster rates result in the sample sounding higher in pitch and slower rates result in the sample sounding lower in pitch. Another place you may have heard this effect in every day life is when you’re cycling or driving past a beeping pedestrian crossing - as you’re heading towards the sound source the pitch is higher than when you’re moving away from the sound - the so-called Doppler effect. Why is this?

Let’s consider a simple beep which is represented by a sine wave. If we use an oscilloscope to plot a beep, we’ll see something like Figure A. If we plot a beep an octave higher, we’ll see Figure B and an octave lower will look like Figure C. Notice that the waves of higher notes are more compact and the waves of lower notes are more spread out.

A sample of a beep is nothing more than a lot of numbers (x, y, coordinates) which when plotted onto a graph will re-draw the original curves. See figure D where each circle represents a coordinate. To turn the coordinates back into audio, the computer works through each x value and sends the corresponding y value to the speakers. The trick here is that the rate at which the computer works through the x numbers does not have to be the same as the rate with which they were recorded. In other words, the space (representing an amount of time) between each circle can be stretched or compressed. So, if the computer walks through the x values faster than the original rate, it will have the effect of squashing the circles closer together which will result in a higher sounding beep. It will also make the beep shorter as we will work through all the circles faster. This is shown in Figure E.

Finally, one last thing to know is that a mathematician called Fourier proved that any sound is actually lots and lots of sine waves all combined together. Therefore, when we compress and stretch any recorded sound we’re actually stretching and compressing many sine waves all at the same time in exactly this manner.

- Pitch Bending

As we’ve seen, using a faster rate will make the sound higher in pitch and a slower rate will make the sound lower in pitch. A very simple and useful trick is to know that doubling the rate actually results in the pitch being an octave higher and inversely halving the rate results in the pitch being an octave lower. This means that for melodic samples, playing it alongside itself at double/half rates actually sounds rather nice:

sample :bass_trance_c, rate: 1
sample :bass_trance_c, rate: 2
sample :bass_trance_c, rate: 0.5

However, what if we just want to alter the rate such that the pitch goes up one semitone (one note up on a piano)? Sonic Pi makes this very easy via the rpitch: opt:

sample :bass_trance_c
sample :bass_trance_c, rpitch: 3
sample :bass_trance_c, rpitch: 7

If you take a look at the log on the right, you’ll notice that an rpitch: of 3 actually corresponds to a rate of 1.1892 and a rpitch: of 7 corresponds to a rate of 1.4983. Finally, we can even combine rate: and rpitch: opts:

sample :ambi_choir, rate: 0.25, rpitch: 3
sleep 3
sample :ambi_choir, rate: 0.25, rpitch: 5
sleep 2
sample :ambi_choir, rate: 0.25, rpitch: 6
sleep 1
sample :ambi_choir, rate: 0.25, rpitch: 1

- Соединим Всё Вместе

Let’s take a look at a simple piece which combines these ideas. Copy it into an empty Sonic Pi buffer, hit play, listen to it for a while and then use it as a starting point for your own piece. See how much fun it is to manipulate the playback rate of samples. As an added exercise try recording your own sounds and play around with the rate to see what crazy sounds you can make.

sample :guit_em9

- Additive Synthesis

This is the first of a short series of articles on how to use Sonic Pi for sound design. We’ll be take a quick tour of a number of different techniques available for you to craft your own unique sound. The first technique we’ll look at is called additive synthesis. This may sound complicated - but if we expand each word slightly the meaning pops right out. Firstly, additive means a combination of things and secondly synthesis means to create sound. Additive synthesis therefore means nothing more complicated than combining existing sounds to create new ones. This synthesis technique dates back a very long time - for example, pipe organs in the middle ages had lots of slightly different sounding pipes which you could enable or disable with stops. Pulling out the stop for a given pipe ‘added it to the mix’ making the sound richer and more complex. Now, let’s see how we can pull out all the stops with Sonic Pi.

- Simple Combinations

Let’s start with the most basic sound there is - the humble pure-toned sine wave:

synth :sine, note: :d3

Now, let’s see how this sounds combined with a square wave:

synth :sine, note: :d3
synth :square, note: :d3

Notice how the two sounds combine to form a new, richer sound. Of course, we don’t have to stop there, we can add as many sounds as we need. However, we need to be careful with how many sounds we add together. Just like when we mix paints to create new colours, adding too many colours will result in a messy brown, similarly - adding too many sounds together will result in a muddy sound.

- Blending

Let’s add something to make it sound a little brighter. We could use a triangle wave at an octave higher (for that high bright sound) yet only play it at amp 0.4 so it adds something extra to the sound rather than taking it over:

synth :sine, note: :d3
synth :square, note: :d3
synth :tri, note: :d4, amp: 0.4

Now, try creating your own sounds by combining 2 or more synths at different octaves and amplitudes. Also, note that you can play around with each synth’s opts to modify each source sound before it is mixed in for even more combinations of sounds.

- Detuning

So far, when combining our different synths we’ve used either the same pitch or switched octave. How might it sound if we didn’t stick to octaves but instead chose a slightly higher or lower note? Let’s try it:

detune = 0.7
synth :square, note: :e3
synth :square, note: :e3 + detune

If we detune our square waves by 0.7 notes we hear something that perhaps doesn’t sound in tune or correct - a ‘bad’ note. However, as we move closer to 0 it will sound less and less out of tune as the pitches of the two waves get closer and more similar. Try it for yourself! Change the detune: opt value from 0.7 to 0.5 and listen to the new sound. Try 0.2, 0.1, 0.05, 0. Each time you change the value, take a listen and see if you can hear how the sound is changing. Notice that low detune values such as 0.1 produce a really nice ‘thick’ sound, with both slightly different pitches interacting with each other in interesting, often surprising, ways.

:dsaw

- Амплитуда

Another way we can finely craft our sound is to use a different envelope and options for each synth trigger. For example this will allow you to make some aspects of the sound percussive and other aspects ring out for a period of time.

detune = 0.1
synth :square, note: :e1, release: 2
synth :square, note: :e1 + detune, amp: 2, release: 2
synth :gnoise, release: 2, amp: 1, cutoff: 60
synth :gnoise, release: 0.5, amp: 1, cutoff: 100
synth :noise, release: 0.2, amp: 1, cutoff: 90

In the example above I have mixed in a noisy percussive element to the sound along with some more persistent background rumbling. This was achieved firstly by using two noise synths with middling cutoff values (90 and 100) using short release times along with a noise with a longer release time but with a low cutoff value (which makes the noise less crisp and more rumbly.)

- Соединим Всё Вместе

Let’s combine all these techniques to see if we can use additive synthesis to re-create a basic bell sound. I’ve broken this example into four sections. Firstly we have the ‘hit’ section which is the initial onset part of the bell sound - so uses a short envelope (e.g. a release: of around 0.1). Next we have the long ringing section in which I’m using the pure sound of the sine wave. Notice that I’m often increasing the note by roughly 12 and 24 which are the number of notes in one and two octaves. I have also thrown in a couple of low sine waves to give the sound some bass and depth. Finally, I used define to wrap my code in a function which I can then use to play a melody. Try playing your own melody and also messing around with the contents of the :bell function until you create your own crazy sound to play with!

define :bell do |n| # Triangle waves for the ‘hit’ synth :tri, note: n - 12, release: 0.1 synth :tri, note: n + 0.1, release: 0.1 synth :tri, note: n - 0.1, release: 0.1 synth :tri, note: n, release: 0.2

# Sine waves for the ‘ringing’ synth :sine, note: n + 24, release: 2 synth :sine, note: n + 24.1, release: 2 synth :sine, note: n + 24.2, release: 0.5 synth :sine, note: n + 11.8, release: 2 synth :sine, note: n, release: 2

# Low sine waves for the bass synth :sine, note: n - 11.8, release: 2 synth :sine, note: n - 12, release: 2 end

- Play a melody with our new bell!

bell :e3 sleep 1 bell :c2 sleep 1 bell :d3 sleep 1 bell :g2


- Subtractive Synthesis

This is the second in a series of articles on how to use Sonic Pi for sound design. Last month we looked at additive synthesis which we discovered was the simple act of playing multiple sounds at the same time to make a new combined sound. For example we could combine different sounding synths or even the same synth at different pitches to build a new complex sound from simple ingredients. This month we’ll look at a new technique commonly called subtractive synthesis which is simply the act of taking an existing complex sound and removing parts of it to create something new. This is a technique which is commonly associated with the sound of analog synthesisers of the 1960s and 1970s but also with the recent renaissance of modular analog synths through popular standards such as Eurorack.

Despite this sounding like a particularly complicated and advanced technique, Sonic Pi makes it surprisingly simple and easy - so let’s dive right in.

- Complex Source Signal

For a sound to work well with subtractive synthesis, it typically needs to be fairly rich and interesting. This doesn’t mean we need something hugely complex - in fact, just a standard :square or :saw wave will do:

synth :saw, note: :e2, release: 4

Notice that this sound is already pretty interesting and contains many different frequencies above :e2 (the second E on a piano) which add to create the timbre. If that didn’t make much sense to you, try comparing it with the :beep:

synth :beep, note: :e2, release: 4

As the :beep synth is just a sine wave, you’ll hear a much purer tone and only at :e2 and none of the high crispy/buzzy sounds which you heard in the :saw. It’s this buzziness and variation from a pure sine wave that we can play with when we use subtractive synthesis.

- Фильтры

Once we have our raw source signal, the next step is to pass it through a filter of some kind which will modify the sound by removing or reducing parts of it. One of the most common filters used for subtractive synthesis is something called a low pass filter. This will allow all the low parts of the sound through but will reduce or remove the higher parts. Sonic Pi has a powerful yet simple to use FX system that includes a low pass filter, called :lpf. Let’s play with it:

with_fx :lpf, cutoff: 100 do
  synth :saw, note: :e2, release: 4
end

If you listen carefully you’ll hear how some of that buzziness and crispiness has been removed. In fact, all the frequencies in the sound above note 100 have been reduced or removed and only the ones below are still present in the sound. Try changing that cutoff: point to lower notes, say 70 and then 50 and compare the sounds.

Of course, the :lpf isn’t the only filter you can use to manipulate the source signal. Another important FX is the high pass filter referred to as :hpf in Sonic Pi. This does the opposite to :lpf in that it lets the high parts of the sound through and cuts off the low parts.

with_fx :hpf, cutoff: 90 do
  synth :saw, note: :e2, release: 4
end

Notice how this sounds much more buzzy and raspy now that all the low frequency sounds have been removed. Play around with the cutoff value - notice how lower values let more of the original bass parts of the source signal through and higher values sound increasingly tinny and quiet.

- [Low Pass Filter] - breakout box

:prophet

- Filter Modulation

So far we’ve just produced fairly static sounds. In other words, the sound doesn’t change in any way for the entirety of its duration. Often you might want some movement in the sound to give the timbre some life. One way to achieve this is via filter modulation - changing the filter’s options through time. Luckily Sonic Pi gives you powerful tools to manipulate an FX’s opts through time. For example, you can set a slide time to each modulatable opt to specify how long it should take for the current value to linearly slide to the target value:

with_fx :lpf, cutoff: 50 do |fx|
  control fx, cutoff_slide: 3, cutoff: 130
  synth :prophet, note: :e2, sustain: 3.5
end

Let’s take a quick look at what’s going on here. Firstly we start an :lpf FX block as normal with an initial cutoff: of a very low 20. However, the first line also finishes with the strange |fx| at the end. This is an optional part of the with_fx syntax which allows you to directly name and control the running FX synth. Line 2 does exactly this and controls the FX to set the cutoff_slide: opt to 4 and the new target cutoff: to be 130. The FX will now start sliding the cutoff: opt’s value from 50 to 130 over a period of 3 beats. Finally we also trigger a source signal synth so we can hear the effect of the modulated low pass filter.

- Соединим Всё Вместе

This is just a very basic taster of what’s possible when you use filters to modify and change a source sound. Try playing with Sonic Pi’s many built-in FX to see what crazy sounds you can design. If your sound feels too static, remember you can start modulating the options to create some movement.

Let’s finish by designing a function which will play a new sound created with subtractive synthesis. See if you can figure out what’s going on here - and for the advanced Sonic Pi readers out there - see if you can work out why I wrapped everything inside a call to at (please send answers to @samaaron on Twitter).

rrand

- Необходимые Знания

Этот раздел будет охватывать некоторые очень полезные - на самом деле даже необходимые знания для получения максимальной отдачи от вашего опыта работы с Sonic Pi.

Мы поговорим о том, как получить пользу от множества доступных вам клавиатурных сочетаний, как делиться своими работами, а ещё, я дам вам несколько советов о выступлениях с Sonic Pi.


- Использование Клавиатурных Сочетаний

Sonic Pi такой же инструмент, как и среда разработки. Следовательно, клавиатурные сочетания моуг сделать работу c Sonic Pi намного более эффективной и естественной - особенно когда вы играете вживую перед аудиторией.

Многое в Sonic Pi может контролироваться с помощью клавиатуры. Когда вы лучше познакомитесь с работой и выступлениями с Sonic Pi, вы, вероятно, начнёте использовать клавиатурные сочетания всё чаще и чаще. Лично я печатаю вслепую (рекомендую обдумать возможность обучения этому навыку) и огорчаюсь всякий раз, когда мне нужно тянуться за мышкой, так как это меня замедляет. Поэтому я использую все эти клавиатурные сочетания на постоянной основе!

Следовательно, если вы изучите эти клавиатурные сочетания, вы узнаете, как эффективно использовать клавиатуру, и вы начнёте кодировать вживую как профи в кратчайшие сроки.

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

Согласованность Всех Платформ

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

К сожалению три основные операционные системы (Linux, Mac ОС X и Windows) поставляются с собственными стандартами по умолчанию для таких действий, как вырезать, вставить и т. д. Sonic Pi будет пытаться соблюдать эти стандарты. Однако, вместо попыток соответствовать стандартам текущей платформы, Sonic Pi приоритетное внимание уделяет согласованности между платформами. Это значит, что если вы изучите сочетания клавиш во время игры с Sonic Pi на Raspberry Pi, то сможете перейти на Mac или PC и чувствовать себя там как дома.

Control и Meta

Частью понятия согласованности является именование клавиатурных сочетаний. В Sonic Pi мы используем имена Control и Meta, ссылаясь на две главные комбинации клавиш. На всех платформах Control один и тот же (Ctrl). Однако, на Linux и Windows, Meta - это клавиша Alt, в то время как на Mac, Meta - это клавиша Command. Для единообразия мы будем использовать термин Meta - только не забудьте сопоставить его с соответствующей клавишей вашей операционной системы.

Аббревиатуры

Чтобы сохранить вещи простыми и читабельными, мы будем использовать аббревиатуры - С- для Control плюс ещё одна клавиша, и М- - для Meta плюс ещё одна клавиша. Например, если клавиатурное сочетание требует от нас нажать одновременно Meta и r, мы напишем это как M-r. Символ - означает “одновременно с”.

Ниже приведены клавиатурные сочетания, которые я считаю самыми полезными.

Выполнение и Остановка

Вместо того, чтобы постоянно тянуться за мышью, чтобы выполнить свой код, вы можешьте просто нажать M-r. Аналогично, чтобы остановить выполнение кода, вы можешьте нажать M-s.

Навигация

Я действительно потерялся бы без навигационных клавиш. Поэтому я настоятельно рекомендую вам потратить время, чтобы выучить их. Эти клавиатурные комбинации работают чрезвычайно хорошо в том случае, если вы освоили слепую печать, так как они используют обычные буквы и не нужно перемещать руку на мышь или клавиши со стрелками на вашей клавиатуре.

Вы можете двигаться к началу строки, используя C-a, к концу строки, используя C-e, на строку вверх - C-p, на строку вниз - C-n, на символ вперёд - C-f, и на символ назад - C-b.

Аккуратный Код

Чтобы применить автовыравнивание к вашему коду, просто нажмите M-m

Справочная Система

Для переключения к справочной системе вы можете нажать M-i. Однако, гораздо полезнее знать сочетание C-i, которое будет искать слово под курсором в документации и отображать её, если что-нибудь найдет. Мгновенная помощь!

Чтобы увидеть полный список клавиатурных сочетаний, загляните в раздел 10.2 Шпаргалка по клавиатурным сочетаниям.


- Шпаргалка По Клавиатурным Сочетаниям

Ниже приводится обзор основных клавиатурных сочетаний, доступных в Sonic Pi. Пожалуйста, посмотрите раздел 10.1 для подготовки и мотивации.

Соглашения

В этом списке мы используем следующие соглашения (где Meta называется Alt в Windows/Linux или Cmd в Mac):

C-a значит удерживая клавишу Control, нажмите клавишу a. И после того, как обе клваиши будут нажаты одновременно, отпустите их. M-r значит удерживая клавишу Meta, нажмите клавишу r. И после того, как обе клваиши будут нажаты одновременно, отпустите их. S-M-z значит удерживая клавиши Shift и Meta, нажмите клавишу z. И после того, как все три клваиши будут нажаты одновременно, отпустите их. C-M-f значит удерживая клавиши Control и Meta, нажмите клавишу f. И после того, как все три клваиши будут нажаты одновременно, отпустите их.

Основные Манипуляции С Приложением

M-r - Выполнить код M-s - Остановить выполнение кода M-i - Показать/скрыть справочную систему M-p - Показать/скрыть настройки M-{ - Переключить буфер влево M-} - Переключить буфер вправо M-+ - Увеличить размер текста в текущем буфере M-- - Уменьшить размер текста в текущем буфере

Выделение/Копирование/Вставка

M-a - Выбрать всё M-c - Копировать выделение в буфер обмена M-] - Копировать выделение в буфер обмена M-x - Вырезать выделение в буфер обмена C-] - Вырезать выделение в буфер обмена C-k - Вырезать от курсора до конца строки M-v - Вставить из буфера обмена в редактор C-y - Вставить из буфера обмена в редактор C-SPACE - Установить метку. Теперь навигация управляет областью выделения. Используйте C-g для отмены.

Работа С Тексом

M-m - Выровнять весь текст Tab - Выровнять текущую линию/выделение C-l - Отобразить буфер с текущей строкой по центру экрана M-/ - Комментировать/раскомментировать текущую строку C-t - Перемещение/замена символов M-u - Преобразовать следующее слово (или выделение) в верхний регистр M-l - Преобразовать следующее слово (или выделение) в нижний регистр

Навигация

C-a - Перейти в начало строки C-e - Перейти в конец строки C-p - Перейти на предыдущую строку C-n - Перейти на следующую строку C-f - Перейти вперёд на один символ C-b - Перейти назад на один символ M-f - Перейти вперёд на одно слово M-b - Перейти назад на одно слово C-M-n - Переместить строку или выделение вниз C-M-p - Переместить строку или выделение вверх S-M-u - Подняться на 10 строк S-M-d - Опуститься на 10 строк M-< - Перейти в начало буфера M-> - Перейти в конец буфера

Удаление

C-h - Удалить предыдущий символ C-d - Удалить следующий символ

Расширенные Возможности Редактора

C-i - Показать документацию по слову под курсором M-z - Отмена S-M-z - Повтор C-g - Escape S-M-f - Переключение полноэкранного режима S-M-b - Показать/скрыть кнопоки S-M-l - Показать/скрыть журнал S-M-m - Переключение между светлым и тёмным режимом S-M-s - Сохранить содержание буфера в файл S-M-o - Загрузить содержания из файла в буфер


- Обмен

Sonic Pi создан для обучения и обмена друг с другом.

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

Если вы не уверены, какой способ поделиться своей работой с остальными лучший, я рекомендую размещать свой код на GitHub и музыку на SoundCloud. Таким образом вы сможете охватить большую аудиторию.

Код -> GitHub

GitHub - это сайт для обмена и работы с кодом. Он используется профессиональными разработчиками, а также художниками для обмена и совместной работы с кодом. Самый простой способ поделиться новым куском кода (или даже незавершённым куском) - создать Gist. Gist - это простой способ загружать свой код в удобной форме, чтобы другие могли увидеть, скопировать и поделиться им.

Аудио -> SoundCloud

Еще один отличный способ поделиться вашей работой - сделать аудио запись и загрузить её на SoundCloud. После того как вы загрузили свою запись, другие пользователи могут комментировать и обсуждать её. Я также рекомендую поместить ссылку на ваш код в Gist в описание трека.

Для того, чтобы записать свою работу, нажмите кнопку Запись в панели инструментов, и запись начнётся немедленно. Нажмите кнопку Выполнить, чтобы запустить свой код, если он ещё не запущен. Когда вы закончите запись, нажмите мигающую кнопку Запись снова, и вам будет предложено ввести имя файла. Запись будет сохранена в формате WAV-файл, который можно редактировать и конвертировать любым аудиоредактором с открытым исходным кодом (попробуйте, например, Audacity).

Надежда

Я призываю вас делиться своей работой и на самом деле надеюсь, что мы все будем учить друг друга новым трюкам и развиваться вместе с Sonic Pi. Я действительно очень воодушевлён тем, что у вас будет, что показать мне.


- Выступление

Один из самых интересных аспектов Sonic Pi заключается в том, что он позволяет использовать код как музыкальный инструмент. Это значит, что написание кода вживую может теперь рассматриваться как новый вид музыкального исполнения.

Мы называем это Лайвкодинг.

Показывай Свой Экран

Я рекомендую вам показывать сой экран аудитории во время лайвкодинга. Это как играть на гитаре, но скрывая свои пальцы и струны. Когда я практикуюсь дома, я использую Raspberry Pi и небольшой мини-проектор, направленный на стену моей гостинной. Вы можете использовать свой телевизор или один из своих школьных/рабочих проекторов для своего шоу. Попробуйте, это очень весело.

Собери группу

Играйте не просто для себя - соберите группу! Это очень весело - играть с другими. Один человек может делать бит, другой - эмбиент фон, и т.д. Посмотрите, какие интересные сочетания звуков вы сможете получить вместе.

TOPLAP

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

Алгорэйв

Ещё один отличный ресурс для изучения лайвкодинга, это Algorave. Здесь вы можете найти всё о конкретном направлении лайвкодинга - создании музыки в ночных клубах.


- Minecraft Pi

Sonic Pi теперь поддерживает простой API для взаимодействия с Minecraft Pi - специальным изданием Minecraft, которое устанавливается по умолчанию в Raspbian - операционную систему на основе Linux.

Нет Нужды Импортировать Библиотеки

Интеграция в Minecraft Pi была разработана с целью быть безумно простой в использовании. Все, что вам нужно сделать, это запустить Minecraft Pi и создать мир. Затем можно свободно использовать mc_* функции так, как если бы вы могли использовать play и synth. Не надо ничего импортировать или устанавливать какие-либо библиотеки - все готово и работает из коробки.

Автоматическое Подключение

Minecraft Pi API отвечает за управление подключением в приложении Sonic Pi. Это означает, что вам не надо беспокоиться ни о чем. Если вы попытаетесь использовать Minecraft API, пока Minecraft не запущен, Sonic Pi вежливо сообщит вам об этом. Аналогично, если закрыть Minecraft Pi в то время, как всё ещё повторяется цикл live_loop, использующий Minecraft API, цикл остановится и Sonic Pi вежливо скажет вам, что не может подключиться к Minecraft. Чтобы переподключиться, просто запустите Minecraft снова и Sonic Pi автоматически обнаружит и заново создаст подключение.

Разработано Для Лайвкодинга

Minecraft Pi API был разработан для безупречной работы с циклами live_loop. Это значит, что можно синхронизировать изменения в вашем Minecraft Pi мире с изменениями звука в Sonic Pi. Мгновенные, основанные на Minecraft, музыкальные клипы! Заметим, однако, что Minecraft Pi - альфа версия программы и, как известно, немного глючит. Если у вас возникнут проблемы, просто перезапустите Minecraft Pi и продолжайте как ни в чём не бывало. Sonic Pi позаботится о том, чтобы автоматически соедениться c Minecraft Pi.

Требуется Raspberry Pi 2

Настоятельно рекомендуется, использовать Raspberry Pi 2, если вы хотите запускать Sonic Pi и Minecraft в одно и то же время - особенно если вы хотите использовать звуковые возможности Sonic Pi.

Поддержка API

На данном этапе, Sonic Pi поддерживает базовые манипуляции блоками и игроком, подробно описанные в разделе 11.1. Поддержка обратных вызовов событий, вызванных взаимодействием игрока с миром, планируется в будущих релизах.


11.1 - Базовый API Minecraft Pi

Sonic Pi в настоящее время поддерживает следующие основные взаимосвязи с Minecraft Pi:

Отображение сообщений в чате Установка позиции пользователя Получение позиции пользователя Установка типа блока по заданным координатам Получение типа блока по заданным координатам

Давайте рассмотрим каждую из них по очереди.

Отображение сообщений в чате

Давайте посмотрим, как легко управлять Minecraft Pi из Sonic Pi. Во-первых, убедись, что и Minecraft Pi, и Sonic Pi, открыты в одно и то же время, а также, что вы вошли в мир Minecraft и можете там передвигаться.

Просто наберите в пустом буфере Sonic Pi следующий код:

mc_message "Hello from Sonic Pi"

Когда вы нажмёте кнопку Выполнить, вы увидите своё сообщение в окне Minecraft. Поздравляю, вы написали свой первый код в Minecraft! Это было просто, не так ли?

Установка позиции пользователя

Теперь давайте попробуем немного магии. Телепортируем себя куда-нибудь! Наберите следующее:

mc_teleport 50, 50, 50

Когда вы нажмёте Выполнить - бум! Вы мгновенно переноситесь на новое место. Скорее всего, это будет где-то в небе, и вы упадёте на землю, либо на сушу, или в воду. Что это за цифры: 50, 50, 50? Это координаты места, в которыое вы пытаетесь телепортироваться. Давайте сделаем короткий перерыв, чтобы выяснить, что такое координаты и как они работают, потому что они очень, очень важны для программирования Minecraft.

Координаты

Представьте пиратскую карту с большой X меткой на месте расположения каких-нибудь сокровищ. Точное местоположение X может быть описано с помощью двух чисел - как далеко по карте слева направо и как далеко по карте снизу вверх. Например 10см по горизонтали и 8 см вверх. Эти числа 10 и 8 - это координаты. Вы можете легко представить местоположение других тайников с сокровищами, описанное другой парой чисел. Возможно, есть большой сундук золота на 2 по горизонтали и 9 по вертикали…

Но, в Minecraft двух чисел не достаточно. Нам также нужно знать как высоко мы находимся. Следовательно, нам нужно три числа:

Как далеко с права на лево в мире - x Как далеко от передней до задней части мира - z Как высоко мы находимся в мире - y

Ещё один ньюанс - мы описываем эти координаты в таком порядке: x, y, z.

Нахождение Твоих Текущих Координат

Давайте поиграем с координатами. Переместитесь в приятное место на карте Minecraft, а потом переключитесь на Sonic Pi. Введите следующее:

puts mc_location

Когда вы нажмёте кнопку Выполнить вы увидите, что координаты текущего положения отобразятся в окне журнала. Запишите их, затем двигайтесь вперёд в мире Minecraft и попробуйте получить координаы снова. Обратите внимание, как они изменились! А сейчас, я рекомендую вам потратить некоторое время на повторение именно этого - немного переместитесь в мире, посмотрите координаты и повтори это снова. Делайте это до тех пор, пока не начнёте чувствовать, как изменяются координаты, когда вы перемещаетесь. Как только вы поймёте, как координаты работают, программирование с Minecraft API будет совершенной мелочью.

Давайте Строить!

Теперь, когда вы знаете, как найти текущую позицию и телепортироваться, используя координаты, у вас есть все инструменты, чтобы начать строить различные вещи в Minecraft с помощью кода. Допустим, вы хочтите сделать блок с координатами 40, 50, 60 стеклянным. Это супер просто:

mc_set_block :glass, 40, 50, 60

Ха-ха, это действительно было просто. Увидеть дело рук своих проще простого - телепортируйтесь рядом и посмотрите:

mc_teleport 35, 50, 60

Теперь повернитесь и ты увидите свой стеклянный блок! Попробуйте изменить его на алмаз:

mc_set_block :diamond, 40, 50, 60

Если вы смотрели в правильном направлении, то могли увидеть эти изменения соими глазами! Это начало чего-то захватывающего…

Смотрим На Блоки

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

puts mc_get_block 40, 50, 60

Эй! Это :diamond. Попробуйте изменить его обратно на стекло и спросите Minecraft об этом снова - он ответит :glass? Я уверен, что да :-)

Доступные Типы Блоков

Перед тем, как пойти неистово кодировать на Minecraft Pi, вы могли бы найти этот список доступных типов блоков полезным:

    :air
    :stone
    :grass
    :dirt
    :cobblestone
    :wood_plank
    :sapling
    :bedrock
    :water_flowing
    :water
    :water_stationary
    :lava_flowing
    :lava
    :lava_stationary
    :sand
    :gravel
    :gold_ore
    :iron_ore
    :coal_ore
    :wood
    :leaves
    :glass
    :lapis
    :lapis_lazuli_block
    :sandstone
    :bed
    :cobweb
    :grass_tall
    :flower_yellow
    :flower_cyan
    :mushroom_brown
    :mushroom_red
    :gold_block
    :gold
    :iron_block
    :iron
    :stone_slab_double
    :stone_slab
    :brick
    :brick_block
    :tnt
    :bookshelf
    :moss_stone
    :obsidian
    :torch
    :fire
    :stairs_wood
    :chest
    :diamond_ore
    :diamond_block
    :diamond
    :crafting_table
    :farmland
    :furnace_inactive
    :furnace_active
    :door_wood
    :ladder
    :stairs_cobblestone
    :door_iron
    :redstone_ore
    :snow
    :ice
    :snow_block
    :cactus
    :clay
    :sugar_cane
    :fence
    :glowstone_block
    :bedrock_invisible
    :stone_brick
    :glass_pane
    :melon
    :fence_gate
    :glowing_obsidian
    :nether_reactor_core