Arreglos - Procesamiento de sonido

main1.png main2.png main3.png

Los arreglos de datos (arrays) nos facilitan guardar y trabajar con grupos de datos del mismo tipo. Los datos se guardan en espacios de memoria consecutivos a los que se puede acceder utilizando el nombre del arreglo con índices o suscritos que indican la posición en la que se encuentra el dato. Las estructuras de repetición nos proveen una manera simple de acceder a los datos de un arreglo. En la experiencia de laboratorio de hoy, te expondrás a algoritmos de procesamiento de sonido, simples pero ingeniosos, para practicar el uso de ciclos en la manipulación de arreglos.

Esta experiencia de laboratorio es una adaptación de un "nifty assignment" presentado por Daniel Zingaro en [1].

Objetivos:

  1. Practicar el uso de ciclos en la manipulación de arreglos.

  2. Aprender algoritmos simples para procesar sonido.

  3. Practicar la programación modular.

Pre-Lab:

Antes de llegar al laboratorio debes haber:

  1. Repasado los conceptos relacionados a arreglos y ciclos.

  2. Estudiado los atributos left y right de la clase QAudioBuffer::S16S de la librería de multimedios de Qt.

  3. Estudiado los conceptos e instrucciones para la sesión de laboratorio.

  4. Tomado el quiz Pre-Lab, disponible en Moodle.



Procesamiento de sonido digital

El sonido es una vibración que se propaga en medios elásticos tales como el aire, el agua y los sólidos. Las ondas sonoras son generadas por una fuente de sonido como por ejemplo, la vibración del diafragma de una bocina de sonido [2]. Las ondas de sonido consisten de segmentos de alta y baja presión llamados compresiones y rarefacciones, respectivamente.

Los micrófonos convierten las ondas de sonido a señales eléctricas. Estas señales eléctricas pueden digitalizarse, o sea, pueden ser convertidas a sucesiones de números, en donde cada número representa la intensidad de la señal eléctrica en un momento en el tiempo. La razón de la muestra es el número de muestras de la señal de sonido, tomadas en un segundo. Por ejemplo, para obtener la calidad de sonido de una grabación estéreo de CD se usa una razón de muestra de 44,100 muestras por segundo. Esto significa que en cada segundo, para cada uno de los canales (izquierdo y derecho), se toman 44,100 muestras de sonido y se convierten a números.


image1.jpg

Figura 1. Ilustración de los pasos para realizar una digitalización de sonido. El micrófono convierte la onda de presión en una señal de voltaje. El convertidor de señal análoga a señal digital toma muestras de la señal de voltaje y convierte cada muestra en un valor numérico. La sucesión de números forma el sonido digitalizado. Tomado de [3].


Pregunta:

Supón que cada muestra de sonido se convierte a un número que ocupa 2 bytes. ¿Cuántos bytes se necesitan para guardar una canción que dura exactamente 180 segundos y que fue grabada en calidad de CD estéreo?

Respuesta:

Afortunadamente, existen técnicas de compresión de datos como MP3 y Ogg que reducen la cantidad de memoria necesaria para guardar música con calidad de CD.


El procesamiento digital de sonido se utiliza para mejorar la calidad del sonido removiendo ruido y eco, para comprimir los datos, para mejorar la transmisión y para añadir efectos especiales. También juega un papel importante en las aplicaciones de reconocimiento de voz y en investigaciones científicas de detección de biodiversidad utilizando sensores de sonido [4].

Como las grabaciones de sonido digital son esencialmente una colección de valores numéricos que representan una onda de sonido, el procesamiento de sonido digital puede ser tan simple como aplicar operaciones aritméticas a esos valores. Por ejemplo, digamos que tienes una grabación de sonido digital; mientras más alto el volúmen de la grabación, más altos los valores absolutos de los números que contiene. Para reducir el volúmen de toda la grabación solo tendríamos que multiplicar cada valor en la grabación por un número positivo menor que 1.


image2.png

Figura 2. Una de las tareas más simples en el procesamiento de sonido digital: cambiar el volúmen de una onda de sonido multiplicando cada punto por un valor positivo menor que 1 (en este caso 0.5).


Bibliotecas

Para esta experiencia de laboratorio usarás bibliotecas multimedios de Qt. Para poder trabajar los ejercicios necesitarás conocer los atributos left y right de la clase QAudioBuffer::S16S. En esta experiencia de laboratorio utilizamos el nombre AudioBuffer al referirnos a QAudioBuffer::S16S.

Cada objeto de la clase AudioBuffer tendrá atributos o variables miembro left y right que contienen el valor izquierdo y derecho de la muestra de sonido estéreo. Estas variables son públicas y podrás acceder su contenido escribiendo el nombre del objeto, seguido de un punto y luego el nombre de la variable. Para representar una señal de sonido, usamos un arreglo de objetos de la clase AudioBuffer. Cada elemento del arreglo es un objeto que contiene los valores izquierdo y derecho de la señal en un instante en el tiempo (recuerda que cada segundo contiene 44,100 muestras). Por ejemplo, si tenemos un arreglo de objetos AudioBuffer, llamado frames, entonces frames[i].left se refiere al valor del canal izquierdo del sonido en la muestra i.


image3.png

Figura 3. En la figura, frame es un arreglo de objetos AudioBuffer. En esta experiencia de laboratorio, las señales de sonido estarán representadas por un arreglo de objetos AudioBuffer. Un objeto con índice i guarda el valor de los canales izquierdo y derecho de la muestra i.


La función HalfVolume en el siguiente ejemplo ilustra cómo leer y modificar un arreglo de objetos AudioBuffer:

// Dado frames (un arreglo de AudioBuffers) y N (su tamaño)
// dividir entre dos cada uno de los elementos de frames (tanto
// del canal izquierdo como el derecho).

void HalfVolume(AudioBuffer frames[], int N){

    // para cada muestra en la señal, reduce su valor a la mitad

    for (int i=0; i < N; i++) {
        frames[i].left  = frames[i].left / 2;
        frames[i].right = frames[i].right / 2; 
    }
}


¿Cuál es el output del siguiente código?

La primera opción es la correcta. Las siguientes figuras ilustran ejecuciones a mano de los casos i = 0,4,8 and i=1,2,3,5,6,7.


Suponga que tiene un arreglo que guarda elementos de tipo int llamado A. El contenido de A es: 10, 36, 400, 82, 350. ¿Cuál será el contenido del arreglo luego de ejecutar el siguiente código?

5, 18, 200, 41, 175 20, 72, 800, 164, 700 0, 0.5, 1, 1.5, 2 10, 18, 800, 41, 700 1, 2, 3, 4, 5 La siguiente figura ilustra la ejecución a mano del programa.
La respuesta correcta es la cuarta: 10, 18, 800, 41, 700.

Suponga que existe un arreglo de objetos de clase AudioBuffer llamado playlist. Queremos multiplicar el contenido del canal izquierdo de cada objeto por 5, y dividir el contenido del canal derecho de cada objeto entre 10. ¿Cuál de las siguientes instrucciones lograría esta tarea? Suponga que las instrucciones se llevan a cabo dentro de un for loop que itera a través del arreglo entero utilizando una variable llamada i. Como puede ver en la documentación de la clase AudioBuffer, los atributos left y right de cada objeto son públicos, por la cual se pueden acceder utilizando la instrucción .right o .left luego del nombre del objeto. Si tenemos un arreglo de objetos AudioBuffer y queremos acceder un elemento en particular, y luego su atributo left o right, utilizariamos la instrucción nombreObjeto[indice].right, e igualmente podemos utilizar la misma instrucción para alterar los contenidos del objeto. Por lo tanto, la contestación correcta es la cuarta. La quinta opción no es válida pues la clase AudioBuffer no tiene setters y getters que se llamen setLeft, setRight, y getLeft, getRight.

¿Cuál de las siguientes funciones imprime los números desde el 0 hasta casi 1 en n pasos equidistantes? Por ejemplo, si n =100, imprime 0, 0.01, 0.02, .., 0.98, 0.99. La función correcta es ji. El ciclo for de esta función genera enteros desde i = 0 hasta n-1. Por lo tanto, la primera iteración imprime 0, seguida por el resultado de 1.0/n, seguido de 2.0/n, .. , y la última iteración imprime el resultado de (n-1)/n.



Sesión de laboratorio:

El proyecto SoundProcessing contiene el esqueleto de una aplicación para hacer procesamiento de sonido estéreo. La aplicación que completarás permitirá al usuario aplicar cuatro algoritmos diferentes para procesamiento de sonidos. La sub-carpeta llamada WaveSamples contiene archivos de onda para que pruebes tus implementaciones.

Ejercicio 1 - Remover las voces de una grabación

Una forma simple (pero muchas veces inefectiva) de remover las voces de una grabación es tomando ventaja del hecho de que las voces usualmente se graban en ambos canales, izquierdo y derecho, mientras que el resto de los instrumentos quizás no. Si este fuese el caso, podemos remover las voces de una grabación restando el canal izquierdo y derecho.

Instrucciones

  1. Carga a QtCreator el proyecto SoundProcessing. Hay dos maneras de hacer esto:

    • Utilizando la máquina virtual: Haz doble “click” en el archivo SoundProcessing.pro que se encuentra en el directorio /home/eip/labs/arrays-soundprocessing de la máquina virtual.
    • Descargando la carpeta del proyecto de Bitbucket: Utiliza un terminal y escribe el comando git clone http://bitbucket.org/eip-uprrp/arrays-soundprocessing para descargar la carpeta arrays-soundprocessing de Bitbucket. En esa carpeta, haz doble “click” en el archivo SoundProcessing.pro.
  2. Compila y corre el programa. Aparecerá un interfaz gráfico para procesamiento de sonido de grabaciones.

  3. Carga cualquiera de los archivos de onda love.wav, cartoon.wav, o grace.wav marcando el botón de búsqueda (Search) en el lado derecho de la etiqueta Audio In, y reprodúcela marcando el botón Play Audio In.

  4. Tu tarea en este ejercicio es completar la función RemoveVocals que se encuentra en el archivo audiomanip.cpp para que remueva las voces de una grabación. La función recibe un arreglo de objetos de la clase AudioBuffer y el tamaño del arreglo.

Algoritmo:

Para cada muestra en el arreglo, computa la diferencia de la muestra del canal izquierdo menos el derecho, divídelo por 2 y asigna el resultado a ambos canales.

Marca el botón Play Audio Out en la aplicación para reproducir el sonido del archivo de salida.

Ejercicio 2 - Intensificar

Un efecto de sonido común es la intensificación gradual del volumen de una grabación. Esto se consigue aumentando gradualmente el valor de muestras consecutivas en el arreglo de muestras de sonido.

Instrucciones

  1. Carga y reproduce cualquiera de los archivos de onda rain.wav, o water.wav como hiciste en el Ejercicio 1. Estos son los archivos con el audio sin procesar.

  2. Tu tarea en este ejercicio es completar la función AudioFadeIn que se encuentra en el archivo audiomanip.cpp de modo que se intensifique gradualmente el volumen de una grabación hasta cierto punto. La función recibe un arreglo de objetos de la clase AudioBuffer, el tamaño del arreglo, y un largo de duración para el aumento en intensidad (fade_length) que será aplicado a AudioBuffer. Por ejemplo, si fade_length es 88200, el aumento en intensidad no debe afectar ninguna muestra en posición mayor o igual a 88200.

  3. Reproduce las siguientes grabaciones contenidas en la carpeta WaveSamples:

    • rain.fi.wav
    • water.fi.wav

    Esas dos grabaciones fueron creadas utilizando el filtro de intensidad con fade_length = 88200. Debes escuchar como el sonido del agua y la lluvia se intensifican linealmente durante los primeros dos segundos y luego se quedan en el mismo volúmen. Nota que, como estamos usando sonidos grabados a 44100 muestras por segundo, 88200 corresponde a dos segundos de grabación.

Algoritmo:

Para aplicar el aumento de intensidad a un sonido, multiplicamos cada muestra sucesiva por números entre 0 y 1 que van en aumento constante. Si la muestra se multiplica por 0 se silencia, si se multiplica por 1 se queda igual; si se multiplica por un valor entre 0 y 1 el volúmen se escala por ese factor. Es importante destacar que ambos canales deben ser multiplicados por el mismo factor.

Por ejemplo, si fade_length fuera 4, aplicaríamos el filtro a las primeras 4 muestras de la siguiente manera:

Número de muestra Multiplica por factor
0 0
1 0.25
2 0.5
3 0.75
>= 4 1 (No modifica la muestra)

Nota que tenemos 4 muestras y el factor por el que se multiplica la muestra en cada canal comienza en 0 e incrementa 0.25 cada vez hasta llegar a 1.

Ejercicio 3 - Desvanecer

Otro efecto de sonido común es la disminución gradual del volumen de una grabación. Esto se consigue disminuyendo constantemente el valor de muestras consecutivas en el arreglo de muestras de sonido.

Instrucciones

  1. Carga y reproduce cualquiera de los archivos de onda rain.wav, o water.wav como hiciste en los ejercicios anteriores.

  2. Tu tarea en este ejercicio es completar la función AudioFadeOut que se encuentra en el archivo audiomanip.cpp para que desvanezca el volúmen gradualmente faltando una cantidad de muestras antes del final. La función recibe un arreglo de objetos de la clase AudioBuffer, el tamaño del arreglo, y un largo de duración del desvanecimiento que será aplicado a AudioBuffer. Por ejemplo, si fade_length es 88200, el desvanecimiento debe comenzar cuando faltan 88200 muestras para el final de la grabación.

  3. Reproduce las siguientes grabaciones contenidas en la carpeta WaveSamples:

    • rain.fo.wav
    • water.fo.wav

    Esas dos grabaciones fueron creadas utilizando el filtro de desvanecer con fade_length 88200. Debes escuchar el sonido del agua y la lluvia en volúmen constante y luego, en los últimos dos segundos, el volumen disminuye linealmente hasta desaparecer.

Algoritmo:

Los factores para desvanecer son los mismos que para intensificar, pero se aplican en el orden opuesto. Por ejemplo, si fade_length fuera 4, las muestras de los canales en la posición cuatro antes de la última se multiplican por 0.75, las muestras de los canales en la posición tres antes de la última se multiplican por 0.5, las muestras de los canales en la penúltima posición se multiplican por 0.25, y las muestras en los canales en la última posición se multiplican por 0.0.

Ejercicio 4 - Recorrido de izquierda a derecha

El efecto de sonido que queremos lograr en este ejercicio es comenzar a escuchar un sonido por el canal izquierdo, que vaya desvaneciéndose en ese canal, vaya intensificándose en el canal derecho, y termine completamente en el canal derecho.

Instrucciones

  1. Carga y reproduce el archivo airplane.wav como hiciste en los ejercicios anteriores.

  2. Tu tarea en este ejercicio es completar la función LeftToRight que se encuentra en el archivo audiomanip.cpp para que haga que el sonido vaya "moviéndose" del canal izquierdo al canal derecho. La función recibe un arreglo de objetos de la clase AudioBuffer, el tamaño del arreglo, y un largo de recorrido 'pan_length' que será aplicado a AudioBuffer. Por ejemplo, si pan_length es 88200, el recorrido no debe afectar ninguna muestra en posiciones mayores o iguales a 88200.

  3. Reproduce la grabación en airplane.out.wav. Debes poder oir cómo el sonido del avión se escucha primero completamente a la izquierda y luego se mueve lentamente hacia la derecha, terminando completamente a la derecha en la última muestra. En este ejemplo el recorrido termina en la última muestra. Esto no ocurrirá si el largo del recorrido 'pan_length' no es igual al número de muestras; en este caso, luego de llegar al largo del recorrido escucharás el sonido normal en ambos canales.

Algoritmo:

Para crear el efecto de que el sonido se mueve de izquerda a derecha se necesita un desvanecimiento en el canal de la izquierda y una intensificación en el canal de la derecha. Por ejemplo, si el pan_length es 4, el filtro será aplicado a las primeras 4 muestras:

Número de muestra Factor a multiplicar por canal izquierdo Factor a multiplicar por canal derecho
0 0.75 0
1 0.5 0.25
2 0.25 0.5
3 0 0.75
>= 4 (No modificar la muestra) (No modificar la muestra)


Entrega

Utiliza "Entrega" en Moodle para entregar el archivo audiomanip.cpp. Recuerda utilizar buenas prácticas de programación, incluye el nombre de los programadores, y documenta tu programa.



Referencias

[1] Daniel Zingaro, http://nifty.stanford.edu/2012/zingaro-stereo-sound-processing/

[2] http://en.wikipedia.org/wiki/Sound

[3] http://homepages.udayton.edu/~hardierc/ece203/sound_files/image001.jpg.

[4] Arbimon, A web based network for storing, sharing, and analyzing acoustic information. http://arbimon.com/

[5] https://somnathbanik.wordpress.com/2012/10/22/digital-signal-processing-featured-project/

[6] http://www.hearingreview.com/2013/03/designing-hearing-aid-technology-to-support-benefits-in-demanding-situations-part-1/

[7] http://diveintodotnet.com/2014/12/02/programming-basics-what-are-strings/

results matching ""

    No results matching ""