%23%20gallery_category%3A%20Allpass%20FDNs%0A%23%20gallery_title%3A%20Schroeder%20allpass%20reverberator%0A%23%20gallery_description%3A%20Build%20the%20classic%20Schroeder%20series-allpass%20reverberator%20and%20verify%20that%20it%20is%20uniallpass%20--%20allpass%20whatever%20the%20delays.%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%20Schroeder's%20Series%20Allpass%20FDN%0A%0A%20%20%20%20Example%20for%20Schroeder's%20series%20(cascade)%20allpass%3A%20a%20cascade%20of%20first-order%20allpass%20sections%20realized%20as%20an%20FDN%20with%20diagonal%20feedback%20matrix.%20SISO.%0A%0A%20%20%20%20**Reference%3A**%20*%7BpyFDN.paper_link(%22Schroeder1961ColorlessArtificialReverberation%22)%7D*.%0A%0A%20%20%20%20See%20also%3A%20%7BpyFDN.paper_link(%22Allpass_Feedback_Delay_Networks%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(42)%0A%20%20%20%20return%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%20Build%20series%20allpass%20FDN%0A%0A%20%20%20%20Use%20a%20vector%20of%20gains%20**g**%20(one%20per%20section).%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%206%0A%20%20%20%20g%20%3D%20np.array(%5B0.3%2C%200.4%2C%200.5%2C%200.6%2C%200.7%2C%200.8%5D)%0A%20%20%20%20fs%20%3D%2048000%0A%20%20%20%20delays%20%3D%20np.random.randint(200%2C%201000%2C%20size%3DN)%0A%0A%20%20%20%20A%2C%20B%2C%20C%2C%20D%20%3D%20pyFDN.series_allpass(g)%0A%20%20%20%20return%20A%2C%20B%2C%20C%2C%20D%2C%20delays%2C%20fs%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%20The%20system%20matrix%0A%0A%20%20%20%20%60%5BA%2C%20B%3B%20C%2C%20D%5D%60%20as%20one%20block%20heatmap.%20%60series_allpass%60%20puts%20the%20gains%20on%20the%20diagonal%20of%20%60A%60%20and%20the%20feedforward%20taps%20in%20the%20strictly%20triangular%20part%20%E2%80%94%20the%20cascade%20structure%20read%20off%20directly.%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%20pyFDN)%3A%0A%20%20%20%20pyFDN.plot_system_matrix(A%2C%20B%2C%20C%2C%20D)%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%20Test%3A%20uniallpass%0A%0A%20%20%20%20Check%20that%20the%20FDN%20is%20uniallpass%20(lossless%20with%20diagonal%20Lyapunov%20matrix).%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%20pyFDN)%3A%0A%20%20%20%20is_uniallpass%2C%20_P%20%3D%20pyFDN.is_uniallpass(A%2C%20B%2C%20C%2C%20D)%0A%20%20%20%20assert%20is_uniallpass%2C%20%22Expected%20uniallpass%22%0A%20%20%20%20print(%22Uniallpass%3A%20OK%22)%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%0A%20%20%20%20Two%20seconds%20of%20the%20cascade's%20response.%20Each%20section%20recirculates%20at%20its%20own%20delay%2C%20and%20because%20the%20six%20delays%20are%20unequal%20their%20echo%20patterns%20interleave%20instead%20of%20landing%20on%20top%20of%20each%20other%2C%20so%20the%20response%20fills%20in%20over%20time.%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%20delays%2C%20fs%2C%20pyFDN)%3A%0A%20%20%20%20impulse_response%20%3D%20pyFDN.dss_to_impz(2%20*%20fs%2C%20delays%2C%20A%2C%20B%2C%20C%2C%20D).squeeze()%0A%0A%20%20%20%20pyFDN.plot_impulse_response(%0A%20%20%20%20%20%20%20%20impulse_response%2C%0A%20%20%20%20%20%20%20%20fs%3Dfs%2C%0A%20%20%20%20%20%20%20%20title%3D%22Schroeder%20series%20allpass%20%E2%80%94%20impulse%20response%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(impulse_response%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%20Spectrogram%0A%0A%20%20%20%20The%20flat%20magnitude%20response%20is%20the%20allpass%20property%3B%20what%20the%20ear%20objects%20to%20is%20the%20structure%20*in%20time*%2C%20which%20the%20spectrogram%20shows%20and%20the%20magnitude%20response%20cannot.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20impulse_response%2C%20mo%2C%20pyFDN)%3A%0A%20%20%20%20_fig%20%3D%20pyFDN.plot_spectrogram(%0A%20%20%20%20%20%20%20%20impulse_response%2C%20fs%2C%20title%3D%22Schroeder%20series%20allpass%20%E2%80%94%20spectrogram%22%0A%20%20%20%20)%0A%0A%20%20%20%20mo.vstack(%5B_fig%2C%20mo.audio(impulse_response%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
4777a66c26a2539e339d56fdb342ba54