DavidShiver/mastering-ai
0
1import gradio as gr2import librosa3import numpy as np4import soundfile as sf5import scipy.signal as signal6 7def calculate_loudness_db(y):8 rms = np.sqrt(np.mean(y**2)) + 1e-99 return 20 * np.log10(rms)10 11def match_loudness(target, source):12 target_rms = np.sqrt(np.mean(target**2)) + 1e-913 source_rms = np.sqrt(np.mean(source**2)) + 1e-914 return source * (target_rms / source_rms)15 16def apply_high_shelf(y, sr, gain_db, freq=10000):17 if abs(gain_db) < 0.1: return y18 A = 10**(gain_db/40)19 w0 = 2 * np.pi * freq / sr20 alpha = np.sin(w0) / 2 * np.sqrt(2)21 cos_w0 = np.cos(w0)22 b0 = A * ((A+1) + (A-1)*cos_w0 + 2*np.sqrt(A)*alpha)23 b1 = -2 * A * ((A-1) + (A+1)*cos_w0)24 b2 = A * ((A+1) + (A-1)*cos_w0 - 2*np.sqrt(A)*alpha)25 a0 = (A+1) - (A-1)*cos_w0 + 2*np.sqrt(A)*alpha26 a1 = 2 * ((A-1) - (A+1)*cos_w0)27 a2 = (A+1) - (A-1)*cos_w0 - 2*np.sqrt(A)*alpha28 return signal.lfilter([b0, b1, b2], [a0, a1, a2], y)29 30def master_v14_engine(audio, saturation_bp, stereo_bp, bass_bp, air_shelf, l_drive, l_ceiling, post_gain):31 if audio is None: return None, None, "Brak pliku."32 try:33 y, sr = librosa.load(audio, sr=44100, mono=False)34 if len(y.shape) < 2: y = np.array([y, y])35 orig_db = calculate_loudness_db(y)36 37 mid = (y[0] + y[1]) * 0.538 side = (y[0] - y[1]) * 0.539 40 # 1. Korekcja Tonalna41 sos_low = signal.butter(4, 100, 'lp', fs=sr, output='sos')42 mid = mid + (signal.sosfilt(sos_low, mid) * (bass_bp / 100))43 44 if stereo_bp < 0:45 side *= (1.0 + (stereo_bp / 100))46 else:47 sos_high_s = signal.butter(2, 200, 'hp', fs=sr, output='sos')48 side += signal.sosfilt(sos_high_s, side) * (stereo_bp / 100)49 50 y_proc = np.array([mid + side, mid - side])51 52 # 2. Saturation & Air53 if abs(saturation_bp) > 0.1:54 drive = 1.0 + (abs(saturation_bp) / 100)55 y_proc = np.tanh(y_proc * drive) / drive if saturation_bp > 0 else y_proc * (1.0 - abs(saturation_bp)/200)56 57 y_proc = apply_high_shelf(y_proc, sr, air_shelf / 15, freq=10000)58 59 # 3. LOGIKA LIMITERA (ZMIANA)60 # Limiter Drive - zwiększa głośność odczuwalną przed "sufitem"61 drive_factor = 10**(l_drive / 20)62 y_limited = np.tanh(y_proc * drive_factor) # Soft-clipping jako limiter63 64 # Final Ceiling & Post-Gain65 # Teraz l_ceiling to absolutna granica, a post_gain to Twoja kontrola głośności zgrania66 ceiling_linear = 10**(l_ceiling / 20)67 gain_linear = 10**(post_gain / 20)68 69 # Skalowanie bez wymuszonej normalizacji do 0dB70 y_final = y_limited * ceiling_linear * gain_linear71 72 # Zabezpieczenie przed twardym clippingiem powyżej ceilingu73 y_final = np.clip(y_final, -ceiling_linear, ceiling_linear)74 75 # 4. Export & Report76 out_pro_path = "master_v14_24bit.wav"77 out_match_path = "compare_matched.wav"78 sf.write(out_pro_path, y_final.T, sr, subtype='PCM_24')79 sf.write(out_match_path, match_loudness(y, y_final).T, sr, subtype='PCM_16')80 81 db_diff = calculate_loudness_db(y_final) - orig_db82 83 report = (f"Raport Inżynieryjny:\n"84 f"• Wzmocnienie efektywne: {db_diff:.2f} dB\n"85 f"• Ustawiony sufic (Ceiling): {l_ceiling} dB\n"86 f"• Dodatkowe tłumienie zgrania: {post_gain} dB\n"87 f"• Dynamika: Zachowana dzięki architekturze Gain-Stage")88 89 return out_pro_path, out_match_path, report90 91 except Exception as e:92 return None, None, f"Błąd: {str(e)}"93 94with gr.Blocks(theme=gr.themes.Monochrome()) as demo:95 gr.Markdown("# 🎚️ AI MASTERING STUDIO V14 - DYNAMIC ARCHITECTURE")96 with gr.Row():97 with gr.Column(scale=1):98 in_audio = gr.Audio(label="Mix", type="filepath")99 gr.Markdown("### 🎛️ Korekcja")100 with gr.Row():101 saturator = gr.Slider(-100, 100, value=10, label="Saturation")102 stereo = gr.Slider(-100, 100, value=20, label="Stereo Width")103 with gr.Row():104 bass = gr.Slider(-100, 100, value=10, label="Bass")105 air = gr.Slider(-100, 100, value=15, label="10kHz Air")106 107 gr.Markdown("### 🛡️ Sekcja Dynamiki (Limiter)")108 l_drive = gr.Slider(0, 20, value=3, step=0.5, label="Limiter Input Drive (dB) - 'Gęstość'")109 l_ceiling = gr.Slider(-6.0, -0.1, value=-0.3, step=0.1, label="Ceiling (Limit Peak)")110 post_gain = gr.Slider(-20.0, 0.0, value=0.0, step=0.5, label="Final Export Gain (dB) - 'Głośność zgrania'")111 112 btn = gr.Button("🚀 GENERUJ MASTER", variant="primary")113 114 with gr.Column(scale=1):115 status = gr.Textbox(label="Analiza AI", lines=5)116 out_pro = gr.Audio(label="24-bit MASTER")117 out_matched = gr.Audio(label="PORÓWNANIE (Loudness Match)")118 119 btn.click(fn=master_v14_engine, inputs=[in_audio, saturator, stereo, bass, air, l_drive, l_ceiling, post_gain], outputs=[out_pro, out_matched, status])120 121demo.launch()122 123 124 125 126 