再现THX Deep Note声音

如果您曾经去过电影院,可能会听过THX 声音商标 Deep Note 。 这是在THX认证大厅的拖车开始时首先听到的声音之一。 我一直喜欢他可辨认的渐强,从可怕的音符混合开始,到明亮而宏大的结局( 声音 )结束。 多么高兴的耳朵!

昨天(大概)无缘无故地对声音的起源感兴趣,我做了一些研究。 我很想与他分享他的故事。 然后我们继续-我们将自己创建声音,准备剪刀和胶水!

我认为,关于声音的最佳信息来源是他完整的电声成分,该成分于2005年发表在出色的Music Thing Blog中。 这是帖子链接

有关声音的一些事实:

  • 由James Andy Moorer博士于1982年创建。
  • 历史上的一天,它每天几乎每20秒丢失4,000次! 穆尔博士的话:
    “我想说的是THX的声音是世界上最受欢迎的计算机音乐作品。 也许是真的,但听起来很酷!”
  • 它是在可以实时合成声音的ASP(音频信号处理器)计算机上创建的。
  • 20,000行C代码的程序生成了要在ASP上回放的数据。 生成的数据由ASP处理的250,000行组成。
  • 语音振荡器使用数字化的大提琴音调作为信号。 穆勒博士回忆说,样品中大约有12个谐波。 ASP可以实时运行其中的30个振荡器(作为比较,我的笔记本电脑现在可以处理1000个以上的这些振荡器而不会出现故障)。
  • 声音本身受版权保护,但这是问题所在:Murer博士的代码依赖于随机数生成器(生成过程),并且每次声音都略有不同。 因此,我认为不能肯定地说该过程本身是“可以受到版权保护”的。 声音本身,是的,特定样本受到保护。
  • 声音在1983年首映之前在THX拖车《绝地归来》中首次亮相。
  • 该过程的生成特性在某些时候变得有问题。 在《绝地归来》发行后,原始的Deep Note记录丢失了。 Murer博士为公司重新创作了作品,但他们不断抱怨它听起来不像原始作品。 最后,找到原始记录并将其存储在安全的地方。
  • 博士 德雷要求允许在他的音乐中使用该样本,但他被拒绝了。 无论如何,他还是使用了它并提起了诉讼。
  • Janis Xenakis(1954)的Metastaseis作品中有一个非常相似的开放式渐进式(与其他作曲家的作品一样)。 但是它以单一音调开始,以半调音调簇结束,而不是像Deep Note那样完全是辅音。 在这里可以听到专利申请的录音。

请务必听声音,因为当我们重新创建Deep Note时,我们将参考此特定录音。

在着手进行声音合成之前,这里有一些技术/理论事实:

  • 我的观察:在专利局网站的原始条目上,主音位于D和Eb之间,而在新版本中,基音介于E和F之间。我们将使用原始常数D / Eb。 新选项通常较短,即使没有记错的话。 显然,我更喜欢向专利局提交的选择。
  • 根据Murer博士的说法(也得到我的耳朵的证实),该片段始于将振荡器调谐到200 Hz至400 Hz之间的随机频率。 但是振荡器不仅会嗡嗡作响-它们的频率是随机调制的,而且它们使用平滑滤波器来平滑音调的随机过渡。 这一直持续到渐强开始。
  • 在渐强内部和声音片段的末尾,随机发生器仍在调制振荡器的频率,因此在任何给定时间它们都不稳定。 但是随机扫描范围太窄,以至于它只是增加了自然/合唱声。
  • 穆勒博士回忆说,数字化大提琴声音的频谱中大约有12种不同的谐波。
  • 据我所知,生成器的值(用于获取版权)以书面形式尚未公布。 Moorer博士说,如果得到THX的许可,他可以记录下来。 但是我认为没有必要重新创建声音。
  • 结局中的声音(技术上不是和弦)-在我的耳朵中,只是基本音调的八度音阶的加法。 因此,在重建时,我们从随机调谐的振荡器(200至400 Hz之间)开始,或多或少地进行复杂的扫描,并通过对低D / Eb之间的音高应用八度来完成。

因此,让我们开始吧。 我的工作工具是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; ) 

正如Murer博士所说,根据ASP计算机的功能,我选择了30个振荡器来产生声音。 我创建了一个由200至400 Hz之间的30个随机频率组成的数组,使用Pan2.ar将rrand参数(-0.5,0.5)随机分配到立体声场中,将频率分配给锯齿振荡器(30个副本)。 听起来就是这样

如果您研究Moorer博士提供的信息和/或仔细聆听原始片段,您会听到振荡器频率随机上下波动。 我想添加此效果以获得更自然的声音。 频率范围是对数的,因此在较低的频率下,振荡范围应比较高的范围窄。 这可以通过在Mix宏中按mul参数的LFNoise2(生成二次插值随机值)对我们随机生成的频率进行排序来完成。 另外,我还为振荡器添加了一个低通滤波器,其截止频率为振荡器频率的五倍,且中等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; ) 

这是样例在最新编辑时的声音

这似乎已经是一个不错的起点,所以让我们开始实施扫频,起初非常不礼貌。 要进行扫描,首先需要确定每个振荡器的最终频率。 这不是很简单,但也不是很困难。 主音应在低D和Eb之间,因此该音的平均频率将为14.5(0为C,按色计数,没有第一个八度)。 因此,对于30个振荡器,我们将200到400 Hz之间的随机频率转换为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; ) 

声音在这里

如我所说,这是一个非常粗糙的扫描。 它从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; ) 

在这里,我还根据自己的喜好调整了低通滤波器的截止频率。 如果结果不会变得更糟,我想修复问题。无论如何, 这就是发生的事情

我不太喜欢这种扫描模式。 需要延长开始时间并加快完成速度。 还是等等……是否真的有必要为所有振荡器实现相同的电路? 绝对不会! 每个振荡器应具有其自己的电路,其时间和曲率值略有不同-我相信这会更有趣。 随机锯齿形簇的高频泛音仍然有些令人讨厌,因此我们在总体结果中添加了一个低通滤波器,其截止值由一个与振荡器电路无关的全局“外部”值控制。 这是修改后的代码:

 // .      ( { 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 Hz低通滤波器有助于驯服初始簇。 听起来就是这样

还剩下使事情变得更有趣的一件事。 还记得我们在一开始对随机振荡器进行排序吗? 好了,现在我们可以按相反的顺序对它们进行排序,并确保在渐强之后,较高随机频率的振荡器以较低的声音结尾,反之亦然。 这将为渐进增加更多的“运动”,并且与原始片段的结构保持一致。 我不确定Murer博士是用这种方式编程的,但是记录中有这个过程,而且听起来很酷,无论它是生成过程的随机产物还是特殊选择。 (哦,我是不是这么说的?如果过程提供了这样的选择,那么这就是选择……还是没有?)。 因此,我们将更改代码的排序顺序和结构,以使具有较高频率的锯齿最终落入较低的声音中,反之亦然。

还有一件事:您需要更大声的低音。 现在所有声音都具有相同的振幅。 我希望低声音听起来会大一些,并随频率的增加而逐渐消失。 因此,我们相应地修改Pan2的mul参数。 重新调整各个振荡器的低通滤波器的截止频率。 我将添加一个振幅缩放方案,该方案将顺利生效并在最后消失,并且摆脱束缚。 到处都是一些数字设置-这是最终代码:

 // 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; ) 

这是作品的最终录音

您可以将其与原始版本进行比较。

是的,这是我的解释。 当然,可以通过更改样式,频率,分布等来优化它,以至于无法实现……尽管如此,我认为这是值得保留声音遗产的尝试。 我想听听您的评论和/或您自己尝试合成此渐强的方法。



是的,这是我为了娱乐而做的另一件事。 记住,我告诉过您,要花费20,000行C代码来生成原始代码,我很确定Moorer博士必须用手编写所有内容,因此这个数字并不令人惊讶。 但您知道,由于Twitter的普及,我们正试图将所有内容压缩为140个字符的代码。 出于乐趣,我尝试用代码的140个字符重现组成的基本元素。 我认为样本听起来仍然很酷,这是代码(这里的主音为F / E):

 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} 

这是此版本产生的声音

一个文档中 -此页面上用于实验的所有代码。

渐强,朋友们!

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


All Articles