Хабрахабр

[Перевод] AudioKit и синтезирование звука в iOS/OSX

Он предоставляет инструменты для обработки и синтезирования звука. AudioKit — это аудиофреймворк, разработанный аудиоинженерами, программистами и музыкантами специально для iOS и OSX. Потрясающие (и довольно сложные) технологии завёрнуты в очень дружелюбное Swift API, к которому можно обращаться прямо из Xcode Playgrounds! Под капотом — это смесь из Swift, Objective-C, C++ и С, и Audio Unit API от Apple.

Вы узнаете об основах физики звука и том, как работали первые синтезаторы, такие как Hammond Organ. В этом туториале мы отправимся в путешествие по AudioKit, а заодно и истории синтезирования звука. Также будут рассмотрены и современные техники, например, семплинг.

Приготовьте ваш любимый напиток, устраивайтесь поудобнее и вперёд!

Начиная с AudioKit 3.6, настройка плейграундов для работы с фреймворком стала совсем простой. Скачайте и разархивируйте стартовый плейграунд по ссылке.

Путешествие начнётся с нескольких шагов по настройки плейграундов для работы с AudioKit.

Нажмите кнопку + в левом нижнем углу экрана, выберите File… и далее Blank в секции Playground, сохраните новый файл в папке Playgrounds вместе с демонстрационными плейграундами. Откройте AudioKitPlaygrounds.xcodeproj файл в Xcode.

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

Замените сгенерированный код на следующий:

import AudioKitPlaygrounds
import AudioKit let oscillator = AKOscillator() AudioKit.output = oscillator
try AudioKit.start() oscillator.start() sleep(10)

После этого запустите плейграунд ещё раз и услышите гудящий на протяжении 10 секунд звук. Плейграунд не будет запускаться до тех пор, пока вы, хотя бы, один раз не соберёте проект, для этого можно использовать пункт меню Product / Build или комбинацию клавиш ⌘-B. переводчика: иногда ошибка не пропадает, и нужно перейти в другой плейграунд, и вернуться обратно, чтобы всё заработало). (Прим. Вы можете использовать Плей/Стоп кнопку слева внизу окна плейграунда, для того чтобы остановить плейграунд или запустить его заново.

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

Человечество воспроизводит музыку с помощью различных физических объектов на протяжении тысяч лет.  Многим привычным для нас инструментам, таким как гитара или барабаны, насчитываются сотни лет. Первый зафиксированный опыт использования электронных схем для генерации звука был проведён в 1874 году Илайшей Грей. Илайша работал в области телекоммуникаций, и он изобрёл осциллятор — самый простой из музыкальных синтезаторов. C него мы и начнём наше погружение.

Нажмите правой кнопкой мыши на ваш плейграунд, выберите New Playground Page и замените сгенерированный код приведённым ниже:

import AudioKitPlaygrounds
import AudioKit
import PlaygroundSupport // 1. Create an oscillator
let oscillator = AKOscillator() // 2. Start the AudioKit 'engine'
AudioKit.output = oscillator
try AudioKit.start() // 3. Start the oscillator
oscillator.start() PlaygroundPage.current.needsIndefiniteExecution = true

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

Это практически то же самое, что мы делали в предыдущем плейграунде, но в этот раз мы погрузимся в детали.

Рассмотрим все шаги по порядку:

  1. Тут мы создаём осциллятор. Осциллятор является наследником AKNode. Ноды — это элементы, из которых строится аудиоцепочка, генерирующая и модифицирующая звук.
  2. Тут мы связываем выход нашей последней ноды с движком AudioKit. В нашем случае всего одна нода. Дальше фреймворк направит выход ноды на устройство воспроизведения звука.
  3. Ну и стартуем осциллятор, на выход он начинает отправлять звуковую волну.

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

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

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

oscillator.frequency = 300
oscillator.amplitude = 0.5

Частота (frequency) сигнала измеряется в герцах (количество повторений в секунду) и определяет высоту ноты. Звук изменился, теперь он звучит в два раза тише и намного ниже. Амплитуда (amplitude) задаётся от 0 до 1 и отвечает за громкость.

Изобретение Илайши Грея было зафиксировано в первом в истории патенте на электронный музыкальный инструмент.

В терменвоксе вы можете изменять частоту звука движениями рук над инструментом. Много лет спустя Лев Термен изобрёл немного странный музыкальный инструмент, используемый и по сей день, — терменвокс. Чтобы понять, как звучит терменвокс, вы можете послушать композицию Vibrations коллектива Beach Boys, звук терменвокса там ни с чем не перепутаешь.

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

oscillator.rampDuration = 0.2

И замените строчку стартующую AudioKit (try AudioKit.start()) на следующие:

let performance = AKPeriodicFunction(every: 0.5) { oscillator.frequency = oscillator.frequency == 500 ? 100 : 500
}
try AudioKit.start(withPeriodicFunctions: performance)
performance.start()

AKPeriodicFunction — полезная утилита из AudioKit для периодического выполнения кода. Свойство rampDuration позволяет осциллятору плавно менять значения его параметров (например, частоты или амплитуды). 5 секунды. В нашем примере она меняет частоту синусоиды с 500Hz на 100Hz каждые 0.

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

Когда музыкальный инструмент исполняет ноту, громкость её звучания меняется со временем, а характер изменений отличается от инструмента к инструменту. Модель, которая может имитировать этот эффект называется Attack-Decay-Sustain-Release(ADSR) огибающая (Прим. пер.: синтезаторы, доступные к покупке никогда не локализуются, поэтому название кривой такое, каким его можно увидеть на панели настоящего синтезатора).

ADSR-огибающая состоит из:

  • Attack: атака или время, за которое звук набирает максимальную громкость
  • Decay: дикей или время за которое громкость опустится от максимальной до основной
  • Sustain: основная громкость, в отличие от предыдущих двух параметров не является временем, после того, как атака и дикей пройдут и до того, как вы отпустите клавишу синтезатора звук будет генерироваться с этой громкостью
  • Release: релиз или время, за которое громкость станет нулевой

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

Этот инструмент был изготовлен в 1939 году, состоял из 163 вакуумных ламп и более 1000 конденсаторов, а весил он 230 килограмм. Одним из первых электронных музыкальных инструментов был Hammond Novachord. К сожалению, было изготовлено всего несколько сотен его экземпляров, и он так и не получил коммерческого успеха.

Замените сгенерированный код следующим: Кликните правой кнопкой мыши на плейграунд Journey, выберите New Playground Page и создайте новую страничку с именем ADSR.

import AudioKitPlaygrounds
import AudioKit
import PlaygroundSupport let oscillator = AKOscillator()

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

let envelope = AKAmplitudeEnvelope(oscillator)
envelope.attackDuration = 0.01
envelope.decayDuration = 0.1
envelope.sustainLevel = 0.1
envelope.releaseDuration = 0.3

Параметры длительности (attackDuration, decayDuration, releaseDuration) указываются в секундах, а громкость (sustainLevel) задаётся в интервале от 0 до 1. Тут создаётся AKAmplitudeEnvelope, который задаёт ADSR-огибающую.

AKAmplitudeEnvelope — это наследник AKNode точно так же, как и AKOscillator.В коде выше осциллятор мы передаём в инициализатор ноды огибающих, тем самым соединяя ноды.

Дальше добавьте следующий код:

AudioKit.output = envelope
let performance = AKPeriodicFunction(every: 0.5) else { envelope.start() }
}
try AudioKit.start(withPeriodicFunctions: performance)
performance.start()
oscillator.start() PlaygroundPage.current.needsIndefiniteExecution = true

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

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

Когда ADSR стартует с быстрой атакой, громкость достигает максимальной величины за 0. Цикл выполняется два раза в секунду на каждой итерации, стартуя или останавливая ADSR. 1 секунды снижается до основного уровня и находится там в течении 0. 01 секунду, после этого громкость за 0. 3 секунды. 5 секунды, и в конце затухает за 0.

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

Несмотря на то, что ASDR помогает сгладить резкое звучание, назвать эти звуки музыкальными всё ещё нельзя. Звуки, с которыми мы познакомились, основываются на синусоиде, которую генерирует AKOscillator.

Дальше мы рассмотрим как получают более глубокое звучание.

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

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

Замените сгенерированный код следующим: Нажмите правой кнопкой мыши на плейграунд, выберите New Playground Page и создайте новую страницу с названием Additive Synthesis.

import AudioKitPlaygrounds
import AudioKit
import AudioKitUI
import PlaygroundSupport func createAndStartOscillator(frequency: Double) -> AKOscillator { let oscillator = AKOscillator() oscillator.frequency = frequency return oscillator
}

createAndStartOscillator Для аддитивного синтеза вам потребуется несколько осцилляторов, для этого будем использовать.

Дальше добавьте код:

let frequencies = (1...5).map { $0 * 261.63 }

53 — частоту ноты До. Здесь мы берём интервал чисел от 1 до 5 и умножаем каждое на число 261. Получившиеся кратные частоты называются гармоники.

Теперь добавим код:

let oscillators = frequencies.map { createAndStartOscillator(frequency: $0)
}

Тут мы создали осциллятор под каждую из используемых нами частот.

Чтобы объединить осцилляторы добавьте такой код:

let mixer = AKMixer()
oscillators.forEach { $0.connect(to: mixer) }

Он принимает на вход сигналы с одной или нескольких нод и объединяет их в один. AKMixer — это ещё один вид AudioKit-нод.

Добавьте вот такой код:

let envelope = AKAmplitudeEnvelope(mixer)
envelope.attackDuration = 0.01
envelope.decayDuration = 0.1
envelope.sustainLevel = 0.1
envelope.releaseDuration = 0.3 AudioKit.output = envelope
let performance = AKPeriodicFunction(every: 0.5) { if (envelope.isStarted) { envelope.stop() } else { envelope.start() }
}
try AudioKit.start(withPeriodicFunctions: performance)
performance.start()
oscillators.forEach { $0.start() }

С этим кодом всё должно быть понятно: добавляем ADSR к выходу миксера, выводим через AudioKit и периодически включаем/выключаем.

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

Для этого добавьте код:

class LiveView: AKLiveViewController { override func viewDidLoad() { addTitle("Harmonics") oscillators.forEach { oscillator in let harmonicSlider = AKSlider( property: "\(oscillator.frequency) Hz", value: oscillator.amplitude ) { amplitude in oscillator.amplitude = amplitude } addView(harmonicSlider) } }
} PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = LiveView()

Мы в нашем примере используем AKLiveViewController, с помощью него мы вертикально размещаем элементы. В AudioKit есть классы специально для удобной работы в плейграундах. Слайдеры инициализируются значениями частоты и амплитуды осцилляторов, и вызывают блок при взаимодействии с ними. А так же для каждого осциллятора мы создаём AKSlider. Так просто можно добавить интерактивности в ваши плейграунды. В блоке каждого слайдера мы меняем амплитуду соответствующего осциллятора.

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

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

Его невероятный вес и размеры, скорей всего, и стали причиной его безвестности. Одним из первых синтезаторов, использовавших аддитивный синтез был Teleharmonium и весил он 200 тонн! Изобретённый в 1935 году, он до сих пор широко известен как популярный в эпоху прогрессивного рока инструмент. Более успешный Hammond орган использовал схожие тоновые колёса, но был намного меньших размеров.

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

Предлагаю вам попробовать обе, подставив их вместо AKOscillator, которые мы использовали ранее. Для того чтобы генерировать более реалистичный звуковой спектр, есть ещё несколько техник: частотная модуляция (Frequency Modulation или FM) и широтно-импульсная модуляция (Pulse Width Modulation или PWM), обе техники доступны в AudioKit в классах AKFMOscillator and AKPWMOscillator соответственно.

В 1970-х началось движение от модульных синтезаторов, состоящих из отдельных осцилляторов огибающих и фильтров, к микропроцессорным. Вместо использования аналоговых схем звук начали генерировать в цифровом формате. Это позволило сделать синтезаторы более дешёвыми и компактными, а синтезаторы таких брендов как Yamaha — очень популярными.

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

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

Заменить сгенерированный код следующим: Создайте новую страницу в плейграунд и назовите её Polyphony.

import AudioKitPlaygrounds
import AudioKit
import AudioKitUI
import PlaygroundSupport let bank = AKOscillatorBank()
AudioKit.output = bank
try AudioKit.start()

Если перейти в объявление класса, то можно обнаружить, что он является наследником AKPolyphonicNode, который, в свою очередь, является наследником уже известного нам AKNode, а также реализует протокол AKPolyphonic. Тут мы создали банк осцилляторов AKOscillatorBank.

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

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

Добавьте следующий код в плейграунд и убедитесь, что Live View открыто:

class LiveView: AKLiveViewController, AKKeyboardDelegate { override func viewDidLoad() { let keyboard = AKKeyboardView(width: 440, height: 100) addView(keyboard) }
} PlaygroundPage.current.liveView = LiveView()
PlaygroundPage.current.needsIndefiniteExecution = true

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

Музыкальная клавиатура прямо в плейграунде! Круто, да?

Нажмите на клавиши и вы обнаружите, что они не издают никаких звуков. AKKeyboardView — это ещё одна утилита из AudioKit, которая упрощает исследование возможностей фреймворка.

Обновите setUp вашего PlaygroundView на следующий:

let keyboard = AKKeyboardView(width: 440, height: 100)
keyboard.delegate = self
addView(keyboard)

Это сделает наше PlaygroundView делегатом клавиатуры и позволит реагировать на нажатия клавиш.

Обновите объявление класса следующим образом:

class LiveView: AKLiveViewController, AKKeyboardDelegate

Также добавьте пару методов сразу после setUp:

func noteOn(note: MIDINoteNumber) { bank.play(noteNumber: note, velocity: 80)
} func noteOff(note: MIDINoteNumber) { bank.stop(noteNumber: note)
}

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

Банк осцилляторов уже содержит встроенный ADSR-эффект, в результате затухание одной ноты смешивается с атакой следующей и звучит довольно приятно. Зажмите мышкой и проведите по клавишам, вы услышите прекрасное крещендо (музыкальный термин, обозначающий постепенное увеличение силы звука).

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

public typealias MIDINoteNumber = Int

Это широко распространённый формат взаимодействия музыкальных инструментов между собой. MIDI расшифровывается как Musical Instrument Digital Interface (цифровой интерфейс музыкальных инструментов). Второй параметр — это велосити (velocity) соответствует силе удара по клавише. Номера нот соответствуют нотам на стандартной музыкальной клавиатуре. Чем ниже значение, тем более мягкое касание по клавише, тем тише итоговый звук.

Добавьте следующий код в метод setUp: В завершении нужно включить полифонический режим на клавишах.

keyboard.polyphonicMode = true

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

Это, кстати, До-мажор 🙂

Сегодня он использует Soundpipe и код из Csound (проекта MIT, который был запущен в 1985 году). AudioKit начал свою историю довольно давно. Удивительно, что код, который сейчас мы запускаем в плейграундах и добавляем на iPhone, был написан почти 30 лет назад.

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

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

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

  • Bit depth: или разрядность, количество отдельных уровней громкости, которые сэмплер может воспроизвести
  • Sample rate: или частота дискретизации, говорит о том как часто производятся замеры амплитуды. Измеряется в герцах.

Создайте новую страницу и назовите её «Samples». Давайте изучим эти свойства в новом плейграунде. Замените сгенерированный код следующим:

import AudioKitPlaygrounds
import AudioKit
import PlaygroundSupport let file = try AKAudioFile(readFileName: "climax-disco-part2.wav", baseDir: .resources)
let player = try AKAudioPlayer(file: file)
player.looping = true

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

Скачайте его и разархивируйте в папку Resources вашего плейграунда. Архив с WAV-файлом для этого туториала доступен по ссылке.

Затем добавьте следующий код в конец плейграунда:

AudioKit.output = player
try AudioKit.start() player.play() PlaygroundPage.current.needsIndefiniteExecution = true

Это соединит ваш аудиоплеер с AudioKit, остаётся лишь прибавить громкость и наслаждаться!

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

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

let bitcrusher = AKBitCrusher(player)
bitcrusher.bitDepth = 16
bitcrusher.sampleRate = 40000

Так же поправьте вывод в AudioKit:

AudioKit.output = bitcrusher

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

Звуки, которые получаются похожими на те, что издавали старые компьютеры на подобие ZX Spectrum или BBC Micro. AKBitCrusher — это эффект AudioKit, который эмулирует уменьшение разрядности и частоты дискретизации. У них было всего несколько килобайт памяти и намного более медленные процессоры, чем сегодняшние.

delay). В нашем последнем эксперименте мы соберём несколько нод вместе для того, чтобы получить эффект стереозадержки(анг. А затем добавьте следующее: Для начала удалите три строчки, в которых мы создали и настроили AKBitCrusher.

let delay = AKDelay(player)
delay.time = 0.1
delay.dryWetMix = 1

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

Дальше добавьте следующее:

let leftPan = AKPanner(player, pan: -1)
let rightPan = AKPanner(delay, pan: 1)

В нашем примере мы выводим звук с задержкой влево а оригинальный звук вправо. AKPanner даёт возможность переместить звук вправо, влево или куда-то посередине.

Замените строчку, которая выводила AKBitCrusher в AudioKit на такой код: Последним шагом мы смешаем оба сигнала и выведем в AudioKit.

let mix = AKMixer(leftPan, rightPan)
AudioKit.output = mix

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

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

Законченный плейграунд можно найти по ссылке.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть