%23%20gallery_category%3A%20Allpass%20FDNs%0A%23%20gallery_title%3A%20Schroeder%20allpass%20in%20a%20feedback%20loop%0A%23%20gallery_description%3A%20Place%20a%20Schroeder%20allpass%20cascade%20inside%20a%20recursive%20loop%20and%20examine%20the%20resulting%20reverberator.%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.24.0%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%2C%20pyFDN)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%23%20FDN%20with%20Schroeder%20allpass%20filters%20in%20the%20loop%0A%0A%20%20%20%20Schroeder%20allpass%20filters%20can%20be%20placed%20**behind%20the%20delays**%20in%20the%20FDN%20loop%20to%20increase%20echo%20density.%20The%20allpass%20cascade%20on%20its%20own%20is%20rendered%20in%20the%20time%20domain%3B%20the%20recursive%20networks%20are%20rendered%20with%20**FLAMO**%20(gain%20and%20delay%20modules).%0A%0A%20%20%20%20Steps%3A%0A%20%20%20%201.%20Build%20a%20**MIMO%20parallel%20Schroeder%20allpass**%20(block-diagonal).%0A%20%20%20%202.%20Build%20a%20**vanilla%20FDN%20(SISO)**.%0A%20%20%20%203.%20Place%20the%20**Schroeder%20allpass%20behind%20the%20delays**%20of%20the%20FDN%20and%20render.%0A%0A%20%20%20%20%3E%20Reference%3A%20*%7BpyFDN.paper_link(%22Vaananen1997EfficientParametricReverberator%22)%7D*.%0A%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%0A%20%20%20%20import%20pyFDN%0A%0A%20%20%20%20np.random.seed(6)%0A%20%20%20%20fs%20%3D%2048000%0A%20%20%20%20nfft%20%3D%202**16%0A%20%20%20%20return%20fs%2C%20nfft%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%201.%20MIMO%20parallel%20Schroeder%20allpass%0A%0A%20%20%20%20Build%20N%20parallel%20SISO%20Schroeder%20allpasses%20(block-diagonal%20DSS)%2C%20render%20the%20impulse%20response%20in%20the%20time%20domain%20with%20**dss_to_impz**%2C%20and%20play%20it.%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%20N%20%3D%204%0A%20%20%20%20sections_per_channel%20%3D%202%0A%20%20%20%20ir_len_schroeder%20%3D%202**10%0A%0A%20%20%20%20allpass_delays%20%3D%20np.random.randint(30%2C%20200%2C%20size%3D(N%2C%20sections_per_channel))%0A%20%20%20%20allpass_gains%20%3D%20np.full(allpass_delays.shape%2C%200.7)%0A%0A%20%20%20%20A_list%2C%20B_list%2C%20C_list%2C%20D_list%2C%20delays_list%20%3D%20%5B%5D%2C%20%5B%5D%2C%20%5B%5D%2C%20%5B%5D%2C%20%5B%5D%0A%20%20%20%20for%20i%20in%20range(N)%3A%0A%20%20%20%20%20%20%20%20Ai%2C%20bi%2C%20ci%2C%20di%20%3D%20pyFDN.series_allpass(allpass_gains%5Bi%5D)%0A%20%20%20%20%20%20%20%20A_list.append(Ai)%0A%20%20%20%20%20%20%20%20B_list.append(bi)%0A%20%20%20%20%20%20%20%20C_list.append(ci)%0A%20%20%20%20%20%20%20%20D_list.append(di)%0A%20%20%20%20%20%20%20%20delays_list.append(allpass_delays%5Bi%5D)%0A%0A%20%20%20%20from%20scipy.linalg%20import%20block_diag%0A%0A%20%20%20%20A_schroeder%20%3D%20block_diag(*A_list)%0A%20%20%20%20B_schroeder%20%3D%20block_diag(*B_list)%0A%20%20%20%20C_schroeder%20%3D%20block_diag(*C_list)%0A%20%20%20%20D_schroeder%20%3D%20block_diag(*D_list)%0A%20%20%20%20delays_schroeder%20%3D%20np.concatenate(delays_list)%0A%0A%20%20%20%20ir_schroeder%20%3D%20pyFDN.dss_to_impz(%0A%20%20%20%20%20%20%20%20ir_len_schroeder%2C%0A%20%20%20%20%20%20%20%20delays_schroeder%2C%0A%20%20%20%20%20%20%20%20A_schroeder%2C%0A%20%20%20%20%20%20%20%20B_schroeder%2C%0A%20%20%20%20%20%20%20%20C_schroeder%2C%0A%20%20%20%20%20%20%20%20D_schroeder%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20A_schroeder%2C%0A%20%20%20%20%20%20%20%20B_schroeder%2C%0A%20%20%20%20%20%20%20%20C_schroeder%2C%0A%20%20%20%20%20%20%20%20D_schroeder%2C%0A%20%20%20%20%20%20%20%20N%2C%0A%20%20%20%20%20%20%20%20delays_schroeder%2C%0A%20%20%20%20%20%20%20%20ir_schroeder%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20A_schroeder%2C%0A%20%20%20%20B_schroeder%2C%0A%20%20%20%20C_schroeder%2C%0A%20%20%20%20D_schroeder%2C%0A%20%20%20%20delays_schroeder%2C%0A%20%20%20%20ir_schroeder%2C%0A%20%20%20%20pyFDN%2C%0A)%3A%0A%20%20%20%20print(ir_schroeder.shape)%0A%20%20%20%20print(delays_schroeder)%0A%20%20%20%20pyFDN.plot_system_matrix(A_schroeder%2C%20B_schroeder%2C%20C_schroeder%2C%20D_schroeder)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20ir_schroeder%2C%20mo%2C%20np%2C%20pyFDN)%3A%0A%20%20%20%20ir_schroeder_channel%20%3D%20ir_schroeder%5B%3A%2C%200%2C%200%5D%0A%20%20%20%20_fig%20%3D%20pyFDN.plot_impulse_response(%0A%20%20%20%20%20%20%20%20ir_schroeder_channel%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20title%3D%22MIMO%20parallel%20Schroeder%20allpass%20%E2%80%94%20impulse%20response%20(in0%E2%86%92out0)%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20mo.vstack(%5B_fig%2C%20mo.audio(np.asarray(ir_schroeder_channel)%2C%20fs)%5D)%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%202.%20Vanilla%20FDN%20(SISO)%0A%0A%20%20%20%20Let%20**fdn_build_gallery**%20assemble%20the%20whole%20thing%20--%20random%20orthogonal%20feedback%20matrix%2C%20delays%2C%20B%2FC%2FD%2C%20and%20the%20per-delay-line%20absorption%20matching%20the%20requested%20reverberation%20time%20--%20into%20a%20single%20**FDNBuild**.%20Render%20it%20with%20**build_to_flamo**%20and%20play.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(N%2C%20fs%2C%20nfft%2C%20pyFDN)%3A%0A%20%20%20%20fdn_build%20%3D%20pyFDN.fdn_build_gallery(%0A%20%20%20%20%20%20%20%20N%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20delay_range%3D(600%2C%203900)%2C%0A%20%20%20%20%20%20%20%20rt%3D2.0%2C%0A%20%20%20%20%20%20%20%20rt_nyquist%3D0.7%2C%0A%20%20%20%20%20%20%20%20rng%3D6%2C%0A%20%20%20%20)%0A%0A%20%20%20%20model_fdn%20%3D%20pyFDN.build_to_flamo(fdn_build%2C%20nfft%3Dnfft)%0A%20%20%20%20ir_fdn%20%3D%20pyFDN.flamo_time_response(model_fdn).squeeze()%0A%20%20%20%20return%20fdn_build%2C%20ir_fdn%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20ir_fdn%2C%20mo%2C%20np%2C%20pyFDN)%3A%0A%20%20%20%20_fig%20%3D%20pyFDN.plot_impulse_response(%0A%20%20%20%20%20%20%20%20ir_fdn%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20title%3D%22Vanilla%20FDN%20(SISO)%20%E2%80%94%20impulse%20response%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20mo.vstack(%5B_fig%2C%20mo.audio(np.asarray(ir_fdn)%2C%20fs)%5D)%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%203.%20FDN%20with%20Schroeder%20allpass%20behind%20the%20delays%0A%0A%20%20%20%20Reuse%20the%20two%20models%20above%3A%20take%20the%20**vanilla%20FDN**%20(the%20%60fdn_build%60)%20and%20the%20**MIMO%20Schroeder%20allpass**%20(A_schroeder%2C%20B_schroeder%2C%20C_schroeder%2C%20D_schroeder%2C%20delays_schroeder).%20Build%20the%20Schroeder%20as%20a%20core%20with%20**dss_to_flamo(...%2C%20shell%3DFalse)**%20and%20pass%20it%20in%20the%20**post_delay**%20hook%20of%20**build_to_flamo**%20--%20extra%20hook%20modules%20are%20appended%20after%20whatever%20the%20build%20already%20carries%2C%20so%20the%20loop%20becomes%20delay%20-%3E%20absorption%20-%3E%20Schroeder.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20A_schroeder%2C%0A%20%20%20%20B_schroeder%2C%0A%20%20%20%20C_schroeder%2C%0A%20%20%20%20D_schroeder%2C%0A%20%20%20%20delays_schroeder%2C%0A%20%20%20%20fdn_build%2C%0A%20%20%20%20fs%2C%0A%20%20%20%20nfft%2C%0A%20%20%20%20pyFDN%2C%0A)%3A%0A%20%20%20%20%23%20Schroeder%20core%20(4-in%2C%204-out)%20from%20section%201%3B%20append%20to%20FDN%20forward%20path%0A%20%20%20%20schroeder_core%20%3D%20pyFDN.dss_to_flamo(%0A%20%20%20%20%20%20%20%20A_schroeder%2C%0A%20%20%20%20%20%20%20%20B_schroeder%2C%0A%20%20%20%20%20%20%20%20C_schroeder%2C%0A%20%20%20%20%20%20%20%20D_schroeder%2C%0A%20%20%20%20%20%20%20%20delays_schroeder%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%20shell%3DFalse%2C%0A%20%20%20%20)%0A%20%20%20%20model_fdn_allpass%20%3D%20pyFDN.build_to_flamo(%0A%20%20%20%20%20%20%20%20fdn_build%2C%0A%20%20%20%20%20%20%20%20nfft%3Dnfft%2C%0A%20%20%20%20%20%20%20%20post_delay%3Dschroeder_core%2C%0A%20%20%20%20)%0A%20%20%20%20ir_fdn_allpass%20%3D%20pyFDN.flamo_time_response(model_fdn_allpass).squeeze()%0A%0A%20%20%20%20pyFDN.plot_flamo_graph(model_fdn_allpass)%0A%20%20%20%20return%20(ir_fdn_allpass%2C)%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20ir_fdn_allpass%2C%20mo%2C%20np%2C%20pyFDN)%3A%0A%20%20%20%20_fig%20%3D%20pyFDN.plot_impulse_response(%0A%20%20%20%20%20%20%20%20ir_fdn_allpass%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20title%3D%22FDN%20with%20Schroeder%20allpass%20behind%20delays%20%E2%80%94%20impulse%20response%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20mo.vstack(%5B_fig%2C%20mo.audio(np.asarray(ir_fdn_allpass)%2C%20fs)%5D)%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
0e3bea29d9e139e48a51839e432a0fb5