%23%20gallery_category%3A%20Special%20FDNs%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%20Coupled%20Rooms%20FDN%20Example%0A%0A%20%20%20%20This%20example%20models%20**two%20acoustically%20coupled%20rooms**%20with%20a%20Feedback%20Delay%20Network%20(FDN)%3A%20one%20small%20room%20with%20a%20short%20reverberation%20time%20(RT)%2C%20one%20large%20room%20with%20a%20long%20RT.%20Each%20room%20is%20an%20independent%20FDN%20with%20**frequency-dependent%20RT**%20(first-order%20shelving%20absorption)%20and%20its%20own%20**output%20EQ**.%0A%0A%20%20%20%20The%20coupled%20FDN%20is%20assembled%20by%20simple%20**concatenation**%20of%20the%20two%20single-room%20FDNs%20(block-diagonal%20feedback%20matrix%2C%20stacked%20delays%20and%20absorption%20filters)%20plus%20a%20single%20**mixing%20matrix**%20that%20couples%20the%20two%20rooms.%0A%0A%20%20%20%20-%20**Sound%20source%3A**%20in%20the%20first%20room%20(short%20RT)%20only.%0A%20%20%20%20-%20**Receivers%3A**%20two%20outputs%2C%20one%20per%20room%20%E2%80%94%20channel%201%20is%20room%201%20(short%20RT)%2C%20channel%202%20is%20room%202%20(long%20RT).%0A%0A%20%20%20%20Translation%20of%20%60example_coupledRooms.m%60%20to%20Python%20using%20FLAMO.%0A%0A%20%20%20%20Based%20on%3A%0A%20%20%20%20%3E%20Das%2C%20O.%2C%20Abel%2C%20J.%20S.%20%26%20Canfield-Dafilou%2C%20E.%20K.%20*Delay%20Network%20Architectures%20For%20Room%20And%20Coupled%20Space%20Modeling*.%20DAFx2020%20(2020).%0A%0A%20%20%20%20**Original%20MATLAB%3A**%20(c)%20Sebastian%20Jiro%20Schlecht%2C%202020.%20**Python%3A**%20Facundo%20Franchino%2C%202025.%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%20from%20scipy.linalg%20import%20block_diag%0A%0A%20%20%20%20import%20pyFDN%0A%0A%20%20%20%20return%20block_diag%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%20Single-room%20FDNs%0A%0A%20%20%20%20Build%20each%20room%20with%20%60pyFDN.fdn_build_gallery%60.%20Both%20rooms%20have%0A%20%20%20%20frequency-dependent%20reverberation%20(RT%20at%20DC%20and%20at%20Nyquist%2C%20realised%20as%0A%20%20%20%20per-line%20first-order%20shelving%20absorption)%20and%20a%20different%20per-room%20output%20EQ.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(pyFDN)%3A%0A%20%20%20%20fs%20%3D%2048000%0A%20%20%20%20nfft%20%3D%202**17%0A%0A%20%20%20%20%23%20Equal%20room%20sizes%20keep%20the%20coupling%20matrix%20a%20simple%20block%20rotation.%0A%20%20%20%20N1%20%3D%20N2%20%3D%2012%0A%20%20%20%20N%20%3D%20N1%20%2B%20N2%0A%20%20%20%20ix1%20%3D%20slice(0%2C%20N1)%20%20%23%20delay%20lines%20of%20room%201%0A%20%20%20%20ix2%20%3D%20slice(N1%2C%20N)%20%20%23%20delay%20lines%20of%20room%202%0A%20%20%20%20num_input%20%3D%201%0A%20%20%20%20num_output%20%3D%202%0A%0A%20%20%20%20%23%20Room%201%3A%20small%2C%20short%20RT%2C%20brighter%20(high%20frequencies%20decay%20faster)%3B%20dark%20EQ.%0A%20%20%20%20room1%20%3D%20pyFDN.fdn_build_gallery(%0A%20%20%20%20%20%20%20%20N1%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20delay_range%3D(400%2C%20800)%2C%0A%20%20%20%20%20%20%20%20rt%3D0.825%2C%0A%20%20%20%20%20%20%20%20rt_nyquist%3D0.6%2C%0A%20%20%20%20%20%20%20%20rt_crossover%3D1000.0%2C%0A%20%20%20%20%20%20%20%20post_eq_db_dc%3D0.0%2C%0A%20%20%20%20%20%20%20%20post_eq_db_nyquist%3D-8.0%2C%0A%20%20%20%20%20%20%20%20post_eq_crossover%3D1000.0%2C%0A%20%20%20%20%20%20%20%20io_type%3D%22ones%22%2C%0A%20%20%20%20%20%20%20%20rng%3D5%2C%0A%20%20%20%20)%0A%20%20%20%20%23%20Room%202%3A%20large%2C%20long%20RT%3B%20different%20(brighter)%20EQ.%0A%20%20%20%20room2%20%3D%20pyFDN.fdn_build_gallery(%0A%20%20%20%20%20%20%20%20N2%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20delay_range%3D(1100%2C%202600)%2C%0A%20%20%20%20%20%20%20%20rt%3D4.2%2C%0A%20%20%20%20%20%20%20%20rt_nyquist%3D1.5%2C%0A%20%20%20%20%20%20%20%20rt_crossover%3D2000.0%2C%0A%20%20%20%20%20%20%20%20post_eq_db_dc%3D2.0%2C%0A%20%20%20%20%20%20%20%20post_eq_db_nyquist%3D-12.0%2C%0A%20%20%20%20%20%20%20%20post_eq_crossover%3D2000.0%2C%0A%20%20%20%20%20%20%20%20io_type%3D%22ones%22%2C%0A%20%20%20%20%20%20%20%20rng%3D6%2C%0A%20%20%20%20)%0A%20%20%20%20return%20N%2C%20fs%2C%20ix1%2C%20ix2%2C%20nfft%2C%20num_input%2C%20num_output%2C%20room1%2C%20room2%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%20Concatenate%20and%20couple%0A%0A%20%20%20%20The%20coupled%20FDN%20is%20the%20concatenation%20of%20the%20two%20rooms%20%E2%80%94%20block-diagonal%0A%20%20%20%20feedback%20matrix%2C%20stacked%20delays%20and%20absorption%20filters%2C%20source%20in%20room%201%2C%0A%20%20%20%20one%20output%20per%20room%20%E2%80%94%20composed%20with%20a%20single%20orthogonal%20**mixing%20matrix**%0A%20%20%20%20that%20couples%20the%20rooms%20by%20a%20coupling%20angle.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(N%2C%20block_diag%2C%20ix1%2C%20ix2%2C%20np%2C%20num_input%2C%20num_output%2C%20room1%2C%20room2)%3A%0A%20%20%20%20coupling%20%3D%200.2%20%20%23%20coupling%20angle%20between%20the%20two%20rooms%20(0%20%3D%20uncoupled)%0A%0A%20%20%20%20%23%20Concatenate%20the%20two%20single-room%20FDNs.%0A%20%20%20%20delays%20%3D%20np.concatenate(%5Broom1.delays%2C%20room2.delays%5D)%0A%20%20%20%20A_rooms%20%3D%20block_diag(room1.A%2C%20room2.A)%0A%20%20%20%20attenuation_sos%20%3D%20np.concatenate(%5Broom1.filters%2C%20room2.filters%5D%2C%20axis%3D2)%0A%20%20%20%20post_eq_sos%20%3D%20np.concatenate(%5Broom1.post_eq%2C%20room2.post_eq%5D%2C%20axis%3D2)%0A%0A%20%20%20%20%23%20Mixing%20matrix%3A%20orthogonal%20block%20rotation%20coupling%20room%201%20with%20room%202.%0A%20%20%20%20eye%20%3D%20np.eye(room1.delays.size)%0A%20%20%20%20mixing_matrix%20%3D%20np.block(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%5Bnp.cos(coupling)%20*%20eye%2C%20np.sin(coupling)%20*%20eye%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B-np.sin(coupling)%20*%20eye%2C%20np.cos(coupling)%20*%20eye%5D%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%20%20%20%20A%20%3D%20mixing_matrix%20%40%20A_rooms%0A%0A%20%20%20%20%23%20Source%20in%20room%201%20only%3B%20one%20receiver%20per%20room.%0A%20%20%20%20B%20%3D%20np.zeros((N%2C%20num_input))%0A%20%20%20%20B%5Bix1%5D%20%3D%20room1.B%0A%20%20%20%20C%20%3D%20np.zeros((num_output%2C%20N))%0A%20%20%20%20C%5B0%2C%20ix1%5D%20%3D%20room1.C%5B0%5D%0A%20%20%20%20C%5B1%2C%20ix2%5D%20%3D%20room2.C%5B0%5D%0A%20%20%20%20D%20%3D%20np.zeros((num_output%2C%20num_input))%0A%20%20%20%20return%20A%2C%20B%2C%20C%2C%20D%2C%20attenuation_sos%2C%20delays%2C%20post_eq_sos%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%20Build%20FDN%20(FLAMO)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(A%2C%20B%2C%20C%2C%20D%2C%20attenuation_sos%2C%20delays%2C%20fs%2C%20nfft%2C%20post_eq_sos%2C%20pyFDN)%3A%0A%20%20%20%20model%20%3D%20pyFDN.dss_to_flamo(%0A%20%20%20%20%20%20%20%20A%2C%0A%20%20%20%20%20%20%20%20B%2C%0A%20%20%20%20%20%20%20%20C%2C%0A%20%20%20%20%20%20%20%20D%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%3Dattenuation_sos%2C%0A%20%20%20%20%20%20%20%20output_filter%3Dpost_eq_sos%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(model%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%20Impulse%20response%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%20ir%20%3D%20pyFDN.flamo_time_response(model).squeeze()%0A%20%20%20%20return%20(ir%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%20FDN%20signal-flow%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%20FDN%20parameters%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(A%2C%20B%2C%20C%2C%20D%2C%20attenuation_sos%2C%20delays%2C%20fs%2C%20post_eq_sos%2C%20pyFDN)%3A%0A%20%20%20%20pyFDN.plot_fdn_parameter(%0A%20%20%20%20%20%20%20%20delays%2C%0A%20%20%20%20%20%20%20%20A%2C%0A%20%20%20%20%20%20%20%20B%2C%0A%20%20%20%20%20%20%20%20C%2C%0A%20%20%20%20%20%20%20%20D%2C%0A%20%20%20%20%20%20%20%20attenuation_sos%3Dattenuation_sos%2C%0A%20%20%20%20%20%20%20%20post_eq_sos%3Dpost_eq_sos%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20title%3D%22Coupled%20Rooms%20FDN%20Parameters%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%20Plots%20and%20audio%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20ir%2C%20pyFDN)%3A%0A%20%20%20%20pyFDN.plot_impulse_response(%0A%20%20%20%20%20%20%20%20ir%5B%3A%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20ir%5B%3A%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20labels%3D%5B%22Room%201%20(source)%22%2C%20%22Room%202%22%5D%2C%0A%20%20%20%20%20%20%20%20title%3D%22Coupled%20Rooms%20Impulse%20Response%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20fig_edc%20%3D%20pyFDN.plot_edc(%0A%20%20%20%20%20%20%20%20ir%5B%3A%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20ir%5B%3A%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20labels%3D%5B%22Room%201%22%2C%20%22Room%202%22%5D%2C%0A%20%20%20%20%20%20%20%20title%3D%22Energy%20decay%20curves%22%2C%0A%20%20%20%20)%0A%20%20%20%20fig_edc.update_xaxes(range%3D%5B0%2C%20min(2%2C%20len(ir)%20%2F%20fs)%5D)%0A%20%20%20%20fig_edc.update_yaxes(range%3D%5B-40%2C%2015%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20ir%2C%20mo)%3A%0A%20%20%20%20mo.vstack(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%22Room%201%20RIR%3A%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.audio(ir%5B%3A%2C%200%5D%2C%20rate%3Dfs)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%22Room%202%20RIR%3A%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.audio(ir%5B%3A%2C%201%5D%2C%20rate%3Dfs)%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%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
ac635593e369d79de4f8b120c1ea135c