paint-brush
Tocar una fibra sensible: el arte de animar la música con significadopor@charnog
331 lecturas
331 lecturas

Tocar una fibra sensible: el arte de animar la música con significado

por Denis Gonchar10m2023/10/18
Read on Terminal Reader

Demasiado Largo; Para Leer

Combinando la pasión por la música con la experiencia en tecnología, este artículo guía a los lectores a través de la creación de un sitio web de álbum visual distintivo. Se destacan los desafíos, los atajos de codificación y el papel del protocolo Open Graph, al tiempo que se enfatiza el equilibrio entre el perfeccionismo y la ejecución rápida.
featured image - Tocar una fibra sensible: el arte de animar la música con significado
Denis Gonchar HackerNoon profile picture
0-item

Portada del disco de DALL·E 3


En este artículo, compartiré cómo hice un proyecto durante un fin de semana para lanzar mi álbum ( https://evhaevla.netlify.app/ ). No soy un músico ni un compositor capacitado, pero a veces me vienen a la mente melodías. Los anoto y luego dejo que la computadora los reproduzca.


En 2021, lancé mi álbum titulado "Todos están felices, todos están riendo". Es un álbum sencillo de un "compositor" desconocido: ese soy yo.


No sólo me gusta la música; También soy desarrollador y recientemente me centré principalmente en el trabajo frontend. Pensé, ¿por qué no combinar estos dos amores? Entonces, me propuse diseñar un sitio web para presentar visualmente mi álbum.


Este artículo no profundizará en todos los detalles técnicos; sería demasiado extenso y podría no ser del agrado de todos. En lugar de ello, destacaré los conceptos centrales y los obstáculos que encontré. Para aquellos interesados, todo el código se puede encontrar en GitHub .

Visual

Mi álbum está compuesto para piano, por lo que la decisión es sencilla. Imagínese rectángulos descendiendo sobre las teclas del piano. Cualquiera que tenga inclinaciones musicales probablemente haya encontrado numerosos vídeos en YouTube que muestran notas de esta manera. Un rectángulo toca una tecla, iluminándola, indicando el momento preciso para tocar la nota.


No estoy seguro del origen de este estilo visual, pero una búsqueda rápida en Google arroja predominantemente capturas de pantalla de Synthesia.

Interfaz de usuario de síntesis

En YouTube hay creadores que logran producir efectos visualmente impresionantes. Ver vídeos de este tipo es un placer, tanto desde el punto de vista estético como musical. Mira esto o esto .


¿Qué necesitaremos implementar?

  1. Llaves
  2. Rectángulos
  3. Audio
  4. Animación


Abordemos cada punto y pongámoslos todos en acción.

Llaves

Al principio supuse que implementar las claves plantearía el mayor desafío. Sin embargo, una búsqueda rápida en línea reveló una gran cantidad de ejemplos y guías sobre cómo hacer precisamente eso. Buscando un diseño con un toque de elegancia, opté por un ejemplo creado por Philip Zastrow .


Todo lo que me quedaba por hacer era replicar las teclas varias veces y establecer una cuadrícula para que las notas se deslizaran. Utilicé Vue.js como marco de interfaz y a continuación se muestra el código del componente.

 <template> <ul style="transform: translate3d(0, 0, 0)"> <li :id="`key_${OFFSET - 1}`" style="display: none"></li> <template v-for="key in keys" :key="key.number"> <li :class="`${key.color} ${key.name}`" :id="`key_${key.number}`"></li> </template> </ul> </template> <script setup lang="ts"> import { ref } from 'vue' import type { Key } from '@/components/types' const OFFSET = 24 const template = [ { color: 'white', name: 'c' // Do }, { color: 'black', name: 'cs' // Do-diez }, { color: 'white', name: 'd' // Re }, /* ... */ ] const keys = ref<Key[]>([]) for (let i = 0; i < 72; i++) { keys.value.push({ ...template[i % 12], number: i + OFFSET }) } </script>

Me gustaría mencionar que agregué un atributo id a cada clave, que será esencial al iniciar sus animaciones.

Rectángulos

Si bien este puede parecer el segmento más simple, algunos desafíos se esconden a simple vista.


  1. ¿Cómo se puede lograr el efecto de notas descendentes?


  2. ¿Es necesario mantener una estructura que pueda consultarse para recuperar las notas actuales?


  3. ¿Cuál es el mejor enfoque para representar los resultados de tales consultas?


Cada pregunta presenta un obstáculo que hay que sortear para lograr el efecto deseado sin problemas.


No me detendré en cada pregunta, sino que iré directo al grano. Dados los innumerables desafíos asociados con el enfoque dinámico, es aconsejable prestar atención a la navaja de Occam y optar por una solución estática.


Así es como lo abordé: rendericé las 6215 notas simultáneamente en un único lienzo expansivo. Este lienzo está alojado dentro de un contenedor con el estilo overflow: hidden . Lograr el efecto de notas que caen es simplemente una cuestión de animar la scrollTop de este contenedor.


Sin embargo, queda una pregunta pendiente: ¿cómo obtengo las coordenadas de cada nota?


Afortunadamente, tengo un archivo MIDI donde se archivan todas estas notas, una comodidad que me brinda ser el compositor del álbum. Todo se reduce a renderizar las notas utilizando los datos extraídos del archivo MIDI.


Dado que el archivo MIDI está en formato binario y no tenía intención de analizarlo yo mismo, solicité la ayuda de la biblioteca de archivos midi .


La biblioteca midi-file es eficaz para extraer datos sin procesar de archivos MIDI, pero para mis necesidades, eso no es suficiente. Mi objetivo es transformar estos datos en un formato más accesible y fácil de usar para facilitar la representación perfecta dentro de la aplicación.


En un archivo MIDI, no estoy tratando con notas en el sentido habitual, sino con eventos. Hay una gran variedad de estos eventos, pero me centro principalmente en dos tipos: 'noteOn', que se activa cuando se presiona una tecla, y 'noteOff', cuando se suelta la tecla.


Tanto los eventos 'noteOn' como 'noteOff' especifican el número de nota particular que se presionó o soltó, respectivamente. El tiempo, en el sentido convencional, está ausente en MIDI. En cambio, tenemos "garrapatas". El número de ticks por tiempo se detalla en el encabezado del archivo MIDI.

Azul: tiempos (la duración predeterminada es 96 ticks), Rojo: eventos de notas


De hecho, hay más que considerar. También está presente una pista de tempo que contiene eventos 'setTempo' que son parte integral del proceso, considerando que el tempo puede cambiar durante la reproducción. Mi enfoque inicial implicó ajustar la velocidad de animación de la propiedad scrollTop del contenedor para alinearla con el tempo.


Sin embargo, pronto me di cuenta de que esto no produciría el resultado esperado debido a la acumulación excesiva de errores. Una extensión de tiempo "lineal" resultó más efectiva para animar el scrollTop .


Incluso con el aspecto de la animación ordenado, el tempo aún requería incorporación. Resolví esto ajustando las longitudes de los rectángulos de las notas. Si bien no es la solución óptima (lo ideal sería manipular la velocidad), este método aseguró un funcionamiento más fluido.


Esta solución no es perfecta, principalmente porque asocio un evento de tempo con un evento de nota en función de si tienen el mismo o menos tiempo. Esto significa que si se produce otro evento de tempo mientras una nota todavía está sonando, simplemente se ignorará.


Potencialmente, esto podría introducir un error, especialmente si una nota es muy larga y se produce un cambio de tempo dramático durante su tiempo de reproducción. Es una compensación. He aceptado este pequeño defecto porque estoy concentrado en un desarrollo rápido.


Hay casos en los que la velocidad prima y es más pragmático no enredarse en cada detalle.

Sólo el primer evento de tempo está asociado con la nota.


Entonces, estamos equipados con la siguiente información:


  1. El número de clave específico
  2. El momento preciso en que se presiona la tecla
  3. La hora exacta en que se suelta la llave.
  4. La intensidad o "velocidad" a la que se presiona la tecla


Con estos detalles a mano, puedo señalar las coordenadas exactas de cada nota en el lienzo. El número de tecla determina el eje X, mientras que el comienzo de la pulsación de tecla es el eje Y. La longitud de la prensa dicta la altura del rectángulo.


Al utilizar un elemento div estándar y establecer su posición en "absoluta", logré con éxito el efecto deseado.

Los rectángulos que observa aquí son simplemente elementos `<div>` sencillos con estilos aplicados. Están colocados absolutamente sobre un lienzo alargado, que luego se desplaza


Audio

No tenía intención de crear un sintetizador para piano, ya que me habría llevado mucho tiempo. En su lugar, utilicé un archivo OGG existente que ya había sido "renderizado" y seleccioné The Grandeur de Native Instruments para la biblioteca de sonidos.


Personalmente, creo que es el mejor instrumento de piano VST disponible.


Incrusté el archivo OGG resultante en un elemento de audio estándar. Mi tarea principal entonces era sincronizar el audio con la animación scrollTop de mi lienzo de notas.

Animación

Antes de poder abordar la sincronización, primero había que establecer la animación. La animación del lienzo es bastante sencilla: animo el scrollTop desde un valor infinito hasta cero, usando interpolación lineal. La duración de esta animación coincide con la duración del álbum.


Cuando una nota desciende sobre una tecla, esa tecla se ilumina. Esto significa que para el descenso de cada nota, necesito "activar" la tecla correspondiente, y una vez que la nota complete su recorrido, desactivarla.


Con un total de 6215 notas, esto equivale a la friolera de 12,430 animaciones de activación y desactivación de notas.


Además, mi objetivo era brindar a los usuarios la capacidad de rebobinar el audio, permitiéndoles navegar a cualquier lugar dentro del álbum. Para implementar una característica de este tipo, es esencial una solución sólida.


Y cuando me enfrento a la necesidad de una solución confiable que "simplemente funcione", mi opción es siempre la plataforma de animación GreenSock .


Mira la cantidad de código que se necesita para crear todas las animaciones para cada una de las claves. Usar id para animar componentes no es la mejor práctica para aplicaciones de una sola página. Sin embargo, este método supone un verdadero ahorro de tiempo. ¿Recuerda la id que mencioné para cada clave? Aquí es donde entran en juego.


 const keysTl = gsap.timeline() notes.value.forEach((note) => { const keySelector = `#note_${note.noteNumber}` keysTl .set(keySelector, KEY_ACTIVE_STATE, note.positionSeconds) .set(keySelector, KEY_INACTIVE_STATE, note.positionSeconds + note.durationSeconds - 0.02) })


Básicamente, el código de sincronización establece una conexión a través de eventos entre el audio y la línea de tiempo global de GSAP.


 audioRef.value?.addEventListener('timeupdate', () => { const time = audioRef.value?.currentTime ?? 0 globalTl.time(time) }) audioRef.value?.addEventListener('play', () => { globalTl.play() }) audioRef.value?.addEventListener('playing', () => { globalTl.play() }) audioRef.value?.addEventListener('waiting', () => { globalTl.pause() }) audioRef.value?.addEventListener('pause', () => { globalTl.pause() })


Subtítulos

Justo cuando tenía ganas de terminar, me vino a la mente una idea intrigante. ¿Qué pasaría si le añadiera un toque único al álbum? Originalmente no estaba en mi lista de tareas pendientes, pero sentí que el proyecto no brillaría realmente sin esta característica. Entonces, decidí incorporarlo también.


Cada vez que me sumerjo en una canción, me encuentro reflexionando sobre sus significados más profundos. ¿Qué mensaje intentaba transmitir el compositor? Consideremos, por ejemplo, un segmento del "Nightbook" de Ludovico Einaudi. El piano resuena en el oído izquierdo, mientras que las cuerdas resuenan en el derecho.


Crea un ambiente de diálogo que se desarrolla entre los dos. Se siente como si las teclas del piano estuvieran sondeando: "¿Estás de acuerdo?" Las cuerdas responden afirmativamente. "¿Es esa la pregunta?" Las cuerdas hacen eco de su afirmación. La secuencia culmina con ambos instrumentos convergiendo, simbolizando la realización de la unidad y la armonía. ¿No es una experiencia fascinante?


Es imperativo mencionar que esta es puramente mi interpretación personal. Una vez tuve la oportunidad de asistir a un concierto de Ludovico en Milán. Después de la actuación, me acerqué a él y le pregunté si realmente había tenido la intención de incorporar la noción de diálogo en ese segmento en particular.


Su respuesta fue esclarecedora: "Nunca lo pensé de esa manera, pero ciertamente posees una imaginación vívida".


A partir de esa experiencia, reflexioné: ¿y si integro subtítulos en la partitura? A medida que se reproducen segmentos específicos, los comentarios podrían materializarse en la pantalla, proporcionando ideas o interpretaciones sobre la intención del compositor.


Esta característica podría ofrecer a los oyentes una comprensión más profunda o una nueva perspectiva sobre "¿qué quiso decir realmente el autor?"


Fue una suerte elegir GSAP como mi herramienta de animación. Me permitió integrar sin esfuerzo otra línea de tiempo, específicamente encargada de animar el comentario. Esta adición simplificó el proceso e hizo que la implementación de mi idea fuera mucho más sencilla.


Me sentí inclinado a introducir los comentarios mediante marcado HTML. Para lograr esto, creé un componente que presenta la animación durante el evento onMounted .

 <template> <div :class="$style.comment" ref="commentRef"> <slot></slot> </div> </template> <script setup lang="ts"> /* ... */ onMounted(() => { if (!commentRef.value) return props.timeline .fromTo( commentRef.value, { autoAlpha: 0 }, { autoAlpha: 1, duration: 0.5 }, props.time ? parseTime(props.time) : props.delay ? `+=${props.delay}` : '+=1' ) .to( commentRef.value, { autoAlpha: 0, duration: 0.5 }, props.time ? parseTime(props.time) + props.duration : `+=${props.duration}` ) }) </script>


El uso de este componente sería el siguiente.

 <template> <div> <Comment time="0:01" :duration="5" :timeline="commentsTl"> <h1>A title for a track</h1> </Comment> <Comment :delay="1" :duration="13" :timeline="commentsTl"> I would like to say... </Comment>

Apoteosis

Con todos los elementos en su lugar, el siguiente paso fue alojar el sitio. Opté por Netlify. Ahora los invito a experimentar el álbum y ver la presentación final.


https://evhaevla.netlify.app


Realmente espero que haya otros desarrolladores amantes del piano y deseosos de mostrar sus álbumes de una manera tan única. Si eres uno de ellos, no dudes en bifurcar el proyecto.