- PVSM.RU - https://www.pvsm.ru -
Если вы когда-нибудь посещали кинотеатр, то наверняка слышали Deep Note [1] — звуковой трейдмарк [2] компании THX [3]. Это один из первых звуков, который раздаётся в начале трейлеров в залах, сертифицированных THX. Мне всегда нравилось его узнаваемое крещендо, начиная с жуткого смешения нот и заканчивая ярким и грандиозным финалом (звук [4]). Какая услада для уха!
Вчера (вероятно) без всяких причин меня заинтересовало происхождение этого звука, и я провёл небольшое исследование. Меня глубоко тронула его история, которой хочу поделиться с вами. Затем мы продолжим — и сами создадим этот звук, готовьте ножницы и клей!
Лучший источник информации о звуке, который я смог найти — по-моему, это его полная электроакустическая композиция, опубликованная в отличном блоге Music Thing Blog [5] в 2005 году. Вот ссылка на сообщение [6].
Некоторые факты о звуке:
«Хотел бы сказать, что звук THX — самое популярное произведение компьютерной музыки в мире. Это может быть правдой или нет, но звучит круто!»
Обязательно послушайте звук, потому что при воссоздании Deep Note мы будем обращаться к этой конкретной записи.
Вот некоторые технические/теоретические факты, прежде чем приступить к синтезу звука:
Итак, приступим. Здесь мой рабочий инструмент — SuperCollider. Начнём с простого сэмпла. В качестве источника хочу использовать пилообразную волну, у неё богатый и гармонический спектр из чётных и нечётных компонентов. Позже я планирую отфильтровать вершины. Вот фрагмент из начальной части кода:
//30 осцилляторов, распределённых по стереополю
(
{
var numVoices = 30;
//generating initial random fundamentals:
var fundamentals = {rrand(200.0, 400.0)}!numVoices;
Mix
({|numTone|
var freq = fundamentals[numTone];
Pan2.ar
(
Saw.ar(freq),
rrand(-0.5, 0.5), //stereo placement of voices
numVoices.reciprocal //scale the amplitude of each voice
)
}!numVoices);
}.play;
)
Я выбрал 30 осцилляторов для генерации звука, в соответствии с возможностями компьютера ASP, как рассказывал д-р Мурер. Создал массив из 30 случайных частот между 200 и 400 Гц, распределил их случайным образом по стереополю с помощью Pan2.ar с аргументом rrand(-0.5, 0.5), назначил частоты пилообразным осцилляторам (30 экземпляров). Вот как это звучит [11].
Если изучить информацию от д-ра Мурера и/или внимательно прислушаться к оригинальному фрагменту, то можно услышать, что частоты осцилляторов случайным образом смещаются вверх и вниз. Хочется добавить этот эффект для более органичного звучания. Частотная шкала логарифмическая, поэтому на низких частотах должны быть более узкие диапазоны колебаний, чем на более высоких. Такое можно реализовать сортировкой наших случайно сгенерированных частот с присвоением LFNoise2 (который генерирует квадратично интерполированные случайные значения) аргументов mul по порядку внутри нашего макроса Mix. И я ещё добавил для осцилляторов фильтр нижних частот с частотой среза по пятикратной частоте осциллятора и умеренным 1/q:
//добавление к частотам случайных колебаний, сортировка, фильтр низких частот
(
{
var numVoices = 30;
//sorting to get high freqs at top
var fundamentals = ({rrand(200.0, 400.0)}!numVoices).sort;
Mix
({|numTone|
//fundamentals are sorted, so higher frequencies drift more.
var freq = fundamentals[numTone] + LFNoise2.kr(0.5, 3 * (numTone + 1));
Pan2.ar
(
BLowPass.ar(Saw.ar(freq), freq * 5, 0.5),
rrand(-0.5, 0.5),
numVoices.reciprocal
)
}!numVoices);
}.play;
)
Вот как звучит образец [12] с последними правками.
Это уже выглядит хорошей отправной точкой, поэтому приступим к реализации развёртки, сначала очень грубо. Чтобы реализовать развёртку, сначала нужно определить окончательные частоты для каждого осциллятора. Это не очень просто, но и не очень сложно. Основной тон должен находиться между низкими D и Eb, так что средней частотой для этого тона будет 14,5 (0 — это C, отсчитывая хроматически, без первой октавы). Так что для 30 осцилляторов переводим случайные частоты между 200 и 400 Гц в значение 14,5 и соответствующие октавы. На слух я выбрал первые 6 октав. Итак, конечный массив частот получается таким:
(numVoices.collect({|nv| (nv/(numVoices/6)).round * 12; }) + 14.5).midicps;
Будем использовать развёртку от 0 до 1. Случайные частоты умножаются на значение (1 − развёртка), а целевые частоты умножаются на саму развёртку. Поэтому когда развёртка равна 0 (начало), то частота будет случайной. Когда развёртка 0,5, то получается ((рандом + целевая частота) / 2), а когда равна 1, то частота и будет конечным значением. Вот модифицированный код:
//создаём начальную развёртку (грубо), создаём финальные частоты
(
{
var numVoices = 30;
var fundamentals = ({rrand(200.0, 400.0)}!numVoices).sort;
var finalPitches = (numVoices.collect({|nv| (nv/(numVoices/6)).round * 12; }) + 14.5).midicps;
var sweepEnv = EnvGen.kr(Env([0, 1], [13]));
Mix
({|numTone|
var initRandomFreq = fundamentals[numTone] + LFNoise2.kr(0.5, 3 * (numTone + 1));
var destinationFreq = finalPitches[numTone];
var freq = ((1 - sweepEnv) * initRandomFreq) + (sweepEnv * destinationFreq);
Pan2.ar
(
BLowPass.ar(Saw.ar(freq), freq * 5, 0.5),
rrand(-0.5, 0.5),
numVoices.reciprocal //scale the amplitude of each voice
)
}!numVoices);
}.play;
)
Звук здесь [13].
Как я уже говорил, это очень грубая развёртка. Она линейно повышается от 0 до 1, что не согласуется с оригинальной композицией. Также вы могли заметить, что последние октавы звучат ужасно, потому что они настроены на идеальные октавы и сливаются друг с другом как базовые тона и овертоны. Мы это исправим, добавив случайное колебание на финальном этапе — так же, как делалось вначале, и это будет звучать гораздо более органично.
Сначала нужно исправить общую формулу частотной развёртки. Предыдущая была просто для пробы. Если посмотрим на оригинал, то заметим, что в первые 5−6 секунд в звучании очень мало изменений. После этого происходит быстрая и экспоненциальная развёртка, которая уводит осцилляторы в конечные октавные интервалы. Вот вариант, который я выбрал:
sweepEnv = EnvGen.kr(Env([0, 0.1, 1], [5, 8], [2, 5]));
Здесь переход от 0 до 0,1 занимает 5 секунд, а переход от 0,1 до 1 занимает 8 секунд. Курватуры для этих сегментов установлены в 2 и 5. Позже послушаем, что получилось, но сначала надо исправить ещё окончательные интервалы. Как и раньше, добавим случайные колебания с LFNoise2, диапазон которого пропорционален конечной частоте осциллятора. Это сделает финал более органичным. Вот изменённый код:
//настройка развёртки и финального аккорда
(
{
var numVoices = 30;
var fundamentals = ({rrand(200.0, 400.0)}!numVoices).sort;
var finalPitches = (numVoices.collect({|nv| (nv/(numVoices/6)).round * 12; }) + 14.5).midicps;
var sweepEnv = EnvGen.kr(Env([0, 0.1, 1], [5, 8], [2, 5]));
Mix
({|numTone|
var initRandomFreq = fundamentals[numTone] + LFNoise2.kr(0.5, 3 * (numTone + 1));
var destinationFreq = finalPitches[numTone] + LFNoise2.kr(0.1, (numTone / 4));
var freq = ((1 - sweepEnv) * initRandomFreq) + (sweepEnv * destinationFreq);
Pan2.ar
(
BLowPass.ar(Saw.ar(freq), freq * 8, 0.5),
rrand(-0.5, 0.5),
numVoices.reciprocal
)
}!numVoices);
}.play;
)
Здесь я ещё подкорректировал частоту среза фильтра нижних частот на свой вкус. Мне нравится подправлять вещи, если результат не ухудшается… В любом случае, вот что получилось [14].
Мне не очень нравится эта схема развёртки. Нужно растянуть начало и ускорить финиш. Или подождите… разве обязательно реализовать одинаковую схему для всех осцилляторов? Совершенно нет! Каждый осциллятор должен иметь собственную схему с немного разными значениями времени и курватуры — я уверен, что это будет интереснее. Ещё немного раздражают высокочастотные обертоны случайного пилообразного кластера, поэтому добавляем к общему результату фильтр нижних частот, отсечение которых контролируется глобальным «внешним» значением, не имеющим ничего общего со схемами осцилляторов. Вот измененный код:
//кастомные схемы. Фильтр нижних частот в конце
(
{
var numVoices = 30;
var fundamentals = ({rrand(200.0, 400.0)}!numVoices).sort;
var finalPitches = (numVoices.collect({|nv| (nv/(numVoices/6)).round * 12; }) + 14.5).midicps;
var outerEnv = EnvGen.kr(Env([0, 0.1, 1], [8, 4], [2, 4]));
var snd = Mix
({|numTone|
var initRandomFreq = fundamentals[numTone] + LFNoise2.kr(0.5, 3 * (numTone + 1));
var destinationFreq = finalPitches[numTone] + LFNoise2.kr(0.1, (numTone / 4));
var sweepEnv =
EnvGen.kr(
Env([0, rrand(0.1, 0.2), 1], [rrand(5.0, 6), rrand(8.0, 9)],
[rrand(2.0, 3.0), rrand(4.0, 5.0)]));
var freq = ((1 - sweepEnv) * initRandomFreq) + (sweepEnv * destinationFreq);
Pan2.ar
(
BLowPass.ar(Saw.ar(freq), freq * 8, 0.5),
rrand(-0.5, 0.5),
numVoices.reciprocal
)
}!numVoices);
BLowPass.ar(snd, 2000 + (outerEnv * 18000), 0.5);
}.play;
)
Небольшое изменение сделало развёртку немного более интересной. Фильтр нижних частот на 2000 Гц помогает укротить начальный кластер. Вот как это звучит [15].
Осталось ещё одно, что сделает процесс интереснее. Помните, мы сортировали случайные осцилляторы в начале? Ну, теперь мы можем отсортировать их в обратном порядке и убедиться, что осцилляторы на более высоких случайных частотах в конечном итоге попадают в нижние голоса после крещендо, и наоборот. Это добавит в крещендо больше «движения» и вполне согласуется с тем, как структурирован оригинальный фрагмент. Не уверен, что доктор Мурер запрограммировал его именно так, но на записи есть этот процесс, и она звучит круто, будь то случайный продукт генеративного процесса или специальный выбор. (О, я это сказал? Если процесс предусматривает такой вариант, то это и есть выбор… или нет?). Таким образом, изменим порядок сортировки и структуру кода, чтобы зубья пилы с более высокими частотами попадали в более низкие голоса в финале, и наоборот.
Ещё одно: нужен более громкий бас. Сейчас у всех голосов одинаковая амплитуда. Я хочу, чтобы низкие звуки звучали чуть громче и затухали пропорционально увеличению частоты. Поэтому соответствующим образом изменим аргумент mul для Pan2. Повторно настроим частоты среза фильтров нижних частот для отдельных осцилляторов. И я собираюсь добавить схему масштабирования амплитуды, которая будет плавно вступать в действие и исчезать к финалу, и освободиться от scserver. Еще несколько численных настроек там и тут — и вот окончательный код:
//инвертирование init sort, более громкий бас, финальная схема громкости, несколько маленьких подкруток
(
{
var numVoices = 30;
var fundamentals = ({rrand(200.0, 400.0)}!numVoices).sort.reverse;
var finalPitches = (numVoices.collect({|nv| (nv/(numVoices/6)).round * 12; }) + 14.5).midicps;
var outerEnv = EnvGen.kr(Env([0, 0.1, 1], [8, 4], [2, 4]));
var ampEnvelope = EnvGen.kr(Env([0, 1, 1, 0], [3, 21, 3], [2, 0, -4]), doneAction: 2);
var snd = Mix
({|numTone|
var initRandomFreq = fundamentals[numTone] + LFNoise2.kr(0.5, 6 * (numVoices - (numTone + 1)));
var destinationFreq = finalPitches[numTone] + LFNoise2.kr(0.1, (numTone / 3));
var sweepEnv =
EnvGen.kr(
Env([0, rrand(0.1, 0.2), 1], [rrand(5.5, 6), rrand(8.5, 9)],
[rrand(2.0, 3.0), rrand(4.0, 5.0)]));
var freq = ((1 - sweepEnv) * initRandomFreq) + (sweepEnv * destinationFreq);
Pan2.ar
(
BLowPass.ar(Saw.ar(freq), freq * 6, 0.6),
rrand(-0.5, 0.5),
(1 - (1/(numTone + 1))) * 1.5
) / numVoices
}!numVoices);
Limiter.ar(BLowPass.ar(snd, 2000 + (outerEnv * 18000), 0.5, (2 + outerEnv) * ampEnvelope));
}.play;
)
А вот и окончательная запись произведения [16].
Можете сравнить с оригиналом [4].
Да, это моя интерпретация. И конечно, её можно оптимизировать до смерти, меняя схемы, частоты, распределение, что угодно… тем не менее, я думаю, что это достойная попытка сохранить звуковое наследие. Хотелось бы услышать ваши комментарии и/или собственные попытки синтеза этого крещендо.
play{Mix({|k|k=k+1/2;2/k*Mix({|i|i=i+1;Blip.ar(i*XLine.kr(rand(2e2,4e2),87+LFNoise2.kr(2)*k,15),2,1/(i/a=XLine.kr(0.3,1,9))/9)}!9)}!40)!2*a}
И вот звук [17], который генерирует эта версия.
В одном документе [18] — весь код с этой страницы для ваших экспериментов.
Удачного крещендо, друзья!
Автор: m1rko
Источник [19]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/zvyozdny-e-vojny/296229
Ссылки в тексте:
[1] Deep Note: https://en.wikipedia.org/wiki/Deep_Note
[2] звуковой трейдмарк: https://en.wikipedia.org/wiki/Sound_trademark
[3] THX: https://www.thx.com/
[4] звук: https://www.uspto.gov/sites/default/files/74309951.mp3
[5] Music Thing Blog: http://musicthing.blogspot.com/
[6] ссылка на сообщение: http://musicthing.blogspot.com/2005/05/tiny-music-makers-pt-3-thx-sound.html
[7] д-р Джеймс Энди Мурер: http://en.wikipedia.org/wiki/James_A._Moorer
[8] «Возвращение джедая»: http://en.wikipedia.org/wiki/Return_of_the_Jedi
[9] Dr. Dre: http://en.wikipedia.org/wiki/Dr_dre
[10] Metastaseis: https://en.wikipedia.org/wiki/Metastaseis_(Xenakis)
[11] Вот как это звучит: http://www.earslap.com/assets/thxdeepsound/sound1.mp3
[12] Вот как звучит образец: http://www.earslap.com/assets/thxdeepsound/sound2.mp3
[13] здесь: http://www.earslap.com/assets/thxdeepsound/sound3.mp3
[14] вот что получилось: http://www.earslap.com/assets/thxdeepsound/sound4.mp3
[15] Вот как это звучит: http://www.earslap.com/assets/thxdeepsound/sound5.mp3
[16] окончательная запись произведения: http://www.earslap.com/assets/thxdeepsound/sound6.mp3
[17] вот звук: http://www.earslap.com/assets/thxdeepsound/soundtweet.mp3
[18] одном документе: http://www.earslap.com/assets/thxdeepsound/deepnote_tutorial.sc
[19] Источник: https://habr.com/post/426949/?utm_campaign=426949
Нажмите здесь для печати.