pyFDN 0.4.0__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. {pyfdn-0.4.0 → pyfdn-0.4.2}/HISTORY.rst +51 -2
  2. {pyfdn-0.4.0 → pyfdn-0.4.2}/PKG-INFO +5 -4
  3. {pyfdn-0.4.0 → pyfdn-0.4.2}/README.rst +4 -4
  4. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/api_reference.rst +34 -20
  5. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/example_gallery.py +7 -4
  6. pyfdn-0.4.2/docs/usage.rst +102 -0
  7. {pyfdn-0.4.0 → pyfdn-0.4.2}/pyproject.toml +2 -2
  8. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/__init__.py +47 -32
  9. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/acoustics.py +2 -9
  10. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/allpass.py +26 -0
  11. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/flamo.py +8 -345
  12. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/flamo_graph.py +2 -2
  13. pyfdn-0.4.0/src/pyFDN/build_io.py → pyfdn-0.4.2/src/pyFDN/build.py +50 -25
  14. pyfdn-0.4.2/src/pyFDN/eq/__init__.py +49 -0
  15. pyfdn-0.4.2/src/pyFDN/eq/_design_record.py +25 -0
  16. pyfdn-0.4.2/src/pyFDN/eq/biquads.py +120 -0
  17. pyfdn-0.4.2/src/pyFDN/eq/design.py +235 -0
  18. pyfdn-0.4.2/src/pyFDN/eq/graphic_eq.py +194 -0
  19. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/SDN.py +15 -15
  20. pyfdn-0.4.2/src/pyFDN/generate/fdn_build_gallery.py +305 -0
  21. pyfdn-0.4.2/src/pyFDN/generate/fdn_matrix_gallery.py +341 -0
  22. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/sample_delay_lengths.py +9 -4
  23. pyfdn-0.4.2/src/pyFDN/preset.py +195 -0
  24. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/ATTRIBUTION.md +11 -2
  25. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/audio/metadata.json +11 -0
  26. pyfdn-0.4.2/src/pyFDN/resources/audio/rir/meetingroom_to_hallway_290cm.wav +0 -0
  27. pyfdn-0.4.2/src/pyFDN/resources/licenses/coupled-rooms-CC-BY-4.0.txt +413 -0
  28. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_N16_d1.json +412 -0
  29. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_N16_d2.json +412 -0
  30. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_N4_d1.json +88 -0
  31. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_N4_d2.json +88 -0
  32. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_N6_d1.json +122 -0
  33. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_N6_d2.json +122 -0
  34. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_N8_d1.json +164 -0
  35. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_N8_d2.json +164 -0
  36. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N16_d1.json +412 -0
  37. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N16_d2.json +412 -0
  38. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N4_d1.json +88 -0
  39. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N4_d2.json +88 -0
  40. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N6_d1.json +122 -0
  41. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N6_d2.json +122 -0
  42. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N8_d1.json +164 -0
  43. pyfdn-0.4.2/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N8_d2.json +164 -0
  44. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/references.bib +10 -0
  45. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/td/__init__.py +12 -0
  46. pyfdn-0.4.2/src/pyFDN/td/operators.py +1028 -0
  47. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/__init__.py +6 -0
  48. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/build.py +154 -24
  49. pyfdn-0.4.2/src/pyFDN/train/filters.py +304 -0
  50. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/losses/_targets.py +22 -4
  51. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/losses/spectral.py +16 -4
  52. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/losses/temporal.py +10 -4
  53. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/params.py +2 -2
  54. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/translate/dss_to_flamo.py +6 -6
  55. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/translate/dss_to_impz.py +1 -1
  56. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/translate/dss_to_pr.py +2 -2
  57. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN.egg-info/PKG-INFO +5 -4
  58. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN.egg-info/SOURCES.txt +12 -10
  59. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_api_reference.py +21 -0
  60. pyfdn-0.4.2/tests/test_auxiliary_allpass.py +36 -0
  61. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_auxiliary_flamo.py +4 -4
  62. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_auxiliary_plot.py +3 -3
  63. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_auxiliary_utils.py +6 -10
  64. pyfdn-0.4.0/tests/test_build_io.py → pyfdn-0.4.2/tests/test_build.py +3 -3
  65. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_dss_to_pr.py +1 -1
  66. pyfdn-0.4.2/tests/test_eq_designs.py +392 -0
  67. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_flamo_one_pole_absorption.py +2 -2
  68. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_flamo_to_pr.py +5 -5
  69. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_generate.py +87 -15
  70. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_packaged_resources.py +11 -5
  71. pyfdn-0.4.2/tests/test_preset.py +298 -0
  72. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_process_extensions.py +4 -5
  73. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_td_graph_processing.py +1 -1
  74. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_td_operators.py +232 -1
  75. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_train.py +152 -65
  76. pyfdn-0.4.0/docs/usage.rst +0 -39
  77. pyfdn-0.4.0/src/pyFDN/eq/__init__.py +0 -45
  78. pyfdn-0.4.0/src/pyFDN/eq/absorption_geq.py +0 -52
  79. pyfdn-0.4.0/src/pyFDN/eq/bandpass_filter.py +0 -42
  80. pyfdn-0.4.0/src/pyFDN/eq/design_geq.py +0 -175
  81. pyfdn-0.4.0/src/pyFDN/eq/designs.py +0 -177
  82. pyfdn-0.4.0/src/pyFDN/eq/first_order.py +0 -168
  83. pyfdn-0.4.0/src/pyFDN/eq/graphic_eq.py +0 -83
  84. pyfdn-0.4.0/src/pyFDN/eq/one_pole.py +0 -76
  85. pyfdn-0.4.0/src/pyFDN/eq/shelving_filter.py +0 -64
  86. pyfdn-0.4.0/src/pyFDN/generate/fdn_matrix_gallery.py +0 -599
  87. pyfdn-0.4.0/src/pyFDN/presets.py +0 -50
  88. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_N16_d1.json +0 -396
  89. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_N16_d2.json +0 -396
  90. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_N4_d1.json +0 -72
  91. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_N4_d2.json +0 -72
  92. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_N6_d1.json +0 -106
  93. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_N6_d2.json +0 -106
  94. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_N8_d1.json +0 -148
  95. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_N8_d2.json +0 -148
  96. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N16_d1.json +0 -396
  97. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N16_d2.json +0 -396
  98. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N4_d1.json +0 -72
  99. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N4_d2.json +0 -72
  100. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N6_d1.json +0 -106
  101. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N6_d2.json +0 -106
  102. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N8_d1.json +0 -148
  103. pyfdn-0.4.0/src/pyFDN/resources/presets/colorless_FDN/colorless_init_N8_d2.json +0 -148
  104. pyfdn-0.4.0/src/pyFDN/td/operators.py +0 -508
  105. pyfdn-0.4.0/tests/test_eq_designs.py +0 -289
  106. {pyfdn-0.4.0 → pyfdn-0.4.2}/AUTHORS.rst +0 -0
  107. {pyfdn-0.4.0 → pyfdn-0.4.2}/CONTRIBUTING.rst +0 -0
  108. {pyfdn-0.4.0 → pyfdn-0.4.2}/LICENSE +0 -0
  109. {pyfdn-0.4.0 → pyfdn-0.4.2}/MANIFEST.in +0 -0
  110. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/Makefile +0 -0
  111. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/__init__.py +0 -0
  112. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/audio_gallery.py +0 -0
  113. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/authors.rst +0 -0
  114. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/conf.py +0 -0
  115. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/contributing.rst +0 -0
  116. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/history.rst +0 -0
  117. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/index.rst +0 -0
  118. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/installation.rst +0 -0
  119. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/logo/logo_pyFDN_3.png +0 -0
  120. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/logo/logo_pyFDN_4.png +0 -0
  121. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/logo/pyfdn_logo_basic.png +0 -0
  122. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/logo/pyfdn_logo_wave.png +0 -0
  123. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/make.bat +0 -0
  124. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/patch_rst.py +0 -0
  125. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/readme.rst +0 -0
  126. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/references.py +0 -0
  127. {pyfdn-0.4.0 → pyfdn-0.4.2}/docs/training_losses.rst +0 -0
  128. {pyfdn-0.4.0 → pyfdn-0.4.2}/setup.cfg +0 -0
  129. {pyfdn-0.4.0 → pyfdn-0.4.2}/setup.py +0 -0
  130. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/__init__.py +0 -0
  131. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/audio.py +0 -0
  132. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/coupled_rooms.py +0 -0
  133. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/delay.py +0 -0
  134. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/marimo_utils.py +0 -0
  135. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/math.py +0 -0
  136. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/plot.py +0 -0
  137. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/poles.py +0 -0
  138. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/tiny_rotation_matrix.py +0 -0
  139. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/auxiliary/utils.py +0 -0
  140. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/eq/_backend.py +0 -0
  141. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/eq/probe_sos.py +0 -0
  142. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/allpass_FDN/__init__.py +0 -0
  143. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/allpass_FDN/allpass_completion.py +0 -0
  144. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/allpass_FDN/homogeneous_allpass_fdn.py +0 -0
  145. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/allpass_FDN/rand_admissible_homogeneous_allpass.py +0 -0
  146. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/allpass_in_fdn.py +0 -0
  147. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/anderson_matrix.py +0 -0
  148. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/complete_orthogonal.py +0 -0
  149. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/construct_cascaded_paraunitary_matrix.py +0 -0
  150. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/construct_paraunitary_from_elementals.py +0 -0
  151. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/construct_velvet_feedback_matrix.py +0 -0
  152. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/degree_one_lossless.py +0 -0
  153. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/householder_matrix.py +0 -0
  154. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/is_almost_zero.py +0 -0
  155. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/nearest_orthogonal.py +0 -0
  156. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/nearest_sign_agnostic_orthogonal.py +0 -0
  157. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/random_matrix_shift.py +0 -0
  158. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/random_orthogonal.py +0 -0
  159. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/schroeder_reverberator.py +0 -0
  160. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/shift_matrix.py +0 -0
  161. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/generate/shift_matrix_distribute.py +0 -0
  162. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/process.py +0 -0
  163. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/references.py +0 -0
  164. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/__init__.py +0 -0
  165. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/audio/drums/drums.wav +0 -0
  166. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/audio/general/synth_dry.wav +0 -0
  167. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/audio/rir/s3_r4_o.wav +0 -0
  168. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/audio/speech/P1_Set1_8.wav +0 -0
  169. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/audio/speech/P20_Set1_8.wav +0 -0
  170. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/audio/strings/Kleiderschrank_Taylor_DI.wav +0 -0
  171. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/audio/strings/Sommerhit2016_Taylor_DI.wav +0 -0
  172. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/resources/licenses/diff-fdn-colorless-MIT.txt +0 -0
  173. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/td/connectors.py +0 -0
  174. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/engine.py +0 -0
  175. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/losses/__init__.py +0 -0
  176. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/losses/base.py +0 -0
  177. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/losses/parameter.py +0 -0
  178. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/train/response.py +0 -0
  179. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/translate/dss_to_ss.py +0 -0
  180. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/translate/dss_to_tf.py +0 -0
  181. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/translate/flamo_to_pr.py +0 -0
  182. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/translate/impz_to_res.py +0 -0
  183. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/translate/mtf_to_impz.py +0 -0
  184. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN/translate/pr_to_impz.py +0 -0
  185. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN.egg-info/dependency_links.txt +0 -0
  186. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN.egg-info/requires.txt +0 -0
  187. {pyfdn-0.4.0 → pyfdn-0.4.2}/src/pyFDN.egg-info/top_level.txt +0 -0
  188. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/__init__.py +0 -0
  189. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/conftest.py +0 -0
  190. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/matloader.py +0 -0
  191. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/reference/README.md +0 -0
  192. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/reference/example_onePoleAbsorption.mat +0 -0
  193. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/reference/onePoleAbsorption_reference.mat +0 -0
  194. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_audio_gallery.py +0 -0
  195. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_auxiliary_acoustics.py +0 -0
  196. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_auxiliary_delay.py +0 -0
  197. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_auxiliary_math.py +0 -0
  198. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_coupled_rooms.py +0 -0
  199. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_example_gallery.py +0 -0
  200. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_flamo_graph.py +0 -0
  201. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_marimo_examples_run.py +0 -0
  202. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_pyFDN.py +0 -0
  203. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_sdn.py +0 -0
  204. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_td_connectors.py +0 -0
  205. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_time_varying_matrix.py +0 -0
  206. {pyfdn-0.4.0 → pyfdn-0.4.2}/tests/test_translate.py +0 -0
@@ -2,9 +2,58 @@
2
2
  History
3
3
  =======
4
4
 
5
+ 0.4.2 (2026-08-27)
6
+ ------------------
7
+
8
+ * Add the ``example_multislope_rir_to_fdn`` notebook: a measured room-transition
9
+ response is fitted with two decay slopes per octave band using DecayFitNet
10
+ from the ``multislope`` package, and each slope is resynthesised by its own
11
+ FDN. The response it uses ships with the package as the
12
+ ``meetingroom_to_hallway_290cm`` audio resource, taken from the coupled-rooms
13
+ transition dataset.
14
+ * Add nonlinear and pitch-shifting time-domain operators for shimmer
15
+ reverberation -- ``DCBlocker``, ``ControllableFullWaveRect``, ``SDFD``,
16
+ ``RingModulator``, ``PitchShift`` and ``GranularPitchShift`` -- all usable as
17
+ ``process_fdn`` hooks, plus the ``example_shimmer_fdn`` notebook that walks
18
+ through each one inside the feedback loop of the same 8-line FDN.
19
+ * ``is_uniallpass`` no longer solves a singular Lyapunov equation when ``A`` is
20
+ itself lossless (as in the allpass-in-FDN structure). The spectral radius is
21
+ checked first and such a system is reported as not uniallpass, instead of the
22
+ result depending on whether LAPACK chose to warn or raise on the ill-
23
+ conditioned solve.
24
+ * Fix a device mismatch when a loss built for one run is used in another: every
25
+ loss that holds a reference impulse response (``MatchCumulativeEnergy``,
26
+ ``MatchEnergyDecay``, ``MatchMagnitude``, ``MatchImpulseResponse``,
27
+ ``MatchSpectrogram``, ``MatchMelSpectrogram``) aligned its reference once, on
28
+ the first response it saw, and then handed that CPU tensor to a later CUDA
29
+ step -- the way a notebook cell that builds the loss but does not re-run on a
30
+ runtime switch would hit it. The aligned reference is now keyed on the
31
+ response's shape, device, dtype and sample rate, and rebuilt when any of them
32
+ changes. ``MatchSpectrogram`` and ``MatchMelSpectrogram`` also default their
33
+ ``device`` to the response's rather than to the CPU, so FLAMO builds its
34
+ filterbanks where the model is.
35
+
36
+ 0.4.1 (2026-08-24)
37
+ ------------------
38
+
39
+ * Let ``fdn_build_gallery`` and the target-to-EQ functions optionally return
40
+ the design choices used to produce their coefficients, ready to store in an
41
+ ``FDNPreset``. Build generation now also exposes the delay distribution and
42
+ coprimality options and lives separately from the matrix galleries.
43
+ * **Breaking:** rename the ``Fs`` parameter to ``fs`` everywhere it is still
44
+ spelled with a capital -- ``dss_to_flamo``, ``dss_to_pr``, ``delay_module``
45
+ and ``SDN``. Every other sampling-rate argument in the package was already
46
+ ``fs``, and the odd one out forced callers to remember which spelling each
47
+ function wanted. ``SDN`` also renames its ``fs`` attribute and the ``"fs"``
48
+ key of the dictionary ``SDN.compute()`` returns.
49
+
5
50
  0.4.0 (2026-08-23)
6
51
  ------------------
7
52
 
53
+ * Add ``FDNPreset`` JSON documents: a baked ``FDNBuild`` plus catalog
54
+ metadata and a controlled vocabulary for delays, matrices, and the three
55
+ filter hooks. ``trainable_from_preset`` restores filter targets as meaningful
56
+ FLAMO parameters only when they reproduce the baked coefficients.
8
57
  * **Breaking:** replace ``train_fdn``'s ``mode`` string, and the ``target``,
9
58
  ``criteria``, ``sparsity_alpha`` and ``mss_nfft`` arguments that went with
10
59
  it, with a composed loss object. An objective is now written out --
@@ -27,7 +76,7 @@ History
27
76
  from ``rt`` (``LOSSLESS_ALIAS_DECAY_DB`` when ``rt`` is None) and
28
77
  ``trainable_from_build`` threads it into every module, so a magnitude
29
78
  objective sees a bounded response.
30
- * Train the decay: ``DecayFilter`` parametrizes the in-loop absorption filter
79
+ * Train the decay: ``AttenuationFilter`` parametrizes the in-loop absorption filter
31
80
  by reverberation time per band, so the loop stays contractive for every value
32
81
  the parameter can take, and takes either one RT curve for the network or one
33
82
  per delay line. ``OutputEQ`` trains the output filter outside the recursion,
@@ -43,7 +92,7 @@ History
43
92
  enumerates them.
44
93
  * Export the FLAMO graph builders (``assemble_fdn_core``, ``wrap_fdn_shell``,
45
94
  ``gain_module``, ``delay_module``, ``matrix_module``, ``fir_matrix_module``,
46
- ``sos_filter_module``, ``hook_module``, ``DecayFilter``, ``OutputEQ``) from
95
+ ``sos_filter_module``, ``hook_module``, ``AttenuationFilter``, ``OutputEQ``) from
47
96
  the top-level namespace.
48
97
  * Make ``build_to_impz`` apply all three hooks, so it no longer rejects builds
49
98
  that carry an output EQ, and make ``extract_build`` refuse a hook it cannot
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyFDN
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Python library for Feedback Delay Networks
5
5
  Author: Sebastian J. Schlecht, Facundo Franchino, Jeremy Bai
6
6
  Maintainer: Facundo Franchino, Sebastian J. Schlecht
@@ -17,6 +17,7 @@ Requires-Python: >=3.10
17
17
  Description-Content-Type: text/x-rst
18
18
  License-File: LICENSE
19
19
  License-File: src/pyFDN/resources/ATTRIBUTION.md
20
+ License-File: src/pyFDN/resources/licenses/coupled-rooms-CC-BY-4.0.txt
20
21
  License-File: src/pyFDN/resources/licenses/diff-fdn-colorless-MIT.txt
21
22
  Requires-Dist: numpy
22
23
  Requires-Dist: soundfile
@@ -148,17 +149,17 @@ All main functions are accessible directly from the top-level ``pyFDN`` namespac
148
149
  feedback = pyFDN.random_orthogonal(len(delays))
149
150
 
150
151
  # one-pole absorption filters targeting RT of 1.2 s at DC and 0.9 s at Nyquist
151
- absorption = pyFDN.one_pole_absorption(1.2, 0.9, delays, fs)
152
+ absorption = pyFDN.decay_to_one_pole(1.2, 0.9, delays, fs)
152
153
 
153
154
  # convert delay state-space to standard state-space (A_ss, b, c, d)
154
155
  A_ss, b, c, d = pyFDN.dss_to_ss(delays, feedback)
155
156
 
156
157
  Alternatively, import specific functions directly::
157
158
 
158
- from pyFDN import random_orthogonal, one_pole_absorption, lin_to_db
159
+ from pyFDN import decay_to_one_pole, lin_to_db, random_orthogonal
159
160
 
160
161
  feedback = random_orthogonal(4)
161
- absorption = one_pole_absorption(1.2, 0.9, [100, 150, 200, 250], 48_000)
162
+ absorption = decay_to_one_pole(1.2, 0.9, [100, 150, 200, 250], 48_000)
162
163
 
163
164
 
164
165
  Development
@@ -78,17 +78,17 @@ All main functions are accessible directly from the top-level ``pyFDN`` namespac
78
78
  feedback = pyFDN.random_orthogonal(len(delays))
79
79
 
80
80
  # one-pole absorption filters targeting RT of 1.2 s at DC and 0.9 s at Nyquist
81
- absorption = pyFDN.one_pole_absorption(1.2, 0.9, delays, fs)
81
+ absorption = pyFDN.decay_to_one_pole(1.2, 0.9, delays, fs)
82
82
 
83
83
  # convert delay state-space to standard state-space (A_ss, b, c, d)
84
84
  A_ss, b, c, d = pyFDN.dss_to_ss(delays, feedback)
85
85
 
86
86
  Alternatively, import specific functions directly::
87
87
 
88
- from pyFDN import random_orthogonal, one_pole_absorption, lin_to_db
88
+ from pyFDN import decay_to_one_pole, lin_to_db, random_orthogonal
89
89
 
90
90
  feedback = random_orthogonal(4)
91
- absorption = one_pole_absorption(1.2, 0.9, [100, 150, 200, 250], 48_000)
91
+ absorption = decay_to_one_pole(1.2, 0.9, [100, 150, 200, 250], 48_000)
92
92
 
93
93
 
94
94
  Development
@@ -112,4 +112,4 @@ Helpful links
112
112
  * `List of papers on Feedback Delay Networks <https://github.com/gdalsanto/delay-network-reverbs>`_ .
113
113
  * `Multislope Estimation library <https://github.com/artificial-audio/multislope>`_ .
114
114
  * `FLAMO (An Open-Source Library for Frequency-Domain Differentiable Audio Processing) <https://github.com/gdalsanto/flamo>`_ .
115
- * `ADAC (Automatic Differentiable Audio Compilation) library <https://github.com/cucuwritescode/adac>`_ .
115
+ * `ADAC (Automatic Differentiable Audio Compilation) library <https://github.com/cucuwritescode/adac>`_ .
@@ -95,34 +95,36 @@ Acoustics & Absorption
95
95
  pyFDN.slope_amplitude_to_level
96
96
  pyFDN.slope_to_rt
97
97
 
98
- EQ & Absorption Design (``pyFDN.eq``)
99
- --------------------------------------
98
+ EQ Design (``pyFDN.eq``)
99
+ -------------------------
100
100
 
101
- Three designs of the same two filters -- an FDN's in-loop absorption and its
102
- output EQ -- behind one interface. A design carries its own target and maps it
103
- to biquad sections in numpy or in torch from a single implementation, which is
104
- what a trainable :class:`pyFDN.DecayFilter` or :class:`pyFDN.OutputEQ` runs
105
- inside a training loop.
101
+ Explicit functions map either decay targets or gain targets onto a named
102
+ filter design. The same functions run in NumPy or Torch; the trainable
103
+ :class:`pyFDN.AttenuationFilter` and :class:`pyFDN.OutputEQ` modules use these
104
+ mappings inside a training loop. ``EQDesign`` is the literal choice of
105
+ ``"graphic_eq"``, ``"first_order_shelf"``, or ``"one_pole"`` used by those
106
+ modules. The target-to-EQ functions accept ``return_design=True`` when their
107
+ JSON-compatible design record is also needed for an ``FDNPreset``.
106
108
 
107
109
  .. autosummary::
108
110
  :toctree: generated/
109
111
  :nosignatures:
110
112
 
111
113
  pyFDN.EQDesign
112
- pyFDN.GraphicEQ
113
- pyFDN.FirstOrderShelf
114
- pyFDN.OnePole
115
- pyFDN.design_geq
116
- pyFDN.geq_sos
114
+ pyFDN.decay_to_geq
115
+ pyFDN.decay_to_first_order_shelf
116
+ pyFDN.decay_to_one_pole
117
+ pyFDN.gain_to_geq
118
+ pyFDN.gain_to_bounded_geq
119
+ pyFDN.gain_to_first_order_shelf
120
+ pyFDN.gain_to_one_pole
117
121
  pyFDN.geq_design_matrix
118
- pyFDN.graphic_eq
119
- pyFDN.absorption_geq
120
- pyFDN.first_order_absorption
121
- pyFDN.first_order_shelving_eq
122
- pyFDN.one_pole_absorption
122
+ pyFDN.lowshelf_biquad
123
+ pyFDN.highshelf_biquad
124
+ pyFDN.peaking_biquad
125
+ pyFDN.first_order_shelf_biquad
126
+ pyFDN.one_pole_biquad
123
127
  pyFDN.probe_sos
124
- pyFDN.shelving_filter
125
- pyFDN.bandpass_filter
126
128
 
127
129
  Time-Domain Graph (``pyFDN.td``)
128
130
  --------------------------------
@@ -139,6 +141,12 @@ rendered with ``.process(signal)``. See :mod:`pyFDN.td`.
139
141
  pyFDN.td.Gain
140
142
  pyFDN.td.Delay
141
143
  pyFDN.td.AbsoluteValue
144
+ pyFDN.td.DCBlocker
145
+ pyFDN.td.ControllableFullWaveRect
146
+ pyFDN.td.SDFD
147
+ pyFDN.td.RingModulator
148
+ pyFDN.td.PitchShift
149
+ pyFDN.td.GranularPitchShift
142
150
  pyFDN.td.SOSBank
143
151
  pyFDN.td.MatrixFIR
144
152
  pyFDN.td.MatrixConvolver
@@ -181,7 +189,7 @@ An FDN as FLAMO modules, assembled from numpy values. The three filter hooks --
181
189
  pyFDN.fir_matrix_module
182
190
  pyFDN.sos_filter_module
183
191
  pyFDN.hook_module
184
- pyFDN.DecayFilter
192
+ pyFDN.AttenuationFilter
185
193
  pyFDN.OutputEQ
186
194
 
187
195
  Polynomial & Matrix Maths
@@ -245,6 +253,11 @@ Build Files, Packaged Examples & References
245
253
  pyFDN.audio_metadata
246
254
  pyFDN.load_audio
247
255
  pyFDN.available_fdn_presets
256
+ pyFDN.get_fdn_preset
257
+ pyFDN.FDNPreset
258
+ pyFDN.fdn_preset_to_dict
259
+ pyFDN.fdn_preset_from_dict
260
+ pyFDN.save_fdn_preset
248
261
  pyFDN.load_fdn_preset
249
262
  pyFDN.fdn_build_to_dict
250
263
  pyFDN.fdn_build_from_dict
@@ -294,6 +307,7 @@ Training
294
307
 
295
308
  pyFDN.build_fdn
296
309
  pyFDN.trainable_from_build
310
+ pyFDN.trainable_from_preset
297
311
  pyFDN.build_set_decay
298
312
  pyFDN.Trainable
299
313
  pyFDN.train_fdn
@@ -15,11 +15,14 @@ OUTPUT_FILE = DOCS_DIR / "examples_gallery.rst"
15
15
 
16
16
  CATEGORY_ORDER = (
17
17
  "Getting Started",
18
- "Absorption & Filters",
19
- "Translation Examples",
20
- "Allpass FDN Examples",
18
+ "Feedback Matrices",
19
+ "Absorption & Decay",
20
+ "Analysis & Verification",
21
+ "Optimization",
22
+ "Allpass FDNs",
21
23
  "Special FDNs",
22
- "FDN Design & Analysis",
24
+ "Representations",
25
+ "Export & Deployment",
23
26
  )
24
27
 
25
28
 
@@ -0,0 +1,102 @@
1
+ =====
2
+ Usage
3
+ =====
4
+
5
+ All main functions are accessible directly from ``pyFDN``::
6
+
7
+ import pyFDN
8
+
9
+ feedback = pyFDN.random_orthogonal(4)
10
+ attenuation = pyFDN.decay_to_first_order_shelf(
11
+ 1.2, 0.9, None, [100, 150, 200, 250], 48_000
12
+ )
13
+ gain_db = pyFDN.lin_to_db([0.5, 1.0, 2.0])
14
+
15
+ Or import specific functions::
16
+
17
+ from pyFDN import random_orthogonal, decay_to_first_order_shelf, dss_to_ss
18
+
19
+ feedback = random_orthogonal(4)
20
+
21
+ Packaged tutorial resources
22
+ ---------------------------
23
+
24
+ The small resources used by the browser tutorials are available through the
25
+ public API, independent of the current working directory::
26
+
27
+ dry, fs = pyFDN.load_audio("synth_dry")
28
+
29
+ preset = pyFDN.get_fdn_preset("colorless_N8_d1")
30
+ reverberator = pyFDN.build_set_decay(preset.build, 1.5)
31
+
32
+ citation = pyFDN.paper_link("Allpass_Feedback_Delay_Networks")
33
+
34
+ Use ``pyFDN.available_audio()`` and ``pyFDN.available_fdn_presets()`` to list
35
+ the bundled choices. Attribution and license information for audio is returned
36
+ by ``pyFDN.audio_metadata(name)``.
37
+
38
+ FDN builds can also be exchanged as readable, versioned JSON files::
39
+
40
+ pyFDN.save_fdn_build("reverberator.json", reverberator)
41
+ restored = pyFDN.load_fdn_build("reverberator.json")
42
+
43
+ Preset documents
44
+ ----------------
45
+
46
+ An ``FDNPreset`` keeps a baked ``FDNBuild`` together with catalog metadata and
47
+ the design choices that cannot be recovered reliably from its numbers. Unknown
48
+ design choices are left out rather than guessed. Generators and EQ design
49
+ functions return only their numerical result by default; opt into their design
50
+ record when preparing a preset::
51
+
52
+ build, design = pyFDN.fdn_build_gallery(
53
+ N=8,
54
+ fs=48_000,
55
+ delay_distribution="geometric",
56
+ coprime=True,
57
+ rt=1.2,
58
+ rt_nyquist=0.8,
59
+ return_design=True,
60
+ )
61
+
62
+ A separately designed filter can return the same kind of record. The caller
63
+ only has to say which FDN hook receives it::
64
+
65
+ import dataclasses
66
+
67
+ post_matrix, post_matrix_design = pyFDN.gain_to_one_pole(
68
+ gain_db=[0.0] * 8,
69
+ gain_db_nyquist=[-3.0] * 8,
70
+ return_design=True,
71
+ )
72
+ build = dataclasses.replace(build, post_matrix=post_matrix)
73
+ design["post_matrix"] = post_matrix_design
74
+
75
+ Once all numerical and design parts are assembled, freeze that particular
76
+ realization as the preset::
77
+
78
+ preset = pyFDN.FDNPreset(
79
+ build=build,
80
+ metadata={
81
+ "name": "small-room",
82
+ "description": "A short, neutral room",
83
+ "authors": ["A. Author"],
84
+ "license": "CC0-1.0",
85
+ "tags": ["room", "short"],
86
+ },
87
+ design=design,
88
+ )
89
+ pyFDN.save_fdn_preset("small-room.json", preset)
90
+ restored = pyFDN.load_fdn_preset("small-room.json")
91
+
92
+ Metadata is an open dictionary because it is descriptive rather than part of
93
+ the sound. By convention, ``tags`` is a list of strings, so selecting presets
94
+ does not require a metadata class::
95
+
96
+ requested = {"room", "short"}
97
+ matches = requested <= set(restored.metadata.get("tags", []))
98
+
99
+ The numerical build, including its sample rate, is authoritative. Filter design
100
+ records may additionally carry RT or gain targets. In that case
101
+ ``pyFDN.trainable_from_preset`` restores the meaningful FLAMO parameters and
102
+ checks that they reproduce the baked SOS coefficients before building a model.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyFDN"
7
- version = "0.4.0"
7
+ version = "0.4.2"
8
8
  description = "Python library for Feedback Delay Networks"
9
9
  readme = "README.rst"
10
10
  authors = [
@@ -113,7 +113,7 @@ include = ["pyFDN*"]
113
113
  # triggers on. Usage: bump-my-version bump {major|minor|patch}
114
114
 
115
115
  [tool.bumpversion]
116
- current_version = "0.4.0"
116
+ current_version = "0.4.2"
117
117
  allow_dirty = false
118
118
  commit = true
119
119
  message = "Bump version: {current_version} → {new_version}"
@@ -3,7 +3,7 @@
3
3
  from importlib import import_module
4
4
 
5
5
  __author__ = "Facundo Franchino"
6
- __version__ = "0.4.0"
6
+ __version__ = "0.4.2"
7
7
 
8
8
  __all__ = [
9
9
  # acoustics
@@ -11,11 +11,8 @@ __all__ = [
11
11
  "estimate_initial_level_bands",
12
12
  "estimate_rt_bands",
13
13
  "edc",
14
- "first_order_absorption",
15
- "first_order_shelving_eq",
16
14
  "octave_band_filterbank",
17
15
  "octave_bands",
18
- "one_pole_absorption",
19
16
  "rt_to_gain_per_sample",
20
17
  "rt_to_slope",
21
18
  "slope_amplitude_to_level",
@@ -39,7 +36,7 @@ __all__ = [
39
36
  "fir_matrix_module",
40
37
  "sos_filter_module",
41
38
  "hook_module",
42
- "DecayFilter",
39
+ "AttenuationFilter",
43
40
  "OutputEQ",
44
41
  "audio_metadata",
45
42
  "available_audio",
@@ -48,7 +45,12 @@ __all__ = [
48
45
  "paper_link",
49
46
  "paper_reference",
50
47
  "available_fdn_presets",
48
+ "get_fdn_preset",
51
49
  "load_fdn_preset",
50
+ "FDNPreset",
51
+ "fdn_preset_from_dict",
52
+ "fdn_preset_to_dict",
53
+ "save_fdn_preset",
52
54
  "fdn_build_from_dict",
53
55
  "fdn_build_to_dict",
54
56
  "load_fdn_build",
@@ -80,18 +82,21 @@ __all__ = [
80
82
  "shift_matrix_distribute",
81
83
  "tiny_rotation_matrix",
82
84
  # eq
83
- "absorption_geq",
84
- "bandpass_filter",
85
85
  "EQDesign",
86
- "FirstOrderShelf",
87
- "GraphicEQ",
88
- "OnePole",
89
- "design_geq",
86
+ "decay_to_first_order_shelf",
87
+ "decay_to_geq",
88
+ "decay_to_one_pole",
89
+ "first_order_shelf_biquad",
90
+ "gain_to_bounded_geq",
91
+ "gain_to_first_order_shelf",
92
+ "gain_to_geq",
93
+ "gain_to_one_pole",
90
94
  "geq_design_matrix",
91
- "geq_sos",
92
- "graphic_eq",
95
+ "highshelf_biquad",
96
+ "lowshelf_biquad",
97
+ "one_pole_biquad",
98
+ "peaking_biquad",
93
99
  "probe_sos",
94
- "shelving_filter",
95
100
  # polynomial and matrix maths
96
101
  "adj_poly",
97
102
  "adjugate",
@@ -148,6 +153,7 @@ __all__ = [
148
153
  # training
149
154
  "build_fdn",
150
155
  "trainable_from_build",
156
+ "trainable_from_preset",
151
157
  "LOSSLESS_ALIAS_DECAY_DB",
152
158
  "build_set_decay",
153
159
  "Trainable",
@@ -263,8 +269,6 @@ from .auxiliary.delay import (
263
269
  swap_flamo_recursion_paths,
264
270
  )
265
271
  from .auxiliary.flamo import (
266
- DecayFilter,
267
- OutputEQ,
268
272
  assemble_fdn_core,
269
273
  delay_module,
270
274
  fir_matrix_module,
@@ -350,7 +354,8 @@ from .auxiliary.utils import (
350
354
  skew,
351
355
  sq_to_db,
352
356
  )
353
- from .build_io import (
357
+ from .build import (
358
+ FDNBuild,
354
359
  fdn_build_from_dict,
355
360
  fdn_build_to_dict,
356
361
  load_fdn_build,
@@ -358,20 +363,20 @@ from .build_io import (
358
363
  )
359
364
  from .eq import (
360
365
  EQDesign,
361
- FirstOrderShelf,
362
- GraphicEQ,
363
- OnePole,
364
- absorption_geq,
365
- bandpass_filter,
366
- design_geq,
367
- first_order_absorption,
368
- first_order_shelving_eq,
366
+ decay_to_first_order_shelf,
367
+ decay_to_geq,
368
+ decay_to_one_pole,
369
+ first_order_shelf_biquad,
370
+ gain_to_bounded_geq,
371
+ gain_to_first_order_shelf,
372
+ gain_to_geq,
373
+ gain_to_one_pole,
369
374
  geq_design_matrix,
370
- geq_sos,
371
- graphic_eq,
372
- one_pole_absorption,
375
+ highshelf_biquad,
376
+ lowshelf_biquad,
377
+ one_pole_biquad,
378
+ peaking_biquad,
373
379
  probe_sos,
374
- shelving_filter,
375
380
  )
376
381
  from .generate.allpass_FDN import allpass_completion
377
382
  from .generate.allpass_FDN.allpass_completion import (
@@ -405,10 +410,9 @@ from .generate.construct_paraunitary_from_elementals import (
405
410
  )
406
411
  from .generate.construct_velvet_feedback_matrix import construct_velvet_feedback_matrix
407
412
  from .generate.degree_one_lossless import degree_one_lossless
413
+ from .generate.fdn_build_gallery import fdn_build_gallery
408
414
  from .generate.fdn_matrix_gallery import (
409
- FDNBuild,
410
415
  FDNSystem,
411
- fdn_build_gallery,
412
416
  fdn_matrix_gallery,
413
417
  fdn_system_gallery,
414
418
  filter_matrix_gallery,
@@ -426,7 +430,15 @@ from .generate.schroeder_reverberator import schroeder_reverberator
426
430
  from .generate.SDN import SDN
427
431
  from .generate.shift_matrix import shift_matrix
428
432
  from .generate.shift_matrix_distribute import shift_matrix_distribute
429
- from .presets import available_fdn_presets, load_fdn_preset
433
+ from .preset import (
434
+ FDNPreset,
435
+ available_fdn_presets,
436
+ fdn_preset_from_dict,
437
+ fdn_preset_to_dict,
438
+ get_fdn_preset,
439
+ load_fdn_preset,
440
+ save_fdn_preset,
441
+ )
430
442
 
431
443
  # fdn processing
432
444
  from .process import process_fdn
@@ -438,6 +450,7 @@ from .train import (
438
450
  L2,
439
451
  LOSSLESS_ALIAS_DECAY_DB,
440
452
  AsymmetricFlatMagnitude,
453
+ AttenuationFilter,
441
454
  Energy,
442
455
  FlatMagnitude,
443
456
  FlatSpectrogram,
@@ -448,6 +461,7 @@ from .train import (
448
461
  MatchMagnitude,
449
462
  MatchMelSpectrogram,
450
463
  MatchSpectrogram,
464
+ OutputEQ,
451
465
  ParameterLoss,
452
466
  ParamRef,
453
467
  Response,
@@ -463,6 +477,7 @@ from .train import (
463
477
  params,
464
478
  train_fdn,
465
479
  trainable_from_build,
480
+ trainable_from_preset,
466
481
  )
467
482
 
468
483
  # state-space translators
@@ -9,14 +9,6 @@ from numpy.typing import ArrayLike
9
9
  from scipy.signal import sosfreqz
10
10
  from scipy.special import erfc
11
11
 
12
- # The filter designs live in pyFDN.eq, and are re-exported here because this
13
- # module's rt_to_slope is what turns a reverberation time into the dB target
14
- # they take. They import it inside a function, not at module level, so the two
15
- # modules do not cycle.
16
- from pyFDN.eq.first_order import first_order_absorption as first_order_absorption
17
- from pyFDN.eq.first_order import first_order_shelving_eq as first_order_shelving_eq
18
- from pyFDN.eq.one_pole import one_pole_absorption as one_pole_absorption
19
-
20
12
 
21
13
  def rt_to_slope(rt: ArrayLike, fs: float) -> np.ndarray:
22
14
  """Convert reverb time (RT, seconds) to energy decay slope (dB per sample)."""
@@ -457,7 +449,8 @@ def sos_gain_per_sample_curves(
457
449
  ----------
458
450
  sos : (n_sections, 6, N) array
459
451
  Per-channel SOS bank; section rows are ``[b0, b1, b2, a0, a1, a2]``. Same
460
- format as :func:`one_pole_absorption` / :func:`first_order_absorption`
452
+ format as :func:`pyFDN.decay_to_one_pole` /
453
+ :func:`pyFDN.decay_to_first_order_shelf`
461
454
  return.
462
455
  delays : (N,) array-like
463
456
  Delay lengths in samples, one per channel. Used to scale gain to per-sample.
@@ -15,6 +15,10 @@ from scipy.linalg import solve_discrete_lyapunov
15
15
  from pyFDN.auxiliary.math import general_char_poly
16
16
  from pyFDN.generate.is_almost_zero import is_almost_zero
17
17
 
18
+ # How far inside the unit circle the eigenvalues of A must sit for the discrete
19
+ # Lyapunov equation solved by is_uniallpass to be well posed.
20
+ _STABILITY_TOL = 1e-9
21
+
18
22
 
19
23
  def poletti_allpass(
20
24
  g: float, U: np.ndarray
@@ -72,11 +76,33 @@ def is_uniallpass(
72
76
  True if the system is uniallpass.
73
77
  P : ndarray
74
78
  Solution of discrete Lyapunov A P A' - P + B B' = 0; diagonal if uniallpass.
79
+ All-NaN when A is not strictly stable, see the note below.
80
+
81
+ Notes
82
+ -----
83
+ The Lyapunov equation only has a (unique, finite) solution when A is
84
+ strictly stable. If A itself is lossless -- as in the allpass-in-FDN
85
+ structure, where the feedback matrix has all its eigenvalues on the unit
86
+ circle -- no such P exists, and the linear system scipy solves is exactly
87
+ singular. Rather than let that surface as an ill-conditioned solve (which
88
+ raises or returns garbage depending on the LAPACK build), the spectral
89
+ radius is checked up front and the system reported as not uniallpass.
75
90
  """
76
91
  A = np.asarray(A, dtype=float)
77
92
  B = np.asarray(B, dtype=float)
78
93
  C = np.asarray(C, dtype=float)
79
94
  D = np.asarray(D, dtype=float)
95
+ # P exists only for a strictly stable A; on the unit circle the Lyapunov
96
+ # operator P -> A P A' - P is singular (eigenvalue pairs with λi·λj = 1).
97
+ spectral_radius = float(np.max(np.abs(np.linalg.eigvals(A)))) if A.size else 0.0
98
+ if spectral_radius >= 1.0 - _STABILITY_TOL:
99
+ warnings.warn(
100
+ f"A has spectral radius {spectral_radius:.6g} >= 1; the discrete "
101
+ "Lyapunov equation has no finite solution, so the system is not "
102
+ "uniallpass.",
103
+ stacklevel=2,
104
+ )
105
+ return False, np.full_like(A, np.nan)
80
106
  # P = dlyap(A, B @ B')
81
107
  P = solve_discrete_lyapunov(A, B @ B.T)
82
108
  # Check P is diagonal