Matlab Application on Initial Reflections in Typical Concert Hall Designs

by carloslmgisi in Design > Software

27 Views, 0 Favorites, 0 Comments

Matlab Application on Initial Reflections in Typical Concert Hall Designs

Constructor.png
portada.png

Para la aplicación de Matlab, dividiremos el código en 5 partes:

  1. Constructor de la aplicación.
  2. Representación Gráfica y Auralización (Reflexiones Laterales y IR).
  3. Carga y Guardado del archivo de audio.
  4. Reproducción de auralización.
  5. Funciones Auxiliares.

______

For the Matlab application, we will divide the code into 5 parts:

  1. Application constructor.
  2. Graphical representation and auralization (lateral reflections and IR).
  3. Loading and saving the audio file.
  4. Auralization playback.
  5. Auxiliary functions.

Constructor De La Aplicación

propiedades públicas_ma.png
propiedades privadas_ma.png
métodos públicos_ma.png

En este primer paso, definiremos todas las propiedades, tanto públicas como privadas, los métodos públicos, así como los métodos privados correspondientes para generar la interfaz.

Propiedades

Públicas: Todas estas propiedades serán modificadas por el usuario.

Privadas: Estas propiedades serán utilizadas exclusivamente a lo largo del código y no serán accesibles para el usuario.

Métodos Públicos

Funciones que se ejecutan de forma independiente al abrir y cerrar la aplicación.

Constructor: La función app = Reflexiones_v2 es llamada al ejecutar el código de la aplicación. Dentro de esta función encontramos:

interfaz(app): Crea y configura los botones y la distribución de la aplicación. Dentro de la interfaz, añadimos las relaciones entre los botones y los triggers junto a las funciones del código correspondientes.

formaCambio(app): Configura los valores por defecto de las distintas salas.

onReceptorcentrado(app): Comprueba si está activado el checkbox de “Oyente en centro público”. Si está activo, el usuario no podrá cambiar la posición del oyente en el público. En cambio, si no está activo, el usuario podrá insertar las coordenadas X e Y del oyente.

registerApp(app, app.UIFigure): Registra la aplicación para que el propio Matlab pueda gestionarla junto con la interfaz recién creada.

Destructor: Cuando se cierra la aplicación, se ejecuta delete(app) y se elimina la interfaz creada.

Además, en la aplicación he incluido una línea después de todos los botones que sirve de línea de estados para mostrar actualizaciones sobre los procesos que realiza la aplicación.

______

In this first step, we will define all properties, both public and private, public methods, as well as the corresponding private methods to generate the interface.

Properties

Public: All these properties will be modified by the user.

Private: These properties will be used exclusively throughout the code and will not be accessible to the user.

Public Methods

Functions that run independently when opening and closing the application.

Constructor: The function app = Reflexiones_v2 is called when the application code is executed. Within this function we find:

interface(app): Creates and configures the buttons and layout of the application. Within the interface, we add the relationships between the buttons and triggers along with the corresponding code functions.

formaCambio(app): Configures the default values for the different rooms.

onReceptorcentrado(app): Checks whether the “Listener in public center” checkbox is activated. If it is active, the user will not be able to change the position of the listener in the audience. However, if it is not active, the user will be able to insert the X and Y coordinates of the listener.

registerApp(app, app.UIFigure): Registers the application so that Matlab itself can manage it together with the newly created interface.

Destructor: When the application is closed, delete(app) is executed and the created interface is removed.

In addition, I have included a line after all the buttons in the application that serves as a status line to display updates on the processes performed by the application.

Representación Gráfica Y Auralización (Reflexiones Laterales Y IR)

Representacion_sala_ma.png
Representacion_sala_2_ma.png
simulacion_1_ma.png
Reflexiones_1_ma.png
Reflexiones_2_ma.png
Reflexiones_3_ma.png

La aplicación está diseñada para que, antes de poder escuchar la auralización o guardarla, se deba representar la sala y las reflexiones, porque a la vez que la representamos, calculamos la respuesta al impulso.

Para la representación de la sala, tenemos el bloque más extenso del trabajo, usando 3 funciones auxiliares ( intersectRayPolygon(pos, dir, poly) ,intersectRaySegment(rayPos, rayDir, p1, p2) , pointSegmentDistance(P, A, B) ) que analizaremos más tarde.

Podemos dividir la representación en 3 objetivos:

  1. Representar el recinto
  2. Simular reflexiones
  3. Representación de los rayos

Para la representación del recinto, comprobamos el recinto seleccionado por el usuario y ajustamos las dimensiones de la sala y además, representamos la fuente y el oyente.

Para simular las reflexiones, primero, generamos los ángulos en los que se van a desplazar los rayos, dependiendo de la cantidad de rayos insertada por el usuario, desde -80º hasta +80º. Definimos un bucle principal que dependerá del número de rayos totales, el cual irá rayo por rayo comprobando:

¿Choca con las paredes del polígono? Si choca con alguna, comprobamos si el rayo ha pasado cerca del oyente. Si esta distancia es menor al radio de la cabeza del oyente, calculamos la distancia total, el tiempo de llegada al usuario y su posición en el vector de audio, además de la amplitud de la reflexión con la pared. Por último, guardamos los datos obtenidos para calcular la respuesta al impulso.

Aparte, guardamos el punto de choque con la pared en un historial y calculamos el rebote. Para ello, cambiamos los parámetros de la distancia acumulada y la energía del rebote, y calculamos la dirección resultante de este choque.

Si la energía resultante del choque es menor a 0.01 o si no choca con ninguna pared, dejamos de trazar el rayo y pasamos al siguiente rayo.

Por último, para representar el rayo, calculamos la transparencia del rayo basándonos en la distancia total recorrida por este: siendo muy pequeña, será opaca, y a los 40 metros casi invisible, siendo ya considerándose una gran distancia. Si el checkbox de “Animación” está activado, iremos dibujando 1 de cada 5 rayos cada vez que terminemos con los cálculos, si no está activado, representaremos el conjunto al terminar.

Para terminar con la representación, normalizamos la respuesta al impulso para que no sature y podamos escuchar la auralización.

______

The application is designed so that, before you can listen to the auralization or save it, you must represent the room and the reflections, because as we represent them, we calculate the impulse response.

The representation of the room is the most extensive part of the work, using three auxiliary functions (intersectRayPolygon(pos, dir, poly), intersectRaySegment(rayPos, rayDir, p1, p2), and pointSegmentDistance(P, A, B)), which we will analyze later.

We can divide the representation into three objectives:

  1. Represent the room
  2. Simulate reflections
  3. Represent the rays

To represent the room, we check the room selected by the user and adjust the dimensions of the room. We also represent the source and the listener.

To simulate reflections, we first generate the angles at which the rays will travel, depending on the number of rays inserted by the user, from -80º to +80º. We define a main loop that will depend on the total number of rays, which will go ray by ray checking:

Does it collide with the walls of the polygon? If it collides with any, we check if the ray has passed close to the listener. If this distance is less than the radius of the listener's head, we calculate the total distance, the time of arrival at the user and their position in the audio vector, as well as the amplitude of the reflection with the wall. Finally, we save the data obtained to calculate the impulse response.

In addition, we save the point of collision with the wall in a history and calculate the rebound. To do this, we change the parameters of the accumulated distance and the energy of the rebound, and calculate the direction resulting from this collision.

If the energy resulting from the collision is less than 0.01 or if it does not collide with any wall, we stop tracing the ray and move on to the next ray.

Finally, to represent the ray, we calculate its transparency based on the total distance traveled: if it is very small, it will be opaque, and at 40 meters it will be almost invisible, as this is already considered a great distance. If the “Animation” checkbox is enabled, we will draw 1 out of every 5 rays each time we finish the calculations; if it is not enabled, we will represent the whole set when we finish.

To finish the representation, we normalize the impulse response so that it does not saturate and we can hear the auralization.

Carga Y Guardado Del Archivo De Audio.

Load_ma.png
Save_ma.png

Con estos botones, podemos subir el archivo de audio .wav con el que queremos realizar la auralización y descargar el resultado obtenido para poder compararlo con otras simulaciones con mayor facilidad.

Dentro de la función del botón de cargar audio, pedimos al usuario el archivo de audio y actualizamos las variables locales del programa, mostrando en la línea de estado de la aplicación la ruta del archivo seleccionado.

Dentro de la función del botón de guardar el resultado obtenido, primero comprobamos que se ha realizado la simulación y si tenemos un archivo de audio subido al programa. Si se cumplen las condiciones, se abre una pestaña para que el usuario seleccione la ruta donde se guardará el archivo monofónico y su nombre. Además, comprobamos si la frecuencia de muestreo del programa y la del audio son iguales, haciendo un remuestreo si no lo son, y mostrando en la línea de estado de la aplicación la ruta del archivo guardado cuando se termine con el guardado del archivo.

______

With these buttons, we can upload the .wav audio file we want to use for auralization and download the result so we can compare it with other simulations more easily.

Within the audio upload button function, we ask the user for the audio file and update the program's local variables, displaying the path of the selected file in the application's status bar.

Within the save result button function, we first check that the simulation has been performed and that we have an audio file uploaded to the program. If the conditions are met, a tab opens for the user to select the path where the monophonic file will be saved and its name. In addition, we check whether the sampling frequency of the program and that of the audio are the same, resampling if they are not, and displaying the path of the saved file in the application status bar when the file has been saved.

Reproducción De Auralización.

Play_ma.png

Dentro de esta función, comprobamos de nuevo que se haya calculado la respuesta al impulso y si tenemos un archivo de audio subido al programa. Junto a esto, comprobamos si la frecuencia de muestreo del programa y la del audio son iguales, haciendo un remuestreo si no lo son, y promediamos los valores del archivo de audio para convertir la señal de estéreo a monofónico.

Para finalizar con el proceso, convolucionamos la señal de entrada y la respuesta al impulso, la normalizamos y la reproducimos.

______

Within this function, we check again that the impulse response has been calculated and that we have an audio file uploaded to the program. In addition, we check whether the sampling frequency of the program and that of the audio are the same, resampling if they are not, and we average the values of the audio file to convert the stereo signal to monophonic.

To complete the process, we convolve the input signal and the impulse response, normalize it, and play it back.

Funciones Auxiliares

EscenarioCentro_ma.png
intersectRayPolygon_ma.png
intersectRaySegment_ma.png
pointSegmentDistance_ma.png

A parte de las funciones auxiliares ya nombradas, podemos encontrar 4 funciones auxiliares más:

EscenarioCentrado(app): Función que calcula el centro del escenario para colocar la fuente.

intersectRayPolygon(pos, dir, poly): Esta función es la más importante para el cálculo de reflexiones. Esta función busca cuál será la próxima pared con la que chocará el rayo, sabiendo la posición,la dirección y el polígono de la sala. Esta función devuelve hit (booleano que ayuda a comprobar si choca con alguna pared), tmin (tiempo mínimo desde la pared A hasta la pared B para asegurarnos de que es el choque más cercano), hitPoint (vector con las coordenadas del choque) y edgeNormal (Calcula el vector perpendicular a la pared golpeada porque el ángulo incidente depende de la normal).

intersectRaySegment(rayPos, rayDir, p1, p2): Esta función se encarga de los cálculos geométricos del rebote. Comprueba si un rayo (que empieza en rayPos y va en la dirección rayDir) se cruza con un segmento de línea (la pared que va del punto p1 al p2). Devuelve inter (booleano que indica si el rayo corta la linea y si este punto está dentro del polígono) y t(tiempo que tarda en llegar desde el punto de origen del rayo hasta la intersección con la pared).

pointSegmentDistance(P, A, B): Esta función calcula la distancia mínima entre un punto (el oyente) y una línea (la trayectoria del rayo) para saber si el rayo ha pasado lo suficientemente cerca como para ser escuchado. La función devuelve dmin (la distancia mínima entre el punto P y la trayectoria del rayo AB) y tproj (número entre 0 y 1 que indica en qué parte del segmento cayó el punto más cercano, siendo 0 más cercano de A y 1 más cercano de B).

______

Apart from the auxiliary functions already mentioned, there are four more auxiliary functions:

EscenarioCentrado(app): Function that calculates the center of the stage to place the source.

intersectRayPolygon(pos, dir, poly): This function is the most important for calculating reflections. This function searches for the next wall that the ray will hit, knowing the position, direction, and polygon of the room. This function returns hit (a Boolean that helps check if it collides with any wall), tmin (minimum time from wall A to wall B to ensure that it is the closest collision), hitPoint (vector with the coordinates of the collision), and edgeNormal (calculates the vector perpendicular to the wall hit because the incident angle depends on the normal).

intersectRaySegment(rayPos, rayDir, p1, p2): This function handles the geometric calculations of the rebound. It checks whether a ray (starting at rayPos and going in the direction rayDir) intersects with a line segment (the wall going from point p1 to p2). It returns inter (a Boolean indicating whether the ray intersects the line and whether this point is inside the polygon) and t (the time it takes to travel from the ray's point of origin to the intersection with the wall).

pointSegmentDistance(P, A, B): This function calculates the minimum distance between a point (the listener) and a line (the ray's trajectory) to determine whether the ray has passed close enough to be heard. The function returns dmin (the minimum distance between point P and the ray trajectory AB) and tproj (a number between 0 and 1 indicating where on the segment the closest point fell, with 0 being closest to A and 1 being closest to B).