iOS / OSX中的AudioKit和音频合成



AudioKit是由音频工程师,程序员和音乐家针对iOS和OSX开发的音频框架。 它提供了用于处理合成声音的工具。 引擎盖下面是Swift,Objective-C,C ++和C以及A​​pple的Audio Unit API的组合。 令人敬畏的(而且相当复杂的)技术封装在非常友好的Swift API中,可以直接从Xcode Playgrounds访问!

在本教程中,我们将继续学习AudioKit,以及声音合成的历史 。 您将学习声音物理学的基础知识,以及像Hammond Organ这样的首批合成器是如何工作的。 同样,将考虑现代技术,例如采样。

做自己喜欢的饮料,坐下来走!

第一步


从AudioKit 3.6开始,设置游乐场以使用框架变得非常简单。 在此处下载并解压缩入门播放列表。

旅程从几个步骤开始,以配置游乐场以使用AudioKit。

在Xcode中打开AudioKitPlaygrounds.xcodeproj文件。 单击屏幕左下角的+按钮,选择“ 文件...” ,然后在“ 操场”部分中选择“ 空白 ”,将新文件与演示操场一起保存在“操场”文件夹中。

新添加的游乐场将启动,并且将如下所示:


将生成的代码替换为以下内容:

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

直到您至少组装了一次项目,游乐场才会启动,为此,您可以使用菜单项Product / Build或组合键⌘-B 。 之后,再次运行游乐场,并听到嗡嗡声10秒钟。 (译者注:有时错误不会消失,您需要切换到另一个游乐场,然后再使它工作)。 您可以使用播放窗口左下方的“播放/停止”按钮停止播放或重新开始播放。
注意:如果仍然看到错误,请尝试重新启动Xcode。 不幸的是,框架和游乐场不是彼此很好的朋友,它们的行为可能无法预测。

振荡器和声音物理


人类已经通过各种物理对象播放音乐了数千年。 我们许多常用的乐器(例如吉他或鼓)已有数百年历史。 Elias Gray在1874年进行了使用电子电路产生声音的第一笔经记录的经验。 Elisha在电信领域工作,他发明了振荡器-最简单的音乐合成器。 和他一起,我们将开始潜水。

右键单击您的游乐场,选择“ 新建游乐场页面”,然后将生成的代码替换为以下代码:

 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 

声音发生了变化,现在听起来是安静的两倍,并且声音要低得多。 信号的频率以赫兹(每秒重复数)测量,并确定音符的音高。 振幅(振幅)设置为0到1,并负责音量。

Elijah Gray的发明记录在有史以来的第一项电子乐器专利中。



许多年后,利奥·特雷敏(Leo Theremin)发明了一种至今使用的奇怪乐器-特雷敏(Theremin)。 在那儿,您可以通过手在乐器上方移动来改变声音的频率。 要了解Theremin的声音,您可以听海滩男孩的振动,Theremin的声音没什么可混淆的。

您可以模拟Thereminvox的声音,为此,将以下代码添加到振荡器设置中:

 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() 

rampDuration属性使振荡器能够平滑地更改其参数的值(例如,频率或幅度)。 AKPeriodicFunctionAKPeriodicFunction的有用工具,用于定期执行代码。 在我们的示例中,它每0.5秒将正弦波的频率从500Hz更改为100Hz。

恭喜你! 您刚刚完成了第一批。 一个简单的振荡器可以产生音符,但是听起来并不好。 有许多因素会影响物理仪器(例如钢琴)的声音。 进一步,我们将考虑其中的一些。

声音包络


乐器弹奏音符时,其声音的音量会随着时间而变化,并且变化的性质因乐器而异。 可以模拟这种效果的模型称为Attack-Decay-Sustain-Release(ADSR)包络(注意:可购买的合成器从未本地化,因此曲线的名称与该合成器面板上的名称相同)。



ADSR信封包含:

  • 音:声音达到最大音量的起音或时间。
  • 衰减 :狂野或音量从最大下降到主要的时间
  • 延音 :与前两个参数不同,主要音量不是时间,在经过攻击和野蛮之后以及释放合成器琴键之前,此音量将产生声音
  • 释放 :释放或音量变为零的时间

在钢琴中,声音是通过敲击弦槌的锤子来提取的,因此在钢琴中很快,或者说是短促的打击和狂野。 小提琴的攻击力很强,狂野而持久,音乐家可以通过使用弓的方式来影响这些参数。

Hammond Novachord是最早的电子乐器之一。 该仪器于1939年制造,由163个真空管和1000多个电容器组成,重230千克。 不幸的是,只制作了几百本,他从未获得商业上的成功。



右键单击“ 旅程 游乐场” ,选择“ 新建游乐场页面”,然后创建一个名为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 

这将创建一个定义ADSR包络的AKAmplitudeEnvelope 。 持续时间参数(attackDuration,destroyDuration,releaseDuration)以秒为单位指定,音量(sustainLevel)的设置范围为0到1。

AKAmplitudeEnvelopeAKAmplitudeEnvelope的后继者,与AKNode相同,在上面的代码中,我们将振荡器包络节点传递给初始化程序,从而连接这些节点。

接下来,添加以下代码:

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

它将启动AudioKit,但是这次我们将ADSR节点的输出馈送到其输入。 为了听到ADSR效果,我们使用AKPeriodicFunction不断打开和关闭节点。



现在您可以听到音符如何循环播放,但是这次有点像钢琴。

该循环在每次迭代中每秒运行两次,以启动或停止ADSR。 当ADSR快速开始攻击时,音量在0.01秒内达到最大值,此后音量在0.1秒内下降到主音量并保持在0.5秒,最后在0.3秒后衰减。

您可以自己演奏这些参数,然后尝试演奏小提琴的声音。

我们遇到的声音基于AKOscillator生成的正弦波。 尽管ASDR有助于消除刺耳的声音,但您仍然不能将这些声音称为音乐声。

此外,我们将考虑如何获得更深的声音。

加成合成


每种乐器都有自己的特殊声音或音色。 音色是区分钢琴声音和小提琴声音的一种方法,即使它们可以演奏相同的音符。 音色的重要参数是声谱。 声谱描述了重现频率的范围,这些频率加在一起时会引起注意。 我们当前的游乐场使用的振荡器仅能以一种频率发出声音,而且听起来很人为。

使用一组振荡器演奏单个音符,您可以获得更生动的声音。 这种方法称为加成合成,这是我们下一个游乐场的主题。

右键单击操场,选择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 } 

在这里,我们采用从1到5的数字间隔,并将每个数字乘以261.53,即音符的频率。 由此产生的多个频率称为谐波。

现在添加代码:

 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() 

在AudioKit中,有专门用于在操场上方便工作的类。 在我们的示例中, AKLiveViewController使用AKLiveViewController ,使用它我们垂直放置元素。 并为每个振荡器创建一个AKSlider 。 滑块由振荡器的频率和幅度值初始化,并在与它们相互作用时引起阻塞。 在每个滑块的块中,我们更改相应振荡器的幅度。 因此,您只需向操场添加互动即可。

为了查看运动场的结果,您需要在屏幕上显示实时视图。 为此,请在窗口右上方选择带有相交圆圈的按钮,并确保为实时显示选择了正确的播放列表。



为了更改乐器的音调,您可以分别更改每个滑块的值。 为了获得逼真的声音,建议您尝试上面屏幕截图中所示的配置。

Teleharmonium是最早使用添加剂合成的合成器之一,重达200吨! 他难以置信的体重和体型,很可能成为他默默无闻的原因。 比较成功的哈蒙德风琴使用了类似的音调轮,但体积要小得多。 它是1935年发明的,在渐进摇滚时代仍然是一种广受欢迎的乐器。



音调轮是一个旋转盘,沿边缘有小孔,而拾音器位于边缘附近。 Hammond管风琴具有整套的音调轮,可以以不同的速度旋转。 产生声音的一种非常不寻常的方法是机电的而不是电子的。

为了产生更逼真的声谱,还有其他几种技术:频率调制(Frequency Modulation或FM)和脉冲宽度调制(Pulse Width Modulation或PWM),这两种技术分别在AKFMOscillatorAKPWMOscillatorAKFMOscillator中可用。 我建议您尝试将两者都替换,而不是我们之前使用的AKOscillator

和弦


在1970年代,运动开始于由模块化合成器(由单独的包络振荡器和滤波器组成)到微处理器的合成器。 代替使用模拟电路,声音开始以数字格式产生。 这使得合成器更便宜,更紧凑,雅马哈等品牌的合成器非常受欢迎。



我们所有的游乐场都只能一次演奏一个音符。 许多乐器一次可以演奏多个音符,它们被称为和弦 。 只能演奏一个音符的乐器称为单音

为了获得和弦声音,可以创建多个振荡器并将其发送到混音器,但是在AudioKit中,有一种更合适的方法。

在操场上创建一个新页面并将其命名为Polyphony。 将生成的代码替换为以下内容:

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

在这里,我们创建了振荡器组AKOscillatorBank 。 如果转到类声明,则会发现它是AKPolyphonicNode的继承人,而后者又是我们已经知道的AKNode的继承人,并且还实现了AKPolyphonic协议。

结果,振荡器组与我们之前讨论的是相同的AudioKit节点。 它的输出可以发送到混音器,信封或任何其他滤镜和效果。 AKPolyphonic协议描述了如何在和弦音符上弹奏音符;让我们更详细地考虑它。

为了测试振荡器,我们需要一种同时演奏多个音符的方法。 这一点都不困难!

将以下代码添加到播放列表中,并确保实时显示处于打开状态:

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

运动场编译时,您将看到以下内容:



酷吧? 音乐键盘就在操场上!

AKKeyboardViewAKKeyboardView的另一个实用程序,可让您轻松探索框架的可能性。 按这些键,您将发现它们没有发出任何声音。

使用以下命令更新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方法都会被noteOn ,它所做的只是告诉振荡器组开始播放该音符,在noteOff方法中,该noteOff将停止播放。

按住鼠标并滑动键,您会听到一个优美的渐强声(一个音乐术语,表示声功率逐渐增加)。 振荡器组已经包含一个内置的ADSR效果,因此,一个音符的衰减与下一个音符的攻击混合在一起,听起来非常不错。

您可能已经注意到,给我们提供按键的音符不是以频率的形式出现的。 它们被声明为MIDINoteNumber 。 如果您转到此类广告,则会看到以下内容:

 public typealias MIDINoteNumber = Int 

MIDI代表乐器数字接口。 这是乐器相互之间相互作用的一种普遍形式。 音符编号对应于标准音乐键盘上的音符。 第二个参数-此力度(力度)对应于按键的击打强度。 值越低,按键的触感越柔和,最终声音就越安静。

最后,您需要在按键上启用复音模式。 将以下代码添加到setUp方法:

 keyboard.polyphonicMode = true 

现在,您可以同时演奏多个音符,如图所示:



顺便说一下,这是在C专业:)

AudioKit的故事早就开始了。 今天,它使用了SoundpipeCsound的代码(1985年启动的MIT项目)。 令人惊讶的是,我们现在在操场上启动并添加到iPhone中的代码是大约30年前编写的。

取样方式


我们之前探讨的声音合成技术正在尝试使用简单的基本块(振荡器,滤波器和混音器)来再现逼真的声音。 1970年代初期,计算机功能的发展导致出现了一种新的方法-采样声音,其目的是创建声音的数字副本。

采样是一种非常简单的技术,类似于数码摄影。 在以固定间隔进行采样的过程中,将记录声波的振幅:



有两个参数会影响声音的录制精度:

  • 位深度 :或位深度,采样器可以播放的各个音量级别的数量
  • 采样率 :或采样率,表示多久进行一次幅度测量。 以赫兹为单位。

让我们在一个新的游乐场中探索这些属性。 创建一个新页面并将其命名为“ 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 

上面的代码加载了样本,创建了音频播放器,并将其无休止地播放。

在此处找到带有本教程的WAV文件的存档。 下载它并解压缩到游乐场的Resources文件夹。

然后在游乐场的末尾添加以下代码:

 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 

. , .

AKBitCrusher — AudioKit, . , , ZX Spectrum BBC Micro. , .

, (. delay). , AKBitCrusher . :

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

0.1 . dryWetMix . 1 , .

:

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

AKPanner , - . .

AudioKit. , AKBitCrusher AudioKit :

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

, , .



接下来是什么?


AudioKit. , -, , , . , , .

.

Source: https://habr.com/ru/post/zh-CN440226/


All Articles