%23%20gallery_category%3A%20Special%20FDNs%0A%23%20gallery_description%3A%20Process%20music%20through%20an%20FDN%20whose%20orthogonal%20feedback%20matrix%20changes%20over%20time%20at%20selectable%20modulation%20rates.%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)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Time-varying%20FDN%0A%0A%20%20%20%20A%20static%20FDN%20has%20fixed%20modes%2C%20and%20a%20sustained%20tone%20excites%20the%20same%20few%20of%20them%20for%20as%20long%20as%20it%20lasts%20%E2%80%94%20which%20is%20what%20%22metallic%22%20or%20%22ringing%22%20describes.%20Modulating%20the%20feedback%20matrix%20moves%20the%20modes%20while%20the%20sound%20decays%2C%20so%20no%20single%20one%20is%20driven%20long%20enough%20to%20stand%20out.%0A%0A%20%20%20%20The%20modulation%20costs%20something%20too%3A%20move%20the%20matrix%20too%20fast%20and%20the%20shifting%20pitch%20becomes%20audible%20as%20chorusing.%20This%20notebook%20runs%20one%20musical%20phrase%20through%20three%20settings%20%E2%80%94%20none%2C%20slow%2C%20fast%20%E2%80%94%20so%20the%20trade%20is%20audible%20in%20the%20same%20signal.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%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%20Reference%3A%20*%7BpyFDN.paper_link(%22Schlecht2015PracticalConsiderationsTimevarying%22)%7D*.%20%3Cbr%2F%3E%0A%20%20%20%20Reference%3A%20*%7BpyFDN.paper_link(%22Schlecht2015TimevaryingFeedbackMatrices%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%20%20%20%20import%20scipy.linalg%20as%20la%0A%0A%20%20%20%20import%20pyFDN%0A%20%20%20%20from%20pyFDN%20import%20td%0A%20%20%20%20from%20pyFDN.generate.random_orthogonal%20import%20random_orthogonal%0A%20%20%20%20from%20pyFDN.process%20import%20process_fdn%0A%0A%20%20%20%20return%20la%2C%20np%2C%20process_fdn%2C%20pyFDN%2C%20random_orthogonal%2C%20td%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%20dry%20signal%0A%0A%20%20%20%20Either%20a%20two-tone%20sine%20that%20stops%20two%20seconds%20before%20the%20end%20%E2%80%94%20the%20tail%20is%20then%20all%20reverb%2C%20which%20is%20where%20modulation%20artefacts%20are%20easiest%20to%20hear%20%E2%80%94%20or%20a%20synth%20phrase.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20sound_selection%20%3D%20mo.ui.dropdown(%0A%20%20%20%20%20%20%20%20options%3D%5B%22sine%22%2C%20%22melody%22%5D%2C%0A%20%20%20%20%20%20%20%20value%3D%22melody%22%2C%0A%20%20%20%20%20%20%20%20label%3D%22Sound%22%2C%0A%20%20%20%20)%0A%20%20%20%20mo.output.replace(sound_selection)%0A%20%20%20%20return%20(sound_selection%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20np%2C%20pyFDN%2C%20sound_selection)%3A%0A%20%20%20%20np.random.seed(1)%0A%0A%20%20%20%20%23%20init%20source%20signal%0A%20%20%20%20mode%20%3D%20sound_selection.value%0A%0A%20%20%20%20if%20mode%20%3D%3D%20%22sine%22%3A%0A%20%20%20%20%20%20%20%20fs%20%3D%2048000%0A%20%20%20%20%20%20%20%20duration%20%3D%204%0A%20%20%20%20%20%20%20%20time%20%3D%20np.linspace(0%2C%20duration%2C%20duration%20*%20fs)%5B%3A%2C%20None%5D%0A%0A%20%20%20%20%20%20%20%20synth1%20%3D%200.5%20*%20np.sin(time%20*%20440%20*%202%20*%20np.pi)%0A%20%20%20%20%20%20%20%20synth2%20%3D%200.5%20*%20np.sin(time%20*%20660%20*%202%20*%20np.pi)%0A%0A%20%20%20%20%20%20%20%20%23%20Concatenate%20columns%20horizontally%0A%20%20%20%20%20%20%20%20synth%20%3D%20synth1%20%2B%20synth2%0A%20%20%20%20%20%20%20%20synth%5B-2%20*%20fs%20%3A%2C%20%3A%5D%20%3D%200.0%0A%0A%20%20%20%20elif%20mode%20%3D%3D%20%22melody%22%3A%0A%20%20%20%20%20%20%20%20synth%2C%20fs%20%3D%20pyFDN.load_audio(%22synth_dry%22)%0A%0A%20%20%20%20%20%20%20%20print(f%22Loaded%20%7Blen(synth)%7D%20samples%20at%20%7Bfs%7D%20Hz%20(%7Blen(synth)%20%2F%20fs%3A.2f%7D%20s)%22)%0A%0A%20%20%20%20%20%20%20%20samples%20%3D%20np.arange(len(synth))%0A%20%20%20%20%20%20%20%20time%20%3D%20(samples%20%2F%20fs)%20*%201000%20*%201000%0A%0A%20%20%20%20_audio_src%20%3D%20synth.T%20if%20synth.ndim%20%3D%3D%202%20else%20synth%0A%20%20%20%20mo.audio(_audio_src%2C%20fs)%0A%20%20%20%20return%20fs%2C%20synth%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%20FDN%0A%0A%20%20%20%20Eight%20delay%20lines%2C%20one%20input%20and%20two%20outputs%20for%20a%20stereo%20return.%20The%20input%20and%20output%20gains%20are%20orthonormalised%20so%20neither%20adds%20any%20colouration%20of%20its%20own%2C%20leaving%20the%20loop%20responsible%20for%20everything%20that%20is%20heard.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(la%2C%20np%2C%20random_orthogonal)%3A%0A%20%20%20%20N%20%3D%208%0A%20%20%20%20num_input%20%3D%201%0A%20%20%20%20num_output%20%3D%202%0A%0A%20%20%20%20input_gain%20%3D%20la.orth(np.random.randn(N%2C%20num_input))%0A%0A%20%20%20%20random_matrix%20%3D%20np.random.randn(num_output%2C%20N)%0A%20%20%20%20output_gain%20%3D%20la.orth(random_matrix.T).T%0A%0A%20%20%20%20direct%20%3D%20np.zeros((num_output%2C%20num_input))%0A%20%20%20%20delays%20%3D%20np.random.randint(750%2C%202001%2C%20size%3DN)%5BNone%2C%20%3A%5D%0A%0A%20%20%20%20feedback_matrix%20%3D%20random_orthogonal(N)%0A%20%20%20%20return%20N%2C%20delays%2C%20direct%2C%20feedback_matrix%2C%20input_gain%2C%20output_gain%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%0A%0A%20%20%20%20A%20one-pole%20filter%20per%20delay%20line%2C%20giving%204%20s%20of%20reverberation%20at%20DC%20falling%20to%201%20s%20at%20Nyquist.%20Absorption%20is%20what%20makes%20the%20decay%20finite%3B%20the%20modulation%20below%20changes%20*which*%20modes%20decay%2C%20not%20how%20fast.%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%20td)%3A%0A%20%20%20%20RT_DC%20%3D%204%20%20%23%20seconds%0A%20%20%20%20RT_NY%20%3D%201%20%20%23%20seconds%0A%0A%20%20%20%20coeffs%20%3D%20pyFDN.decay_to_one_pole(RT_DC%2C%20RT_NY%2C%20delays%2C%20fs)%0A%0A%20%20%20%20absorption%20%3D%20td.SOSBank(coeffs)%0A%20%20%20%20return%20(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%20Three%20modulation%20rates%0A%0A%20%20%20%20%60td.TimeVaryingMatrix%60%20rotates%20the%20feedback%20matrix%20continuously%2C%20and%20takes%20a%20modulation%20frequency%2C%20an%20amplitude%20and%20a%20spread%20over%20the%20delay%20lines.%20The%20same%20phrase%20is%20rendered%20three%20times%3A%20static%2C%201%20Hz%2C%20and%2010%20Hz.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20N%2C%0A%20%20%20%20absorption%2C%0A%20%20%20%20delays%2C%0A%20%20%20%20direct%2C%0A%20%20%20%20feedback_matrix%2C%0A%20%20%20%20fs%2C%0A%20%20%20%20input_gain%2C%0A%20%20%20%20output_gain%2C%0A%20%20%20%20process_fdn%2C%0A%20%20%20%20synth%2C%0A%20%20%20%20td%2C%0A)%3A%0A%20%20%20%20matrix_types%20%3D%20%5B%22no_variation%22%2C%20%22slow_variation%22%2C%20%22fast_variation%22%5D%0A%0A%20%20%20%20reverbed_synth%20%3D%20%7B%7D%0A%0A%20%20%20%20for%20matrix_type%20in%20matrix_types%3A%0A%20%20%20%20%20%20%20%20if%20matrix_type%20%3D%3D%20%22no_variation%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20modulation_frequency%20%3D%200%20%20%23%20hz%0A%20%20%20%20%20%20%20%20%20%20%20%20modulation_amplitude%20%3D%200.0%0A%20%20%20%20%20%20%20%20%20%20%20%20spread%20%3D%200%0A%0A%20%20%20%20%20%20%20%20elif%20matrix_type%20%3D%3D%20%22slow_variation%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20modulation_frequency%20%3D%201.0%20%20%23%20hz%0A%20%20%20%20%20%20%20%20%20%20%20%20modulation_amplitude%20%3D%203.0%0A%20%20%20%20%20%20%20%20%20%20%20%20spread%20%3D%200.3%0A%0A%20%20%20%20%20%20%20%20elif%20matrix_type%20%3D%3D%20%22fast_variation%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20modulation_frequency%20%3D%2010%20%20%23%20hz%0A%20%20%20%20%20%20%20%20%20%20%20%20modulation_amplitude%20%3D%201.1%0A%20%20%20%20%20%20%20%20%20%20%20%20spread%20%3D%200.7%0A%0A%20%20%20%20%20%20%20%20tv_matrix%20%3D%20td.TimeVaryingMatrix(%0A%20%20%20%20%20%20%20%20%20%20%20%20N%2C%20modulation_frequency%2C%20modulation_amplitude%2C%20fs%2C%20spread%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20reverbed_synth%5Bmatrix_type%5D%20%3D%20process_fdn(%0A%20%20%20%20%20%20%20%20%20%20%20%20synth%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20delays%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20feedback_matrix%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20input_gain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_gain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20direct%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20post_delay%3Dabsorption%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20post_matrix%3Dtv_matrix%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20return%20matrix_types%2C%20reverbed_synth%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%20What%20the%20modulation%20does%20to%20the%20spectrum%0A%0A%20%20%20%20Read%20the%20three%20spectrograms%20downwards.%20The%20static%20FDN%20leaves%20horizontal%20ridges%20where%20individual%20modes%20sustain%3B%20slow%20modulation%20smears%20them%3B%20fast%20modulation%20broadens%20them%20into%20bands%2C%20which%20is%20the%20point%20at%20which%20the%20movement%20starts%20to%20become%20audible%20in%20its%20own%20right.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20matrix_types%2C%20mo%2C%20pyFDN%2C%20reverbed_synth)%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%20pyFDN.plot_spectrogram(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20reverbed_synth%5Bname%5D%5B%3A%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fs%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nperseg%3D2048%20*%208%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20noverlap%3D2048%20*%201%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3Df%22%7Bname%7D%20%E2%80%94%20spectrogram%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20colorscale%3D%22Magma%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20height%3D350%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20name%20in%20matrix_types%0A%20%20%20%20%20%20%20%20%5D%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%20Listen%0A%0A%20%20%20%20The%20ringing%20of%20the%20static%20version%20and%20the%20chorusing%20of%20the%20fast%20one%20are%20both%20clearest%20in%20the%20tail%2C%20once%20the%20dry%20signal%20has%20stopped.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fs%2C%20matrix_types%2C%20mo%2C%20reverbed_synth)%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.vstack(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5Bmo.md(f%22**%7Bname%7D**%22)%2C%20mo.audio(src%3Dreverbed_synth%5Bname%5D.T%2C%20rate%3Dfs)%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20name%20in%20matrix_types%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
ba3822fac90ee114c0d525b5caa50332