%23%20gallery_category%3A%20Absorption%20%26%20Filters%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.23.13%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Absorption%20GEQ%20in%20an%20FDN%0A%0A%20%20%20%20Demonstrates%20%60pyFDN.absorption_geq%60%3A%20frequency-dependent%20absorption%20designed%0A%20%20%20%20as%20a%2010-band%20graphic%20EQ%20(11%20biquad%20sections)%20targeting%20a%20given%20reverberation%0A%20%20%20%20time%20curve.%0A%0A%20%20%20%20The%20absorption%20filters%20are%20applied%20per%20delay%20line.%20%20Here%20we%3A%0A%0A%20%20%20%201.%20Design%20the%20filters%20from%20a%20target%20T60%20curve.%0A%20%20%20%202.%20Run%20a%20one-channel%20FDN%20using%20FLAMO.%0A%20%20%20%203.%20Estimate%20T60%20from%20the%20impulse%20response%20and%20compare%20with%20the%20target.%0A%0A%20%20%20%20Reference%3A%20*Schlecht%20and%20Habets%202020.*%0A%20%20%20%20Reference%3A%20*V%C3%A4lim%C3%A4ki%20and%20Reiss%2C%20%22All%20About%20Audio%20Equalization%3A%20Solutions%20and%20Frontiers%2C%22%20Applied%20Sciences%2C%20vol.%206%2C%20no.%205%2C%20p.%20129%2C%202016.*%0A%0A%20%20%20%20Original%20MATLAB%3A%20Sebastian%20J.%20Schlecht%2C%2022%20October%202020.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%20%20%20%20import%20plotly.io%20as%20pio%0A%0A%20%20%20%20import%20pyFDN%0A%0A%20%20%20%20pio.renderers.default%20%3D%20%22sphinx_gallery%22%0A%20%20%20%20return%20go%2C%20np%2C%20pyFDN%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20FDN%20parameters%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20pyFDN)%3A%0A%20%20%20%20np.random.seed(5)%0A%20%20%20%20fs%20%3D%2048000%0A%20%20%20%20num_delays%20%3D%208%0A%20%20%20%20rir_len%20%3D%203%20*%20fs%20%20%23%203%20seconds%0A%0A%20%20%20%20build%20%3D%20pyFDN.fdn_build_gallery(%0A%20%20%20%20%20%20%20%20num_delays%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20delay_range%3D(500%2C%202001)%2C%0A%20%20%20%20%20%20%20%20sort_delays%3DTrue%2C%0A%20%20%20%20%20%20%20%20io_type%3D%22ones%22%2C%0A%20%20%20%20%20%20%20%20input_scale%3D1%20%2F%20num_delays%2C%0A%20%20%20%20%20%20%20%20direct_gain%3D0.0%2C%0A%20%20%20%20%20%20%20%20rt%3DNone%2C%0A%20%20%20%20%20%20%20%20rng%3D5%2C%0A%20%20%20%20)%0A%20%20%20%20delays%20%3D%20build.delays%0A%20%20%20%20feedback_matrix%20%3D%20build.A%0A%20%20%20%20B_in%2C%20C_out%2C%20D_dir%20%3D%20build.B%2C%20build.C%2C%20build.D%0A%0A%20%20%20%20%23%20Target%20RT%20at%20the%2010%20GEQ%20bands%20(seconds)%0A%20%20%20%20target_rt%20%3D%20np.array(%5B2.0%2C%202.0%2C%202.2%2C%202.3%2C%202.1%2C%201.5%2C%201.1%2C%200.8%2C%200.7%2C%200.7%5D)%0A%0A%20%20%20%20print(f%22Delays%3A%20%7Bdelays%7D%22)%0A%20%20%20%20print(f%22Target%20RT%3A%20%7Btarget_rt%7D%22)%0A%20%20%20%20return%20B_in%2C%20C_out%2C%20D_dir%2C%20delays%2C%20feedback_matrix%2C%20fs%2C%20rir_len%2C%20target_rt%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Design%20absorption%20filters%0A%0A%20%20%20%20%60absorption_geq%60%20converts%20T60%20to%20a%20per-sample%20dB%20slope%2C%20fits%20a%20GEQ%2C%20and%20returns%0A%20%20%20%20SOS%20coefficients%20for%20each%20delay%20line.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(delays%2C%20fs%2C%20pyFDN%2C%20target_rt)%3A%0A%20%20%20%20%23%20absorption_geq%20uses%20the%208%20interior%20RT%20values%20(bands%201..8)%0A%20%20%20%20%23%20The%20outer%20two%20are%20the%20shelf%20bounds%3B%20strip%20them%20to%20match%20the%2010%20GEQ%20bands%0A%20%20%20%20sos_absorption%20%3D%20pyFDN.absorption_geq(target_rt%2C%20delays%2C%20fs)%0A%20%20%20%20print(f%22Absorption%20SOS%20shape%3A%20%7Bsos_absorption.shape%7D%22)%0A%20%20%20%20%23%20shape%3A%20(11%2C%206%2C%20num_delays)%20%20-%3E%20(n_sections%2C%206%2C%20N)%0A%20%20%20%20return%20(sos_absorption%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Absorption%20filter%20magnitude%20responses%0A%0A%20%20%20%20Plot%20the%20cascaded%20per-delay%20absorption%20filter%20response%20for%20each%20of%20the%208%20delay%0A%20%20%20%20lines.%20%20The%20curves%20should%20decay%20toward%20lower%20dB%20at%20higher%20frequencies%20(shorter%0A%20%20%20%20T60%20%3D%20more%20attenuation%20per%20sample%20at%20HF).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(delays%2C%20fs%2C%20pyFDN%2C%20sos_absorption)%3A%0A%20%20%20%20pyFDN.plot_db_per_sample(%0A%20%20%20%20%20%20%20%20sos_absorption%2C%0A%20%20%20%20%20%20%20%20delays%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20nfft%3D2**14%2C%0A%20%20%20%20%20%20%20%20title%3D%22Per-delay%20absorption%20filter%20magnitude%20(one%20application)%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Compute%20impulse%20response%0A%0A%20%20%20%20Build%20a%20FLAMO%20FDN%20with%20the%20GEQ%20absorption%20filters%20in%20the%20loop%20via%20%60dss_to_flamo%60.%0A%20%20%20%20Signal%20path%3A%20input%20%E2%86%92%20B%20%E2%86%92%20%5Bdelays%20%E2%86%92%20SOS%20%E2%86%92%20A%5D%20%E2%86%92%20C%20%E2%86%92%20output.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20B_in%2C%0A%20%20%20%20C_out%2C%0A%20%20%20%20D_dir%2C%0A%20%20%20%20delays%2C%0A%20%20%20%20feedback_matrix%2C%0A%20%20%20%20fs%2C%0A%20%20%20%20np%2C%0A%20%20%20%20pyFDN%2C%0A%20%20%20%20rir_len%2C%0A%20%20%20%20sos_absorption%2C%0A)%3A%0A%20%20%20%20nfft%20%3D%20int(2%20**%20np.ceil(np.log2(rir_len)))%0A%20%20%20%20model%20%3D%20pyFDN.dss_to_flamo(%0A%20%20%20%20%20%20%20%20feedback_matrix%2C%0A%20%20%20%20%20%20%20%20B_in%2C%0A%20%20%20%20%20%20%20%20C_out%2C%0A%20%20%20%20%20%20%20%20D_dir%2C%0A%20%20%20%20%20%20%20%20delays%2C%0A%20%20%20%20%20%20%20%20fs%2C%0A%20%20%20%20%20%20%20%20nfft%3Dnfft%2C%0A%20%20%20%20%20%20%20%20sos_filter%3Dsos_absorption%2C%20%20%23%20canonical%20(n_sections%2C%206%2C%20N)%20bank%0A%20%20%20%20%20%20%20%20shell%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20rir%20%3D%20pyFDN.flamo_time_response(model).squeeze()%5B%3Arir_len%5D%0A%20%20%20%20rir%20%2F%3D%20np.max(np.abs(rir))%20%2B%201e-300%0A%20%20%20%20print(f%22RIR%20computed%3A%20%7Brir_len%7D%20samples%2C%20peak%20at%20sample%20%7Bnp.argmax(np.abs(rir))%7D%22)%0A%20%20%20%20return%20model%2C%20rir%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Visualize%20DSP%20graph%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(model%2C%20pyFDN)%3A%0A%20%20%20%20pyFDN.plot_flamo_graph(model)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Impulse%20response%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20pyFDN%2C%20rir)%3A%0A%20%20%20%20pyFDN.plot_spectrogram(%0A%20%20%20%20%20%20%20%20rir%2C%20fs%2C%20title%3D%22FDN%20impulse%20response%20%E2%80%94%20time-frequency%20energy%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20mo%2C%20np%2C%20rir)%3A%0A%20%20%20%20mo.audio(np.asarray(rir)%2C%20fs)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20RT%20estimate%20vs%20target%0A%0A%20%20%20%20Estimate%20RT%20in%20octave%20bands%20(63%E2%80%938000%20Hz)%20by%20Butterworth%20bandpass%20filtering%0A%20%20%20%20and%20compare%20with%20the%20design%20target.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20go%2C%20pyFDN%2C%20rir%2C%20target_rt)%3A%0A%20%20%20%20rt_est%2C%20f_centre%20%3D%20pyFDN.estimate_rt_bands(rir%2C%20fs)%0A%0A%20%20%20%20%23%20target_rt%5B1%3A9%5D%20covers%20the%20same%208%20octave%20bands%20(63%E2%80%938k%20Hz)%0A%20%20%20%20fig_rt%20%3D%20go.Figure()%0A%20%20%20%20fig_rt.add_trace(%0A%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Df_centre%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dtarget_rt%5B1%3A9%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22lines%2Bmarkers%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Target%20RT%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line%3D%7B%22dash%22%3A%20%22dash%22%7D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20fig_rt.add_trace(%0A%20%20%20%20%20%20%20%20go.Scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Df_centre%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Drt_est%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mode%3D%22lines%2Bmarkers%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Estimated%20RT%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20fig_rt.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22RT%3A%20estimated%20vs%20target%22%2C%0A%20%20%20%20%20%20%20%20xaxis%3D%7B%22title%22%3A%20%22Frequency%20(Hz)%22%2C%20%22type%22%3A%20%22log%22%7D%2C%0A%20%20%20%20%20%20%20%20yaxis%3D%7B%22title%22%3A%20%22RT%20(s)%22%7D%2C%0A%20%20%20%20%20%20%20%20yaxis_range%3D%5B0%2C%20None%5D%2C%0A%20%20%20%20%20%20%20%20template%3D%22plotly_white%22%2C%0A%20%20%20%20%20%20%20%20height%3D380%2C%0A%20%20%20%20)%0A%20%20%20%20fig_rt.show()%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
199716877546781a510e23e46ec5f09c