Целую неделю (23.01-31.01.2018) в арт-галерее «Дар» (Псков) работала выставка, озвученная при помощи программы на языке Chuck. Я попытаюсь рассказать, почему и как все это получилось.
Небольшая необходимая преамбула
Бывает так, что фейсбучная активность перерастает в нечто большее; так образовался своеобразный проект «Пост-исторический город П.» — онлайн-сообщество, иногда, впрочем, выходящее и на оффлайновые акции. По материалам сообщества в сентябре 2017 года вышла книга «Пост-исторический город П. Антипутеводитель», одним из авторов (и издателей) которой оказался я. Подробнее о книге можно почитать по ссылкам ниже, пока же достаточно сказать, что это сборник фотографий и текстов, нечто вроде каталога, который мифологизирует скучную бетонную реальность современного города.
Где-то осенью же зародилась идея выставки, крупноформатного собрания «greatest hits» по мотивам книги, а также кое-чего, в книгу не вошедшего. Тогда же появилась мысль озвучить пространство, в котором будут расположены изображения и тексты, звуками города. Не откладывая дело в долгий ящик, Дмитрий, мой соавтор, вооружившись соответствующим устройством, отправился бродить по городу П., записывая шумы, разговоры, скрипы и шорохи. В итоге получилось что-то около полутора часов сырого звука из разных мест города.
Потом, как водится, начались авралы на работе, командировки, другие интересные занятия, Новый год, наконец, и когда вдруг вопрос с выставкой решился – неожиданно быстро – оказалось, что уже через пару недель надо иметь готовый звук.
Реализация
Изначально мне хотелось сделать что-то незафиксированное, непредсказуемое – не просто нарезать и склеить готовых звуков, а чтобы они накладывались друг на друга и жили как бы своей жизнью. Про ChucK я на тот момент не знал практически ничего, но по первом рассмотрении он показался подходящим инструментом – знакомые языковые конструкции, классы с наследованием, многопоточность задаром, возможность работать с аудио на достаточно низком уровне. Прослушав в быстром темпе за несколько вечеров курс с лекций Kadenze, в выходные я засел за реализацию проекта.
Структура в итоге сложилась следующая:
- перетекающие друг в друга в случайном порядке звуковые ландшафты – среда, фон, на котором все происходит
- события – отдельные реплики или звуки, которые возникают тоже случайно с применением набора несложных эффектов (обратное воспроизведение, эхо, «робот», ускорение/замедление)
- «биты» — набор достаточно стандартных электро/хип-хоп паттернов, озвученных скрипами, всхлипами, репликами, вырезанными из оригинального звука
- простенький гранулярный синтез на небольшом наборе отдельных звуков: из случайных мест оригинального файла проигрывается небольшой кусочек, каждый раз разный
Получившаяся в итоге композиция стала удачным дополнением к выставке: пока человек разглядывает фото и читает текст, из размещенных на потолке динамиков слышится, допустим, гулкий звук двора, отдаленные детские голоса, потом вдруг возникает какая-нибудь странная реплика, и крутится, отдаляясь и ускоряясь, или появляется нервный икающий бит, на фоне которого шуршит обрывками чужого голоса синтезатор. Все это, разумеется, происходит непредсказуемым образом, и никогда не знаешь, что прозвучит в следующий момент.
В качестве музыки это достаточно жестокая по отношению к слушателю вещь, но в качестве фона, сопровождения – результат оказался вполне удовлетворителен. Не менее регулярная и не более шизофреническая, чем окружающая панельная повседневность, эта звуковая среда создает именно тот эффект, который и был нам нужен: не погрузиться в реальность города П., а выйти из нее, взглянуть со стороны, удивиться и задуматься.
Я не буду разбирать здесь исходный код – кто хочет, может прочитать его на GitHub, ничего сложного там нет. Чтобы запустить и послушать, достаточно скачать и установить ChucK и запустить run.cmd из корневой папки. Все – разработка и воспроизведение – делалось под Windows.
Плюшки и очарования
Одна из самых изящных вещей в ChucK – это одноименный перегруженный оператор, который выглядит как «=>». Да, присваивания (и, соответственно, инициализация с объявлением переменных) выглядят несколько непривычно:
0 => int i;
"test" => string message;
поначалу машинально путаешь правую и левую стороны. Но зато как красиво соединяются объекты. Допустим, мы хотим взять звуковой файл, добавить реверберации, отрегулировать громкость и вывести получившийся результат в отдельный канал. В этом случае строится простая интуитивно понятная цепочка:
SndBuf sound => JCRev reverb => Gain master => Pan2 pan => dac;
Дальше можно настроить необходимые параметры – поскольку мы никак еще не работаем со временем, все это пока не звучит.
Обращение со временем – еще одна изящно сделанная часть ChucK. Выражения, исчисляющие время, выглядят как «число:: единица времени». Например:
1::second
1000::samp
2::day
(x + y)::minute
и так далее. Чтобы раздался какой-нибудь звук, нужно определить, сколько он будет звучать. Для этого тоже используется оператор chuck:
1::second => now;
Такое выражение называется «advance time» — мне почему-то нравится переводить это как «занять времени». Таким образом, чтобы издать первый звук на ChucK, нужно сделать следующее:
SinOsc sin => dac; // это уже полностью инициализированный объект-осциллятор, соединенный с устройством воспроизведения, но он пока молчит
// чтобы заставить его издать звук, нужно задать частоту (хотя по умолчанию она тоже не нулевая)
440 => sin.freq; // 440 Hz
// и «занять времени»
1::second => now;
// вуаля – ровно одну секунду мы слышим звук указанной частоты
ChucK вообще довольно дружелюбная штука, и многие вещи делаются совсем просто. Например, проиграем звук из файла:
SndBuf snd => dac; // инициализируем объект и направляем вывод
"c:/music/test.wav" => snd.read; // читаем файл
snd.samples()::samp => now; // проигрываем до конца
Еще одна красиво реализованная вещь – создание потоков (в терминологии ChucK – shred). Для того, чтобы создать новый «шред» используется ключевое слово «spork» и специальный оператор «~». Вот как это выглядит:
// играем случайные звуки в заданный канал
fun void play(int channel)
{
SinOsc sin => dac.chan(channel); // создаем осциллятор
while(true)
{
Math.random2f(100, 500) => sin.freq; // задаем частоту
.1::second => now; // занимаем 100 мс
}
}
// основная программа
spork ~ play(0); // играем в левый канал
spork ~ play(1); // играем в правый канал
// висим бесконечно, чтобы порожденные потоки не уничтожились
while(true) 1::second => now;
В общем я не намерен, конечно, писать тут «ChucK для начинающих»; приведенных примеров, думаю, достаточно, чтобы заинтересовать тех, кто может заинтересоваться.
Глюки и разочарования
При всей простоте, дружелюбности и интуитивной понятности, в ChucK (по крайней мере, под Windows – может, с другими платформами дела обстоят получше) встречаются и проблемы.
Среда разработки – miniAudicle – конечно, мало пригодна для таковой. Да, есть подсветка синтаксиса, обзор устройств, контроль виртуальной машины, консоль. Но – нет даже элементарного поиска/замены. К тому же периодически она грохается с ошибкой – например, если забыть поставить @ в операторе присваивания по ссылке «@=>» — и если в этот момент окно консоли не видно, то нет никакой возможности понять, в чем проблема.
В ChucK есть классы с наследованием, и это очень круто – но в них нет конструкторов, что несколько расстраивает. Вообще, когда я начал собственно писать код, возник какой-то когнитивный диссонанс – если достаточно сложные вещи делаются в ChucK просто, то что касается простых вещей – вроде работы со строками – в документации ни слова. Оказалось, что есть еще одна документация, где присутствует некоторое количество информации о стандартных библиотеках.
Поначалу я делал по потоку на каждый тип звука, и старался повторно использовать объекты SndBuf – но обнаружилось, что по прошествии некоторого времени, во-первых, начинает появляться какое-то потрескивание при воспроизведении, во-вторых, некоторые эффекты не отключаются, а аккумулируются, несмотря на все мои усилия их обнулить.
В результате я пришел к конструкции, когда каждое событие создает отдельный shred, где все объекты создаются заново и потом удаляются. Однако, выяснилось, что они не удаляются – заявленная сборка мусора, видимо, далека от совершенства, и при воспроизведении процесс постепенно съедает всю доступную память. Победить эту проблему в отведенный срок я так и не смог, поэтому просто попросил работников галереи время от времени перезапускать исполняемый файл.
В целом – ChucK производит очень приятное впечатление, для быстрого наброска, небольшого проекта, демонстрации – самое то. Но если вы задумали более серьезный проект – стоит основательно взвесить все «за» и «против», местами ChucK еще сыроват.
Дополнительные материалы
ChucK – основные ресурсы
- Дистрибутив
- Документация. Это далеко не полная документация. Удивительно, но здесь ни слова нет о базовых вещах, которые наверняка потребуются, вроде работы со строками или файлового ввода/вывода.
- Другая документация. Этот материал частично пересекается с предыдущим, но также – что очень ценно – заполняет пробелы относительно стандартных библиотек.
- Курс на Kadenze. Для прослушивания лекций требуется регистрация, для выполнения заданий и получения сертификата нужно платить. Этот курс рассчитан на людей, не имеющих опыта программирования, так что имеющим таковой местами будет скучновато. Но зато вы получите множество ценных сведений о ChucK практически из первых рук.
- Книга «Programming for Musiciands and Digital Artists». Книга содержит примерно ту же информацию, что и видеокурс. Пришедшим с Kadenze дают скидку; но в то же время полный вариант книжки достаточно легко нагуглить в pdf