repos/TB065

Commits Files Refs
commit d64bcc5c4e43415652983b866337149ee47da2f4
parent ad0d958764910a9b27f99a91bc241fc41192612f
Author: Martin Kloeckner <mjkloeckner@gmail.com>
Date:   Sun, 14 Sep 2025 22:09:24 -0300

updated 'tp/**'

Diffstat:
Atp/data/a4_clarinete.wav | 0
Atp/data/a4_flauta.wav | 0
Atp/data/a4_violin.wav | 0
Atp/file1_filter1_output.wav | 0
Atp/file1_filter2_output.wav | 0
Atp/file2_filter1_output.wav | 0
Atp/file2_filter2_output.wav | 0
Mtp/informe/informe.md | 124++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mtp/informe/informe.pdf | 0
Mtp/main.py | 62+++++++++++++++++++++++++++++++++++++++++++++++++-------------
Atp/main2.py | 22++++++++++++++++++++++
Atp/plot/a4_clarinete.png | 0
Atp/plot/a4_flauta.png | 0
Atp/plot/a4_violin.png | 0
Mtp/plot/cancion1.png | 0
Mtp/plot/cancion1_0_248s_a_0_256s.png | 0
Mtp/plot/cancion1_0_52s_a_0_528s.png | 0
Dtp/plot/cancion1_filter1_output.png | 0
Atp/plot/cancion1_filter1_output_compare.png | 0
Dtp/plot/cancion1_filter2_output.png | 0
Atp/plot/cancion1_filter2_output_compare.png | 0
Mtp/plot/cancion2_14_72s_a_14_73s.png | 0
Mtp/plot/cancion2_26_57s_a_26_58s.png | 0
Mtp/plot/cancion2_6s.png | 0
Dtp/plot/cancion2_6s_filter1_output.png | 0
Atp/plot/cancion2_6s_filter1_output_compare.png | 0
Dtp/plot/cancion2_6s_filter2_output.png | 0
Atp/plot/cancion2_6s_filter2_output_compare.png | 0
Mtp/utils.py | 82++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
29 files changed, 253 insertions(+), 37 deletions(-)
diff --git a/tp/data/a4_clarinete.wav b/tp/data/a4_clarinete.wav
Binary files differ.
diff --git a/tp/data/a4_flauta.wav b/tp/data/a4_flauta.wav
Binary files differ.
diff --git a/tp/data/a4_violin.wav b/tp/data/a4_violin.wav
Binary files differ.
diff --git a/tp/file1_filter1_output.wav b/tp/file1_filter1_output.wav
Binary files differ.
diff --git a/tp/file1_filter2_output.wav b/tp/file1_filter2_output.wav
Binary files differ.
diff --git a/tp/file2_filter1_output.wav b/tp/file2_filter1_output.wav
Binary files differ.
diff --git a/tp/file2_filter2_output.wav b/tp/file2_filter2_output.wav
Binary files differ.
diff --git a/tp/informe/informe.md b/tp/informe/informe.md
@@ -116,7 +116,7 @@ muestra.
 
 ## Filtrado
 
-Para obtener la salida de la señal luego de aplicarle un filtro (respuesta al
+Para obtener la salida de la señal luego de pasarla por un filtro (respuesta al
 impulso del primer filtro correspondiente al archivo `respuesta_impulso_1.txt` y
 del segundo filtro correspondiente al archivo `respuesta_impulso_2.txt`) es
 necesario realizar una convolución entre la señal de entrada y la respuesta al
@@ -124,50 +124,140 @@ impulso del filtro, esto suponiendo que el filtro es un sistema LTI (si no lo
 fuera no se podría calcular la salida solo teniendo la respuesta al impulso).
 
 La salida del filtro 1 al aplicar la primer muestra se puede ver en la figura 7,
-se puede ver que atenúa partes de la señal principalmente cerca del segundo
-$7$ y también una parte cerca del segundo $8.75$.
+en la figura se puede ver que atenúa partes de la señal y amplifica otras, en
+particular amplifica principalmente antes del segundo $6$ y atenúa drásticamente
+luego.
 
 \begin{figure}[H]
 \centering
-\includegraphics[width=\linewidth]{../plot/cancion1_filter1_output.png}
+\includegraphics[width=\linewidth]{../plot/cancion1_filter1_output_compare.png}
 \caption{Primer muestra salida de filtro 1}
 \end{figure}
 
 Aplicando el segundo filtro a la primer muestra resulta como se muestra e el
 gráfico de figura 8. Se puede ver que esta a diferencia del filtro 1, no atenúa
-o amplifica significativamente partes de la señal, si no que es más leve.
+o amplifica significativamente partes de la señal, si no que realiza una leve
+atenuación de toda la señal.
 
 \begin{figure}[H]
 \centering
-\includegraphics[width=\linewidth]{../plot/cancion1_filter2_output.png}
+\includegraphics[width=\linewidth]{../plot/cancion1_filter2_output_compare.png}
 \caption{Primer muestra salida de filtro 2}
 \end{figure}
 
+<!--
+En las figuras figuras 8 y 9 a continuación se superpone la señal original con
+la salida de esta señal "pasada" por los filtros 1 y 2 respectivamente, en estés
+figuras se ve aún mas claro el efecto de los filtros descritos anteriormente, en
+incluso se aprecia que se amplifica la primer parte de la señal, entre el inicio
+y el segundo $4$ aproximadamente.
+
+\begin{figure}[H]
+\centering
+\includegraphics[width=\linewidth]{../plot/cancion1_filter1_output_compare.png}
+\caption{Comparacion entre señal original y filtrada}
+\end{figure}
+
+\begin{figure}[H]
+\centering
+\includegraphics[width=\linewidth]{../plot/cancion1_filter2_output_compare.png}
+\caption{Comparacion entre señal original y filtrada}
+\end{figure}
+-->
+
 De manera análoga para la segunda muestra se aplican los filtros mediante la
 convolución entre la señal de muestra y la respuesta al impulso del respectivo
 filtro. La salida de la segunda muestra al aplicar el primer y segundo filtro se
 puede ver en las figuras 9 y 10 respectivamente.
 
-Se puede ver claramente en la figura 9 y comparando con el gráfico de la muestra
-original (figura 4) que se atenúa la señal principalmente en la primera parte de
-la señal (antes del segundo $18.5$) aunque también ocurre en la segunda parte,
-por ejemplo en el pico que ocurre cerca del segundo $24.5$, en la señal original
-alcanza un máximo de $1.00$ mientras que en la señal filtrada se atenúa a
-$0.75$.
+Se puede ver claramente en la figura 9 que se atenúa la mayor parte de la señal
+pero principalmente en la primera mitad (antes del segundo $18.5$
+aproximadamente) y en menor medida en la mitad restante, aunque en partes de la
+segunda mitad se atenúa drásticamente de todas formas, como por ejemplo en el
+segundo $29$ en el que se atenúa aproximadamente un 70% de la señal.
+
+<!--
+, por ejemplo en el pico
+que ocurre cerca del segundo $24.5$, en la señal original alcanza un máximo de
+$1.00$ mientras que en la señal filtrada se atenúa a $0.75$.
+-->
 
 \begin{figure}[H]
 \centering
-\includegraphics[width=\linewidth]{../plot/cancion2_6s_filter1_output.png}
-\caption{Segunda muestra salida filtro 1}
+\includegraphics[width=\linewidth]{../plot/cancion2_6s_filter1_output_compare.png}
+\caption{Segunda muestra salida de filtro 1}
 \end{figure}
 
 \begin{figure}[H]
 \centering
-\includegraphics[width=\linewidth]{../plot/cancion2_6s_filter2_output.png}
-\caption{Segunda muestra salida filtro 2}
+\includegraphics[width=\linewidth]{../plot/cancion2_6s_filter2_output_compare.png}
+\caption{Segunda muestra salida de filtro 2}
 \end{figure}
 
 En el caso del segundo filtro, de manera análoga a lo que ocurría con la primer
 muestra, no se observa un cambio significativo en la señal filtrada, si no mas
 bien una leve atenuación general, aunque si se percibe una drástica atenuación
-de un instante especifico de la señal en el segundo $29.9$ aproximadamente.
+de un instante especifico de la señal, en el segundo $29.9$ aproximadamente.
+
+En todos los casos, tanto para la primer muestra como para la segunda y tanto
+para el primer filtro como el segundo, escuchando la respectiva salida se
+confirma lo analizado desde el punto de vista del gráfico de la señal, pero
+ademas se aprecia que el primer filtro realiza una atenuación de las frecuencias
+mas altas (sonidos agudos) mientras que el segundo disminuye las frecuencias
+bajas (o sonidos graves) esto ultimo no se aprecia en el gráfico de la señal, ya
+que parece no tener efecto mas que une leve atenuación.
+
+## Sonido de diferentes instrumentos
+
+Se generaron 3 muestras diferentes a las ya utilizadas, correspondientes con la
+nota *A4* (La4, $440$ Hz) con 3 instrumentos musicales distintos: un clarinete,
+una flauta y un violin. Los gráficos de las muestras resultantes se muestran en
+las figuras 11, 12 y 13, para el clarinete, la flauta y el violin,
+respectivamente.
+
+Si bien todos los sonidos tienen la misma frecuencia, ya que es la misma nota
+musical, el sonido escuchado percibido es diferente, esto puede ser producto de
+la forma de onda, lo cual queda clara la diferencia entre la onda producida por
+cada instrumento en los respectivos gráficos.
+
+\pagebreak
+
+En cuanto a los sonidos percibidos, el mas apagado o neutro es el producido por
+la flauta, mientras que el mas agudo o "afilado" es el producido por el violin,
+el sonido del clarinete es un intermedio entre ambos, un sonido ni muy agudo ni
+muy grave o apagado, y con cierto carácter metálico.
+
+Para el caso del clarinete, cuya señal se puede ver en la figura 11 a
+continuación se puede ver que la onda se parece a una onda cuadrada. En el
+dominio de frecuencia, las ondas cuadradas ideales se componen de armónicos
+impares.
+
+\begin{figure}[H]
+\centering
+\includegraphics[width=\linewidth]{../plot/a4_clarinete.png}
+\caption{Sonido de clarinete}
+\end{figure}
+
+Para la señal producida por la flauta que se puede ver en la figura 12, se puede
+ver que se asemeja a una señal senoidal pura, aunque no tan simétrica en los
+picos, las ondas sinodales en el dominio de frecuencia tienen un único armónico,
+y es el fundamental, es por esto que el sonido es mas neutro y no tan
+"brillante" o agudo.
+
+\begin{figure}[H]
+\centering
+\includegraphics[width=\linewidth]{../plot/a4_flauta.png}
+\caption{Sonido de flauta}
+\end{figure}
+
+Por ultimo para para la señal producida por el violin, la cual se puede ver en
+la figura 13, se asemeja a una onda triangular con pendiente decreciente, estas
+ondas triangulares en el dominio de frecuencia también tienen armónicos impares
+como la onda cuadrada, pero estos armónicos tienen mayor amplitud, es por esto
+que si bien tienen un sonido similar, el sonido del violin es mas agudo.
+
+\begin{figure}[H]
+\centering
+\includegraphics[width=\linewidth]{../plot/a4_violin.png}
+\caption{Sonido de violin}
+\end{figure}
diff --git a/tp/informe/informe.pdf b/tp/informe/informe.pdf
Binary files differ.
diff --git a/tp/main.py b/tp/main.py
@@ -1,7 +1,7 @@
 import matplotlib.pyplot as plt
 import numpy as np
 from scipy.io import wavfile
-from utils import plot, save_plot
+from utils import *
 
 file1_path          = 'data/cancion1.wav'
 file2_path          = 'data/cancion2.wav'
@@ -28,14 +28,31 @@ plot(file1_fs, file1_data, file1_path,
            t_start=0.520, t_width=0.008, a=0.5208, da=0.003)
 
 ## salida de filtro 'cancion1'
-file1_filter1_output = np.convolve(file1_data, filter1_h, mode='full')
-file1_filter2_output = np.convolve(file1_data, filter2_h, mode='full')
+file1_filter1_output = np.convolve(file1_data, filter1_h, mode='same')
+file1_filter2_output = np.convolve(file1_data, filter2_h, mode='same')
 
-fig, ax = plot(file1_fs, file1_filter1_output)
-save_plot(fig, file1_path, extra_name="_filter1_output")
+save_convolved_to_wav(file1_filter1_output, file1_fs, "file1_filter1_output.wav")
+save_convolved_to_wav(file1_filter2_output, file1_fs, "file1_filter2_output.wav")
 
-fig, ax = plot(file1_fs, file1_filter2_output)
-save_plot(fig, file1_path, extra_name="_filter2_output")
+# fig, ax = plot(file1_fs, file1_filter1_output)
+# save_plot(fig, file1_path, extra_name="_filter1_output")
+
+# fig, ax = plot(file1_fs, file1_filter2_output)
+# save_plot(fig, file1_path, extra_name="_filter2_output")
+
+## generar grafico comparando la muestra 1 original y filtrada 1
+data_arr = [normalize(file1_data), normalize(file1_filter1_output)]
+leg_arr = ['Señal de audio', 'Señal de audio filtrada']
+
+fig, ax = plot_multiple(file1_fs, data_arr, leg_arr)
+save_plot(fig, file1_path, extra_name="_filter1_output_compare")
+
+## generar grafico comparando la muestra 1 original y filtrada 2
+data_arr = [normalize(file1_data), normalize(file1_filter2_output)]
+leg_arr = ['Señal de audio', 'Señal de audio filtrada']
+
+fig, ax = plot_multiple(file1_fs, data_arr, leg_arr)
+save_plot(fig, file1_path, extra_name="_filter2_output_compare")
 
 # 'cancion2'
 print(f'"{file2_path}", {file2_fs} Hz')
@@ -48,11 +65,30 @@ plot(file2_fs, file2_data, file2_path, t_start=14.720, t_width=0.01)
 plot(file2_fs, file2_data, file2_path, t_start=26.570, t_width=0.01)
 
 ## salida de filtro 'cancion2'
-file2_filter1_output = np.convolve(file2_data, filter1_h, mode='full')
-file2_filter2_output = np.convolve(file2_data, filter2_h, mode='full')
+file2_filter1_output = np.convolve(file2_data, filter1_h, mode='same')
+file2_filter2_output = np.convolve(file2_data, filter2_h, mode='same')
+
+save_convolved_to_wav(file2_filter1_output, file2_fs, "file2_filter1_output.wav")
+save_convolved_to_wav(file2_filter2_output, file2_fs, "file2_filter2_output.wav")
+
+# fig, ax = plot(file2_fs, file2_filter1_output, t_start=6)
+# save_plot(fig, file2_path, t_start=6, extra_name="_filter1_output")
+
+# fig, ax = plot(file2_fs, file2_filter2_output, t_start=6)
+# save_plot(fig, file2_path, t_start=6, extra_name="_filter2_output")
+
+## generar grafico comparando la muestra 2 original y filtrada 2
+data_arr = [normalize(file2_data), normalize(file2_filter1_output)]
+leg_arr = ['Señal original', 'Señal filtrada']
+
+fig, ax = plot_multiple(file2_fs, data_arr, leg_arr, t=6)
+save_plot(fig, file2_path, t_start=6, extra_name="_filter1_output_compare")
+
+## generar grafico comparando la muestra 2 original y filtrada 2
+data_arr = [normalize(file2_data), normalize(file2_filter2_output)]
+leg_arr = ['Señal original', 'Señal filtrada']
+
+fig, ax = plot_multiple(file2_fs, data_arr, leg_arr, t=6)
+save_plot(fig, file2_path, t_start=6, extra_name="_filter2_output_compare")
 
-fig, ax = plot(file2_fs, file2_filter1_output, t_start=6)
-save_plot(fig, file2_path, t_start=6, extra_name="_filter1_output")
 
-fig, ax = plot(file2_fs, file2_filter2_output, t_start=6)
-save_plot(fig, file2_path, t_start=6, extra_name="_filter2_output")
diff --git a/tp/main2.py b/tp/main2.py
@@ -0,0 +1,22 @@
+import matplotlib.pyplot as plt
+import numpy as np
+from scipy.io import wavfile
+from utils import *
+
+a4_flauta_file_path    = 'data/a4_flauta.wav'
+a4_clarinete_file_path = 'data/a4_clarinete.wav'
+a4_violin_file_path    = 'data/a4_violin.wav'
+
+a4_flauta_fs, a4_flauta_data = wavfile.read(a4_flauta_file_path)
+a4_clarinete_fs, a4_clarinete_data = wavfile.read(a4_clarinete_file_path)
+a4_violin_fs, a4_violin_data = wavfile.read(a4_violin_file_path)
+
+## grafico completo
+fig, ax = plot(a4_flauta_fs, a4_flauta_data, t_start=0.25, t_width=0.010)
+save_plot(fig, a4_flauta_file_path)
+
+fig, ax = plot(a4_clarinete_fs, a4_clarinete_data, t_start=0.25, t_width=0.010)
+save_plot(fig, a4_clarinete_file_path)
+
+fig, ax = plot(a4_violin_fs, a4_violin_data, t_start=0.25, t_width=0.010)
+save_plot(fig, a4_violin_file_path)
diff --git a/tp/plot/a4_clarinete.png b/tp/plot/a4_clarinete.png
Binary files differ.
diff --git a/tp/plot/a4_flauta.png b/tp/plot/a4_flauta.png
Binary files differ.
diff --git a/tp/plot/a4_violin.png b/tp/plot/a4_violin.png
Binary files differ.
diff --git a/tp/plot/cancion1.png b/tp/plot/cancion1.png
Binary files differ.
diff --git a/tp/plot/cancion1_0_248s_a_0_256s.png b/tp/plot/cancion1_0_248s_a_0_256s.png
Binary files differ.
diff --git a/tp/plot/cancion1_0_52s_a_0_528s.png b/tp/plot/cancion1_0_52s_a_0_528s.png
Binary files differ.
diff --git a/tp/plot/cancion1_filter1_output.png b/tp/plot/cancion1_filter1_output.png
Binary files differ.
diff --git a/tp/plot/cancion1_filter1_output_compare.png b/tp/plot/cancion1_filter1_output_compare.png
Binary files differ.
diff --git a/tp/plot/cancion1_filter2_output.png b/tp/plot/cancion1_filter2_output.png
Binary files differ.
diff --git a/tp/plot/cancion1_filter2_output_compare.png b/tp/plot/cancion1_filter2_output_compare.png
Binary files differ.
diff --git a/tp/plot/cancion2_14_72s_a_14_73s.png b/tp/plot/cancion2_14_72s_a_14_73s.png
Binary files differ.
diff --git a/tp/plot/cancion2_26_57s_a_26_58s.png b/tp/plot/cancion2_26_57s_a_26_58s.png
Binary files differ.
diff --git a/tp/plot/cancion2_6s.png b/tp/plot/cancion2_6s.png
Binary files differ.
diff --git a/tp/plot/cancion2_6s_filter1_output.png b/tp/plot/cancion2_6s_filter1_output.png
Binary files differ.
diff --git a/tp/plot/cancion2_6s_filter1_output_compare.png b/tp/plot/cancion2_6s_filter1_output_compare.png
Binary files differ.
diff --git a/tp/plot/cancion2_6s_filter2_output.png b/tp/plot/cancion2_6s_filter2_output.png
Binary files differ.
diff --git a/tp/plot/cancion2_6s_filter2_output_compare.png b/tp/plot/cancion2_6s_filter2_output_compare.png
Binary files differ.
diff --git a/tp/utils.py b/tp/utils.py
@@ -3,34 +3,44 @@ from matplotlib.ticker import AutoMinorLocator
 from matplotlib.ticker import MultipleLocator
 from matplotlib.ticker import MaxNLocator
 from matplotlib.ticker import FuncFormatter
+from cycler import cycler
 
 import matplotlib
 import numpy as np
 import os
 
+from scipy.io import wavfile
+
 plot_dir_name = 'plot'
 
 matplotlib.rcParams['font.family'] = 'Inter'
 matplotlib.rcParams['font.size'] = 12
+matplotlib.rcParams['axes.prop_cycle'] = cycler(
+        color=['#1f77b4', '#ff0000', 'green', 'orange'])
 matplotlib.use("TkAgg")
 
 def ticks_label_format(x, pos):
     # 3 decimals, strip trailing zeros
     return f"{x:.3f}".rstrip("0").rstrip(".")
 
-def graph_data(x, y, t=0, dt=0, a=0, da=0, y_min=0, y_max=0, show=True):
+def graph_multiple_data(x, y_arr, y_lab, t=0, dt=0, a=0, da=0, show=True):
     figure, axis = plt.subplots(figsize=(5, 4))
 
-    axis.plot(x, y, label='Señal de Audio')
+    for i, y in enumerate(y_arr):
+        axis.plot(x, y, label=y_lab[i], alpha=0.75)
+
     axis.set(xlabel='Tiempo [s]', ylabel='Amplitud normalizada')
 
     axis.minorticks_on()
-    axis.grid(True, which='major', linestyle='-', linewidth=0.50)
-    axis.grid(True, which='minor', linestyle=':', linewidth=0.50)
+    axis.grid(True, which='major', color='black', linestyle=':', linewidth=1.00)
+    axis.grid(True, which='minor', color='black', linestyle=':', linewidth=0.50)
 
     # configuracion de ticks del eje x
     axis.xaxis.set_major_locator(MaxNLocator(nbins=5))
-    axis.xaxis.set_minor_locator(AutoMinorLocator(4))  # 2 subdivisions between majors
+    axis.xaxis.set_minor_locator(AutoMinorLocator(5))
+
+    axis.yaxis.set_major_locator(MaxNLocator(nbins=5))
+    axis.yaxis.set_minor_locator(AutoMinorLocator(4))
 
     plt.tight_layout()
 
@@ -51,10 +61,56 @@ def graph_data(x, y, t=0, dt=0, a=0, da=0, y_min=0, y_max=0, show=True):
 
     return figure, axis
 
-def plot(fs, data, file_path="", t_start=0, t_width=0, a=0, da=0):
-    # normaliza la amplitud dividiendo por el valor maximo del tipo de dato
+# todos deben la misma cantidad de elementos que el primero
+def plot_multiple(fs, data_arr, leg_arr, t=0, dt=0, a=0, da=0):
+    x = np.arange(len(data_arr[0])) / fs
+    fig, ax = graph_multiple_data(x, data_arr, leg_arr, t=t, dt=dt, a=a, da=da, show=False)
+    return fig, ax
+
+def graph_data(x, y, t=0, dt=0, a=0, da=0, show=True):
+    figure, axis = plt.subplots(figsize=(5, 4))
+
+    axis.plot(x, y, label='Señal de audio')
+    axis.set(xlabel='Tiempo [s]', ylabel='Amplitud normalizada')
+
+    axis.minorticks_on()
+    axis.grid(True, which='major', color='black', linestyle=':', linewidth=1.00)
+    axis.grid(True, which='minor', color='black', linestyle=':', linewidth=0.50)
+
+    # configuracion de ticks del eje x
+    axis.xaxis.set_major_locator(MaxNLocator(nbins=5))
+    axis.xaxis.set_minor_locator(AutoMinorLocator(5))
+
+    axis.yaxis.set_major_locator(MaxNLocator(nbins=5))
+    axis.yaxis.set_minor_locator(AutoMinorLocator(4))
+
+    plt.tight_layout()
+
+    # max 3 decimals
+    axis.xaxis.set_major_formatter(FuncFormatter(ticks_label_format))
+
+    axis.set_xlim([t, t+dt if dt > 0 else x[-1]])
+    axis.set_ylim(-1.1, 1.1)
+
+    # resaltado de parte de la señal (solo si a != 0)
+    axis.axvspan(a, a+da, color='skyblue',
+                 alpha=0 if a == 0 else 0.50,
+                 label=f"Un periodo T={da}s" if da != 0 else "")
+    axis.legend()
+
+    if show:
+        plt.show()
+
+    return figure, axis
+
+def normalize(data):
     data = data.astype(np.float32)
     data /= np.max(np.abs(data))
+    return data
+
+def plot(fs, data, file_path="", t_start=0, t_width=0, a=0, da=0):
+    # normaliza la amplitud dividiendo por el valor maximo del tipo de dato
+    data = normalize(data)
 
     t = np.arange(len(data)) / fs
     fig, ax = graph_data(t, data, t=t_start, dt=t_width, a=a, da=da, show=False)
@@ -82,3 +138,15 @@ def save_plot(fig, src_file_path, t_start=0, t_width=0, extra_name=''):
      # crea carpeta para plots
     os.makedirs(plot_dir_name, exist_ok=True)
     fig.savefig(fig_file_name, dpi=300, bbox_inches="tight")
+
+def save_convolved_to_wav(convolved, fs, file_path):
+    # 4️⃣ Normalize to prevent clippin
+    convolved = convolved / np.max(np.abs(convolved))
+
+    # 5️⃣ Convert to 16-bit PCM for WAV
+    convolved_int16 = np.int16(convolved * 32767)
+
+    print(f'- "{file_path}"')
+    # 6️⃣ Save to WAV
+    wavfile.write(file_path, fs, convolved_int16)
+