Recreando el sonido THX Deep Note

Si alguna vez ha visitado una sala de cine, probablemente escuchó Deep Note , la marca registrada de sonido THX . Este es uno de los primeros sonidos que se escuchan al comienzo de los trailers en salas certificadas por THX. Siempre me gustó su crescendo reconocible, comenzando con una misteriosa mezcla de notas y terminando con un final brillante y grandioso ( sonido ). ¡Qué delicia para el oído!

Ayer (probablemente) sin ninguna razón, estaba interesado en el origen de este sonido, e investigué un poco. Me conmovió profundamente su historia, que quiero compartir con ustedes. Luego continuamos, ¡y crearemos este sonido nosotros mismos, prepararemos tijeras y pegamento!

La mejor fuente de información sobre el sonido que pude encontrar es, en mi opinión, su composición electroacústica completa, publicada en el excelente blog Music Thing en 2005. Aquí está el enlace a la publicación .

Algunos hechos sobre el sonido:

  • Fue creado por el Dr. James Andy Moorer en 1982.
  • Un día en la historia, se perdió 4.000 veces al día, ¡casi cada 20 segundos! Cita del Dr. Moorer:
    “Me gustaría decir que el sonido de THX es el trabajo de música de computadora más popular del mundo. Puede ser cierto o no, ¡pero suena genial!
  • Se crea en una computadora ASP (Procesador de señal de audio) que puede sintetizar sonidos en tiempo real.
  • Un programa de 20,000 líneas de código C generó datos para reproducir en ASP. Los datos generados consistieron en 250,000 filas procesadas por ASP.
  • Los osciladores de voz usan un tono de violonchelo digitalizado como señal. El Dr. Murer recuerda que había alrededor de 12 armónicos en la muestra. ASP podría ejecutar 30 de estos osciladores en tiempo real (en comparación, mi computadora portátil ahora puede procesar más de 1000 de estos sin fallas).
  • El sonido en sí está protegido por derechos de autor, pero aquí está el problema: el código del Dr. Murer se basa en generadores de números aleatorios (proceso generativo) y cada vez que el sonido es ligeramente diferente. Por lo tanto, no creo que sea seguro decir que el proceso en sí está o puede estar "protegido por derechos de autor". El sonido en sí, sí, la muestra específica está protegida.
  • El sonido debutó en el trailer de THX "El retorno del Jedi" antes de su estreno en 1983.
  • Las características generativas del proceso en algún momento se volvieron problemáticas. Tras el lanzamiento de The Return of the Jedi, se perdió el registro original de Deep Note. El Dr. Murer recreó el trabajo para la compañía, pero se quejaban constantemente de que no suena como el original. Al final, la grabación original fue encontrada y almacenada en un lugar seguro.
  • Dr. Dre pidió permiso para usar la muestra en su música, pero fue rechazado. Lo usó de todos modos y consiguió una demanda.
  • En la obra de Metastaseis de Janis Xenakis (1954) hay un crescendo de apertura muy similar (como en otras obras de varios compositores). Pero comienza con un solo tono y termina con un grupo de tonos semi sintonizado en lugar de completamente consonante, como en Deep Note. La grabación de sonido de la solicitud de patente se puede escuchar aquí .

Asegúrese de escuchar el sonido, porque cuando recreamos Deep Note, nos referiremos a esta grabación en particular.

Aquí hay algunos datos técnicos / teóricos antes de embarcarse en la síntesis de sonido:

  • Mi observación: en la entrada original del sitio web de la Oficina de Patentes, el tono principal está entre D y Eb, y en versiones más recientes el valor fundamental está entre E y F. Usaremos la constante original D / Eb. Las nuevas opciones suelen ser más cortas, si no se equivocan. Obviamente, prefiero la opción que se presentó en la oficina de patentes.
  • Según el Dr. Murer (y también confirmado por mis oídos), el fragmento comienza con osciladores sintonizados a frecuencias aleatorias entre 200 Hz y 400 Hz. Pero los osciladores no son solo zumbidos: sus frecuencias se modulan aleatoriamente y utilizan filtros de suavizado para suavizar las transiciones aleatorias de los tonos. Esto continúa hasta el inicio del crescendo.
  • Dentro del crescendo y al final del segmento de sonido, los aleatorizadores aún modulan las frecuencias de los osciladores, por lo que ninguno de ellos es estable en un momento dado. Pero el rango de barrido aleatorio es tan estrecho que simplemente agrega un sonido natural / coral.
  • El Dr. Murer recuerda que había alrededor de 12 armónicos distintos en el espectro del sonido de violonchelo digitalizado.
  • Hasta donde yo sé, los valores del generador (que se utilizaron para obtener los derechos de autor) por escrito nunca se han publicado. El Dr. Moorer dice que puede grabarlos si obtenemos el permiso de THX. Pero creo que no es necesario recrear el sonido.
  • El sonido en el final (técnicamente no es un acorde), en mi oído, solo la adición de las octavas del tono fundamental. Entonces, al reconstruir, comenzamos con osciladores sintonizados aleatoriamente (entre 200 y 400 Hz), hacemos un barrido más o menos complicado y terminamos aplicando octavas al tono entre el D / Eb bajo.

Entonces comencemos. Mi herramienta de trabajo aquí es SuperCollider. Comencemos con una muestra simple. Quiero usar una onda de diente de sierra como fuente, tiene un espectro rico y armónico de componentes pares e impares. Más tarde planeo filtrar los vértices. Aquí hay un fragmento de la parte inicial del código:

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

Elegí 30 osciladores para generar sonido, de acuerdo con las capacidades de la computadora ASP, como dijo el Dr. Murer. Creé una matriz de 30 frecuencias aleatorias entre 200 y 400 Hz, las distribuí aleatoriamente sobre el campo estéreo usando Pan2.ar con el argumento rrand (-0.5, 0.5), asigné frecuencias a osciladores de diente de sierra (30 copias). Así es como suena .

Si estudia la información del Dr. Moorer y / o escucha atentamente el fragmento original, puede escuchar que las frecuencias del oscilador se desplazan aleatoriamente hacia arriba y hacia abajo. Me gustaría agregar este efecto para un sonido más orgánico. La escala de frecuencia es logarítmica, por lo que a frecuencias más bajas debería haber rangos de oscilación más estrechos que en las más altas. Esto se puede hacer clasificando nuestras frecuencias generadas aleatoriamente con LFNoise2 (que genera valores aleatorios interpolados cuadráticamente) de argumentos mul en orden dentro de nuestra macro Mix. Y también agregué un filtro de paso bajo para osciladores con una frecuencia de corte de cinco veces la frecuencia del oscilador y moderado 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; ) 

Así es como suena la muestra con las últimas ediciones.

Esto ya parece un buen punto de partida, así que comencemos a implementar un barrido, muy grosero al principio. Para implementar un barrido, primero debe determinar las frecuencias finales para cada oscilador. No es muy simple, pero tampoco muy difícil. El tono principal debe estar entre D y Eb bajos, por lo que la frecuencia promedio para este tono será 14.5 (0 es C, contando cromáticamente, sin la primera octava). Entonces, para 30 osciladores, traducimos frecuencias aleatorias entre 200 y 400 Hz en un valor de 14.5 y las octavas correspondientes. Por oído, elegí las primeras 6 octavas. Entonces, la matriz final de frecuencias es la siguiente:

 (numVoices.collect({|nv| (nv/(numVoices/6)).round * 12; }) + 14.5).midicps; 

Usaremos el barrido de 0 a 1. Las frecuencias aleatorias se multiplican por el valor (1 − ) , y las frecuencias objetivo se multiplican por el barrido mismo. Por lo tanto, cuando el barrido es 0 (el comienzo), la frecuencia será aleatoria. Cuando el barrido es 0.5, resulta (( + ) / 2) , y cuando es 1, entonces la frecuencia será el valor final. Aquí está el código modificado:

 //   (),    ( { 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; ) 

El sonido esta aquí .

Como dije, esta es una exploración muy aproximada. Aumenta linealmente de 0 a 1, lo que no es consistente con la composición original. También puede haber notado que las últimas octavas suenan horribles porque están sintonizadas para octavas perfectas y se fusionan entre sí como tonos base y sobretonos. Arreglaremos esto agregando un swing aleatorio en la etapa final, tal como se hizo al principio, y sonará mucho más orgánico.

Primero debe corregir la fórmula general de barrido de frecuencia. El anterior fue solo para juicio. Si miramos el original, notamos que en los primeros 5-6 segundos hay muy pocos cambios en el sonido. Después de esto, se produce un barrido rápido y exponencial, que lleva a los osciladores a intervalos de octava finitos. Aquí está la opción que elegí:

 sweepEnv = EnvGen.kr(Env([0, 0.1, 1], [5, 8], [2, 5])); 

Aquí, una transición de 0 a 0.1 toma 5 segundos, y una transición de 0.1 a 1 toma 8 segundos. Las curvaturas para estos segmentos se establecen en 2 y 5. Más tarde escuchamos lo que sucedió, pero primero tenemos que arreglar los intervalos finales nuevamente. Como antes, agregamos oscilaciones aleatorias con LFNoise2, cuyo rango es proporcional a la frecuencia final del oscilador. Esto hará que el final sea más orgánico. Aquí está el código modificado:

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

Aquí también ajusté la frecuencia de corte del filtro de paso bajo a mi gusto. Me gusta arreglar las cosas si el resultado no empeora ... En cualquier caso, esto es lo que sucedió .

Realmente no me gusta este patrón de escaneo. Necesita estirar el inicio y acelerar el final. O espere ... ¿es realmente necesario implementar el mismo circuito para todos los osciladores? ¡Absolutamente no! Cada oscilador debe tener su propio circuito con valores ligeramente diferentes de tiempo y curvatura; estoy seguro de que será más interesante. Los armónicos de alta frecuencia de un grupo aleatorio de dientes de sierra siguen siendo un poco molestos, por lo que agregamos al resultado general un filtro de paso bajo, cuyo corte está controlado por un valor global "externo" que no tiene nada que ver con los circuitos del oscilador. Aquí está el código modificado:

 // .      ( { 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; ) 

Un pequeño cambio hizo que el escaneo fuera un poco más interesante. Un filtro de paso bajo de 2000 Hz ayuda a domar el grupo inicial. Así es como suena .

Queda una cosa más que hará que el proceso sea más interesante. ¿Recuerdas que ordenamos osciladores aleatorios al principio? Bueno, ahora podemos ordenarlos en orden inverso y asegurarnos de que los osciladores en frecuencias aleatorias más altas terminen en las voces más bajas después del crescendo, y viceversa. Esto agregará más "movimiento" al crescendo y es consistente con la forma en que se estructura el fragmento original. No estoy seguro de que el Dr. Murer lo haya programado de esa manera, pero existe este proceso en el registro y suena genial, ya sea un producto aleatorio de un proceso generativo o una elección especial. (Oh, ¿dije eso? Si el proceso proporciona esa opción, entonces esta es la opción ... ¿o no?). Por lo tanto, cambiaremos el orden de clasificación y la estructura del código para que los dientes de sierra con frecuencias más altas caigan en voces más bajas en el final, y viceversa.

Una cosa más: necesitas un bajo más fuerte. Ahora todas las voces tienen la misma amplitud. Quiero que los sonidos bajos suenen un poco más fuertes y se desvanezcan en proporción al aumento de frecuencia. Por lo tanto, modificamos el argumento mul para Pan2 en consecuencia. Reajuste las frecuencias de corte de los filtros de paso bajo para osciladores individuales. Y voy a agregar un esquema de escala de amplitud que surtirá efecto suavemente y desaparecerá al final, y se liberará de scserver. Algunas configuraciones numéricas más aquí y allá, y aquí está el código final:

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

Y aquí está la grabación final del trabajo .

Puedes comparar con el original .

Sí, esta es mi interpretación. Y, por supuesto, puede optimizarse hasta la muerte cambiando los patrones, las frecuencias, la distribución, lo que sea ... sin embargo, creo que este es un intento digno de preservar el legado del sonido. Me gustaría escuchar sus comentarios y / o sus propios intentos de sintetizar este crescendo.



Sí, y aquí hay otra cosa que hice por diversión. Recuerde, le dije que se necesitaron 20,000 líneas de código C para generar el original. Estoy bastante seguro de que el Dr. Moorer tuvo que escribir todo a mano, por lo que este número no es sorprendente. Pero ya sabes, debido a la popularidad de Twitter, estamos tratando de exprimir todo en 140 caracteres de código. Por placer, intenté reproducir los elementos básicos de la composición en 140 caracteres del código. Creo que la muestra todavía suena genial, aquí está el código (aquí con el tono principal 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} 

Y aquí está el sonido que genera esta versión.

En un documento , todo el código de esta página para sus experimentos.

Buen crescendo, amigos!

Source: https://habr.com/ru/post/es426949/


All Articles