<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/styles/rss-styles.xsl"?>
<rss version="2.0" 
    xmlns:atom="http://www.w3.org/2005/Atom"
    xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:source="https://source.scripting.com/"
    >
  <channel>
    <title>Reilly Spitzfaden | Blog</title>
    <link>https://reillyspitzfaden.com/blog</link>
    <description>A blog about my personal interests, including composition and sound design; audio development using Max/MSP, C++, JUCE, and Rust; and web development on the IndieWeb</description>
    <language>en-us</language>
    <pubDate>Sat, 9 Mar 2024 00:00:00 GMT</pubDate>
    <lastBuildDate>Fri, 06 Mar 2026 17:30:00 GMT</lastBuildDate>
    <docs>https://www.rssboard.org/rss-specification</docs>
    <managingEditor>reillypascal@gmail.com (Reilly Spitzfaden)</managingEditor>
    <webMaster>reillypascal@gmail.com (Reilly Spitzfaden)</webMaster>
    <atom:link href="https://reillyspitzfaden.com/blog/feed.xml" rel="self" type="application/rss+xml"/>
    <source:blogroll>https://reillyspitzfaden.com/blogroll.opml</source:blogroll>
    
        <item>
            <title>Tidal Cycles/Neovim: No Plugins, 24 Lines of Lua</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2026/03/tidal-cycles-neovim-no-plugins-24-lines-of-lua/</link>
            <guid>https://reillyspitzfaden.com/posts/2026/03/tidal-cycles-neovim-no-plugins-24-lines-of-lua/</guid>
            <pubDate>Fri, 06 Mar 2026 17:30:00 GMT</pubDate>
            <description>I made a simple, easy-to-maintain way to do “algorave” musical live-coding from Neovim, without needing to depend on other people&#39;s plugins staying updated.</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;p&gt;I&#39;ve been enjoying writing music using code in Neovim — I &lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/02/friendly-lilypond-with-neovim-audacity/&quot;&gt;recently talked about using Lilypond to write sheet music this way, for example&lt;/a&gt;. Another way of writing music with code is &lt;a href=&quot;https://tidalcycles.org/&quot;&gt;Tidal Cycles&lt;/a&gt;. This is a set of pattern-sequencing tools that talk to the &lt;a href=&quot;https://supercollider.github.io/&quot;&gt;SuperCollider&lt;/a&gt; synthesizer. Tidal Cycles allows a highly compact way of notating musical patterns, and one aimed at &lt;a href=&quot;https://www.youtube.com/watch?v=Db0QJo1eaoI&quot;&gt;live-coding&lt;/a&gt; or “algorave” events, where performers&#39; screens are projected, showing code being typed in real time.&lt;/p&gt;
&lt;p&gt;Today, let&#39;s look at how I bypassed the need for plugins when writing Tidal Cycles code, and created something simple and self-sufficient that also gave me more understanding of my tools!&lt;/p&gt;
&lt;h2 id=&quot;existing-tidal-cycles-tools&quot;&gt;Existing Tidal Cycles Tools&lt;/h2&gt;
&lt;p&gt;The officially-recommended plugin &lt;a href=&quot;https://github.com/tidalcycles/vim-tidal&quot;&gt;vim-tidal&lt;/a&gt; hasn&#39;t been updated since 2020, and whenever I quit the Tidal interpreter in this plugin, I wasn&#39;t able to start it again without quitting and re-opening my terminal. &lt;a href=&quot;https://github.com/grddavies/tidal.nvim&quot;&gt;tidal.nvim&lt;/a&gt; was pretty nice and somewhat newer, but development doesn&#39;t seem to be particularly active, and my next experience made me question if I even needed that.&lt;/p&gt;
&lt;p&gt;I was playing with the &lt;a href=&quot;https://github.com/Olical/conjure&quot;&gt;conjure&lt;/a&gt; plugin, &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/03/tidal-cycles-neovim-no-plugins-24-lines-of-lua/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; which provides interactive &lt;a href=&quot;https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop&quot;&gt;REPL&lt;/a&gt; support for many languages, including Scheme, which I&#39;ve been using with Lilypond. I saw that it was pretty easy to set this up with the &lt;a href=&quot;https://lilypond.org/doc/v2.24/Documentation/extending/scheme-sandbox&quot;&gt;&lt;code&gt;lilypond scheme-sandbox&lt;/code&gt;&lt;/a&gt; command, instead of the suggested MIT Scheme or GNU Guile REPLs — you can &lt;a href=&quot;https://github.com/reillypascal/nvim/blob/d84f72ada230811b0879cba3e2e92b4e8c5f4ca4/lua/plugins/conjure.lua#L14-L15&quot;&gt;see my Neovim configuration for this here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This got me thinking: how hard could it be to set something like this up myself for Tidal Cycles? It turned out that I only needed a little bit of Lua! I always prefer small, simple scripts that I wrote and understand, since I know I can keep these maintained without relying on someone else to write a plugin (or having to dig through a plugin&#39;s worth of code myself in order to fork/maintain it).&lt;/p&gt;
&lt;p&gt;Additionally, it&#39;s extremely easy to stop/start the Tidal interpreter with this configuration, and even more so than in the more recent tidal.nvim plugin. Tidal Cycles just uses Haskell&#39;s &lt;a href=&quot;https://wiki.haskell.org/GHC/GHCi&quot;&gt;GHCi&lt;/a&gt; environment started with a custom file titled &lt;code&gt;BootTidal.hs&lt;/code&gt;. GHCi is the interactive version of the Glasgow Haskell Compiler (GHC). You can enter Haskell expressions at the prompt, and they will be evaluated on the fly. The standard way to quit GHCi is ctrl + D, and since we will just be running GHCi in a Neovim terminal pane, this works great here.&lt;/p&gt;
&lt;h2 id=&quot;my-neovim-configuration&quot;&gt;My Neovim Configuration&lt;/h2&gt;
&lt;p&gt;First, we want Neovim to recognize “.tidal” as a file type. We can add a &lt;code&gt;tidal.lua&lt;/code&gt; file in the &lt;code&gt;ftdetect/&lt;/code&gt; folder with the following code, which lets Neovim know about that file type.&lt;/p&gt;
&lt;span class=&quot;code-file&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/reillypascal/nvim/blob/main/ftdetect/tidal.lua&quot;&gt;ftdetect/tidal.lua&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;
&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filetype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	extension &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		tidal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tidal&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, as mentioned previously, Tidal Cycles uses Haskell&#39;s GHCi. All we need to do is start GHCi in a Neovim terminal window using the correct boot file, and then enter the code at the prompt, followed by a &lt;code&gt;&amp;lt;CR&amp;gt;&lt;/code&gt; character (the “Enter”/“Return” key). We can add keymaps that only apply to a buffer with a .tidal file by putting them in &lt;code&gt;ftplugin/tidal.lua&lt;/code&gt; and adding the &lt;code&gt;buffer = true&lt;/code&gt; option as shown below.&lt;/p&gt;
&lt;span class=&quot;code-file&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/reillypascal/nvim/blob/main/ftplugin/tidal.lua&quot;&gt;ftplugin/tidal.lua&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;
&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- open GHCi REPL in term split using Tidal boot file&lt;/span&gt;
vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keymap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;localleader&gt;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;cmd&gt;10 split term://ghci -ghci-script=$TIDAL_BOOT_PATH/BootTidal.hs %&amp;lt;cr&gt;:startinsert&amp;lt;cr&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; desc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Boot Tidal interpreter and open in terminal split&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; noremap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this keymap is local to the buffer, we use the &lt;code&gt;&amp;lt;localleader&amp;gt;&lt;/code&gt; key, which I have mapped to “,“. The command that this keymap runs enters command mode (&lt;code&gt;:&lt;/code&gt;); creates a terminal split that&#39;s 10 characters high; runs GHCi with the Tidal boot file; and then switches to insert mode (Neovim&#39;s terminal defaults to normal mode).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$TIDAL_BOOT_PATH&lt;/code&gt; here could be replaced with a hardcoded path to the Tidal boot file, but using an environment variable like this lets me use my configuration on multiple computers/OSes. You can set this variable with the following line in your .bashrc or .zshrc file. Note that this is under &lt;code&gt;~/.cabal/share/&lt;/code&gt; (at least on my setup), but contains install-specific details, so you will need to locate the &lt;code&gt;BootTidal.hs&lt;/code&gt; file for your installation. If you want or need, you can manually copy &lt;a href=&quot;https://tidalcycles.org/docs/configuration/boot-tidal/&quot;&gt;the code for &lt;code&gt;BootTidal.hs&lt;/code&gt;&lt;/a&gt; into a file and use the directory where you store that file.&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;TIDAL_BOOT_PATH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.cabal/share/aarch64-osx-ghc-9.12.2-ea3d/tidal-1.10.1&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, I added two keymaps to let me run the current line and the current block. For the first one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;yy&lt;/code&gt; “yanks” the current line.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Control-w&lt;/code&gt; accesses window-related commands, and from here, &lt;code&gt;j&lt;/code&gt; will go down a window.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;p&lt;/code&gt; pastes the yanked line, &lt;code&gt;a&lt;/code&gt; enters insert mode &lt;em&gt;after&lt;/em&gt; the line (“append”), and &lt;code&gt;&amp;lt;CR&amp;gt;&lt;/code&gt; sends the line to GHCi.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Control-&#92;&lt;/code&gt; and &lt;code&gt;Control-n&lt;/code&gt; are needed together to exit insert mode in the terminal.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Control-w p&lt;/code&gt; goes to the previous window location.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Running all this from one keymap smoothly takes the current line and runs it in the terminal split.&lt;/p&gt;
&lt;p&gt;Next, we want to do a similar thing for a block of text, potentially including line breaks. (Neo)vim has the concept of &lt;a href=&quot;https://martinlwx.github.io/en/learn-to-use-text-objects-in-vim/&quot;&gt;text objects&lt;/a&gt;, and a block of text with blank lines before and after is a “paragraph” object. &lt;code&gt;yip&lt;/code&gt; (“yank inner paragraph“) will copy the required type of text block. I precede it with &lt;code&gt;&amp;quot;*&lt;/code&gt; to copy to the system clipboard instead of the unnamed &lt;a href=&quot;https://www.brianstorti.com/vim-registers/&quot;&gt;register&lt;/a&gt; used by default. This makes it easier for me to edit the contents of the register before pasting them.&lt;/p&gt;
&lt;p&gt;In order for GCHi/Tidal to accept a block with line breaks, the block needs to be preceded by &lt;code&gt;:{&lt;/code&gt; and followed by &lt;code&gt;:}&lt;/code&gt;, both on separate lines from the block. We can assign things to a register with e.g., &lt;code&gt;:let @* =&lt;/code&gt;, and I chain the required strings together with the existing contents of the register (represented with &lt;code&gt;@*&lt;/code&gt;). Vimscript uses the dot (&lt;code&gt;.&lt;/code&gt;) to chain text together. All told, here are the differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We use &lt;code&gt;yip&lt;/code&gt; instead of &lt;code&gt;yy&lt;/code&gt; to yank the paragraph.&lt;/li&gt;
&lt;li&gt;We yank it to/paste it from the &lt;code&gt;&amp;quot;*&lt;/code&gt; register, rather than the default.&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;:let&lt;/code&gt; to edit the contents of the register.&lt;/li&gt;
&lt;/ul&gt;
&lt;span class=&quot;code-file&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/reillypascal/nvim/blob/main/ftplugin/tidal.lua&quot;&gt;ftplugin/tidal.lua&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;
&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- yank current line; move to term; paste, enter append mode, &amp;lt;CR&gt;; back to normal mode; return to previous location&lt;/span&gt;
vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keymap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;localleader&gt;ee&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;[[ yy&amp;lt;C-w&gt;jpa&amp;lt;CR&gt;&amp;lt;C-&#92;&gt;&amp;lt;C-n&gt;&amp;lt;C-w&gt;p ]]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; desc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Evaluate current line&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; noremap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- yank current block to &quot;*; wrap in :{/:}; move to term; paste from &quot;*, enter append mode, &amp;lt;CR&gt;; back to normal mode; return to previous location&lt;/span&gt;
vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keymap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;localleader&gt;er&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;[[ &quot;*yip &amp;lt;cmd&gt;let @* = &quot;&#92;:&#92;{&#92;n&quot; . @* . &quot;&#92;:&#92;}&quot;&amp;lt;cr&gt; &amp;lt;C-w&gt;j&quot;*pa&amp;lt;CR&gt;&amp;lt;C-&#92;&gt;&amp;lt;C-n&gt;&amp;lt;C-w&gt;p ]]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; desc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Evaluate current block&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; noremap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, I add the following line to associate .tidal files with Haskell syntax. This works well, but note that the Haskell Language Server (HLS) may throw a fit on Tidal files — this has been an issue with some Vim/Neovim plugins, but weirdly not others. HLS is not needed for Tidal Cycles itself, but if you also want to write vanilla Haskell, be aware of this. I ended up using &lt;a href=&quot;https://github.com/reillypascal/nvim/blob/d84f72ada230811b0879cba3e2e92b4e8c5f4ca4/lua/config/lsp.lua#L12-L16&quot;&gt;this function&lt;/a&gt; and &lt;a href=&quot;https://github.com/reillypascal/nvim/blob/d84f72ada230811b0879cba3e2e92b4e8c5f4ca4/lua/config/lsp.lua#L29&quot;&gt;this line&lt;/a&gt; in my LSP configuration to prevent HLS from loading unless there is at least one Haskell project root marker in the folder. A bit of a hack, but it works for now.&lt;/p&gt;
&lt;span class=&quot;code-file&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/reillypascal/nvim/blob/main/ftplugin/tidal.lua&quot;&gt;ftplugin/tidal.lua&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;
&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- enables highlighting&lt;/span&gt;
vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;set ft=haskell&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;postscript&quot;&gt;Postscript&lt;/h2&gt;
&lt;p&gt;The tidal.nvim plugin is certainly still quite nice, and may be more your taste. The benefits I found from this project are 1) more flexibility over the default terminal split size, 2) direct access to the GHCi Tidal process in the resulting terminal, 3) a better understanding of my tools in general, and 4) the knowledge that all my setup depends on is the &lt;a href=&quot;https://codeberg.org/uzu/tidal&quot;&gt;base Tidal code&lt;/a&gt; staying maintained. In general, I find it fun and reassuring to make minimalist scripts to accomplish tasks, and this project was similarly satisfying.&lt;/p&gt;
&lt;p&gt;I plan to keep tinkering and writing about Tidal Cycles/Neovim, as well as Lilypond (which I mentioned at the beginning). I hope to see you then — until next time!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;In addition to the Conjure plugin, &lt;a href=&quot;https://github.com/Vigemus/iron.nvim&quot;&gt;iron.nvim&lt;/a&gt; is another general-purpose REPL plugin you could try. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/03/tidal-cycles-neovim-no-plugins-24-lines-of-lua/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;Tidal%20Cycles/Neovim:%20No%20Plugins,%2024%20Lines%20of%20Lua&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>Friendly Lilypond with Neovim &amp; Audacity</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2026/02/friendly-lilypond-with-neovim-audacity/</link>
            <guid>https://reillyspitzfaden.com/posts/2026/02/friendly-lilypond-with-neovim-audacity/</guid>
            <pubDate>Fri, 27 Feb 2026 17:35:05 GMT</pubDate>
            <description>I made a nice setup to write Lilypond scores in Neovim, while still being able to conveniently view and listen to them</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;p&gt;&lt;a href=&quot;https://lilypond.org/&quot;&gt;Lilypond&lt;/a&gt; is a program and language for writing musical scores using only text. The syntax is similar to &lt;a href=&quot;https://en.wikipedia.org/wiki/LaTeX&quot;&gt;LaTeX&lt;/a&gt;, and this can be easily converted to nice-looking PDF scores, as well as MIDI files for previewing/making mockups. In addition, there are e.g., Python libraries such as &lt;a href=&quot;https://abjad.github.io/&quot;&gt;Abjad&lt;/a&gt; that allow for algorithmic manipulation of musical notation. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/02/friendly-lilypond-with-neovim-audacity/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; I&#39;ve found that I enjoy writing text files more than I enjoy using graphical programs such as Dorico/Sibelius/the late Finale, so I&#39;ve recently been trying out Lilypond.&lt;/p&gt;
&lt;p&gt;Part of what I&#39;ve been enjoying about text files is that I&#39;ve gotten &lt;a href=&quot;https://github.com/reillypascal/nvim&quot;&gt;my Neovim setup&lt;/a&gt; to a place that&#39;s very pleasant and smooth to work with. Additionally, I&#39;ve developed joint issues, and even with a nice vertical mouse, typing exacerbates the joint issues much less.&lt;/p&gt;
&lt;p&gt;For writing Lilypond, the IDE-like program &lt;a href=&quot;https://www.frescobaldi.org/&quot;&gt;Frescobaldi&lt;/a&gt; is popular. It bundles a text editor with tools for handling boilerplate code, transposing and other musical tasks, and a MIDI player and score viewer that make it easy to see and hear the results of the code. Since I prefer using my own Neovim setup, I decided to try getting as much of the convenience of Frescobaldi as possible with the tools I prefer. I think it turned out pretty well! Let&#39;s have a look at how it works.&lt;/p&gt;
&lt;h2 id=&quot;neovim-setup&quot;&gt;Neovim Setup&lt;/h2&gt;
&lt;p&gt;First (and easiest), there is a nice Neovim plugin &lt;a href=&quot;https://github.com/martineausimon/nvim-lilypond-suite&quot;&gt;nvim-lilypond-suite&lt;/a&gt;. This adds syntax highlighting and PDF/playback keyboard shortcuts. I also use the &lt;a href=&quot;https://github.com/stevearc/conform.nvim&quot;&gt;conform.nvim&lt;/a&gt; plugin to perform automatic formatting (e.g., line breaks, spacing, etc.). This plugin communicates with existing formatting tools, and here I used the &lt;a href=&quot;https://python-ly.readthedocs.io/en/latest/&quot;&gt;python-ly&lt;/a&gt; command-line program. It has indentation/formatting options, and I made conform.nvim aware of these commands using &lt;a href=&quot;https://github.com/reillypascal/nvim/blob/0efaa3262aa483d052bbab00c2219893e2a2d8a1/lua/plugins/conform.lua#L72-L81&quot;&gt;this configuration here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This covers most of what Frescobaldi does, but for audio playback, there is one wrinkle that&#39;s specific to my needs that makes the provided playback not work. The nvim-lilypond-suite plugin uses &lt;a href=&quot;https://timidity.sourceforge.net/&quot;&gt;timidity&lt;/a&gt; or &lt;a href=&quot;https://www.fluidsynth.org/&quot;&gt;fluidsynth&lt;/a&gt; to convert MIDI files (which are just instructions to a synthesizer on the notes to play) to audio files. These two programs use a &lt;a href=&quot;https://en.wikipedia.org/wiki/SoundFont&quot;&gt;SoundFont&lt;/a&gt; — a predetermined collection of synthesizers — to convert the MIDI file to audio. In my compositions, I often like to have a &lt;a href=&quot;https://en.wikipedia.org/wiki/Max_(software)&quot;&gt;Max/MSP&lt;/a&gt; patch being controlled by e.g., a live MIDI keyboardist, and played alongside live instruments. This is unfortunately not compatible with timidity/fluidsynth.&lt;/p&gt;
&lt;h2 id=&quot;midi-playback&quot;&gt;MIDI Playback&lt;/h2&gt;
&lt;p&gt;macOS provides the &lt;a href=&quot;https://help.ableton.com/hc/en-us/articles/209774225-Setting-up-a-virtual-MIDI-bus&quot;&gt;IAC&lt;/a&gt; virtual MIDI connection, which allows me to send MIDI data between programs, including sending it to Max. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/02/friendly-lilypond-with-neovim-audacity/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; This means I just needed to find a program that can play the MIDI file that Lilypond generates into the IAC bus in order to hear my Max patches played as the keyboardist would play them.&lt;/p&gt;
&lt;p&gt;I looked for a surprisingly long time before realizing that Audacity &lt;a href=&quot;https://manual.audacityteam.org/man/note_tracks.html&quot;&gt;can load MIDI files&lt;/a&gt; and can play them back over IAC. It would work OK to drag and drop the files in, but I would miss the convenience of Frescobaldi automatically updating the MIDI file, and having to manually load the file and delete the previous ones would partly defeat the purpose of using Neovim to get away from the mouse.&lt;/p&gt;
&lt;p&gt;Fortunately, it turns out &lt;a href=&quot;https://manual.audacityteam.org/man/scripting.html&quot;&gt;Audacity can be scripted&lt;/a&gt;! First, go to Audacity &amp;gt; Preferences &amp;gt; Modules (Mac) or Edit &amp;gt; Preferences &amp;gt; Modules (Win), and make sure that “mod-script-pipe” is set to “Enabled.” The Audacity manual gives an &lt;a href=&quot;https://github.com/audacity/audacity/blob/master/au3/scripts/piped-work/pipe_test.py&quot;&gt;example Python script&lt;/a&gt; to confirm that the connection is now working. At the end of this file, note the line &lt;code&gt;do_command(&#39;Help: Command=Help&#39;)&lt;/code&gt;. This sends the message &lt;code&gt;Help: Command=Help&lt;/code&gt; to Audacity, and is a good template of how to send commands more generally.&lt;/p&gt;
&lt;p&gt;What I wanted to accomplish was have my script select any previously-opened tracks, delete them, and then open the latest version of my MIDI file. From looking at the &lt;a href=&quot;https://manual.audacityteam.org/man/scripting_reference.html&quot;&gt;scripting reference page&lt;/a&gt;, I concluded I would need the &lt;code&gt;SelAllTracks:&lt;/code&gt;, &lt;code&gt;RemoveTracks:&lt;/code&gt;, and &lt;code&gt;Import2:&lt;/code&gt; commands. You can see my use of them below:&lt;/p&gt;
&lt;span class=&quot;code-file&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://codeberg.org/reillypascal/forget/src/branch/main/pipe.py&quot;&gt;pipe.py&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;
&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; argparse
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;

parser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; argparse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ArgumentParser&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
parser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;add_argument&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;input&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;help&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.mid file to open&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

args &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parse_args&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reload_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    do_command&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SelAllTracks:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    do_command&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;RemoveTracks:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    do_command&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&#39;Import2: Filename=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

reload_file&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;ve excerpted the sections that I needed to change from the example. I used the &lt;a href=&quot;https://docs.python.org/3/library/argparse.html&quot;&gt;argparse&lt;/a&gt; library to handle command-line arguments — in this case, the MIDI file name — which is stored in &lt;code&gt;args.input&lt;/code&gt;. After that, I run the required Audacity scripting commands, and use a format string to send the name of the MIDI file to Audacity. Because of the “shebang” in the first line of the Python file, the script can be run like this: &lt;code&gt;./pipe.py &amp;lt;my-score&amp;gt;.midi&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;automatically-reloading-midi%2Fpdfs&quot;&gt;Automatically Reloading MIDI/PDFs&lt;/h2&gt;
&lt;p&gt;This is already nice, but I had also seen the utility &lt;a href=&quot;https://eradman.com/entrproject/&quot;&gt;&lt;code&gt;entr&lt;/code&gt;&lt;/a&gt;, which will run commands whenever a given file changes. The Bash script below runs two copies of &lt;code&gt;entr&lt;/code&gt; to watch the PDF and MIDI files for changes, and then open each file when it changes.&lt;/p&gt;
&lt;span class=&quot;code-file&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://codeberg.org/reillypascal/forget/src/branch/main/watch&quot;&gt;watch&lt;/a&gt;&lt;/p&gt;
&lt;/span&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# run lilypond on score file to ensure MIDI/PDF are made&lt;/span&gt;
lilypond &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;realpath &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# need to run `realpath` *after* MIDI/PDF generated&lt;/span&gt;
	&lt;span class=&quot;token assign-left variable&quot;&gt;midipath&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;realpath &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${1&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;.ly}&lt;/span&gt;.midi&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
	&lt;span class=&quot;token assign-left variable&quot;&gt;pdfpath&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;realpath &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${1&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;.ly}&lt;/span&gt;.pdf&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;# run two separate `entr` processes, one each to watch &amp;amp; open MIDI/PDF&lt;/span&gt;
	&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$midipath&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; entr ./pipe.py &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$midipath&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# different OSes need different actions to open PDF&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$OSTYPE&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;linux-gnu&quot;&lt;/span&gt;* &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
		&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$pdfpath&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; entr evince &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$pdfpath&lt;/span&gt;&quot;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$OSTYPE&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;darwin&quot;&lt;/span&gt;* &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
		&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$pdfpath&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; entr &lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$pdfpath&lt;/span&gt;&quot;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$OSTYPE&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cygwin&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;# POSIX compatibility layer and Linux environment emulation for Windows&lt;/span&gt;
		&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$pdfpath&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; entr start &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; /max &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$pdfpath&lt;/span&gt;&quot;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$OSTYPE&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;msys&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;# Lightweight shell and GNU utilities compiled for Windows (part of MinGW)&lt;/span&gt;
		&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$pdfpath&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; entr start &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; /max &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$pdfpath&lt;/span&gt;&quot;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
		&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unknown OS: PDF not opened&quot;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can run the script with &lt;code&gt;./watch &amp;lt;scorename&amp;gt;.ly&lt;/code&gt;. First, the script runs &lt;code&gt;lilypond &amp;lt;scorename&amp;gt;.ly&lt;/code&gt; to ensure that the required MIDI/PDF exist, even if I haven&#39;t remembered to generate them yet. I use &lt;code&gt;realpath&lt;/code&gt; to get the full path of the files and avoid any potential issues loading them (Audacity was the main source of trouble with this).&lt;/p&gt;
&lt;p&gt;The next block runs two copies of &lt;code&gt;entr&lt;/code&gt;. In both cases we use the command format &lt;code&gt;echo &amp;lt;file-to-watch&amp;gt; | entr &amp;lt;run-on-file-change&amp;gt;&lt;/code&gt;. For the PDF, we check the &lt;code&gt;$OSTYPE&lt;/code&gt; variable, since different operating systems have different PDF viewers. On Linux, Evince is common; macOS provides the &lt;code&gt;open&lt;/code&gt; command to open any file in the default program; and the Windows options are based on poking around the internet (note that I don&#39;t have a Windows machine to test them, so please check before using!)&lt;/p&gt;
&lt;h2 id=&quot;listening-in-max&quot;&gt;Listening in Max&lt;/h2&gt;
&lt;p&gt;Now that I can send MIDI messages between programs over a virtual MIDI device, it&#39;s pretty straightforward to use them in Max. Max has the &lt;code&gt;[notein]&lt;/code&gt;, &lt;code&gt;[ctlin]&lt;/code&gt; and &lt;code&gt;[bendin]&lt;/code&gt; objects for note, CC, and pitch bend data respectively. MIDI files generated by Lilypond send MIDI data over different channels for each instrument, and these objects can take arguments to listen to only one specific channel. The &lt;a href=&quot;https://www.snoize.com/MIDIMonitor/&quot;&gt;MIDI Monitor&lt;/a&gt; app is useful to see which channels are which, but Lilypond seems to assign increasing channel numbers from top to bottom in the score.&lt;/p&gt;
&lt;p&gt;In addition to running the Max patch for the keyboardist, Max is useful for hosting VST plugins to handle mockups for the other instruments. I found the &lt;code&gt;[midiselect]&lt;/code&gt; object useful for handling MIDI input, then send that into &lt;code&gt;[midiformat]&lt;/code&gt;. You can connect the right outlet of &lt;code&gt;[midiformat]&lt;/code&gt; to the &lt;code&gt;[vst~]&lt;/code&gt; object — this will give &lt;code&gt;[vst~]&lt;/code&gt; the “midievent” message format it needs. Also note that &lt;code&gt;[vst~]&lt;/code&gt; should have the &lt;code&gt;@autosave 1&lt;/code&gt; attribute set — this will ensure your VST settings are saved.&lt;/p&gt;
&lt;p&gt;If you don&#39;t use Max, note that most DAWs and other programs that can host virtual instruments (e.g., I&#39;ve tried this with Logic Pro, REAPER, and Mainstage) can also take input over a virtual MIDI bus. You could also connect to SuperCollider, Pure Data, VCV Rack, and many others.&lt;/p&gt;
&lt;h2 id=&quot;postscript&quot;&gt;Postscript&lt;/h2&gt;
&lt;p&gt;I&#39;ve been having a ton of fun with Lilypond, including &lt;a href=&quot;https://en.wikipedia.org/wiki/Microtonality&quot;&gt;microtones&lt;/a&gt;, algorithmic composition with Python/Abjad, and more. There&#39;s a lot of interesting stuff to discuss there, and I&#39;m planning to write a few more posts about these topics sometime soon. I hope to see you then — until next time!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I use “algorithmic” here in the sense of Iannis Xenakis&#39; &lt;a href=&quot;https://en.wikipedia.org/wiki/Formalized_Music&quot;&gt;&lt;em&gt;Formalized Music&lt;/em&gt;&lt;/a&gt; — i.e., handwritten algorithms without large, unethical datasets — not in the LLM sense. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/02/friendly-lilypond-with-neovim-audacity/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;While I haven&#39;t tried this on Linux yet, &lt;a href=&quot;https://qjackctl.sourceforge.io/qjackctl-screenshots.html&quot;&gt;QJackCtl&lt;/a&gt; and &lt;a href=&quot;https://wiki.archlinux.org/title/JACK_Audio_Connection_Kit&quot;&gt;JACK&lt;/a&gt; should allow routing MIDI between programs. &lt;a href=&quot;https://wiki.archlinux.org/title/PulseAudio/Examples#PulseAudio_through_JACK&quot;&gt;This page section&lt;/a&gt; also discusses getting JACK to play nice with PulseAudio. &lt;a href=&quot;https://www.nerds.de/en/loopbe1.html&quot;&gt;LoopBe1&lt;/a&gt; looks like a possibility for Windows, but I haven&#39;t tried it. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/02/friendly-lilypond-with-neovim-audacity/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;Friendly%20Lilypond%20with%20Neovim%20&amp;amp;%20Audacity&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>IndieWeb Movie Club: “Alien”</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2026/01/indieweb-movie-club-alien/</link>
            <guid>https://reillyspitzfaden.com/posts/2026/01/indieweb-movie-club-alien/</guid>
            <pubDate>Sat, 31 Jan 2026 23:53:51 GMT</pubDate>
            <description>Re-watching a sci-fi classic to discuss with IndieWeb friends, and thinking about the music and sound design</description>
            <content:encoded>
            
            

          
          
          &lt;p&gt;January&#39;s IndieWeb movie club is &lt;em&gt;Alien&lt;/em&gt; (1979), and it&#39;s hosted by Al Abut — &lt;a href=&quot;https://alabut.com/writing/alien1979/&quot;&gt;here is his introduction post for the month&lt;/a&gt;. I&#39;ve been in the mood to see some space horror lately. I saw a promotion for &lt;em&gt;Ash&lt;/em&gt; (2025), which looks like a solid B-movie, and that reminded me to add &lt;em&gt;Sunshine&lt;/em&gt; (2007) and &lt;em&gt;Event Horizon&lt;/em&gt; to my watch list. I&#39;ve also been wanting to watch more movies with my husband, so &lt;em&gt;Alien&lt;/em&gt; is perfect for all of that. This is a re-watch for both of us — we&#39;ve seen it (and &lt;em&gt;Aliens&lt;/em&gt;) before, and liked them a lot.&lt;/p&gt;
&lt;h2 id=&quot;sound-design&quot;&gt;Sound Design&lt;/h2&gt;
&lt;p&gt;A few things stood out to me about the sound design of the film. The first happens a few times throughout. When the initial exploration party leaves the ship, the sound environments inside/outside the vessel have a stark contrast, and the cuts between them are immediate. Later, as the face hugger first attacks Kane, the camera cuts rapidly from attack, only allowing the sudden noise to sound for an instant.&lt;/p&gt;
&lt;p&gt;A similar contrast happens when Lambert and Parker are loading coolant into an escape ship while Ripley gets the escape shuttle ready. Ripley&#39;s surroundings are well-lit and quiet, while Lambert and Parker work noisily, loading the coolant, with Lambert being audibly agitated as well. Here as well, the cuts between the sound environments are immediate and jarring.&lt;/p&gt;
&lt;p&gt;To my ear, these contrasting sound environments emphasize the otherness of the alien, with the suddenness of the cuts exaggerating the difference between the sound environments. This also intensifies the harshness of the environment where the alien will appear, and heightens the sense of impending danger. Especially in the second example above, the time between the cuts speeds up slightly as the alien nears, and finally the loudness of Lambert and Parker&#39;s environment intrudes on Ripley&#39;s part of the ship as she receives their distress call and runs down the halls to help. In both cases, I got a sense from the cuts and their pacing of two narrative strands converging and joining.&lt;/p&gt;
&lt;h2 id=&quot;bart%C3%B3k-reference&quot;&gt;Bartók Reference&lt;/h2&gt;
&lt;p&gt;Jerry Goldsmith&#39;s score includes a passage (from about 1:11:55 to 1:12:30 in the film, or in &lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/01/indieweb-movie-club-alien/www.youtube.com/watch?v=9PGvL2L2irw&amp;amp;t=25s&quot;&gt;this clip&lt;/a&gt;) that reminds me very strongly of &lt;a href=&quot;https://www.youtube.com/watch?v=QElT9KD4uX8&amp;amp;t=974s&quot;&gt;another passage&lt;/a&gt; from Béla Bartók&#39;s &lt;em&gt;Music for Strings, Percussion and Celesta&lt;/em&gt; (1936), movement III.&lt;/p&gt;
&lt;p&gt;I initially wondered if this was the actual Bartók piece and had to look up the soundtrack on Wikipedia to confirm that it wasn&#39;t. I also remembered that the actual Bartók piece appeared in the score for Stanley Kubrick&#39;s &lt;em&gt;The Shining&lt;/em&gt; as the boy Danny rides his tricycle through the endless halls of the hotel. While my first thought was that Goldsmith and/or director Ridley Scott were inspired by Kubrick, since &lt;em&gt;The Shining&lt;/em&gt; came out a year later (1980), that&#39;s of course not possible.&lt;/p&gt;
&lt;p&gt;At any rate, the sinuously winding melody in the high strings, and especially the piano octaves ascending and descending chromatically sound similar enough that I imagine Goldsmith was influenced by the earlier piece. It seems that someone else also had the same thought — I was able to find &lt;a href=&quot;https://www.reddit.com/r/StanleyKubrick/comments/12mh72d/is_it_just_me_or_are_these_two_songs_from_the/&quot;&gt;this thread on Reddit&lt;/a&gt;, asking if others thought the passages sounded similar.&lt;/p&gt;
&lt;p&gt;Interestingly, this music does not appear to have been written for the film. According to &lt;a href=&quot;https://www.filmtracks.com/titles/alien.html&quot;&gt;this review&lt;/a&gt;, Scott and Rawlings borrowed significantly from Goldsmith&#39;s earlier score to &lt;em&gt;Freud&lt;/em&gt; (1962), and &lt;a href=&quot;https://www.youtube.com/watch?v=4902R56BXCY&amp;amp;t=27s&quot;&gt;this title from &lt;em&gt;Freud&lt;/em&gt;&lt;/a&gt; appears to be the same music as the scene in &lt;em&gt;Alien&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;I appreciated that this month, there was &lt;a href=&quot;https://events.indieweb.org/2026/01/january-movie-club-DvPcQUkN8eBo&quot;&gt;an event&lt;/a&gt; where people got together on a call and discussed the movies in real time. This both got me to watch the film earlier in the month than I would have otherwise, and was a fun addition to the written discussions. I liked this film choice a lot, and I&#39;m definitely planning to participate in future movie clubs.&lt;/p&gt;
&lt;p&gt;Until next time!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;IndieWeb%20Movie%20Club:%20%E2%80%9CAlien%E2%80%9D&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>Sending Webmentions with a Simple Shell Script</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2026/01/sending-webmentions-with-a-simple-shell-script/</link>
            <guid>https://reillyspitzfaden.com/posts/2026/01/sending-webmentions-with-a-simple-shell-script/</guid>
            <pubDate>Wed, 07 Jan 2026 18:25:04 GMT</pubDate>
            <description>I came across a convenient way to send webmentions from my terminal using a simple Bash script.</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;p&gt;I came across a convenient way to send webmentions from my terminal using a simple Bash script. &lt;a href=&quot;https://indieweb.org/webmention-implementation-guide#One-liner_webmentions&quot;&gt;This page on the IndieWeb wiki&lt;/a&gt; includes a shell script one-liner. It references an older version of the webmention standard, but I was able to figure it out and get it working with my setup. I have the following in &lt;a href=&quot;https://github.com/reillypascal/personalsite-ssg/blob/70701a0af5980082df23f2c89651af6fb7be6d7f/send-wm&quot;&gt;a file titled &lt;code&gt;send-wm&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;my_url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;target_url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$2&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;source=&lt;span class=&quot;token variable&quot;&gt;$my_url&lt;/span&gt;&amp;amp;target=&lt;span class=&quot;token variable&quot;&gt;$target_url&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$target_url&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;rel=&quot;webmention&quot;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-E&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;https?://[^ &quot;&gt;]+&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;uniq&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The example had &lt;code&gt;rel=&amp;quot;http://webmention.org/&amp;quot;&lt;/code&gt; instead of &lt;code&gt;rel=&amp;quot;webmention&amp;quot;&lt;/code&gt;, which is the current standard. It also included an extra step — &lt;code&gt;sed &#39;s/rel=&amp;quot;webmention&amp;quot;//&#39;&lt;/code&gt;. I assume this was originally supposed to read something like &lt;code&gt;sed &#39;s/rel=&amp;quot;http:&#92;/&#92;/webmention.org&#92;/&amp;quot;//&#39;&lt;/code&gt;, which would be necessary to make sure there is only one &amp;quot;http(s)&amp;quot; to &lt;code&gt;grep&lt;/code&gt; for. At any rate, that part doesn&#39;t appear to be currently necessary. (UPDATE: I edited the IndieWeb wiki to reflect the current version of this script!)&lt;/p&gt;
&lt;p&gt;This script can be used as follows. Note that to do this, you will first need to run &lt;code&gt;sudo chmod +x send-wm&lt;/code&gt; to make the script executable:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;./send-wm &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;your_url&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;target_url&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The basic command to send a webmention using cURL looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;source=&lt;span class=&quot;token variable&quot;&gt;$your_url&lt;/span&gt;&amp;amp;target=&lt;span class=&quot;token variable&quot;&gt;$target_url&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$targets_webmention_endpoint&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script inserts the provided source/target URLs into the statement, and the part inside the parentheses evaluates to the webmention endpoint from the target page.&lt;/p&gt;
&lt;p&gt;I also added this to &lt;a href=&quot;https://reillyspitzfaden.com/wiki/tutorials/webmention-tutorial/#sending-webmentions-(command-line)&quot;&gt;my webmentions tutorial&lt;/a&gt; — I like to do that so all my webmention info is in one place. I regularly update this, and there&#39;s a table of contents to jump to specific sections.&lt;/p&gt;
&lt;h2 id=&quot;future-plans&quot;&gt;Future Plans&lt;/h2&gt;
&lt;p&gt;I imagine it&#39;s possible to make a version of this that only needs the URL for the post sending the mention, and can parse out a list of URLs linked from that page and try sending webmentions to each, handling any failure gracefully. If I get around to doing that, I&#39;ll write another post and add it to the tutorial. Until next time!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;Sending%20Webmentions%20with%20a%20Simple%20Shell%20Script&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>Music Listened in 2025</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2026/01/music-listened-in-2025/</link>
            <guid>https://reillyspitzfaden.com/posts/2026/01/music-listened-in-2025/</guid>
            <pubDate>Mon, 05 Jan 2026 15:41:00 GMT</pubDate>
            <description>Artists, albums, and songs I discovered, revisited, and otherwise enjoyed in 2025</description>
            <content:encoded>
            
            

          
          
          &lt;p&gt;Here&#39;s some of the music I&#39;ve listened to in 2025 — things in regular rotation, things from a while ago that I&#39;ve revisited, and new (or new-to-me) music I&#39;ve added to my library of digital downloads/CDs.&lt;/p&gt;
&lt;h2 id=&quot;new-listens&quot;&gt;New Listens&lt;/h2&gt;
&lt;h3 id=&quot;charli-xcx&quot;&gt;Charli XCX&lt;/h3&gt;
&lt;p&gt;I&#39;ve listened to Charli XCX since before &lt;em&gt;Vroom Vroom&lt;/em&gt; came out, but as is often the case with artists I like, I&#39;ve been moving quite slowly through her discography. I hadn&#39;t given &lt;em&gt;Number 1 Angel&lt;/em&gt; a proper listen, but I currently have “Lipgloss” on repeat. I love the over-the-top, scratchy synth design, and I actually tried my hand at recreating the lead part, to some success. Looks like the key is something that has a bright, buzzy formant — I tried using a kick drum-based wavetable, and some oscillator sync.&lt;/p&gt;
&lt;h3 id=&quot;fire-toolz&quot;&gt;Fire-Toolz&lt;/h3&gt;
&lt;p&gt;Fire-Toolz&#39; first album, &lt;em&gt;Interbeing&lt;/em&gt; came out in 2017. I first heard of her with 2018&#39;s &lt;em&gt;Skinless X-1&lt;/em&gt;, and while I bought &lt;em&gt;Interbeing&lt;/em&gt; shortly after, I never really got into it until this year. This is common with me — Autechre, for example is pretty much my favorite group, but it took me a long time to get into albums other than &lt;em&gt;Confield&lt;/em&gt; and &lt;em&gt;Draft 7.30&lt;/em&gt;. This year I found that I actually really like the first track on &lt;em&gt;Interbeing&lt;/em&gt;, “Yellow Rose-Fire.”&lt;/p&gt;
&lt;p&gt;She also released a new mini-EP &lt;a href=&quot;https://fire-toolz.bandcamp.com/album/private-angel-message&quot;&gt;&lt;em&gt;Private Angel Message&lt;/em&gt;&lt;/a&gt; “to hold us over until the next album.” Track 1 mixes a pounding disco beat, sparkly synths, chugging guitars, and metal screams. Track 2 is a metal-infused cover of a Trammell Starks song originally written for the Weather Channel — I remember reading an article where Angel Marcloid listed some of his work among her favorite music. Track 3 is an ambient electronic collage including acoustic guitar/voice recordings with lyrics no longer remembered by the artist.&lt;/p&gt;
&lt;h3 id=&quot;william-fields&quot;&gt;William Fields&lt;/h3&gt;
&lt;p&gt;I got to see William Fields live with Autechre in Philadelphia this October, and this next one is actually a live recording of that concert! It was very cool to see live, and I&#39;m glad he decided to release the set. It&#39;s available &lt;a href=&quot;https://williamfields.bandcamp.com/album/opening-for-autechre-in-philadelphia&quot;&gt;here on Bandcamp&lt;/a&gt;. William Fields &lt;a href=&quot;https://arstechnica.com/gaming/2020/09/turning-code-into-music-anagrams-open-source-marxism-and-vr-raves/&quot;&gt;uses REAPER&#39;s JSFX scripting tools&lt;/a&gt; to create custom interfaces for glitchy, rhythmically uneasy algorithmic music. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/01/music-listened-in-2025/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id=&quot;autechre&quot;&gt;Autechre&lt;/h3&gt;
&lt;p&gt;At the very end of 2025, I got Autechre&#39;s most recent live album, &lt;em&gt;AE_2022–&lt;/em&gt;. It&#39;s taken me the longest to get into their live albums. The sets tend to be a long, monolithic performance that slowly morphs from one idea to the next over the course of an hour or more. Both in my composition and listening, I tend to prefer short and succinct, but after experiencing such a set live, I have a greater appreciation for the duo&#39;s live work — it feels more familiar now, and takes me back to a good experience I had.&lt;/p&gt;
&lt;p&gt;Another reason I hadn&#39;t gotten this particular album is that (at 19 tracks of around an hour each) it&#39;s usually fairly expensive. It turned out that at the end of December/beginning of January, the &lt;a href=&quot;https://bleep.com/release/483225-autechre-ae2022&quot;&gt;Bleep listing of the album&lt;/a&gt; was on sale for half price, and I ended up buying and enjoying it. It has me interested in re-listening to the Philadelphia set I attended, or hearing some other sets from their 2025 North America tour — would be cool if they release those.&lt;/p&gt;
&lt;h3 id=&quot;kelley-sheehan&quot;&gt;Kelley Sheehan&lt;/h3&gt;
&lt;p&gt;Kelley Sheehan is a composer whose work I&#39;ve admired for a while. She pulls from a mix of underground noise music and contemporary classical performance practices, and her music has a wild riot of colors and strange sounds. I actually wrote about her composition “&lt;a href=&quot;https://www.youtube.com/watch?v=gH3kxga4_JY&quot;&gt;Talk Circus&lt;/a&gt;” for no-input mixer and percussion in my composition dissertation, along with a few other pieces that use audio equipment as instruments. I&#39;d been wanting to have a release of hers I could buy from e.g., Bandcamp, so I was excited to see her release &lt;em&gt;new ears&lt;/em&gt; with the Hoodwink Duo.&lt;/p&gt;
&lt;h3 id=&quot;meljoann&quot;&gt;Meljoann&lt;/h3&gt;
&lt;p&gt;Meljoann describes their music as “hyperjack” — i.e., a hyperpop-like cousin of new jack swing. They released &lt;a href=&quot;https://meljoann.com/posts/status/&quot;&gt;&lt;em&gt;Status&lt;/em&gt;&lt;/a&gt; early this year, and I&#39;ve been enjoying it a lot. “Translate Me” has a thick, detuned bass against floating synths and vocals, and a reggaeton-like beat. “Data Ghost” has trap hi-hats, a funky synth bassline, and an 80s power pop-like guitar solo in the middle. It&#39;s all very fun and danceable, with a healthy dose of chaos and genre-bending. Note that in addition to more well-known platforms, including Bandcamp, you can buy this album directly from the artist, via their &lt;a href=&quot;https://faircamp.meljoann.com/status/&quot;&gt;Faircamp page&lt;/a&gt;! &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/01/music-listened-in-2025/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;regular-rotation&quot;&gt;Regular Rotation&lt;/h2&gt;
&lt;p&gt;While I did listen to some new music this year, most of my listening has been familiar, comfortable things. Some of those tracks:&lt;/p&gt;
&lt;h3 id=&quot;autechre-1&quot;&gt;Autechre&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;“Dropp”&lt;/li&gt;
&lt;li&gt;“VL AL 5”&lt;/li&gt;
&lt;li&gt;“Tapr”&lt;/li&gt;
&lt;li&gt;“Gantz Graf”&lt;/li&gt;
&lt;li&gt;“qplay”&lt;/li&gt;
&lt;li&gt;“Altibzz”&lt;/li&gt;
&lt;li&gt;“V-Proc”&lt;/li&gt;
&lt;li&gt;“plyPhon”&lt;/li&gt;
&lt;li&gt;“Fol3”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;fire-toolz-1&quot;&gt;Fire-Toolz&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;“Soaked: Another Name for Everything”&lt;/li&gt;
&lt;li&gt;“✓ iNTERBEiNG”&lt;/li&gt;
&lt;li&gt;“Experience ☆ Slips ☆ Away”&lt;/li&gt;
&lt;li&gt;“Screamography”&lt;/li&gt;
&lt;li&gt;“Second Life”&lt;/li&gt;
&lt;li&gt;“Response To Subdivisions ☾”&lt;/li&gt;
&lt;li&gt;“✓ BEiNG”&lt;/li&gt;
&lt;li&gt;“Clear Light”&lt;/li&gt;
&lt;li&gt;“觀音 Prayer For The Abuser (abridged)”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;william-fields-1&quot;&gt;William Fields&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;“Doquado”&lt;/li&gt;
&lt;li&gt;“Athal”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A fun note on this list is that my spouse and I used ”Altibzz” as background music in a video we showed at our wedding! My spouse told me they just wanted something that wasn&#39;t too cheesy, and I find this track to be hauntingly beautiful, and was happy with how it turned out.&lt;/p&gt;
&lt;h2 id=&quot;revisited&quot;&gt;Revisited&lt;/h2&gt;
&lt;h3 id=&quot;subtle%2F13-%26-god&quot;&gt;Subtle/13 &amp;amp; God&lt;/h3&gt;
&lt;p&gt;Subtle and 13 &amp;amp; God are both groups I loved in high school, and I&#39;ve been listening to them again this year. Both groups are related: 13 &amp;amp; God is a collaboration between Doseone and Jel of Themselves and Subtle (and of other groups from the Anticon experimental hip-hop collective), and members of The Notwist. Both Subtle and 13 &amp;amp; God perform a mix of early 2000s alternative hip hop, trip hop, and indie rock, with rapper Doseone performing in a strange, dense style with dreamlike, abstract lyrics. I&#39;ve been mostly listening to 13 &amp;amp; God&#39;s self-titled debut, and Subtle&#39;s &lt;em&gt;For Hero: For Fool&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;postscript&quot;&gt;Postscript&lt;/h2&gt;
&lt;p&gt;Thanks for reading! If you also listen to any of the artists on this list (or similar ones you think I might like) I would love to hear about it — I have comments, email, and a webmention field below. Until next time!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Here, by “algorithmic,” I&#39;m not referring to LLMs or related technology. Rather, as far as I understand, Fields uses probability and more ”classic” algorithms such as Markov chains — algorithms he has set up, rather than tools trained on others&#39; work. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/01/music-listened-in-2025/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://simonrepp.com/faircamp/&quot;&gt;Faircamp&lt;/a&gt; is a tool for creating a music streaming/download sales page directly on the artist&#39;s website, allowing listeners to pay the artist directly. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2026/01/music-listened-in-2025/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;Music%20Listened%20in%202025&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>On a Knife-Edge</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/fiction/on-a-knife-edge/</link>
            <guid>https://reillyspitzfaden.com/fiction/on-a-knife-edge/</guid>
            <pubDate>Tue, 09 Dec 2025 20:13:14 GMT</pubDate>
            <description>&lt;i&gt;The clouds cascade above you in incredible detail. In places, their bases form an impenetrable ceiling, but that ceiling is temporary. Wisps of shadow float against glowing shards of the sun, illuminating another layer behind…&lt;/i&gt;</description>
            <content:encoded>
            
            

          
          
          &lt;p&gt;The clouds cascade above you in incredible detail. In places, their bases form an impenetrable ceiling, but that ceiling is temporary. Wisps of shadow float against glowing shards of the sun, illuminating another layer behind. The effect continues below the clouds. As you move forward, you can see the boundaries between light and dark projected on the ground. The changing brightness pulsates as it sweeps over you.&lt;/p&gt;
&lt;p&gt;As you learn the contours above you, the scale of things shifts. The sky is closer now: close enough to visit, it seems, and its vault looks comfortable, as if you could live inside. You are much larger than before. Your body warps — for the moment, more spirit than matter — and your extremities flow up to nestle between the darkened floors, in the space of light between. You feel your heart rate slow, coming to rest after the day.&lt;/p&gt;
&lt;p&gt;Remember when you would stir a giant basin of treacle in your mind&#39;s eye? Remember how the thoughts would catch on the circling waves, tearing and smearing, disappearing into the uniform surface? This feels kind of like then.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Eyes on the road&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The violence of collision drifts into your thoughts. It seems to you now as if even its specter transforms your flesh into something new and strange.&lt;/p&gt;
&lt;p&gt;Your flesh remembers the &lt;em&gt;slam&lt;/em&gt; of impact, mere inches from your left arm, only the door&#39;s thin layer of metal and plastic separating you. It&#39;s as if those materials have punched an imprint into your skin and bone, staying there ever since. The acrid smell lingers in your nostrils, a mix of spent propellant and that strange harshness that sprang from within your head.&lt;/p&gt;
&lt;p&gt;You&#39;ve heard it said that if a machine never leaves your side, how different is it from an implant? You wonder where your body ends. You wonder what you would have to remove to become yourself again.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Eyes on the road&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You glide along a knife-edge. All around is peaceful. It would be so easy to float into someone else, into something stable and fixed. How can you be flying and yet still at the same time?&lt;/p&gt;
&lt;div class=&quot;dinkus&quot;&gt;***&lt;/div&gt;
&lt;p&gt;You return your eyes to the clouds. They have blended together into a murky gray while you weren&#39;t looking.&lt;/p&gt;
&lt;div class=&quot;centered asterism&quot;&gt;⁂&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;On%20a%20Knife-Edge&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>Reverb Part 2—Natural Rooms with Allpass Rings</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/</guid>
            <pubDate>Sun, 28 Sep 2025 16:16:34 GMT</pubDate>
            <description>I discuss some lovely-sounding reverbs that make echo densities build up over time, as in a real room. They&#39;re available in my VST/AU plugin, and I link to Max/MSP versions as well.</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/notes-photos.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;p&gt;Continuing from &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1&quot;&gt;a previous post&lt;/a&gt;, today we&#39;ll be looking at another collection of reverb algorithms that I&#39;ve implemented in my &lt;a href=&quot;https://github.com/reillypascal/RSAlgorithmicVerb/releases&quot;&gt;reverb plugin&lt;/a&gt;. With only a small increase in complexity over last time, we can get a reverb that grows in density over its duration, much as a real room would, and that sounds smoother and less metallic.&lt;/p&gt;
&lt;p&gt;There are two key differences between the designs here and &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#the-classic-schroeder-reverberator&quot;&gt;the Schroeder reverb I discussed previously&lt;/a&gt;: 1) in this design we will “nest” an allpass filter within another allpass filter, causing the echo density to grow over time, and 2) we will connect the ends of a chain of allpasses into one large “ring,” smoothing out the sound. Let&#39;s discuss how this all works!&lt;/p&gt;
&lt;h2 id=&quot;nested-allpass-filters&quot;&gt;Nested Allpass Filters&lt;/h2&gt;
&lt;p&gt;As we &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#allpass-filters&quot;&gt;previously discussed&lt;/a&gt;, an allpass filter is created by a feedforward and feedback delay in series, with the gain of one of them being negative. This lets all frequencies through at equal amplitude, but delays different frequencies by different amounts, “smearing” or “blurring” the rhythmic attacks of an incoming sound, which is excellent for simulating reverberation. For the diagrams below, note that both the feedforward and feedback can use the same delay line — the design we&#39;re using is equivalent to two separate delays in series.&lt;/p&gt;
&lt;p&gt;Schroeder found that the “smearing” from allpass filters multiplies the number of echoes about threefold. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; However, the number of echoes is roughly constant over the duration of the reverberation. In a real room, echoes continually build up over the course of a reverb tail. To achieve this buildup, Barry Vercoe and Miller Puckette propose replacing the delay in an allpass filter with another allpass filter creating a “nested” structure. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; Since an allpass filter will multiply the echoes that pass through it, taking the output of the inner allpass and feeding it back to the input will cause that multiplication to compound, increasing the number of echoes over time.&lt;/p&gt;
&lt;p&gt;Below are diagrams of this design. Note that in addition to using a single allpass filter as the delay, we can use multiple allpasses in series, as well as combining them with simple delays. Gardner uses both options depicted below in the reverb algorithms we&#39;ll discuss today. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; In the diagrams, &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D44E TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D456 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D45B TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; and &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D44E TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D45C TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D462 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D461 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; are the input and output; the &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c2295&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mo&gt;⊕&lt;/mo&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; symbol means the sum of two signals; and &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D454 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mn class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c31&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mn&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;g&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt;, &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D454 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mn class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c32&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mn&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;g&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt;, and &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D454 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mn class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c33&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mn&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;g&lt;/mi&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; are separate gain values, with these values made negative for feedforward signals.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/09/reverb_2/single-nested-allpass.webp&quot; alt=&quot;A DSP block diagram. Delay 1 and delay 2 are in series. There are feedforward and feedback values around delay 2, as well as around the entire chain of delays.&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;
&lt;p&gt;A block diagram of a nested allpass filter &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/09/reverb_2/double-nested-allpass.webp&quot; alt=&quot;A DSP block diagram. Delay 1, delay 2, and delay 3 are in series. There are feedforward and feedback values around delay 2 and another set around delay 3, as well as around the entire chain of delays.&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;
&lt;p&gt;A block diagram of a double nested allpass filter &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn4&quot; id=&quot;fnref4:1&quot;&gt;[4:1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;allpass-rings&quot;&gt;Allpass Rings&lt;/h2&gt;
&lt;p&gt;To multiply the echoes even further, Gardner chains a mix of standard and nested allpass filters in series, and then feeds the end of that chain into the chain&#39;s input. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn3&quot; id=&quot;fnref3:1&quot;&gt;[3:1]&lt;/a&gt;&lt;/sup&gt; Last time, we placed a low-pass filter in the feedback path of individual comb filters. This time, Gardner suggests placing a single low-pass filter in the feedback path for the entire chain. In both contexts, the purpose is to cause higher frequencies to decay faster, similar to the effect of the atmosphere in a real room.&lt;/p&gt;
&lt;p&gt;Gardner also suggests taking the output sound from multiple points along the allpass chain. These must be &lt;em&gt;between&lt;/em&gt; allpass filters, rather than inside an allpass — taking it from inside an allpass will transform the allpass into a comb, producing an undesirable metallic quality. The output signals can be scaled in different proportions to customize the decay profile of the reverb, and the gain in the global feedback path can be used to scale the decay time overall.&lt;/p&gt;
&lt;p&gt;Both the multiple outputs and the ring structure prevent this reverberator from being allpass. The ring structure in particular is a comb filter, which (&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#feedforward-and-feedback-delays&quot;&gt;as we discussed previously&lt;/a&gt;) introduces comb tooth-like peaks and valleys in the frequency spectrum. While Gardner does not go into depth about why the comb filtering is acceptable, my best understanding is that it&#39;s because the series allpasses add together to create a long delay time.&lt;/p&gt;
&lt;p&gt;As I discussed &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#series-allpasses&quot;&gt;here&lt;/a&gt;, and as the audio example below demonstrates, feedback combs with longer delay times create an audible “flutter,” and shorter times create a metallic sound. The long time in the allpass ring means no metallic sounds, and because the chain of nested allpasses multiplies the echo density so much, the flutter isn&#39;t meaningfully audible.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/09/reverb_2/clap_comb_demo.mp3&quot; title=&quot;feedback comb-filtered clap&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;h3 id=&quot;gardner&#39;s-reverb-designs&quot;&gt;Gardner&#39;s Reverb Designs&lt;/h3&gt;
&lt;p&gt;Below are the three designs Gardner gives. The small one is good for decay times between 0.38–0.57 seconds; the medium for 0.58–1.29 seconds; and the large for 1.30 seconds and above. Gardner does not find any particular rule for choosing decay times, noting that the process for designing the three topologies he describes was “purely empirical.” &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn3&quot; id=&quot;fnref3:2&quot;&gt;[3:2]&lt;/a&gt;&lt;/sup&gt; As with designing Schroeder reverbs, a good starting point seems to be to avoid delay times that easily divide into each other; this ensures the delays do not rhythmically align. The first number next to a delay is the time in milliseconds, and the float value in parentheses is the gain.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/09/reverb_2/small-room.webp&quot; alt=&quot;A DSP block diagram. There is a chain of delays and nested allpasses with two outputs taken between the delays, and a 4.2 kHz low pass filter in the feedback loop for the whole chain. Delay list: delay (24 ms); double nested allpass (outer: 35 ms &amp;amp; 0.3, inner: 22 ms &amp;amp; 0.4, 8.3 ms &amp;amp; 0.6); output (0.5 gain); nested allpass (outer: 66 ms &amp;amp; 0.1, inner: 30 ms &amp;amp; 0.4); output (0.5 gain)&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;
&lt;p&gt;Small room reverberator &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn3&quot; id=&quot;fnref3:3&quot;&gt;[3:3]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/09/reverb_2/medium-room.webp&quot; alt=&quot;A DSP block diagram. There is a chain of delays and nested allpasses with three outputs taken between the delays, and a 2.5 kHz low pass filter in the feedback loop for the whole chain. Delay list: double nested allpass (outer: 35 ms &amp;amp; 0.3, inner: 8.3 ms &amp;amp; 0.7, 22 ms &amp;amp; 0.5); output (0.5 gain); delay (5 ms); allpass (30 ms &amp;amp; 0.5); delay (67 ms); output (0.5 gain); delay (15 ms); gain; input; nested allpass (outer: 39 ms &amp;amp; 0.3, inner: 9.8 ms &amp;amp; 0.6); output (0.5 gain); delay (108 ms)&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;
&lt;p&gt;Medium room reverberator &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn3&quot; id=&quot;fnref3:4&quot;&gt;[3:4]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/09/reverb_2/large-room.webp&quot; alt=&quot;A DSP block diagram. There is a chain of delays and nested allpasses with two outputs taken between the delays, and a 2.6 kHz low pass filter in the feedback loop for the whole chain. Delay list: allpass (8 ms &amp;amp; 0.3); allpass (12 ms &amp;amp; 0.3); delay (4 ms); output (0.34 gain); delay (17 ms); nested allpass (outer: 87 ms &amp;amp; 0.5, inner: 62 ms &amp;amp; 0.25); delay (31 ms); output (0.14 gain); delay (3 ms) double nested allpass (outer: 120 ms &amp;amp; 0.5, inner: 76 ms &amp;amp; 0.25, 30 ms &amp;amp; 0.25); output (0.14 gain)&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;
&lt;p&gt;Large room reverberator &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn3&quot; id=&quot;fnref3:5&quot;&gt;[3:5]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that these designs have a mix of delays, allpasses, nested allpasses, and double nested allpasses. The medium reverberator also has two inputs, and applies the gain both in the feedback loop and in the middle of the allpass chain, directly before the second input.&lt;/p&gt;
&lt;h2 id=&quot;reverb-characteristics&quot;&gt;Reverb Characteristics&lt;/h2&gt;
&lt;p&gt;Gardner describes these as “room” reverbs. While they are synthesized, not real rooms, it&#39;s still useful to look at the characteristics of the types of reverbs Gardner is emulating. First off, &lt;a href=&quot;https://valhalladsp.com/2018/05/14/effect-o-pedia-reverb-types/&quot;&gt;Sean Costello of Valhalla DSP describes&lt;/a&gt; room reverbs as having prominent early reflections.&lt;/p&gt;
&lt;p&gt;Early reflections refer to sounds within the first 80 ms or so, followed by late reflections. Summarizing psychoacoustic research on the matter, Rungta et al. note that early reflections positively correlate with “the perception of auditory spaciousness,” are “very important in concert halls,” and can “improve speech clarity in rooms.” &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Rungta et al. also note that these early reflections help the listener hear the shape of the room. My best guess for why is because these reflections have traveled a short distance to the listener, maybe only reflecting a single time. This implies for “room”-type reverbs that we get a particularly strong sense of the room&#39;s dimensions. In my next post, I&#39;ll describe Dattorro&#39;s 1997 “plate”-style allpass ring, and we&#39;ll hear how that reverb sounds more amorphous, with less sense of space.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/09/reverb_2/early-late-reflections.webp&quot; alt=&quot;A list of vertical lines representing echoes that decay as we move toward the right. There is a gap between direct sound and early reflections, early reflections are sparse, and reverberation at the end is dense.&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;
&lt;p&gt;Direct sound, early reflections, and late reflections/reverberation; horizontal axis is time and vertical is amplitude &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn5&quot; id=&quot;fnref5:1&quot;&gt;[5:1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In addition to these early reflections, Costello describes a dense sound and a short decay — these are usually smaller rooms than the &lt;a href=&quot;https://en.wikipedia.org/wiki/Reverberation_room&quot;&gt;reverb chambers&lt;/a&gt; in recording studios. Rooms also quickly build up echo density (here accomplished with the nested allpasses) and tend to be “colored,” i.e., they emphasize some frequencies over others. You may want to use an EQ on your reverb send track to reduce low frequencies on these algorithms, depending on your application. Costello suggests these may be useful for adding body to drums and acoustic instruments.&lt;/p&gt;
&lt;h2 id=&quot;postscript&quot;&gt;Postscript&lt;/h2&gt;
&lt;p&gt;These algorithms are all available in &lt;a href=&quot;https://github.com/reillypascal/RSAlgorithmicVerb/releases&quot;&gt;my algorithmic reverb plugin&lt;/a&gt; via the dropdown menu at the bottom. I also have Max/MSP abstractions of them &lt;a href=&quot;https://codeberg.org/reillypascal/rs.reverb&quot;&gt;available on my Codeberg&lt;/a&gt;. I would love to hear if you try them out!&lt;/p&gt;
&lt;p&gt;As I mentioned, in my next post I&#39;ll cover the famous 1997 Dattorro plate algorithm that also uses an allpass ring structure. This is in Max/MSP as the [yafr2] abstraction, and in the &lt;a href=&quot;https://library.vcvrack.com/Valley/Plateau&quot;&gt;Valley Audio “Plateau” module&lt;/a&gt; for VCV Rack, among numerous other places, and is likely based on an algorithm from the Lexicon 224/480 reverb units. I hope to see you then!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;M. R. Schroeder, “Natural sounding artificial reverberation,” in Audio Engineering Society Convention 13, Audio Engineering Society, 1961. Accessed: Dec. 29, 2024. [Online]. Available: &lt;a href=&quot;https://www.aes.org/e-lib/download.cfm?ID=343&quot;&gt;https://www.aes.org/e-lib/download.cfm?ID=343&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref1:1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Unfortunately this paper is unpublished, and I was only able to find William Gardner&#39;s description of it, &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fn1&quot; id=&quot;fnref1:1&quot;&gt;[1:1]&lt;/a&gt;&lt;/sup&gt; but here is the citation:&lt;/p&gt;
&lt;p&gt;B. Vercoe and M. Puckette, “Synthetic Spaces — Artificial Acoustic Ambiance from Active Boundary Computation,” 1985, &lt;em&gt;Music and Cognition Office at MIT Media Lab, Boston, MA&lt;/em&gt;. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;W. G. Gardner, “A realtime multichannel room simulator,” &lt;em&gt;J. Acoust. Soc. Am&lt;/em&gt;, vol. 92, no. 4, p. 2395, 1992. Available: &lt;a href=&quot;https://pubs.aip.org/asa/jasa/article/92/4_Supplement/2395/646024/A-real-time-multichannel-room-simulator&quot;&gt;https://pubs.aip.org/asa/jasa/article/92/4_Supplement/2395/646024/A-real-time-multichannel-room-simulator&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref3:1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref3:2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref3:3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref3:4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref3:5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;H. Mikelson, “A Csound Multi-Effects Processor.” Accessed: Sept. 26, 2025. [Online]. Available: &lt;a href=&quot;https://www.eumus.edu.uy/eme/ensenanza/electivas/csound/materiales/book_chapters/24mikelson/24mikelson.html&quot;&gt;https://www.eumus.edu.uy/eme/ensenanza/electivas/csound/materiales/book_chapters/24mikelson/24mikelson.html&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref4:1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;A. Rungta, N. Rewkowski, R. Klatzky, and D. Manocha, “P-Reverb: Perceptual Characterization of Early and Late Reflections for Auditory Displays,” in 2019 IEEE Conference on Virtual Reality and 3D User Interfaces (VR), Mar. 2019, pp. 455–463. doi: 10.1109/VR.2019.8797914. Available: &lt;a href=&quot;https://arxiv.org/pdf/1902.06880&quot;&gt;https://arxiv.org/pdf/1902.06880&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/09/reverb-part-2/#fnref5:1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;Reverb%20Part%202%E2%80%94Natural%20Rooms%20with%20Allpass%20Rings&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>A Certain Sickness</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/09/september-indieweb-carnival/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/09/september-indieweb-carnival/</guid>
            <pubDate>Thu, 25 Sep 2025 12:38:00 GMT</pubDate>
            <description>&lt;i&gt;You feel that a certain sickness has been pervading your life. It&#39;s a dull ache that suffuses subtly through many unrelated things, a background radiation to your everyday…&lt;/i&gt;</description>
            <content:encoded>
            
            

          
          
          &lt;p&gt;You feel that a certain sickness has been pervading your life. It&#39;s a dull ache that suffuses subtly through many unrelated things, a background radiation to your everyday. You have tried removing possible sources, one by one, but in the wake of each, there is less to distract you, and the sickness only changes its form.&lt;/p&gt;
&lt;p&gt;One day, you are walking back from your lunch and you cast your eyes on the ground. Even amid the concrete, asphalt, and stones stretching on forever, there is life. Shoots of green dart out from cracks. In a few patches the architects have left open, small trees. Further down your walk, someone has kept a small patch of woodchips and soil, holding a collection of flowers and bushes. Even the loose clippings and leaves on the concrete have a depth to them.&lt;/p&gt;
&lt;p&gt;You feel the acceleration of the earth below you, its wide surface supporting you, its pull keeping you from falling into space forever. You note that you haven&#39;t thought of the sickness in several minutes.&lt;/p&gt;
&lt;div class=&quot;centered&quot;&gt;*&amp;nbsp;*&amp;nbsp;*&lt;/div&gt;
&lt;p&gt;Late that evening, as you take out the recycling, you hear birds. Their calls trace out the space around you — the city filtering and reverberating the calls more or less, and in different ways, depending on distance and location. You hear them (for once) without the filter of your thoughts overlaid. Their calls do not mean anything in particular, at least to you, and for that, you are grateful.&lt;/p&gt;
&lt;p&gt;As your eyes pass over a patch of ground you see every day on returning home, you feel less real for a moment, but the patch is realer than it&#39;s been before. There are many others almost (but not quite) like it, and although your view is obstructed, you can sense them faintly beyond it.&lt;/p&gt;
&lt;p&gt;On hearing the crickets in the distance, you decide to linger a moment longer. One sound is a continuous wave of trilling, many voices blending into one. In front of that (you can feel the depth of sound now), individual scratches are much clearer, less blurred by the air between you.&lt;/p&gt;
&lt;p&gt;You recall that air functions roughly as a low-pass filter. You are at the bottom of a great ocean. It&#39;s really not that different from an ocean made of water, is it?&lt;/p&gt;
&lt;p&gt;The waters pulsate around you. It feels peaceful though.&lt;/p&gt;
&lt;p&gt;You continue back indoors.&lt;/p&gt;
&lt;h2 id=&quot;postscript&quot;&gt;Postscript&lt;/h2&gt;
&lt;p&gt;This was written for the September 2025 &lt;a href=&quot;https://indieweb.org/IndieWeb_Carnival&quot;&gt;IndieWeb blog carnival&lt;/a&gt;. &lt;a href=&quot;https://toground.link/second-person-birds/&quot;&gt;Sophia is hosting this month&lt;/a&gt;, and the theme is “second-person birds.” On her blog &lt;a href=&quot;https://toground.link/&quot;&gt;to ground&lt;/a&gt;, she writes about nature in the second person.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;A%20Certain%20Sickness&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>ルビとHTML</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/08/ruby-html/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/08/ruby-html/</guid>
            <pubDate>Tue, 19 Aug 2025 21:14:00 GMT</pubDate>
            <description>I took a few years of Japanese classes in undergrad, and today I&#39;m returning to that and discussing some HTML markup for Japanese and other East Asian languages.</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;p&gt;&lt;ruby&gt;最近&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;さいきん&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt;HTMLでルビを&lt;ruby&gt;振&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;ふ&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt;ることのやり&lt;ruby&gt;方&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;かた&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt;を&lt;ruby&gt;知&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;し&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt;た!&lt;/p&gt;
&lt;p&gt;Or in English, “I recently learned how to write &lt;a href=&quot;https://en.wikipedia.org/wiki/Ruby_character&quot;&gt;ruby characters&lt;/a&gt; in HTML!”&lt;/p&gt;
&lt;p&gt;Japanese writing uses multiple different sets of characters. Chinese &lt;a href=&quot;https://en.wikipedia.org/wiki/Ideogram&quot;&gt;ideographic&lt;/a&gt; characters (called “&lt;a href=&quot;https://en.wikipedia.org/wiki/Kanji&quot;&gt;kanji&lt;/a&gt;”) encode an idea rather than a sound and can have multiple pronunciations. &lt;a href=&quot;https://en.wikipedia.org/wiki/Kana&quot;&gt;Kana&lt;/a&gt; are phonetic characters that each represent one syllable.&lt;/p&gt;
&lt;p&gt;Ruby characters are kana placed above kanji to indicate the pronunciation — these are common in educational materials, children&#39;s/YA books, or for kanji outside the list of &lt;a href=&quot;https://en.wikipedia.org/wiki/J%C5%8Dy%C5%8D_kanji&quot;&gt;jōyō (“regular-use”) kanji&lt;/a&gt;, among other uses. Because there are many more kanji than kana, and their pronunciation can be complicated to learn, the ruby characters make reading easier.&lt;/p&gt;
&lt;p&gt;I took some Japanese classes in undergrad, and while I haven&#39;t kept up with it, I recently stumbled across the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-ruby-element&quot;&gt;HTML markup&lt;/a&gt; for ruby and thought it was a cool thing to know. The markup for the first word from the above example, “saikin” (“recently”) is shown below. The whole thing is enclosed in &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; tags; the &lt;code&gt;&amp;lt;rp&amp;gt;&lt;/code&gt; tag surrounds parentheses for the pronunciation; and &lt;code&gt;&amp;lt;rt&amp;gt;&lt;/code&gt; surrounds the phonetic gloss. This gives a fallback of placing the pronunciation in parentheses after the kanji — i.e., as 最近 (さいきん) — if a browser doesn&#39;t support rendering ruby characters, while allowing the parentheses to be hidden and the phonetic gloss placed above on browsers that do support this.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ruby&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;最近&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;(&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;rp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;さいきん&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;rt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;)&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;rp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ruby&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This all has gotten me a bit nostalgic for learning Japanese, and I think I&#39;ll try tracking down my old textbooks. Because I love typography and web standards in general, it would also give me more typographic practices to try out on my blog — depending on how returning to Japanese goes, it would be cool to try writing some posts in Japanese.&lt;/p&gt;
&lt;p&gt;This also might give me the chance to get internet feedback on my Japanese. One of the reasons I never stuck with it is that even when I was decent at it, I had a lot of anxiety seeking out conversation partners with whom to practice. Writing online doesn&#39;t feel as intense though, so that could be a good way to get feedback. We&#39;ll see how it goes!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;%E3%83%AB%E3%83%93%E3%81%A8HTML&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>We&#39;re Getting Married!</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/08/were-getting-married/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/08/were-getting-married/</guid>
            <pubDate>Sat, 16 Aug 2025 23:18:00 GMT</pubDate>
            <description>Eli and I are engaged, and we&#39;re getting married later this year!</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/notes-photos.css&quot; /&gt;
&lt;p&gt;Eli and I are engaged, and we&#39;re getting married later this year! I&#39;m really excited to spend the rest of my life with them. We didn&#39;t feel like doing an engagement photoshoot, but here are some nice photos from some of the hikes and other outdoor trips we like to do together.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/07/eli-bday/IMG_2509.webp&quot; alt=&quot;My partner and me, with a walkway handrail and a marble cave entrance visible in the background&quot; class=&quot;img-vertical&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;In front of a marble cave at the Natural Stone Bridge &amp; Caves park in the Adirondacks&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/08/IMG_3820.webp&quot; alt=&quot;Me and my partner smiling at the camera, with stromatolite fossils visible below us on the ground.&quot; class=&quot;img-vertical&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;This is at Lester Park in Saratoga Springs, NY and the ground below us is a 490 million year-old lake bed covered in &lt;a href=&quot;https://en.wikipedia.org/wiki/Stromatolite&quot;&gt;stromatolite&lt;/a&gt; fossils!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/08/IMG_3804.webp&quot; alt=&quot;My partner gesturing down at some stromatolite fossils and looking very excited.&quot; class=&quot;img-vertical&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;More of the stromatolite fossils. It makes me happy looking at this photo and seeing Eli so excited.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;We&amp;#39;re%20Getting%20Married!&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>IndieWeb Carnival: Tone Colors</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/08/indieweb-carnival-tone-colors/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/08/indieweb-carnival-tone-colors/</guid>
            <pubDate>Mon, 11 Aug 2025 22:05:00 GMT</pubDate>
            <description>For this month&#39;s IndieWeb Carnival, I&#39;m writing about music that emphasizes timbre or “tone-color,” and how I use this kind of approach in my own composition.</description>
            <content:encoded>
            
            

          
          
          &lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=8AQx0V2lZs8&amp;amp;t=496s&quot;&gt;Movement III, “Farben” (“Colors”)&lt;/a&gt; from Arnold Schoenberg&#39;s &lt;em&gt;Five Pieces for Orchestra, Op. 16&lt;/em&gt; begins very subtly. Two different groups in the orchestra alternate on the same hushed, eerie chord. As this slow, regular pulsation continues, some notes in the chord move up or down very slightly while others remain the same, gradually transforming the harmony.&lt;/p&gt;
&lt;p&gt;Throughout the movement, there is almost no melody in the traditional sense. Rather, by keeping the pitch material nearly static while alternating different instruments (or combinations of instruments), Schoenberg foregrounds the &lt;a href=&quot;https://en.wikipedia.org/wiki/Timbre&quot;&gt;timbre&lt;/a&gt; or “tone-colors” of the orchestra — the aspects of sound by which we can tell, for example, a trumpet from a violin.&lt;/p&gt;
&lt;p&gt;He describes what he is doing as &lt;a href=&quot;https://en.wikipedia.org/wiki/Klangfarbenmelodie&quot;&gt;&lt;em&gt;klangfarbenmelodie&lt;/em&gt;&lt;/a&gt; or “tone-color melody.” By composing this way, he seeks to have the listener perceive this changing of “tone-colors” with “a kind of logic entirely equivalent to that logic which satisfies us in the melody of pitches.” &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/08/indieweb-carnival-tone-colors/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; In other words, just as a more traditional melody might have a distinctive sequence of pitches (often associated with a similarly distinctive rhythm), we can apply the same type of listening to a sequence of &lt;em&gt;timbres&lt;/em&gt;, as well as writing music that encourages this type of listening.&lt;/p&gt;
&lt;h2 id=&quot;color-in-my-earlier-music&quot;&gt;Color in My Earlier Music&lt;/h2&gt;
&lt;p&gt;In my first year of college, I stumbled across a score page for Anton Webern&#39;s &lt;a href=&quot;https://www.youtube.com/watch?v=pVQambrIKNo&quot;&gt;&lt;em&gt;Concerto for Nine Instruments, Op. 24&lt;/em&gt;&lt;/a&gt; discarded in a practice room — likely homework from some classmate&#39;s &lt;a href=&quot;https://en.wikipedia.org/wiki/20th-century_classical_music&quot;&gt;20th-century music theory&lt;/a&gt; class. Webern was one of Schoenberg&#39;s students and someone who also made use of &lt;em&gt;klangfarbenmelodie&lt;/em&gt;, and this piece was my entry point to that idea, among many other musical concepts. My &lt;em&gt;Nonet&lt;/em&gt; from 2014, and &lt;a href=&quot;https://soundcloud.com/reilly-spitzfaden/nonet-iii?in=reilly-spitzfaden/sets/nonet-premiere&quot;&gt;movement III in particular&lt;/a&gt; is a good example of my use of this technique.&lt;/p&gt;
&lt;p&gt;Where Schoenberg&#39;s prototype of &lt;em&gt;klangfarbenmelodie&lt;/em&gt; in his “Farben” movement uses slowly morphing chords, the opening of movement III of my &lt;em&gt;Nonet&lt;/em&gt; is highly pointillistic — more like how Webern tended to write. In the first two measures, the horn, bass clarinet, and cello each play a short &lt;a href=&quot;https://en.wikipedia.org/wiki/Motif_(music)&quot;&gt;motive&lt;/a&gt;, transposing and manipulating it each time, with each instrument just barely overlapping with the end of the previous one. This rapid passing of short ideas between instruments sets the tone for the rest of the composition.&lt;/p&gt;
&lt;p&gt;We can listen to this pointillistic call-and-response in a similar way to how we did in the Schoenberg example. While the musical material is much more active in my piece, the shifts in tone color also occur in quicker succession, and are often accompanied by large &lt;a href=&quot;https://en.wikipedia.org/wiki/Register_(music)&quot;&gt;register&lt;/a&gt; jumps. In addition, almost all of the material derives from that 5-note motive from the first measure. While the melody is quite dissonant, angular, and chromatic, deriving most things from this one short motive gives a consistent overall character or “vibe” to the piece.&lt;/p&gt;
&lt;p&gt;The combination of a consistent character to the melody with large contrasts in timbre and register encourages the listener to focus on the overall quality and color of the sound, rather than trying to follow the specifics of the melody. When I listen like this, the rapid &lt;em&gt;klangfarbenmelodie&lt;/em&gt; shifts in tone color become particularly clear.&lt;/p&gt;
&lt;p&gt;This pointillistic approach of rapidly passing ideas between instruments appeared regularly in many other compositions of mine around this time, and as I wrote this way, I took particular interest in the shifting timbres that resulted.&lt;/p&gt;
&lt;h2 id=&quot;color-in-my-recent-composition&quot;&gt;Color in My Recent Composition&lt;/h2&gt;
&lt;p&gt;Some 13 years after my &lt;em&gt;Nonet&lt;/em&gt;, it&#39;s interesting to look back on what has changed and what has stayed the same in how I use color in my music. I composed &lt;a href=&quot;https://applytriangle.bandcamp.com/track/if-this-reaches-you&quot;&gt;&lt;em&gt;If this reaches you&lt;/em&gt;&lt;/a&gt; in 2021, and &lt;a href=&quot;https://reillyspitzfaden.com/posts/2024/04/new-album-announcement/&quot;&gt;it was recorded in 2024&lt;/a&gt;. The details of my approach are very different, but the emphasis on sound and timbre, and the pointillistic, thin textures are still there. This piece is for MIDI keyboard (controlling a custom software setup in &lt;a href=&quot;https://en.wikipedia.org/wiki/Max_(software)&quot;&gt;Max/MSP&lt;/a&gt;), flute, and clarinet. The flutist briefly uses a bass bow on a cardboard box in the middle, as well. The MIDI keyboard plays a combination of electric piano and sampled amateur radio sounds — &lt;a href=&quot;https://reillyspitzfaden.com/posts/2024/12/radio-listening-musically/&quot;&gt;I wrote about using radio transmissions in my compositions in another post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Where the things that made the &lt;em&gt;Nonet&lt;/em&gt; sound unusual were the choice of pitches and rhythms, in &lt;em&gt;If this reaches you&lt;/em&gt;, the focus is a bit more on &lt;em&gt;sound&lt;/em&gt; than pitch. In addition to the “non-musical” radio samples, I also use a number of so-called “extended techniques,” particularly in the clarinet. &lt;a href=&quot;http://www.heatherroche.net/&quot;&gt;Clarinetist Heather Roche&#39;s blog&lt;/a&gt; is a great source of information on how these work, and I use &lt;a href=&quot;https://heatherroche.net/2018/09/13/27-easy-bb-clarinet-multiphonics/&quot;&gt;various&lt;/a&gt; different &lt;a href=&quot;https://heatherroche.net/2019/11/05/underblown-bb-clarinet-multiphonics/&quot;&gt;multiphonics&lt;/a&gt;, &lt;a href=&quot;https://heatherroche.net/2014/05/11/on-double-trills-for-bb-clarinet/&quot;&gt;double trills&lt;/a&gt;, and other unusual sounds. At the same time, the way I combine these sounds is still quite pointillistic, and emphasizes rapid shifts between the different kinds of noise.&lt;/p&gt;
&lt;p&gt;When I do use clearly pitched material, my more recent compositions tend to be a bit more “tonal” than the &lt;em&gt;Nonet&lt;/em&gt; — less chromatic, and leaning more toward “cheesy” 80s pop, lounge music, or electronic dance music sounds. For example, the piano chords — both notes and instrument sound — in &lt;a href=&quot;https://applytriangle.bandcamp.com/track/if-this-reaches-you&quot;&gt;&lt;em&gt;If this reaches you&lt;/em&gt;&lt;/a&gt; starting at around 0:58 sound like lounge/elevator music mixed in amid the noisy samples and flute/clarinet sounds. The synth chords and scooping accents in the sax melody in &lt;a href=&quot;https://www.youtube.com/watch?v=2dz0iKwHrkI&quot;&gt;the opening of &lt;em&gt;Outlive everything you know&lt;/em&gt;&lt;/a&gt; sound reminiscent of house music, again mixed in with a kaleidoscope of strange glitches and multiphonics.&lt;/p&gt;
&lt;p&gt;Someone I admire who manages to pull together “cheesy” synth sounds with experimental electronics (as well as metal and many other things!) is Fire-Toolz. Listen how the opening of &lt;a href=&quot;https://fire-toolz.bandcamp.com/track/soaked-another-name-for-everything&quot;&gt;&lt;em&gt;Soaked: Another Name for Everything&lt;/em&gt;&lt;/a&gt; moves from acoustic guitar to 80s power ballad to glitchy sound collage, to some sort of prog-rock/elevator music hybrid at the end. Extremely colorful, and while her music is very different from Schoenberg, the quick movement between different tone colors is an integral part of her work too.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Marisabel is hosting &lt;a href=&quot;https://marisabel.nl/public/blog/IndieWeb_Carnival_August_2025_:_Colors&quot;&gt;this month&#39;s Indieweb carnival on the topic of colors&lt;/a&gt;, and this is my contribution. I had been looking forward to writing on this topic since I saw it on &lt;a href=&quot;https://indieweb.org/IndieWeb_Carnival&quot;&gt;the list of upcoming events&lt;/a&gt;. I&#39;ve been enjoying other people&#39;s entries, and thanks to Marisabel for hosting! I&#39;ve particularly enjoyed this exercise of looking at my past work because it has given me some ideas for a current composition project — more info on that as I compose. Until next time!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Schoenberg, Arnold. &lt;em&gt;Theory of Harmony&lt;/em&gt;. Translated by Roy E. Carter. University of California Press, 1978. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/08/indieweb-carnival-tone-colors/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;IndieWeb%20Carnival:%20Tone%20Colors&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>Plugins for Everyone! Cross-Platform JUCE with CMake &amp; GitHub Actions</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/08/plugins-for-everyone-crossplatform-juce-with-cmake-github-actions/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/08/plugins-for-everyone-crossplatform-juce-with-cmake-github-actions/</guid>
            <pubDate>Fri, 01 Aug 2025 18:31:00 GMT</pubDate>
            <description>My C++ reverb plugin is finally available for macOS, Windows, and Linux! Here&#39;s how I&#39;m using JUCE&#39;s CMake API and GitHub actions to make that possible.</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;p&gt;I have &lt;a href=&quot;https://github.com/reillypascal/RSBrokenMedia&quot;&gt;a few&lt;/a&gt; &lt;a href=&quot;https://github.com/reillypascal/RSAlgorithmicVerb&quot;&gt;C++/JUCE&lt;/a&gt; &lt;a href=&quot;https://github.com/reillypascal/RSTelecom&quot;&gt;audio plugins&lt;/a&gt; I&#39;ve worked on off and on over the last couple of years. I&#39;ve wanted them to be available to anyone who&#39;s interested, but I previously wasn&#39;t able to get them compiled for Windows (and Linux was very inconvenient).&lt;/p&gt;
&lt;p&gt;I&#39;m now able to easily compile them for Linux, macOS, and Windows using the &lt;a href=&quot;https://github.com/juce-framework/JUCE/blob/d6181bde38d858c283c3b7bf699ce6340c050b5d/docs/CMake%20API.md#L4&quot;&gt;JUCE CMake API&lt;/a&gt; and GitHub actions. Let&#39;s look at how that works!&lt;/p&gt;
&lt;h2 id=&quot;my-starting-point&quot;&gt;My Starting Point&lt;/h2&gt;
&lt;p&gt;The JUCE C++ framework is a popular way to build audio plugins. When you first learn it, the &lt;a href=&quot;https://juce.com/tutorials/tutorial_new_projucer_project/&quot;&gt;tutorials show how to build projects using the “Projucer” GUI tool&lt;/a&gt;. This generates project files for Visual Studio, Xcode, and Linux Makefiles. It&#39;s a convenient and well-documented way to work if you&#39;re only building for one platform, but if you want to compile for multiple OSes, you need to have each OS available to work with (in order to run the appropriate GUI program for which the Projucer generates files). Since my primary computer is a MacBook, I&#39;ve only had compiled releases for macOS. This was where I was when I started learning the CMake API.&lt;/p&gt;
&lt;h2 id=&quot;cmake-and-juce&quot;&gt;CMake and JUCE&lt;/h2&gt;
&lt;p&gt;When I started on the &lt;a href=&quot;https://github.com/juce-framework/JUCE/blob/d6181bde38d858c283c3b7bf699ce6340c050b5d/docs/CMake%20API.md#L4&quot;&gt;JUCE CMake API&lt;/a&gt;, I had next to no experience with CMake. The official website has this nice web book &lt;a href=&quot;https://cmake.org/cmake/help/book/mastering-cmake/index.html&quot;&gt;&lt;em&gt;Mastering CMake&lt;/em&gt;&lt;/a&gt;. Among other things, it has &lt;a href=&quot;https://cmake.org/cmake/help/book/mastering-cmake/cmake/Help/guide/tutorial/index.html&quot;&gt;a step-by-step tutorial&lt;/a&gt; and &lt;a href=&quot;https://cmake.org/cmake/help/latest/manual/cmake.1.html#manual:cmake(1)&quot;&gt;documentation for the &lt;code&gt;cmake&lt;/code&gt; CLI command&lt;/a&gt;, which were particularly helpful. You can also look at the examples in the JUCE &lt;a href=&quot;https://github.com/juce-framework/JUCE/tree/master/examples/CMake&quot;&gt;examples/CMake folder&lt;/a&gt; for reference on the JUCE end of things.&lt;/p&gt;
&lt;p&gt;Below is the CMakeLists.txt file for my &lt;a href=&quot;https://github.com/reillypascal/RSAlgorithmicVerb&quot;&gt;algorithmic reverb plugin&lt;/a&gt;—this goes in the root directory. Let&#39;s go through line by line:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html&quot;&gt;&lt;code&gt;cmake_minimum_required&lt;/code&gt;&lt;/a&gt; statement should be the first line in any CMakeLists.txt file.&lt;/li&gt;
&lt;li&gt;Without &lt;a href=&quot;https://cmake.org/cmake/help/latest/command/set.html&quot;&gt;&lt;code&gt;set(CMAKE_EXPORT_COMPILE_COMMANDS ON)&lt;/code&gt;&lt;/a&gt;, my text editor (currently using Zed and Neovim) can&#39;t find the JUCE imports it needs.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://cmake.org/cmake/help/latest/command/project.html&quot;&gt;&lt;code&gt;project&lt;/code&gt;&lt;/a&gt; statement gives the name/version.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cmake.org/cmake/help/latest/command/add_subdirectory.html&quot;&gt;&lt;code&gt;add_subdirectory(JUCE)&lt;/code&gt;&lt;/a&gt;: if you clone the &lt;a href=&quot;https://github.com/juce-framework/JUCE&quot;&gt;JUCE framework&lt;/a&gt; into the working directory (&lt;code&gt;git clone https://github.com/juce-framework/JUCE.git&lt;/code&gt;), this statement will make the resulting folder available.
&lt;ul&gt;
&lt;li&gt;I&#39;ve seen the &lt;a href=&quot;https://github.com/cpm-cmake/CPM.cmake&quot;&gt;CPM&lt;/a&gt; package manager used (e.g., in &lt;a href=&quot;https://www.youtube.com/watch?v=Uq7Hwt18s3s&quot;&gt;this video from WolfSound&lt;/a&gt;) to bring in these dependencies, but I was having some kind of issue with dependencies being included twice and decided to figure that out later.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/juce-framework/JUCE/blob/d6181bde38d858c283c3b7bf699ce6340c050b5d/docs/CMake%20API.md#juce_add_target&quot;&gt;&lt;code&gt;juce_add_plugin&lt;/code&gt;&lt;/a&gt; line is from the JUCE API. This contains a list of options and metadata for the plugin.
&lt;ul&gt;
&lt;li&gt;Note in particular &lt;code&gt;COPY_PLUGIN_AFTER_BUILD TRUE&lt;/code&gt;, and the following two lines &lt;code&gt;VST3_COPY_DIR &amp;quot;/Library/Audio/Plug-Ins/VST3&amp;quot;&lt;/code&gt; and &lt;code&gt;AU_COPY_DIR &amp;quot;/Library/Audio/Plug-Ins/Components&amp;quot;&lt;/code&gt;. If you uncomment these, CMake will copy the plugin files to the given directories after building. You &lt;em&gt;don&#39;t&lt;/em&gt; want this for the GitHub actions,&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/juce-framework/JUCE/blob/d6181bde38d858c283c3b7bf699ce6340c050b5d/docs/CMake%20API.md#juce_generate_juce_header&quot;&gt;&lt;code&gt;juce_generate_juce_header&lt;/code&gt;&lt;/a&gt; generates the &lt;code&gt;JuceHeader.h&lt;/code&gt; file. As the link above describes and &lt;a href=&quot;https://melatonin.dev/manuals/pamplejuce/juce/juceheader-h/&quot;&gt;Sudara demonstrates&lt;/a&gt;, if you&#39;re not using the Projucer you can include the JUCE module(s) you need directly.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cmake.org/cmake/help/latest/command/target_sources.html&quot;&gt;&lt;code&gt;target_sources&lt;/code&gt;&lt;/a&gt; lists the .cpp that need to be included in the project.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cmake.org/cmake/help/latest/command/target_compile_definitions.html&quot;&gt;&lt;code&gt;target_compile_definitions&lt;/code&gt;&lt;/a&gt; adds preprocessor definitions to the target—see the options used below.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cmake.org/cmake/help/latest/command/target_link_libraries.html&quot;&gt;&lt;code&gt;target_link_libraries&lt;/code&gt;&lt;/a&gt;: in the &lt;code&gt;PRIVATE&lt;/code&gt; field, we include the JUCE modules we&#39;re using in this project. In the &lt;code&gt;PUBLIC&lt;/code&gt; field, we include some recommended flags.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-cmake&quot;&gt;&lt;code class=&quot;language-cmake&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;cmake_minimum_required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;VERSION&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;CMAKE_EXPORT_COMPILE_COMMANDS&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;ON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;RSAlgorithmicVerb &lt;span class=&quot;token property&quot;&gt;VERSION&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;add_subdirectory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;JUCE&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;                    &lt;span class=&quot;token comment&quot;&gt;# If you&#39;ve put JUCE in a subdirectory called JUCE&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;juce_add_plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;PROJECT_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# VERSION ...                               # Set this if the plugin version is different to the project version&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# ICON_BIG ...                              # ICON_* arguments specify a path to an image file to use as an icon for the Standalone&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# ICON_SMALL ...&lt;/span&gt;
    COMPANY_NAME &lt;span class=&quot;token string&quot;&gt;&quot;Reilly Spitzfaden&quot;&lt;/span&gt;                          &lt;span class=&quot;token comment&quot;&gt;# Specify the name of the plugin&#39;s author&lt;/span&gt;
    BUNDLE_ID &lt;span class=&quot;token string&quot;&gt;&quot;com.reillyspitzfaden.RSAlgorithmicVerb&quot;&lt;/span&gt;
    IS_SYNTH &lt;span class=&quot;token boolean&quot;&gt;FALSE&lt;/span&gt;                       &lt;span class=&quot;token comment&quot;&gt;# Is this a synth or an effect?&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# NEEDS_MIDI_INPUT TRUE/FALSE               # Does the plugin need midi input?&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# NEEDS_MIDI_OUTPUT TRUE/FALSE              # Does the plugin need midi output?&lt;/span&gt;
    IS_MIDI_EFFECT &lt;span class=&quot;token boolean&quot;&gt;FALSE&lt;/span&gt;                 &lt;span class=&quot;token comment&quot;&gt;# Is this plugin a MIDI effect?&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# EDITOR_WANTS_KEYBOARD_FOCUS TRUE/FALSE    # Does the editor need keyboard focus?&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Should the plugin be installed to a default location after building?&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# COPY_PLUGIN_AFTER_BUILD TRUE&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# VST3_COPY_DIR &quot;/Library/Audio/Plug-Ins/VST3&quot;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# AU_COPY_DIR &quot;/Library/Audio/Plug-Ins/Components&quot;&lt;/span&gt;
    PLUGIN_MANUFACTURER_CODE Rspi               &lt;span class=&quot;token comment&quot;&gt;# A four-character manufacturer id with at least one upper-case character&lt;/span&gt;
    PLUGIN_CODE Rsav                            &lt;span class=&quot;token comment&quot;&gt;# A unique four-character plugin id with exactly one upper-case character&lt;/span&gt;
                                                &lt;span class=&quot;token comment&quot;&gt;# GarageBand 10.3 requires the first letter to be upper-case, and the remaining letters to be lower-case&lt;/span&gt;
    FORMATS AU VST3                  &lt;span class=&quot;token comment&quot;&gt;# The formats to build. Other valid formats are: AAX Unity VST AU AUv3 Standalone&lt;/span&gt;
    PRODUCT_NAME &lt;span class=&quot;token string&quot;&gt;&quot;RSAlgorithmicVerb&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;        &lt;span class=&quot;token comment&quot;&gt;# The name of the final executable, which can differ from the target name&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;juce_generate_juce_header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;PROJECT_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;target_sources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;PROJECT_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;PRIVATE&lt;/span&gt;
        Source/ConcertHallB.cpp
        Source/CustomDelays.cpp
        Source/DattorroVerb.cpp
        Source/EarlyReflections.cpp
        Source/FDNs.cpp
        Source/Freeverb.cpp
        Source/GardnerRooms.cpp
        Source/LFO.cpp
        Source/SpecialFX.cpp
        Source/PluginEditor.cpp
        Source/PluginProcessor.cpp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;target_compile_definitions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;PROJECT_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;PUBLIC&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them.&lt;/span&gt;
        JUCE_WEB_BROWSER=&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call&lt;/span&gt;
        JUCE_USE_CURL=&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;# If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call&lt;/span&gt;
        JUCE_VST3_CAN_REPLACE_VST2=&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;target_link_libraries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;PROJECT_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;PRIVATE&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# AudioPluginData           # If we&#39;d created a binary data target, we&#39;d link to it here&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# juce::juce_analytics&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_audio_basics&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_audio_devices&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_audio_formats&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_audio_processors&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_audio_utils&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_core&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# juce::juce_cryptography&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_data_structures&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_dsp&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_events&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_graphics&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_gui_basics&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_gui_extra&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# juce::juce_opengl&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# juce::juce_osc&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# juce::juce_video&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;PUBLIC&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_recommended_config_flags&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_recommended_lto_flags&lt;/span&gt;
        &lt;span class=&quot;token inserted class-name&quot;&gt;juce::juce_recommended_warning_flags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this &lt;code&gt;CMakeLists.txt&lt;/code&gt;, you can run &lt;code&gt;cmake -S . -B build&lt;/code&gt; in the root directory to create the build environment. This means the source is the root directory, and &lt;code&gt;build&lt;/code&gt; is the build directory. You can add the flags &lt;code&gt;-D CMAKE_BUILD_TYPE=Debug&lt;/code&gt; or &lt;code&gt;-D CMAKE_BUILD_TYPE=Release&lt;/code&gt; to specify debug/release builds. After that, run &lt;code&gt;cmake --build build&lt;/code&gt; to compile. Note that if you have the &lt;code&gt;COPY_PLUGIN_AFTER_BUILD TRUE&lt;/code&gt; option in &lt;code&gt;juce_add_plugin&lt;/code&gt;, you will likely need to add &lt;code&gt;sudo&lt;/code&gt; to both of the build commands since my installation directories above are system ones.&lt;/p&gt;
&lt;h2 id=&quot;compiling-with-github-actions&quot;&gt;Compiling with GitHub Actions&lt;/h2&gt;
&lt;p&gt;The nice thing about using CMake is that it doesn&#39;t require a desktop GUI program like Xcode or Visual Studio, meaning that it&#39;s possible to use something like &lt;a href=&quot;https://docs.github.com/en/actions&quot;&gt;GitHub actions&lt;/a&gt; to compile in the terminal on virtual versions of the target OSes. For my build action, I referenced &lt;a href=&quot;https://github.com/sudara/pamplejuce/blob/main/.github/workflows/build_and_test.yml&quot;&gt;Sudara&#39;s Pamplejuce template&lt;/a&gt;. This is the file &lt;code&gt;.github/workflows/cmake.yml&lt;/code&gt;. I have this set to manually trigger, and if you go to the “Actions” tab for your project, you can trigger a build using the GUI.&lt;/p&gt;
&lt;p&gt;The file is fairly straightforward, but I&#39;ll point out some specifics. &lt;a href=&quot;https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manually-run-a-workflow&quot;&gt;&lt;code&gt;on: [workflow_dispatch]&lt;/code&gt;&lt;/a&gt; means that you manually trigger the build. The &lt;a href=&quot;https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/run-job-variations&quot;&gt;&lt;code&gt;matrix&lt;/code&gt; strategy&lt;/a&gt; allows for running multiple copies of the job, in this case, on macOS, Ubuntu, and Windows.&lt;/p&gt;
&lt;p&gt;The first two steps, which only run on Linux, are taken directly from Pamplejuce&#39;s example .yml file. Note however that the &lt;a href=&quot;https://github.com/juce-framework/JUCE/blob/develop/docs/Linux%20Dependencies.md&quot;&gt;current version of the Linux dependencies&lt;/a&gt; is slightly different, and I had to adjust accordingly.&lt;/p&gt;
&lt;p&gt;After this, we check out the project repo, clone the JUCE repo, and run (almost) the same CMake commands as above. Note that we specify the &lt;code&gt;Release&lt;/code&gt; build when we actually build, rather than when we set up the build, and that the syntax is a bit different: &lt;code&gt;--config Release&lt;/code&gt;. &lt;code&gt;-D CMAKE_BUILD_TYPE&lt;/code&gt; only seems to work on *nix OSes, not on Windows.&lt;/p&gt;
&lt;p&gt;Finally, we archive the build artifacts (i.e., the compiled files). Note that I append &lt;code&gt;${{ runner.os }}&lt;/code&gt; to the end of the artifact name. This is important—without it, the artifacts for each OS will all be named the same thing, and the process will fail due to path conflicts.&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rsav&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;compile

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;workflow_dispatch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;matrix.os&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;macos&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; windows&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# Use clang on Linux so we don&#39;t introduce a 3rd compiler (Windows and macOS use MSVC and Clang)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Set up Clang
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; runner.os == &#39;Linux&#39;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; egor&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tensin/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;clang@v1

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install JUCE&#39;s Linux Deps
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; runner.os == &#39;Linux&#39;
        &lt;span class=&quot;token comment&quot;&gt;# Official list of Linux deps: https://github.com/juce-framework/JUCE/blob/develop/docs/Linux%20Dependencies.md&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          sudo apt-get update &amp;amp;&amp;amp; sudo apt install libasound2-dev libjack-jackd2-dev &#92;
              ladspa-sdk &#92;
              libcurl4-openssl-dev  &#92;
              libfreetype-dev libfontconfig1-dev &#92;
              libx11-dev libxcomposite-dev libxcursor-dev libxext-dev libxinerama-dev libxrandr-dev libxrender-dev &#92;
              libwebkit2gtk-4.1-dev &#92;
              libglu1-mesa-dev mesa-common-dev&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Preparation&quot;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v2

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;(JUCE) Clone Repository&quot;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v2
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; juce&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;framework/JUCE
          &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;runner.workspace&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/RSAlgorithmicVerb/JUCE

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Create Build Environment&quot;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;runner.workspace&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/RSAlgorithmicVerb
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cmake &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;S . &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;B build

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Build&quot;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;runner.workspace&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/RSAlgorithmicVerb
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cmake &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;build build &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config Release

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Archive build artifacts
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/upload&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;artifact@v4
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;RSAlgorithmicVerb_${{ runner.os }}&quot;&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;runner.workspace&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/RSAlgorithmicVerb/build/RSAlgorithmicVerb_artefacts/&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;linux-build-issues&quot;&gt;Linux Build Issues&lt;/h2&gt;
&lt;p&gt;Frustratingly, this all works perfectly for Mac and Windows, but gives me a perplexing issue on Linux. The GitHub action works without issue on all OSes. However, when I use &lt;a href=&quot;https://github.com/Tracktion/pluginval&quot;&gt;pluginval&lt;/a&gt; to validate the Linux build on Debian 12.11, I get the following error for both the “open plugin (cold)” and “open plugin (warm)” tests:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; Test &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; failed: Expected value: , Actual value: Unable to load VST-3 plug-in &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; Test &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; failed: Unable to create juce::AudioPluginInstance&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I try to load the plugin in REAPER (same distro/version), the DAW detects that the plugin exists but won&#39;t load it. However, if I manually compile on my Debian laptop using CMake, everything works fine! The only other clue I&#39;ve been able to find is that the VST3 &lt;code&gt;RSAlgorithmicVerb.so&lt;/code&gt; file is significantly bigger when I use the GitHub action than when I manually compile it—31.9 MB, instead of 5.1 MB. At this point, I&#39;ve decided to manually compile the Linux version, although I would very much like to figure this issue out, if anyone has any idea!&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Please check out the &lt;a href=&quot;https://github.com/reillypascal/RSAlgorithmicVerb/releases&quot;&gt;releases for my reverb plugin&lt;/a&gt;—it&#39;s available for Linux, Mac, and Windows! This is the only one for which I&#39;ve set up this workflow, but next on my list is &lt;a href=&quot;https://github.com/reillypascal/RSTelecom&quot;&gt;my telecom codec plugin&lt;/a&gt;—I want to to add a C++ version of the &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/&quot;&gt;Rust ADPCM codec I wrote about previously&lt;/a&gt;, for example.&lt;/p&gt;
&lt;p&gt;For the time being, I don&#39;t have the full release and validation process automated. I decided to leave it as is since the main reason I want to do this is so I have easy access to Windows/Linux compilers. As a next step, I would like to reference &lt;a href=&quot;https://github.com/sudara/pamplejuce/blob/main/.github/workflows/build_and_test.yml&quot;&gt;the Pamplejuce workflow file&lt;/a&gt; and finish automating that. The example uses &lt;a href=&quot;https://keith.github.io/xcode-man-pages/pkgbuild.1.html&quot;&gt;the Xcode &lt;code&gt;pkgbuild&lt;/code&gt; utility&lt;/a&gt; and &lt;a href=&quot;https://github.com/Tracktion/pluginval&quot;&gt;Tracktion&#39;s &lt;code&gt;pluginval&lt;/code&gt; validator&lt;/a&gt;, and it would be very convenient. Until next time!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;Plugins%20for%20Everyone!%20Cross-Platform%20JUCE%20with%20CMake%20&amp;amp;%20GitHub%20Actions&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>IndieWeb Carnival: Take Two</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/</guid>
            <pubDate>Mon, 23 Jun 2025 12:16:00 GMT</pubDate>
            <description>I&#39;ve been mostly making music tools rather than writing music. Today I&#39;m talking about getting out of this rut with a “take two” on my current composition.</description>
            <content:encoded>
            
            

          
          
          &lt;p&gt;Nick Simson is hosting &lt;a href=&quot;https://www.nicksimson.com/posts/2025-indieweb-carnival-take-two.html&quot;&gt;this month&#39;s IndieWeb Carnival&lt;/a&gt; on the theme “take two.” After having needed to pick a new topic, he writes&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ever wish for a do-over? “Take two!” (or three, four, etc.) might be shouted by a film director or audio engineer looking to get a somewhat different outcome from a group of actors or musical performers. Would you like a second shot at something that didn’t land?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I immediately thought of my current experience of composing when I read this. I have a composition that I started back in summer 2023 for which I still haven&#39;t written much in the way of notes on the page. I tend to start a composition by working in &lt;a href=&quot;https://en.wikipedia.org/wiki/Max_(software)&quot;&gt;Max/MSP&lt;/a&gt;, usually using some combination of &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/01/databending-part-1&quot;&gt;databending&lt;/a&gt; and &lt;a href=&quot;https://reillyspitzfaden.com/notes/2025/05/tracking-planes-with-a-raspberry-pi&quot;&gt;software-defined radio&lt;/a&gt; (SDR) &lt;a href=&quot;https://reillyspitzfaden.com/posts/2024/04/new-album-announcement/&quot;&gt;samples&lt;/a&gt;; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2024/05/composition-journal&quot;&gt;sequenced/probabilistic sample/synth patterns&lt;/a&gt;; and VST synths/electric pianos, with these different categories assigned to different regions of a MIDI keyboard.&lt;/p&gt;
&lt;p&gt;In addition to patching in Max, this process requires composing samples (usually in Logic Pro) to “glitch up” or otherwise manipulate in Max; digging through large collections of “databending” files; sessions of scanning the airwaves on my SDR equipment and recording what I hear; and more recently, coding projects such as my &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/04/databending-part-3/&quot;&gt;script to glitch up MP3s&lt;/a&gt;, my &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-4/&quot;&gt;tool to automate the databending process&lt;/a&gt;, or my &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/&quot;&gt;implementation of the VOX ADPCM codec&lt;/a&gt; to get more variety in databending.&lt;/p&gt;
&lt;p&gt;Nathan Ho &lt;a href=&quot;https://nathan.ho.name/posts/system-building-syndrome/&quot;&gt;describes what he calls “system-building syndrome”&lt;/a&gt;. For context,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Synthesizer forums have a long-running joke about “Gear Acquisition Syndrome” that pictures the stereotypical hobbyist with all their Hainbach-endorsed gear, seemingly oblivious to the fact that they’re intended for making music.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In using &lt;a href=&quot;https://en.wikipedia.org/wiki/SuperCollider&quot;&gt;SuperCollider&lt;/a&gt; and C++ to make his own versions of this kind of gear, he comments that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I thought I was being clever circumventing [Gear Acquisition Syndrome (GAS)] by building everything myself, but what really happened is that I had fallen into my own version of GAS: I had System-Building Syndrome.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As I program and patch my own electronic music tools and setups, I find myself in the exact same position of making music tools instead of actually making music. For my “take two” I&#39;m throwing out a bunch of material (or rather putting it in a safe place to draw from later) and restarting this piece with a different approach. I&#39;ll discuss how I seem to have ended up here and some recent thoughts about composition that I&#39;m hoping can take me out of this rut.&lt;/p&gt;
&lt;h2 id=&quot;why-do-i-keep-building-systems%3F&quot;&gt;Why Do I Keep Building Systems?&lt;/h2&gt;
&lt;p&gt;Two things I like that are challenging to do to my satisfaction are 1) I like to write thin, pointillistic textures, and 2) I like when a composition seems to “float” (rather than drilling a pulse into me) but at the same time has rhythmic energy. For both of these, see the opening of &lt;a href=&quot;https://applytriangle.bandcamp.com/track/if-this-reaches-you&quot;&gt;&lt;em&gt;If this reaches you&lt;/em&gt;&lt;/a&gt;. The bursts of clarinet and sampler float around a rhythmic “backbone” in the flute and sampler. This backbone consists of a clicking tremolo from using an &lt;a href=&quot;https://www.youtube.com/watch?v=iQKfKuEqyYs&quot;&gt;electromagnetic pickup&lt;/a&gt; on a wi-fi router, &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; and a syncopated, metronome-like pattern of 8th and quarter notes in the flute, sitting on a single pitch for a while.&lt;/p&gt;
&lt;p&gt;While I&#39;m happy with &lt;em&gt;If this reaches you&lt;/em&gt;, I&#39;ve struggled for a while to write something else that satisfies me in the same way, and I&#39;ve particularly struggled with creating the right rhythmic “backbone.” As further examples of what I like, Autechre&#39;s &lt;a href=&quot;https://autechre.bandcamp.com/track/tapr&quot;&gt;&lt;em&gt;Tapr&lt;/em&gt;&lt;/a&gt; and &lt;a href=&quot;https://autechre.bandcamp.com/track/qplay&quot;&gt;&lt;em&gt;qplay&lt;/em&gt;&lt;/a&gt; (among others) have drum machines, but there is enough irregularity in the rhythm that I can relax and let go of pulse, while still enjoying the energy and character of the beat. Despite how much I like Autechre, I have different goals that make things more complicated.&lt;/p&gt;
&lt;p&gt;I&#39;m writing something in between the experimentalism that comes out of dance or “popular” music and the experimentalism that comes out of “classical music” circles. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; As I&#39;ve heard composer peers flippantly joke, in some ways the distinction comes down to “does it have a (drum) beat?” Drum sets/drum machines are very bold musical elements and require careful recording and sound design to sound right, and between that and my desire to work &lt;em&gt;between&lt;/em&gt; “popular”/“classical” experimentalism, I have very exacting requirements for how the rhythmic backbone should sound. At the same time, because I want to make something that sounds “fresh” to my ears I don&#39;t even fully know what I&#39;d like to hear. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;take-two&quot;&gt;Take Two&lt;/h2&gt;
&lt;p&gt;Because I&#39;ve tried so many different things, the Max patch and the surrounding file folders for my current project have become unworkable. It took a huge load off my shoulders to make a backup copy of the folder and then throw out most of the files and patching from the working copy. The couple of elements that were already working great stayed, and I got a nice blank slate to start again.&lt;/p&gt;
&lt;p&gt;I&#39;m still in the process of selecting samples for my Max patch, but instead of spending so much time designing sequencers and percussion patterns, I&#39;ve been looking at the simple materials I used for &lt;em&gt;If this reaches you&lt;/em&gt; and seeing what I can accomplish if I hold myself to that kind of simplicity this time around too. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve reminded myself that this doesn&#39;t mean I&#39;ve wasted my time. On the contrary, the time I&#39;ve spent on this project has given me a lot of practice on audio programming, sound design, and Max/MSP drum sequencing. The things I played with in this time haven&#39;t gone away either; I can return to the backup folder at any time to borrow bits of what I worked on.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Thanks to Nick for hosting this month&#39;s carnival! This topic helped me write about and think through something that&#39;s been percolating for a while, and I&#39;m hopeful that it will help my composition process moving forward. Until next time!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I&#39;ve found that many routers make a similar sound, although I&#39;m not sure what feature that is. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;For experimental music I like from “popular music” circles, see also Fire-Toolz (example tracks: &lt;a href=&quot;https://fire-toolz.bandcamp.com/track/clear-light&quot;&gt;&lt;em&gt;Clear Light&lt;/em&gt;&lt;/a&gt; or &lt;a href=&quot;https://fire-toolz.bandcamp.com/track/soaked-another-name-for-everything&quot;&gt;&lt;em&gt;Soaked: Another Name For Everything&lt;/em&gt;&lt;/a&gt;). From a “classical music” background, I particularly like Kelley Sheehan (&lt;a href=&quot;https://www.youtube.com/watch?v=gH3kxga4_JY&quot;&gt;&lt;em&gt;Talk Circus&lt;/em&gt;&lt;/a&gt;), Simon Steen-Andersen (&lt;a href=&quot;https://www.youtube.com/watch?v=sYiG2DDVS7Y&quot;&gt;&lt;em&gt;on and off and to and fro&lt;/em&gt;&lt;/a&gt;), and Alexander Schubert (&lt;a href=&quot;https://www.youtube.com/watch?v=VCVMHqzenMA&quot;&gt;&lt;em&gt;Serious Smile&lt;/em&gt;&lt;/a&gt;). &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;To some extent, I&#39;m sure the desire for something new and previously unheard comes from my enjoyment of 20th century modernism, and from absorbing the ideas from those circles. It&#39;s a goal I&#39;ve interrogated for a while, and I may move on from it at some point—at any rate, I realize it&#39;s someting worth questioning! &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;In addition to what I described above, this piece was the first one I wrote with significant use of Max/MSP, and all it uses is a sampler of databending/radio sounds on the bottom keys, and an electric piano VST on the upper ones. I&#39;m hoping that sticking to this kind of minimal setup may help focus my work. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/indieweb-carnival-take-two/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;IndieWeb%20Carnival:%20Take%20Two&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>Reverb Part 1—“Freeverb”</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/</guid>
            <pubDate>Thu, 05 Jun 2025 15:30:00 GMT</pubDate>
            <description>I discuss how algorithmic reverbs work using the popular “Freeverb.” I give details on feedforward/feedback delays and allpass filters, and I include a Max/MSP patch to play with.</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/notes-photos.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;p&gt;I started work on a &lt;a href=&quot;https://github.com/reillypascal/RSAlgorithmicVerb/releases&quot;&gt;reverb plugin&lt;/a&gt; in C++/JUCE back in 2023, and I&#39;ve slowly added to it since. Today, I&#39;m going to discuss how algorithmic (i.e., delay-based) reverbs work using the “Freeverb” algorithm that&#39;s available in that plugin. I&#39;ve included a Max/MSP version of the reverb, and in a later post, I will discuss getting our hands dirty with the code in C++/JUCE, as well as a number of other cool algorithms. Let&#39;s get started!&lt;/p&gt;
&lt;h2 id=&quot;how-reverb-works&quot;&gt;How Reverb Works&lt;/h2&gt;
&lt;p&gt;There are two main categories of digital reverb—&lt;a href=&quot;https://www.bhphotovideo.com/find/newsLetter/Convolution-Reverb.jsp/&quot;&gt;convolution&lt;/a&gt; and algorithmic—and today we will be discussing algorithmic reverb. An algorithmic reverb is based on a network of delays or “echoes,” with individual delays roughly corresponding to the sound reflections from individual walls or objects, and the network of connections between them simulating the way a reflection from one surface may bounce off many others.&lt;/p&gt;
&lt;p&gt;Sean Costello of Valhalla DSP has a &lt;a href=&quot;https://valhalladsp.com/2021/09/20/getting-started-with-reverb-design-part-1-dev-environments/&quot;&gt;series&lt;/a&gt; of &lt;a href=&quot;https://valhalladsp.com/2021/09/22/getting-started-with-reverb-design-part-2-the-foundations/&quot;&gt;posts&lt;/a&gt; giving &lt;a href=&quot;https://valhalladsp.com/2021/09/23/getting-started-with-reverb-design-part-3-online-resources/&quot;&gt;helpful resources&lt;/a&gt; on &lt;a href=&quot;https://valhalladsp.com/2021/09/28/getting-started-with-reverb-design-part-4-books/&quot;&gt;reverb design&lt;/a&gt;, including influential papers, online resources, and books, and you can find the type of algorithm we&#39;ll discuss today among the papers he mentions.&lt;/p&gt;
&lt;h3 id=&quot;feedforward-and-feedback-delays&quot;&gt;Feedforward and Feedback Delays&lt;/h3&gt;
&lt;p&gt;In the most basic form of delay, we take a copy of the incoming audio signal, delay it by a certain amount, and combine it with the original signal, usually with the option to adjust the proportions of original and delayed copies. To contrast with the other types of delay, this is often referred to as a “feedforward” delay—the delayed copy is “fed forward” and recombined with the original without any “feedback,” which we will discuss in the next section.&lt;/p&gt;
&lt;p&gt;In the diagram below, &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D465 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c28&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D45B TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c29&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; represents the incoming signal; &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-msup&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D467 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-script style=&quot;vertical-align: 0.363em;&quot;&gt;&lt;mjx-TeXAtom size=&quot;s&quot; texclass=&quot;ORD&quot;&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c2212&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D440 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;/mjx-TeXAtom&gt;&lt;/mjx-script&gt;&lt;/mjx-msup&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;msup&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;mrow data-mjx-texclass=&quot;ORD&quot;&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; is the delay block (with &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D440 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; representing the amount of delay in samples); &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-msub&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D44F TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-script style=&quot;vertical-align: -0.15em;&quot;&gt;&lt;mjx-mn class=&quot;mjx-n&quot; size=&quot;s&quot;&gt;&lt;mjx-c class=&quot;mjx-c30&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mn&gt;&lt;/mjx-script&gt;&lt;/mjx-msub&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;msub&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; and &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-msub&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D44F TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-script style=&quot;vertical-align: -0.15em;&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot; size=&quot;s&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D440 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;/mjx-script&gt;&lt;/mjx-msub&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;msub&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;/msub&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; represent the amount of gain applied to the original and delayed copies respectively; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; the &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c2B&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; symbol represents summing the two copies; and &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D466 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c28&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D45B TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c29&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; is the output signal.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/feedforward_comb.webp&quot; alt=&quot;block diagram of a feedforward delay/comb filter&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;Feedforward delay/comb filter (diagram from &lt;a href=&quot;https://ccrma.stanford.edu/~jos/pasp04/Feedforward_Comb_Filters.html&quot;&gt;Julius O. Smith&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Next, we have a “feedback” delay. Note how in the diagram below, the incoming signal is summed with the fed-back output of the delay &lt;em&gt;before&lt;/em&gt; entering the delay; the gain &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c2212&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;mjx-msub&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D44E TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-script style=&quot;vertical-align: -0.15em;&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot; size=&quot;s&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D440 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;/mjx-script&gt;&lt;/mjx-msub&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;/msub&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; applied to the fed-back output of the delay is &lt;em&gt;negative&lt;/em&gt;; and the output of the whole delay &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D466 TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c28&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D45B TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;mjx-mo class=&quot;mjx-n&quot;&gt;&lt;mjx-c class=&quot;mjx-c29&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mo&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; is taken from that sum of the input and feedback &lt;em&gt;before&lt;/em&gt; it enters the delay.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/feedback_comb.webp&quot; alt=&quot;block diagram of a feedback delay/comb filter&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;Feedback delay/comb filter (diagram from &lt;a href=&quot;https://ccrma.stanford.edu/~jos/pasp/Feedback_Comb_Filters.html&quot;&gt;Julius O. Smith&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;allpass-filters&quot;&gt;Allpass Filters&lt;/h3&gt;
&lt;p&gt;Below I have the amplitude curves from feedforward and feedback delays, caused by &lt;a href=&quot;https://www.phys.uconn.edu/~gibson/Notes/Section5_2/Sec5_2.htm&quot;&gt;constructive and destructive interference&lt;/a&gt;. The repeated notches in the feedforward delay frequency plot are caused by destructive interference; adding the delay to the original signal causes certain frequencies to cancel out. In the feedback example, as the sound feeds back on itself, certain frequencies constructively interfere—they reinforce each other and create the “spikes” in the amplitude response. Both of these delay configurations are also referred to as “comb filters,” due to the shape of the spikes and notches.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/feedforward_comb_amp.webp&quot; alt=&quot;feedforward comb filter amplitude plot&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;Feedforward comb filter amplitude plot (diagram from &lt;a href=&quot;https://ccrma.stanford.edu/~jos/pasp05/Feedforward_Comb_Filter_Amplitude.html&quot;&gt;Julius O. Smith&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/feedback_comb_neg_amp.webp&quot; alt=&quot;negative feedback comb filter amplitude plot&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;Negative feedback comb filter amplitude plot (diagram from &lt;a href=&quot;https://ccrma.stanford.edu/~jos/pasp05/Feedback_Comb_Filter_Amplitude.html&quot;&gt;Julius O. Smith&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Notice how the feedforward and feedback amplitude responses are the inverse of each other. Having a negative gain in the feedback loop of a feedback delay is necessary for the peaks to align with where the troughs would be in a feedforward delay that has the same delay time. As &lt;a href=&quot;https://ccrma.stanford.edu/~jos/pasp05/Feedback_Comb_Filter_Amplitude.html&quot;&gt;shown here&lt;/a&gt;, if the feedback delay&#39;s gain were positive, the peaks would be at 0.2, 0.4, etc., instead of 0.1, 0.3, etc.&lt;/p&gt;
&lt;p&gt;This alignment is important for our next type of filter: the “allpass” filter. As shown below, we combine a feedforward delay with a feedback delay that has a negative gain on the feedback. As a result, our peaks and troughs in the frequency response cancel out. This kind of delay configuration has a “flat” frequency response; all frequencies pass through at the same loudness.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/allpass_from_2_combs.webp&quot; alt=&quot;block diagram of an allpass filter&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;Allpass filter (diagram from &lt;a href=&quot;https://ccrma.stanford.edu/~jos/pasp/Allpass_Two_Combs.html&quot;&gt;Julius O. Smith&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In contrast to the frequency response, our time response is much more complex. An allpass filter delays the different frequencies in the signal by different amounts. As a result, a single impulse or “click” going through it would become multiplied and “smeared” in time, giving the effect of multiple clicks piling on top of each other, a very useful effect for creating a smooth- and dense-sounding reverb.&lt;/p&gt;
&lt;p&gt;Now let&#39;s put these all together to make a reverb!&lt;/p&gt;
&lt;h2 id=&quot;the-classic-schroeder-reverberator&quot;&gt;The Classic Schroeder Reverberator&lt;/h2&gt;
&lt;h3 id=&quot;series-allpasses&quot;&gt;Series Allpasses&lt;/h3&gt;
&lt;p&gt;Manfred Schroeder has two papers from 1961 that introduce the idea of allpasses and using them for reverberators.&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;“Colorless” Artificial Reverberation&lt;/em&gt;, &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; Schroeder and co-author B.F. Logan note six features that they seek from a delay-based reverberator:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There should be a flat frequency response&lt;/li&gt;
&lt;li&gt;Normal modes (i.e., resonant emphases on specific frequencies) “must overlap and cover the entire audio frequency range”&lt;/li&gt;
&lt;li&gt;Different frequencies should decay at the same or similar rate&lt;/li&gt;
&lt;li&gt;There should be high echo density within a short period after an impulse&lt;/li&gt;
&lt;li&gt;Echoes must be non-periodic (i.e., no “flutter echoes”)&lt;/li&gt;
&lt;li&gt;“Periodic or comb-like” frequency responses must be avoided&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Below, I have an 808 drum machine clap played through a feedback comb filter. The delay is set at successively shorter lengths, from 50ms down to 5ms. Notice how at longer delay times, there is a “fluttering” tremolo effect. This is similar to the echoes with two walls directly facing each other, as in a long, narrow hall. At shorter lengths, there is an audible tone created by the repeated echoes. Both of these features are undesirable in a reverb effect.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/clap_comb.mp3&quot; title=&quot;feedback comb-filtered clap&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;“Colorless” Artificial Reverberation&lt;/em&gt;, Schroeder and Logan propose a chain of allpass filters as an answer to this issue. The allpass filter is already better than the feedback comb at creating smooth-sounding results, and to improve on this further, the authors suggest “incommensurate” delay times. In other words, the delay times do not easily divide into each other, preventing the echoes from one allpass from lining up with another. The result of the incommensurate delay times is something more like the random tapping of rain, rather than any periodic rhythm.&lt;/p&gt;
&lt;p&gt;On this algorithm, &lt;a href=&quot;https://valhalladsp.com/2021/09/22/getting-started-with-reverb-design-part-2-the-foundations/&quot;&gt;Sean Costello comments that&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The resulting reverberator structure is a bit hard to tune, as both the reverb attack and decay are controlled by the feedforward/feedback allpass coefficient, and is less “general purpose” than the structure discussed in Schoeder’s next paper.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As Costello then mentions, some reverbs such as the &lt;a href=&quot;https://store.eventideaudio.com/products/blackhole&quot;&gt;Eventide Blackhole&lt;/a&gt; make artistic use of the weirdness of this structure. However, it would be helpful to have a more general-purpose algorithm.&lt;/p&gt;
&lt;p&gt;Below I have a synth riff: first the dry signal, then a mix of the dry signal with a “wet” signal through 8 series allpasses. For the allpass example, I increase the feedforward/feedback parameter from about 0.1 up to 1, then back to 0.1 again.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/pck_supersaw_dry.mp3&quot; title=&quot;synth riff: dry&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/pck_supersaw_allpass_med.mp3&quot; title=&quot;synth riff through series allpasses&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;As mentioned, this doesn&#39;t sound as natural as we&#39;d like, as well as not being super flexible. It&#39;s a start though, and our next version will sound much nicer.&lt;/p&gt;
&lt;h3 id=&quot;parallel-combs-into-series-allpasses&quot;&gt;Parallel Combs into Series Allpasses&lt;/h3&gt;
&lt;p&gt;In &lt;em&gt;Natural Sounding Artificial Reverberation&lt;/em&gt;, &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; Schroeder proposes the class of algorithm shown below. Feedback comb filters create a nice exponential amplitude decay (unlike the allpass chain in which, as mentioned above, both attack and decay depend on the same parameter). In response to the problem of “coloration” in the frequency spectrum, Schroeder notes that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;extreme response irregularities are imperceptible when the density of peaks and valleys on the frequency scale is high enough&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In other words, if we combine several feedback comb filters in parallel, the “comb teeth” in their frequency spectra will tightly “interlock,” creating what &lt;em&gt;sounds like&lt;/em&gt; a flat frequency response.&lt;/p&gt;
&lt;p&gt;The next problem after the feedback comb&#39;s frequency response is the “flutter” effect. Schroeder gives the goal of around 1000 echoes per second to make a “natural”-sounding reverb. Assuming 4 parallel comb filters with delays that don&#39;t easily divide into each other, but are in the neighborhood of 40ms (25 echoes per second), we get only about 100 echoes per second. Assuming (as Schroeder does) that a single allpass multiplies the number of echoes by about 3, two series allpasses after 4 parallel combs is enough to meet the minimum requirement.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/freeverb.webp&quot; alt=&quot;DSP block diagram of 8 filtered-feedback comb filters summed in parallel, and feeding into four allpass filters in series&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;The “Freeverb” Schroeder reverberator (diagram from &lt;a href=&quot;https://ccrma.stanford.edu/~jos/pasp/Freeverb.html&quot;&gt;Julius O. Smith)&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The combination of parallel feedback combs into series allpasses (or sometimes vice versa) is often referred to as a “Schroeder reverb.” The “Freeverb” algorithm shown above is one popular Schroeder reverb. The delays are given in samples, with the floating point values representing the delay feedback coefficients. In the combs, there are two values, and as far as I can tell, the first is the feedback coefficient, and the second is the lowpass filtering (more details in a moment).&lt;/p&gt;
&lt;p&gt;Below I have first the dry synth riff, followed by the riff through the “Freeverb” algorithm on &lt;a href=&quot;https://github.com/reillypascal/RSAlgorithmicVerb/releases&quot;&gt;my algorithmic reverb plugin&lt;/a&gt;. This time it sounds much more natural, and if you play around with the Max patch (see below) or my plugin, there&#39;s much more flexibility.&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/pck_supersaw_dry.mp3&quot; title=&quot;synth riff: dry&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/06/reverb_1/pck_supersaw_freeverb.mp3&quot; title=&quot;synth riff through &#39;Freeverb&#39; algorithm&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;One additional feature of this particular version is that there is a first-order (i.e., 6dB/octave) low-pass filter in the feedback loop of the combs. This causes higher frequencies to decay faster. While Schroeder and Logan seek to make all frequencies decay at an equal rate, the most important aspect seems to be to prevent individual frequency bands from ringing and standing out. Reverberations in the physical world decay more quickly at higher frequencies, due (at least in my understanding) to the sound absorbency of building materials, as well as the fact that &lt;a href=&quot;https://computingandrecording.wordpress.com/2017/07/05/approximating-atmospheric-absorption-with-a-simple-filter/&quot;&gt;the atmosphere can be approximated with a low-pass filter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some final design notes: First, we have the stereo spread. The right channel has a slightly longer delay time than the left, with a default value of 23 samples added to each delay value, according to &lt;a href=&quot;https://ccrma.stanford.edu/~jos/pasp/Freeverb.html&quot;&gt;Julius O. Smith&lt;/a&gt;. This simulates the effect of different physical environments to the right and left of the listener. Second, in my JUCE implementation I have a &lt;a href=&quot;https://en.wikipedia.org/wiki/Low-frequency_oscillation&quot;&gt;low-frequency oscillator (LFO)&lt;/a&gt; slowly modulating the first and third allpass filter delay times longer and shorter, giving a &lt;a href=&quot;https://en.wikipedia.org/wiki/Chorus_(audio_effect)&quot;&gt;chorusing effect&lt;/a&gt; and making the sound richer.&lt;/p&gt;
&lt;p&gt;If you want to play with this reverb algorithm in Max/MSP, you can use the patch below:&lt;/p&gt;
&lt;div class=&quot;maxmsp-clipboard&quot;&gt;
&lt;details&gt;
  &lt;summary&gt;“Freeverb” Max patch. Copy code and select &lt;i&gt;New From Clipboard&lt;/i&gt; in Max.&lt;/summary&gt;
&lt;pre class=&quot;language-max&quot;&gt;&lt;code class=&quot;language-max&quot;&gt;----------begin_max5_patcher----------
8187.3oc6c10baiikF95jeEZzbW2IYv2jb2axdeuaU6bamtbQaQ6vokk7JQm
NYlZ5e6KIAkDoD+.fBVBR5cposCIkLOmC93.f2GR7ud+6ld+xumrd5j+iI+5
j28t+06e26JOUwIdW0wua5ywe+g4wqK+XSeX4yOmrHa5GzWKK46YkmeV5iO9
55zkK1bkWhyd3qoKd5tUIOjouCBE4SjOLgJEE+RFV7SV9ol7aUemEu9b5h4I
Yk2JZ0ISmUdCVd++3ibxzcexkuls4iRJN4+98uu3Ge3Xcj3meI2r6yMHz5tg
xZ2fE916FOLO8geeR1WSWO44j0qieJYR7hYSVG+sjIYKm75KyhyRlrJ4aIqt
ex5jrrbeb8l+JySWj7vxWWT9mh0cjPpJiDBYY7PW5x41DITtKRT4lsTybU1O
lNnSDxKs9x+Mi0kOvZwGDs6CabW8ox9wKI562zoS9sQ3dKR9i761AdWQIboS
krZXejRKcRUP+NYaETr1cRVmN4GJczpKWTsq5B+0+mhK0vrmL8+r3G+0+2he
lrXl9LNHJ8s3UKhet7NO8UFgQHbNO5fXX7qYKyMlr9hf4euxHHQUqQuUQPZ6
QPQ0Y+Z5rYIK5uhyGp8eauwqRVmsbURUPoJpTdWtOO3V9M+nbmgt4J+J82zW
j1x0XUWi7o.5AWju8hxCtln90JuTYAny5bN4g39ZKyEhZcMKFQWymfLLqS+m
I83CrPUcefasOPOAoWlsJ9oxDJyVs7kIwSdLcdxj7FykIWdYduERTc4hN+OM
bDNnCyZ737k4+QZqCipFP8TPwzETg5wAPNnKgGWt543xOrxTOiu69sJ2PxRV
cWxh36mWZRjd6BN+ub5y4tR9o9Xowr0a2b5Zms09VtONebOipa2tpl7b52MH
.pqoyse7fB2UOnizq+2he4SOrZ450r+b3ZzLsiDD1exAYKk68ma3fRq0oOsH
ddQYVO+qQUR10nnV9RReCsmRXkMBDk+hKsdTTT5IYTTyS+VxmdJNcwe1VS9C
uZ2E1BYYGWTt9nfZ9Z9cK+aEuXQx7sihlXXbP0WG.zVCQRapkT7e484k2szG
J720Y6FHwxUo4sgiyJlDWsaVwX2lcWwviRu+0rjc+q06OjiuEO+0jkOt4zaN
eceIuOprzh5lkCKQsc7Cs7YNzua7ox629otK3Z7QeNuJPYuva59auqlV5ueL
fz90WNK++Wde3sb40ec4pLCMjMkMscadM2qWm8CsCKzWWO1owMDpj+4r3GLn
dbUSV8b3DRKZxJcWuuEiXnnxnA1ajdXC5zs75YKdIM+67vxUEekp5vjsYhmO
e4e7z7k2GOOK44WV1YSxM05yRy6ILaUR9expQz1Ls9hr1uS2GuN8gr7ByhTo
4EjBRKKig8I42zFeSMQ88Y5tVtOkl2.edxhmx1XuEwmb23geecsl31kHo3+l
kV5iwq9wt+L+euFOOMeR0cZHaiwT1twbLKNKd+tLdXd5KUq+zwLegNxd+Txh
+bxmyRyc5IOtJobwNFd7H7pQlRGXZxpVxhIrbZxsjztt8om.W8.Vw.syci0M
6j9cSie4kZmtY2uOG+OVV9GJba+N4tg9TpsmZUx2R278kaOa7p7nTVdH5U8T
Lm9ckXWWaEUGWs30zRSYSeVeXaYadwSQOi4yz+A8Wd15W9zSaGHQw8bSrORO
TPRY01HVYnWFwqO2z6ya+9vumLqdOnkiKIcwK4SBtdtqsWdVxiwuNO6tGWtH
qbdWk0I+TqW+wJqr0KtsG9+qUE4u17QdZU5rkKJGbT8RihSu41k2ykd8wZLI
7xOwh3WZ4KmWQJOrzwEWm6jut993UEEVUcYv1bwrkKm27Ra+dySdLq5xujlO
xjlQwrkuz8EyaS+0d9t2uL+hO22e6xqrNOCm9p2UL9n61rjP69b48eU0Nt4e
9uGmOwl3rjhtjKcWx1Kp617qqyGh974M7W8U9VKWYVd87GR9izY5dJI0qLj+
wSeYSknoaKkmk9Tx5rlmKK9o0MOy1r20N0q2W0N9thtDmm6EM+.4sPxS6kOF
h+XyxgsohV8.vtknud655cJ137804XyNHSWLgM4yUydaxWl92mj2V50ruLs9
mt0bw50YgUN1AZ0hTTqmxC6sjT6BsjKr6g926v+qMJoM4KbbHJu6pMoP9xzM
IQl72GLDwBJa0yCzcrc3BK24DCOHDcDwn8swcITZFN5LwRWIW16q2VRlNRzz
cxlgS3zURmFIdLL4ydIfnrpkHWuNubYYJnPVTyRr1SCYPpHSRGMXJICSK0Sp
IyROMPJpASSMXppARWMbJqASaYPpKSReYSJrdRiMXpr9Sm0eJs9Sq0apstRu
0dJtNRyYTpNCS20dJu86sY+90O35C0+dy93+4IL99WrUMfJ6SHhpWjKVa8n2
4LJasmc0zC+ls269fYAajIrY1PuJ5ETIzpChdxaunmpRjcGD8D2dQOovYQO9
MTziFP05aDJcU3icKE9jDgN7obT3icK01kVUsycgO5MUiWRfiq8QukZ7Fp3N
N5QughdJI0sQuaoN9DUDQ3tzF2PAOFywAuqhls+zDtfZ93kC0nrvCN91spqi
vmvlvWfHxUgO40Q3SJUVLW2J4XcP3SbcD9XLoES1kvcU36pX7x0DxId97WJ9
VFjFIpbtGQZbSnDtYASdOASGjEtM6toxNGFq5Ugm9T4ok+Tco1SOJ9zupOlo
7Sep+bfBPVnBTuJAozq1qpXfD6G46VLHCEDxTQgLRXHKDGZ.AhLWjHCDJxHw
hLRvHCDMxLgiLR7HCEPxTQjrUHoADSxHAkFVTogEVZXwkFTfo9DYpagl5QrI
iEbxBQm5V3o15.rsbSs94LIG09o4OXtFcu3DeRVznrriJVmI5GLY+fZCLbdJ
iR7ePx+CG.vINX+yFFoUxhHMORm+Op3.yhzrAhz7akH8OYXjlFETFpoDWGpo
HT2LTGTVolpIp1gQZVD59nstOXUyEygQ5fakH8rj4w+XxmeLIY18wO76Sn10
wst2DJkZdNxgB8pakPe5hIzcPhVBHpcAeUMVQMK1SFnqb4MTrmuK1u8krwjO
mGrlPx+U72KJbpl0WwicsgEMLh9gbP+KNQ5tBmaoFF0nmtpGphBFS6bhJKGq
Sfd8FBHtqL3lIuPtEaYt.QEX1R2Mcovgi1DCBiGtxJEuHVLb9nk9awm2r375
kut5gMkxapxLo8XQ9b6yRWrcAr90cI.y66wM0ONZqWYo0WLE2N9BKWMSuXpz
KTWipKX5y0H9gqIssNWUkT+v5E1Z8Q9TKFtsUqB8oXO0Vqm6Qw9xJBVWu2ar
9fwzgD4RnCI1nRBdQjFgoFSKlqSWqm1eOmN6kkoKxVucIxJFqnlabIsb5r0O
fpYSU+yxCN9p460Lu4m3xS67PZnyzNmBsyg14P6bncNzNGZmCsyg14PPWncN
zNGZmCsyg14P6bncNzNGZmCsyg14P6bncNzNGZmCsyg14P6bncNzNGZmeApc
tR+KmncNAZmCsyg14P6bncNzNGZmCsygftP6bncNzNGZmCsyg14P6bncNzNG
ZmCsyg14P6bncNzNGZmCsyg14P6bncNzN+BT6bYPjqzNmGAsyg14P6bncNzN
GZmCsyg14PPWncNzNGZmCsyg14P6bncNzNGZmCsyg14P6bncNzNGZmCsyg14
P6bncNzNGZmelzN2P0xiphVA5eczaV7GtE0eguYw+X57rjUIytay5XcW9rGu
2fHKUICqEaoQFBhfnmfKGfHbQAhfd0iUBJ.Q.fH.PD.HBdhLhr.srVRliU2h
Rfhs6Ep0BXQUJGGpifNhCfZidnsLmqiXHpi2Zcb22cxMilH+kOZb0ZNUOKMp
tKbkyzD4lQDvulmRe4peXYka8uDBmEuk2lzHrN8oEGNihd6CupKbavkb.0Vo
2nJd+kproEi4+KSsT5a8fF0ReyHB2UXDbaxFx1Qzz.MDS6VhoWMS8jqY7P2U
bDdCUbHpQCR7y4Sr9oQUZTkTdCW2b2wlCMBbgzUdAgNmrC4BgQtB3BwVoJkW
JpFYqiUrZDlIZDKeHFkfUqa+V8TZTbzkfFngVFWLVKsHQo7Y540UgFZ8Cnxp
QAP1dz0X7hQtTZgXqX39DfHpwz51WHrvVrn7IpnnQiv3olz+AmwKWKnpIKzb
pCUGIz5hx2czkPCMqiYpKEfZniIahuzLjNlN.8FiWcA26cOi05XMd+9MSw3A
DQHjtFPDF.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB
.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q7H.QXblqADg..Q.fH.
PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.
h..Q.fH.PD.HB.DA.h..Q.fH.PDOBPDhlR.GBHxASVD.h..Q.fH.PD.HB.DA
.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.
PD.HB.DA.h..Q.fHmQ.QBIJWyGRH3CA7g.9P.eHfODvGB3CA7g.9P.eHfODv
GB3CA7g.9P.eHfODvGB3CA7g.9P.eHfODvGB3CA7g.9P.eHfODvGB3CA7g.9
P7G9PjZhPbIeHAfODvGB3CA7g.9P.eHfODvGB3CA7g.9P.eHfODvGB3CA7g.
9P.eHfODvGB3CA7g.9P.eHfODvGB3CA7g.9P.eHfODvGB3CwM7g7SEBJnLYW
jIfppMkPA0LHPn8.Ah53g.wOBfgglD.kDd8.H63Cfxqj.HWZTMPV0hnVE.4G
e.TbkD.YAAFsQPEHbb.jbkD.ELlIunjTrZizyAwunqgv2eahQc+EPzxNQ1D0
NxXW30PryE3YxCINFOSPmInybuA9B5L0yV.zYB5L2uJAny7cfNSPm4EdjFzY
dgTGGzYN5HMnyDzYdMCAEny7LF7AcldTgAnyzqJN.cldSoAnyzQBkC5LAclC
xSCnyDzYB5LclwC5Laq+CPmIny7zZ7fNSPm4IhLoHpALfnj5903QNhLoqE1L
UTSHiSTstFNK7csPloTZR3iwbb3iC1tZPMmCY6hQAbW.tK.2Ef6BvcA3t.bW
.tK.2Ef6BvcA3t.bW.tK.2Ef6BvcA3t.bW.tK.2Ef6BvcA3t.bW.tK.2Ef6B
vcA3t.bW.tK.2ka3CoXUULGtFpT2GWnYDfP5g.jqB1tZYQm5AfFGF8BtRhdb
y4hSE4pfW3URvSXBSlZ4ZcWvK5JI3IMYOmUU1fUxYNJ5wuJdUE115a0ZRiHM
uTDRX+qtk4HYN.2fjdBNMtosuJVMiYcMPh8hbls3NcM0O5gCdv3B0wZfgVXf
7nyfAFXgAFdFrOkE1m3LXeRKrO1Yv9D1D.4pygEZSHjKOGVnvFK7bTKTvswB
4mCKjYiEdN5pVPsoqlyRaYhMV3YIFZS93fCWFk2bCjayHF1DuOsVnx1pgm7X
nz1pgmbKTXao7I2B41NvvStEZSG1L94vBI11RgcvGt80Q2yLcZ+lN0iMcR+l
NyeM8xFc8X57SqoaSh0yPVKlM4UKR7yO3ytmLfp.Ms.ZM9JVdnMG0cYhxe84
xdHszoogBpAdMwi8Z1H7ZEKz.ul5wdM0dulKMoBdfG6zjQTTKnRC7Zl+50kR
5aqWyHLC7ZtG60g160QgQF3zBO1oGQZqf.kANs7z5zgVlplMjOKDAZzGK0+R
RjaOxaRUGZapZKcZZPH2.ul3wdMaDdsLfXfWS8XulZuWWn01vNcfG6zjQTTy
kAF30L+0qoQivqoBgAdM2i85P685HN0.mV3wN8HRaEPCMvoOwopCrLUMYHel
FTNJLNuj1Bd3ti7lT0A1lp1RmlpTAF30DO1qYivqERgAdM0i8510m6bkvMv1
Dt1VfwzK9y.EXL+0qaG28A7ZhdweFvq4drWGZuWGpWFjAbZgG6ziH4iRuJHC
3zm3Dt1H9pQUuY7.8a1jRh7BCnaOxaJnswmU16yJEw.e1iKmk16yUCsb.eV4
u9L2qx0Zikyj8a5g9qoSsq0ESuvSL41C8lQyZkWKGgWKoLC7ZpG60hQ30L8y
V8.dMyi8ZxH7ZhdoUGvqOwCIzJFnLpcMkTV3FJ0D7KTaOxaZVakSKsvoCXQ0
b5xi7lV0V4zBKbZk90vSkSqnROpQsUNM2BmVDVujt7HuoMsv10f4TS7Fi64O
sFLluafTe2.I9tAZ4RRepaiPi77.HMz2MPe+YBiZ0LYOGOoFTqdrvNGOwPTq
dTMNGOwPTtsqv9IuqFlsqF9I2Bo1tx0mbKzl7czyRaYa0np+PXsKt6Ja+W6b
fsl0dOP46L2AdPxacio4KSebURwtxx8S9kuTeqKn02kF5GCedfpZ0GZ6gJu4
CTdscYkFOL40ewmz8Ch+.a9LsswyTqzquMblt1rYpW32wlLSGavLcu4xL7FK
SWapLM1PYLbyjo1FISjn5c.fFXmvxCCE6uBBctIxXvFHiIadLCtwwX3lFSOa
XLlsYwLvFEyfaRLCtAwLvlCyvaLLCtovXvFBiIaFL1rQvzyl.yfa.L8u4uz+
F+R+a5K8tguz0l8R6azKcrIuXzF7hgatKsuwt7lt0swEl7dBgq2coBzuyR4A
G8qIDwUxFGnvpvmtKVWD9tR13.kRk4gOk9k4nKBehqivGiIMO7IcWsuqs8cw
7LWuT7sL9ElTnNJRIFtQKx6IXxvFs3E0FsHU+RoNTgMZQrQKhMZQrQK1UtoV
+biXmQaxGMdqpP+XCVsyfD3rspBt3VYiA4mMLRWsAETsQK6tcgNNGa3e6sZh
Q5G6BJw0gZJB0MC0UOCxQgNNRyhP2Gs08AqZtXNLRGfcpUi53l57cpUl51bO
VLeBMulYWvuRWHly13x3xayc0uYoO93qESft4FI2jOWMqu7AlKMcO9S+FflV
8hfl3tcUN9M59eYUOTVssWpeX406VMUaIAtoLH.6rec0cjHP53c1Od3UvN6G
21s2lRh98k82Fts6uMs+t+T2Hvu1Nnr20nctGZ4Y6GTba2ZyX9zdaVOUg5v5
8osyp1e0x1W0JeZKghSs054dTrmEMl58di0GLlNjtH1f5XiJI3EQZDlZLsXt
NcMlw6RsUiUTTNzbo9MsQ8Cn5cKZ8OKO33ql6g6IcGi14AUy+2EZmSg14P6b
ncNzNGZmCsyg14P6bHnKzNGZmCsyg14P6bncNzNGZmCsyg14P6bncNzNGZmC
syg14P6bncNzNGZmCsyu.0NWoGosSzNm.syg14P6bncNzNGZmCsyg14PPWnc
NzNGZmCsyg14P6bncNzNGZmCsyg14P6bncNzNGZmCsyg14P6bncNzNGZmeAp
ctjKbk147HncNzNGZmCsyg14P6bncNzNGB5Bsyg14P6bncNzNGZmCsyg14P6
bncNzNGZmCsyg14P6bncNzNGZmCsyg14P67yj14FpVtVrbNgoCZG8lE+gaQ8
W3aV7OlNOKYUxr61rNV2kO6w6MHxRUxvZwVZjgfHH5I3xAHBWTfHnW8XkfBP
D.HB.DA.hfmHiHKPKqkj4X0snDnX6dgZs.VTkxwg5Hni3.n1nGZKy45HFh53
sVG28cmbynIxe4iFWslS0yRip6BW4LMQtYDA7q4ozWt5GVV4V+KgvYwa4sIM
BqSeZwgynn29vq5B2FbIGPsU5Mph2eoJaZwX9+xTKk9VOnQsz2LhvcEFA2lr
grcDMMPCwztkX5UyTO4ZFOzcEGg2PEGhZzfD+b9DqeZTkFUIk2v0M2cr4Pi.
WHckWPnyI6PtPXjq.tPrUpR4khpQ15XEqFgYhFwxGhQIX051uUOkFEGcInAZ
nkwEi0RKRTJelddcUngV+.prZT.jsGcMFuXjKkVH1JFtOAHhZLst8EBKrEKJ
ehJJZzHLdpI8evY7x0BpZxBMm5P0QBstn7cGcIzPy5Xl5RAnF5Xxl3KMCoio
CPuw3UWv8d2yXsNVi2ueyTLd.QDBoqADgA.Q.fH.PD.HB.DA.h..Q.fH.PD.
HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..
Q.fH.PDOBPDFm4Z.QH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.D
A.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.Dwi.DgnoDv
g.hbvjEAfH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.f
H.PD.HB.DA.h..Q.fH.PD.HB.DA.h..Q.fH.PD.HxYDPjPhx07gDB9P.eHfO
DvGB3CA7g.9P.eHfODvGB3CA7g.9P.eHfODvGB3CA7g.9P.eHfODvGB3CA7g
.9P.eHfODvGB3CA7g.9P.eHfOD+gODolHDWxGR.3CA7g.9P.eHfODvGB3CA7
g.9P.eHfODvGB3CA7g.9P.eHfODvGB3CA7g.9P.eHfODvGB3CA7g.9P.eHfO
DvGB3CA7g.9PbCeH+TgfBJS1EYBnpZSITPMCBDZOPfnNdHP7i.XXnIAPIgWO
.xN9.n7JI.xkFUCjUsHpUAP9wG.EWIAPVPfQaDTABGG.IWIAPAiYxKJIEq1H
8bP7K5ZH782lXT2eAkZevi1tFWGYrK7ZH14B7L4gDGimInyDzYt2.eAcl5YK
.5LAcl6Wk.zY9NPmIny7BORC5LuPpiC5LGcjFzYB5LulgfBzYdFC9fNSOpv.
zY5UEGfNSuoz.zY5HgxAclfNyA4oAzYB5LAclNy3Acls0+AnyDzYdZMdPmIn
y7DQlTD0.FPTRc+Z7HGQlz0BalJpIjwIpVWCmE9tVHyTJMI7wXNN7wAaWMol
ycrcwn.tK.2Ef6BvcA3t.bW.tK.2Ef6BvcA3t.bW.tK.2Ef6BvcA3t.bW.tK
.2Ef6BvcA3t.bW.tK.2Ef6BvcA3t.bW.tK.2Ef6xM7gTrpJlCWCUp6iKzLBP
H8P.xUAaWsrnS8.PiCidAWIQOt4bwohbUvK7JI3ILgISsbstK3EckD7jlrmy
pzui8BCbTzieU7pJrs02p0jF5WydQZFx5dwsLmHyAvFjzSrowMs8EwpYHqqw
QrWfyr01oqY90xn1LtLcrFXnEFHO5vA27lafAVXfgmA6SYSIbvYnDVZgAdNr
OgMQPA6bXg1DBEzygEJrwBImCKjaaGMmbKjYiEROCc0Hn11R4jagDaaobpsP
djssTN4VHw1RY1Ae31W7MOyzo8a5TO1zI8a5L+0zKq+2ioyOsltEVN6bjXkE
Y4nm3G7Y2S7.UfViQsx.ESpbyQcWnn7Welws2oogBpAdMwi8Z1H7ZEKz.ul5
wdM0dulKMoBdfG6zjQTTKnRC7Zl+50kBAZqWyHLC7ZtG60g160QgQF3zBO1o
GQZqf.kANs7z5zgVlplMjOKDAZfoJW0bIQt8HuIUcnsopszooAgbC7ZhG60r
Q30x.hAdM0i8Zp8dcwJzOrSG3wNMYDE0bYfAdMye8ZZzH7ZpPXfWy8XuNzdu
NhSMvoEdrSOhzVAzPCb5Sbp5.KSUSFxmoA5GOyvRYY4g6NxaRUGXapZKcZpR
EXfWS7XulMBuVHEF30TO1qaeU8OWIbCrMgqsEXL8h+LPAFye851gjc.ulnW7
mA7ZtG60g160g5kAY.mV3wN8HR9nzqBx.N8INgqMZraT0aFWmrgUBxSX.c6Q
dSAsM9rxdeVoHF3ydb4rzdetZnkC3yJ+0m4dUtVarblreSOzeMcpcstzuXAC
XxsG5Mil0JuVNBuVRYF30TO1qEivqY5mHyA7ZlG60jQ30D8RqNfWehGRnUnt
YT6ZJorvMHrb4H3B01i7ll0V4zRKbZkFV7Jmt7HuoUsUNsvBmVFQq4zkG4MM
psxo4V3zhFUuEg9z5px8bDpYLe2.o9tAR7bCjF46FXnuaf99CBAUY6Bbepg4
iJscwnO4Vnv1Ue8jagDaWoz9svZWb2U19u14Aasq8d501YuC7Tq07IVq9i4b
6uJg0q2AummVsNeR0Z7HRV+4xrqmPs2V+McwDUs2GXoO93qEux4a9FAaxmqd
OoOg7I4.AGQj9geToEaiXPzgzdzgx6N5TOf12i93advSNvKSsI6hbCUsRT8x
dT+PPV8HQNtHG6BHxU+0PWxCEutiqE2HeJJ++UudWnXfvGWUozotUIKZ7gO5
EP3iV+Mb4uLIcwKul8koCDizucABpDGkL9HzEP.hW+8u5+Lop50mnLotJV3t
ZWzgpZQBzKEE6XaY58ML2+I1t0WRE5mu8iO+mko+192+vGJ6cQf1F6Rs3vgO
kIGLA3tFQkn43l6sbvZqfYnU7lZDFZCaBYuIFAyFinwajqVVgvyikw51xnuI
VF0pRNwoLnYtow51zdihZVUUieRiZVUWieRiZTahZrSZTiZSTicRiZ1XYs20
Z0I0mo7mZCq71VKwu1LZlv+w4KySuVksc52h2tk9Tj089jU+p321bw1lXhhT
8j4oeTG1Oe+zGWt543rZahYsl8u0Ynje+VkaKYIqtSuuJUavS6MjfpMNihcL
szmy8l56rREt6lSR2dxCGF0GlL893EOUMbJWGE48FEITmGEIWiQQVeQQtd2n
vkQwvqwfHsufHSqGkKChANNHZX7pzhaNc.c3a+NnqBhGNntFKp5.rG2ZAkw2
IpA2olI9m90zYyZr6+sWJz2XigXpwPe6MlPSsE1ausDXpsvOAERbSMFwQYL6
ce5vXzig+ntQMlWWO2msi50UsIL0AYt9F2za5AcJ5QFZaZ88yn0IoLjdXnjd
RJCIt9FaRSUkChrTChrpFelC3QW9Ipp1+SFTrTdZF6BJALr7nizNCMofPnq3
eT2n.SuQri7FoL3FIeKpZY5Ml579kDmpJ0ByqUeb2Hlo0VN15+Tp4UWNtaDw
TWRzxHm2aSLu3lr2lU9daP4Gtoj28FQ99a93ka33ssIiWLWm2+ue++OvNwTW
L
-----------end_max5_patcher-----------
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;/div&gt;
&lt;h2 id=&quot;final-notes&quot;&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;That&#39;s all for today! My plan is to do a series of posts, each covering a class of reverb algorithms. For the next one, I&#39;ll be writing about rings of allpass filters—this is relevant to, for example, &lt;a href=&quot;https://ccrma.stanford.edu/~dattorro/EffectDesignPart1.pdf&quot;&gt;Jon Dattorro&#39;s popular 1997 algorithm&lt;/a&gt; that appears to be based on a plate reverb from &lt;a href=&quot;https://en.wikipedia.org/wiki/Lexicon_(company)#Reverb_and_effects&quot;&gt;Lexicon&#39;s 224 and 480 reverb units&lt;/a&gt;. Until next time!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;This notation comes from the idea of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Z-transform&quot;&gt;Z-transform&lt;/a&gt;. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Note that the triangle symbol is the standard DSP symbol for amplification/an amplifier. Also note that &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D44F TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; and &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;CHTML&quot; style=&quot;position: relative;&quot;&gt;&lt;mjx-math class=&quot; MJX-TEX&quot; aria-hidden=&quot;true&quot;&gt;&lt;mjx-mi class=&quot;mjx-i&quot;&gt;&lt;mjx-c class=&quot;mjx-c1D44E TEX-I&quot;&gt;&lt;/mjx-c&gt;&lt;/mjx-mi&gt;&lt;/mjx-math&gt;&lt;mjx-assistive-mml unselectable=&quot;on&quot; display=&quot;inline&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;/math&gt;&lt;/mjx-assistive-mml&gt;&lt;/mjx-container&gt; are often used for feedforward/feedback coefficients, although it seems to be somewhat inconsistent which is which. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;M. R. Schroeder and B. F. Logan, “‘Colorless’ artificial reverberation,” &lt;em&gt;IRE Transactions on Audio&lt;/em&gt;, vol. AU-9, no. 6, pp. 209–214, Nov. 1961, doi: &lt;a href=&quot;https://doi.org/10.1109/TAU.1961.1166351&quot;&gt;10.1109/TAU.1961.1166351&lt;/a&gt;. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;M. R. Schroeder, “Natural sounding artificial reverberation,” in &lt;em&gt;Audio Engineering Society Convention 13&lt;/em&gt;, Audio Engineering Society, 1961. Accessed: Dec. 29, 2024. [Online]. Available: &lt;a href=&quot;https://www.aes.org/e-lib/download.cfm?ID=343&quot;&gt;https://www.aes.org/e-lib/download.cfm?ID=343&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/06/reverb-part-1/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;Reverb%20Part%201%E2%80%94%E2%80%9CFreeverb%E2%80%9D&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>May 2025 IWC—More Easily Joining Small Web Communities</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/</guid>
            <pubDate>Wed, 28 May 2025 23:06:00 GMT</pubDate>
            <description>For this month&#39;s IndieWeb carnival on “small web communities,” I&#39;m thinking about lowering the barrier for web independence and freedom</description>
            <content:encoded>
            
            

          
          
          &lt;p&gt;Chris chose this month&#39;s theme of “&lt;a href=&quot;https://thoughts.uncountable.uk/may-2025-indieweb-carnival-small-web-communities/&quot;&gt;small web communities&lt;/a&gt;.” I&#39;m writing about making it easier to become part of a small web community on the IndieWeb: some potential inroads, potential problems, and open questions I have.&lt;/p&gt;
&lt;p&gt;UPDATE: if you&#39;re interested in making your own site, &lt;a href=&quot;https://osteophage.neocities.org/essays/you-can-make-a-website&quot;&gt;Coyote has a helpful guide here&lt;/a&gt;—please check it out!&lt;/p&gt;
&lt;h2 id=&quot;barriers-to-joining-the-indieweb&quot;&gt;Barriers to Joining the IndieWeb&lt;/h2&gt;
&lt;p&gt;Max Böck (writing back in 2022) &lt;a href=&quot;https://mxb.dev/blog/the-indieweb-for-everyone/&quot;&gt;succinctly captures&lt;/a&gt; an issue that&#39;s been on my mind lately, observing that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Generally speaking: The more independence a technology gives you, the higher its barrier for adoption.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I love the experience I have on the IndieWeb, using technologies like webmentions and microformats &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; to interact with others in a way that&#39;s open and modular. I like the IndieWeb principle of “&lt;a href=&quot;https://indieweb.org/principles&quot;&gt;small pieces loosely joined&lt;/a&gt;,” and how that allows me to replace any one piece of my IndieWeb infrastructure should what I&#39;m using “enshittify.”&lt;/p&gt;
&lt;p&gt;I want to make this independence more accessible—to ease some of the issue Max Böck mentions—but how? I&#39;ll use this post to think through some possibilities I&#39;ve been mulling over (addressing a range of potential levels of tech experience), and I would be very interested to hear other people&#39;s thoughts and input on these possibilities!&lt;/p&gt;
&lt;h2 id=&quot;ways-around-these-barriers&quot;&gt;Ways Around These Barriers&lt;/h2&gt;
&lt;h3 id=&quot;on-the-indieweb-without-your-own-website%3F&quot;&gt;On the IndieWeb Without Your Own Website?&lt;/h3&gt;
&lt;p&gt;First, Tracy Durnell &lt;a href=&quot;https://tracydurnell.com/2024/05/17/indieweb-next-stage/&quot;&gt;suggests&lt;/a&gt; that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;we can help people escape the corporate silos even if they don’t want their own website. […]&lt;/p&gt;
&lt;p&gt;The more people who use the independent web—whether creating new work, participating in conversations, curating links, or simply reading—the healthier it becomes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I like the idea of expanding participation in the IndieWeb beyond just the people who own a website, and including non-site-owning readers. Speaking as someone who maintains my own site on the IndieWeb, I love when I see people interact with my writing on social media sites where I &lt;a href=&quot;https://www.citationneeded.news/posse/&quot;&gt;POSSE&lt;/a&gt; my posts—no webmentions required.&lt;/p&gt;
&lt;p&gt;Comment sections on personal sites can be great as well, &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; and can offer further ways to interact. Alex (someone who does have webmentions on their site) &lt;a href=&quot;https://alexsirac.com/webmentions-make-me-sad/&quot;&gt;writes about&lt;/a&gt; preferring when blogs &lt;em&gt;also&lt;/em&gt; have a comment section, noting that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I’m just sometimes tired of high quality stuff, you know? Sometimes, all I want is to comment on someone’s post to say « lol » or « nice thanks for sharing » or « saaame! » and that’s not something that warrants a whole blog post and entry in my RSS feed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, there&#39;s email. I have a &lt;code&gt;mailto&lt;/code&gt; link at the bottom of every post, and I&#39;ve had nice experiences sending and receiving emails related to my and others&#39; writing on the IndieWeb. Even outside the practicality of more (and more ubiquitous) ways to talk, I like the personal nature of (non-work) email, and the way it feels kind of like letter-writing.&lt;/p&gt;
&lt;h3 id=&quot;website-ownership-for-the-non-developer&quot;&gt;Website Ownership for the Non-Developer&lt;/h3&gt;
&lt;aside&gt;
&lt;p&gt;In &lt;a href=&quot;https://tracydurnell.com/2024/05/17/indieweb-next-stage/&quot;&gt;the link I shared above&lt;/a&gt;, Tracy also wants to see&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;advocating for adoption of IndieWeb tech by platforms like Tumblr, WordPress, Ghost, and Buttondown, versus targeting individuals, so everyone who uses these tools can be brought in without having to do work on their own&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think that makes a lot of sense, and is an important part of the process of welcoming people onto the IndieWeb (and the small-i independent web more broadly). While my discussion here is more narrowly focused on ways to help people get their own websites &lt;em&gt;without&lt;/em&gt; these services, I just wanted to include this note beforehand to contextualize my own thoughts.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;OK so someone wants to have a personal website, but doesn&#39;t know much/anything about programming. What then? Böck &lt;a href=&quot;https://mxb.dev/blog/the-indieweb-for-everyone/#lowering-the-barrier&quot;&gt;mentions&lt;/a&gt; Mastodon, Ghost, Tumblr, and micro.blog as contributing to lowering the barrier of owning one&#39;s own content, and those certainly are decent options.&lt;/p&gt;
&lt;p&gt;Even with the more blog-like Ghost and micro.blog, as well as similar things like &lt;a href=&quot;https://bearblog.dev/&quot;&gt;Bear&lt;/a&gt;, my biggest issues are 1) the editor is coupled to the backend, and 2) the data formats for your pages are less standardized than I would like. To expand on point 1, the tool for creating pages and posts is accessible via a web interface owned by the same people running the hosting servers. For point 2, I would prefer a generic format that has stood the test of time, such as Markdown.&lt;/p&gt;
&lt;p&gt;My stance on both of these issues is because at this point, I use technology with the assumption that any tool or platform &lt;em&gt;will&lt;/em&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/Enshittification&quot;&gt;enshittify&lt;/a&gt;, and I will have to be able to migrate to something else. I see my strategy here as fitting with the IndieWeb approach of “&lt;a href=&quot;https://indieweb.org/principles&quot;&gt;small pieces loosely joined&lt;/a&gt;”; I strive to make my workflow “modular,” with individual parts as generic and replaceable as possible.&lt;/p&gt;
&lt;p&gt;For example, I use &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt; for maintaining a personal wiki: Markdown documents, extended with &lt;a href=&quot;https://en.wikipedia.org/wiki/Help:Link#Wikilinks_(internal_links)&quot;&gt;wiki-style link syntax&lt;/a&gt; to link between notes. However, I make sure not to use plugins or features that would narrow compatibility of my documents, and I ensure that my notebook is compatible with e.g., &lt;a href=&quot;https://vimwiki.github.io/&quot;&gt;Vimwiki&lt;/a&gt;, &lt;a href=&quot;https://www.zettlr.com/&quot;&gt;Zettlr&lt;/a&gt;, the &lt;a href=&quot;https://foambubble.github.io/foam/&quot;&gt;Foam plugin&lt;/a&gt;, etc., so I can switch anytime Obsidian does something I don&#39;t like.&lt;/p&gt;
&lt;p&gt;In my ideal no- or low-code web publishing tool, authors could use a program on the desktop that takes in a common format (such as Markdown) so that all pages can easily be moved to another CMS/static site generator (SSG). Since the program is not bound to any given hosting service, authors could put the resulting generated HTML on any service; Netlify, Neocities, GitHub Pages, and others are currently decent options, but with this setup, could easily be replaced.&lt;/p&gt;
&lt;h4 id=&quot;existing-low--%26-no-code-options&quot;&gt;Existing Low- &amp;amp; No-Code Options&lt;/h4&gt;
&lt;p&gt;One tool that I find to be a promising (but still imperfect) idea is &lt;a href=&quot;https://getpublii.com/&quot;&gt;Publii&lt;/a&gt;. It&#39;s an open-source (GPL-3.0) CMS/SSG that runs as a GUI desktop app. You can use a WYSIWYG, block, or Markdown editor to write pages and posts; create menus, add page tags, add CSS, and many other things; and then generate your site, outputting a folder of static HTML/CSS/JS files that can then be put on a server.&lt;/p&gt;
&lt;p&gt;I like that this program is a standalone desktop app that can work with any server, and that it&#39;s easy to use. What I don&#39;t like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It doesn&#39;t seem to store posts and pages as (e.g.) plain Markdown files that could easily be moved to another similar tool.
&lt;ul&gt;
&lt;li&gt;It might be possible to get it to do this, but I wasn&#39;t able to figure it out; it doesn&#39;t seem to do it by default; and it certainly doesn&#39;t point the user toward this kind of workflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Design is not very flexible. You can choose from a limited selection of free themes, with paid options, but even the paid options are monolithic, and you can&#39;t combine, say, a block layout skeleton with a layout-independent color/font/icon theme.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One idea this gives me, though, is a GUI SSG with Markdown files as input (and maybe with modular layout features), and with community extensibility. I use &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; and enjoy it very much. However, requiring the use of the terminal seems to be a big barrier (whether real or psychological) to use by non-developers.&lt;/p&gt;
&lt;p&gt;Since writing/note-taking tools that use Markdown (such as Obsidian or &lt;a href=&quot;https://ia.net/writer&quot;&gt;iA Writer&lt;/a&gt;) seem to be decently usable and liked by non-developers, I could imagine a GUI wrapper around something like Eleventy/Hugo/Jekyll being helpful. Authors could take a folder of Markdown files (perhaps written using one of the aforementioned writing/note-taking tools) and turn them into a website, with the generator handling all code beyond the Markdown itself.&lt;/p&gt;
&lt;p&gt;It might even be feasible to make a GUI wrapper that&#39;s compatible with multiple SSGs: choose from a menu whether you want to use Eleventy, Hugo, Jekyll, Astro, or another popular option. Because the terminal commands to install and use those are relatively straightforward, the GUI wrapper handles installation and running the SSG for users who might be intimidated by the terminal.&lt;/p&gt;
&lt;p&gt;Inspired by Publii&#39;s model of themes, I could also imagine a system of modular community HTML templates and CSS themes. Perhaps one set of modules handles a CSS &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox&quot;&gt;flexbox&lt;/a&gt;-based block layout, with just the CSS and the HTML&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s/classes necessary to do so, and another handles the styling for colors, fonts, icons, etc. It could be an interesting engineering challenge to integrate something like this with e.g., Eleventy&#39;s &lt;a href=&quot;https://learn-eleventy.pages.dev/lesson/6/&quot;&gt;partials&lt;/a&gt; and/or &lt;a href=&quot;https://www.11ty.dev/docs/shortcodes/&quot;&gt;shortcodes&lt;/a&gt; systems, or similar parts of another SSG.&lt;/p&gt;
&lt;p&gt;I haven&#39;t attempted any of this, so let me know if there&#39;s a roadblock I&#39;m not considering, but I figure that integrating with one or more popular SSGs could further the goal of modularity and flexibility, and make the GUI wrapper easier to create and maintain.&lt;/p&gt;
&lt;h3 id=&quot;what-makes-coding-intimidating&quot;&gt;What Makes Coding Intimidating&lt;/h3&gt;
&lt;p&gt;I have some changes I would like to see in the perception of what a website entails to make things less intimidating if someone &lt;em&gt;does&lt;/em&gt; want to get their hands dirty with coding. I think a lot of people&#39;s perception of what programming entails makes putting together a basic webpage seem more intimidating than it actually is, especially with the “&lt;a href=&quot;https://infrequently.org/2024/11/if-not-react-then-what/&quot;&gt;frameworkism&lt;/a&gt;” and emphasis on JavaScript in many frontend spaces. In addition to the obvious hurdle of coding at all, I imagine the complexity of the websites most people see on a daily basis unnecessarily skews the perception of what &lt;em&gt;needs&lt;/em&gt; to be done to make a website.&lt;/p&gt;
&lt;p&gt;Even if someone knows they &lt;em&gt;can&lt;/em&gt; make a simple site, the lack of such sites creates an unfair comparison for people&#39;s first projects. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; When I first started coding I was unsure of myself and didn&#39;t want to put something online that looked “less than” in comparison to other people&#39;s sites. Now that I&#39;ve had the time and experience to form more thought-out opinions on programming and the web, I look at sites like &lt;a href=&quot;https://motherfuckingwebsite.com/&quot;&gt;motherfuckingwebsite.com&lt;/a&gt; and &lt;a href=&quot;http://bettermotherfuckingwebsite.com/&quot;&gt;bettermotherfuckingwebsite.com&lt;/a&gt; with appreciation for their minimalism and focus on content.&lt;/p&gt;
&lt;p&gt;I want to see more widespread emphasis that what these sites do is plenty, and that there&#39;s nothing shameful or “less than” about putting that online. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; As I &lt;a href=&quot;https://reillyspitzfaden.com/posts/2024/12/hypertext-memex-collaboration-socialization/&quot;&gt;wrote about here&lt;/a&gt;, the magical thing about the web is hypertext itself: the way hyperlinks connect ideas and create something far bigger than any one person&#39;s ideas or writing.&lt;/p&gt;
&lt;p&gt;Additionally, I want to emphasize to those potentially interested in the IndieWeb that webmentions/microformats/etc. are nice but by no means essential. As I mentioned &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#on-the-indieweb-without-your-own-website%3F&quot;&gt;above&lt;/a&gt;, a simple &lt;code&gt;mailto&lt;/code&gt; link on each post—especially combined with POSSE-ing posts—is a fairly decentralized and open way to socialize on the IndieWeb without much technical overhead.&lt;/p&gt;
&lt;p&gt;Of course, I should acknowledge that even this requires at least &lt;em&gt;some&lt;/em&gt; degree of both technical know-how and comfort with searching/willingness to search for technical answers online. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt; Additionally, if only some IndieWeb community members have access to webmentions/etc., while others “only” have email links, I worry that less-technical community members won&#39;t be able to feel like full participants. I don&#39;t have an answer to this—again, it&#39;s that inverse relationship between independence and barrier to entry—but I&#39;m continuing to think about it.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;This was the first post on this blog for which I made an outline, and I even re-wrote the outline multiple times! This was because I apparently have a huge list of thoughts on this inverse relationship between independence/barrier to adoption, and many of them didn&#39;t make it into today&#39;s post (in the interest of brevity and clarity).&lt;/p&gt;
&lt;p&gt;I plan to continue writing on this topic, and as I continue to think through all this, I welcome input from others with whom any of it resonates. Until next time!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I have a &lt;a href=&quot;https://reillyspitzfaden.com/wiki/tutorials/webmention-tutorial/&quot;&gt;collection of tutorials&lt;/a&gt; on webmentions and microformats in my personal wiki, including simple ways to get started that require minimal coding. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I &lt;a href=&quot;https://reillyspitzfaden.com/posts/2024/01/do-read-the-comments/&quot;&gt;made a basic comment section&lt;/a&gt; on my site back when I started. I&#39;ve disabled it from time to time—I&#39;ve mostly gotten spam—but I recently re-enabled it in the spirit of what I&#39;m discussing here. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;My HTML looks kind of messy without the CSS on it, and I&#39;m definitely interested in doing some cleanup so I can do things like participate in &lt;a href=&quot;https://css-naked-day.org/&quot;&gt;CSS Naked Day&lt;/a&gt; and model how things don&#39;t need to be complicated or fancy. I might also do something like &lt;a href=&quot;https://localghost.dev/&quot;&gt;Sophie Koonin&lt;/a&gt; has, with multiple stylesheet options for the user (try clicking on the one with the lowercase “a” in the upper right of the site). &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Blake Watson&#39;s &lt;a href=&quot;https://htmlforpeople.com/&quot;&gt;HTML for People&lt;/a&gt; set of tutorials is a great example of materials in this vein. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I&#39;m always reminded of the evergreen &lt;a href=&quot;https://xkcd.com/2501/&quot;&gt;XKCD #2501&lt;/a&gt; &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/may-2025-iwc/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;May%202025%20IWC%E2%80%94More%20Easily%20Joining%20Small%20Web%20Communities&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>I Would Rather Spend an Evening on a Web Scraper than Use Your App</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/05/i-would-rather-spend-an-evening-on-a-web-scraper-than-use-your-app/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/05/i-would-rather-spend-an-evening-on-a-web-scraper-than-use-your-app/</guid>
            <pubDate>Fri, 23 May 2025 03:48:09 GMT</pubDate>
            <description>My Swedish textbook publisher wanted me to listen to audio examples on their site or in their app. I had other ideas.</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/notes-photos.css&quot; /&gt;
&lt;p&gt;I&#39;m an extremely stubborn person, especially when it comes to how I interact with technology. For example, I vastly prefer to listen to audio (music, textbook audio examples, etc.) offline and in my music player, &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/i-would-rather-spend-an-evening-on-a-web-scraper-than-use-your-app/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; and I&#39;m determined to ensure I can do that.&lt;/p&gt;
&lt;p&gt;My partner&#39;s brother just got married, and since his spouse is Swedish and the U.S. is…*gestures vaguely at everything*, he&#39;s moving to Sweden. My partner is learning Swedish, and I think it could be fun to learn some too (I do like languages/linquistics). I have the textbook &lt;em&gt;Complete Swedish: Beginner to Intermediate&lt;/em&gt; by Anneli Haake, and it seems great—aimed at self-teaching and includes audio resources. Unfortunately, the &lt;a href=&quot;https://library.teachyourself.com/id004325173&quot;&gt;audio resources&lt;/a&gt; are either available through an online player or in the publisher&#39;s app. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/i-would-rather-spend-an-evening-on-a-web-scraper-than-use-your-app/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Since the online audio player embeds MP3s, and the links to the files are visible in the HTML source, I decided to make a web scraper to download the MP3s and use them how &lt;em&gt;I&lt;/em&gt; want. Let&#39;s have a look at what that entails!&lt;/p&gt;
&lt;h2 id=&quot;understanding-the-page-structure&quot;&gt;Understanding the Page Structure&lt;/h2&gt;
&lt;p&gt;First, I looked at the page source to see what I&#39;m dealing with. I found a list of &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt; elements, with the MP3s in the &lt;code&gt;url=&amp;quot;&amp;quot;&lt;/code&gt; field. There were two issues with this, however. First, these &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt; elements don&#39;t show up until you click on the play button in the web player. This meant I had to do that manually and then download the HTML, rather than directly giving the URL to my scraper.&lt;/p&gt;
&lt;p&gt;Second, the MP3 links in the &lt;code&gt;url=&amp;quot;&amp;quot;&lt;/code&gt; fields were relative links, and they didn&#39;t work when I appended them to the page URL. I also wasn&#39;t able to find the CDN URL for them by searching the page source. What did turn out to work was using the “Page Info” feature in Firefox. I clicked on the lock icon in the URL bar, followed by the “connection secure” field in the resulting menu…&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/mp3-scraper/Screenshot%202025-05-23%20at%205.09.02%E2%80%AFPM.webp&quot; alt=&quot;screenshot of clicking on the lock icon in the corner of the Firefox address bar&quot; class=&quot;img-strip&quot; /&gt;&lt;/p&gt;
&lt;p&gt;…I clicked on “more information”…&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/mp3-scraper/Screenshot%202025-05-23%20at%205.09.55%E2%80%AFPM.webp&quot; alt=&quot;screenshot of clicking the “more information” field in the dropdown from the previous step&quot; class=&quot;img-strip&quot; /&gt;&lt;/p&gt;
&lt;p&gt;…and in the resulting “Page Info” popup, I went to the “media” tab and looked for items with the type “Audio.”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/mp3-scraper/Screenshot%202025-05-23%20at%205.11.02%E2%80%AFPM.webp&quot; alt=&quot;the Firefox “page info” menu, showing the link to an embedded MP3&quot; class=&quot;img-square&quot; /&gt;&lt;/p&gt;
&lt;p&gt;By comparing the URL for the resulting media files to the relative URLs in the &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt; HTML tags, I was able to get the CDN root URL for the files, which I confirmed to work with the relative URLs.&lt;/p&gt;
&lt;h2 id=&quot;parsing-the-page-with-cheerio&quot;&gt;Parsing the Page with Cheerio&lt;/h2&gt;
&lt;p&gt;I&#39;ve played around a bit with the &lt;a href=&quot;https://cheerio.js.org/&quot;&gt;cheerio&lt;/a&gt; Node JS library for parsing HTML, and since I wanted some more experience with it for other projects anyway, &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/i-would-rather-spend-an-evening-on-a-web-scraper-than-use-your-app/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; I decided to go with that. My resulting code is below.&lt;/p&gt;
&lt;div class=&quot;code-file&quot;&gt;index.js&lt;/div&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; https &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;https&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cheerio &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;cheerio&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;swedish.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; $ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cheerio&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cdn_root &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://cdn77.papertrell.com/AkplUGiYnqjKEYG4SiU1hQ==,1747972188/Consumers/004/Users/325/Publish/004325173/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item_list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;item&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

item_list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; media_url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;url&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; media_url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createWriteStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;output/&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; file_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cdn_root &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; media_url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;finish&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Downloaded &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;file_name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cheerio uses a subset of jQuery&#39;s syntax. In line 6, I assign a cheerio object to the constant &lt;code&gt;$&lt;/code&gt; with the &lt;code&gt;cheerio.loadBuffer()&lt;/code&gt; method. As I mentioned, I&#39;m doing this with an HTML file I manually downloaded since I need to have clicked the play button.&lt;/p&gt;
&lt;p&gt;In line 10, the syntax &lt;code&gt;$(&#39;item&#39;)&lt;/code&gt; retrieves all &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt; elements, and I can iterate over them with &lt;code&gt;.each()&lt;/code&gt;. I get the relative URL with &lt;code&gt;$(element).attr(&#39;url&#39;)&lt;/code&gt;, and I get only the string after the last &amp;quot;/&amp;quot; with &lt;code&gt;media_url.split(&#39;/&#39;).pop()&lt;/code&gt;. &lt;code&gt;fs.createWriteStream()&lt;/code&gt; opens the file to be written; &lt;code&gt;https.get()&lt;/code&gt; requests the file from the URL I&#39;ve parsed; &lt;code&gt;response.pipe()&lt;/code&gt; writes the response to a file; and on finishing the file, &lt;code&gt;file.close()&lt;/code&gt; closes the write stream.&lt;/p&gt;
&lt;h2 id=&quot;situated-software-and-agency&quot;&gt;Situated Software and Agency&lt;/h2&gt;
&lt;p&gt;In &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/04/a-grimoire-of-shell-scripts/&quot;&gt;this post&lt;/a&gt; I quoted Robin Sloan &lt;a href=&quot;https://www.robinsloan.com/notes/home-cooked-app/&quot;&gt;writing&lt;/a&gt; about “situated” or “home-cooked” software:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;People don’t only learn to cook so they can become chefs. Some do! But many more people learn to cook so they can eat better, or more affordably. Because they want to carry on a tradition. Sometimes they learn because they’re bored! Or even because they enjoy spending time with the person who’s teaching them.&lt;/p&gt;
&lt;p&gt;The list of reasons to “learn to cook” overflows, and only a handful have anything to do with the marketplace.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&#39;m completely self-taught at coding, and in addition to it being fun for me, I&#39;m finding that having coding skills is helpful in working around “&lt;a href=&quot;https://en.wikipedia.org/wiki/Enshittification&quot;&gt;enshittification&lt;/a&gt;”. It&#39;s nice to be able to make small software that gives me agency.&lt;/p&gt;
&lt;p&gt;Since I don&#39;t have much experience with cheerio/web-scraping, it took a little longer than it absolutely needed to, and I may have been able to manually download the MP3s in a similar amount of time, but I would much rather spend that time on something I value (coding) and come out of it with some additional coding practice, so I consider this a success. Until next time!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;For my phone, I use &lt;a href=&quot;https://github.com/OxygenCobalt/Auxio&quot;&gt;Auxio&lt;/a&gt;, which you can get via &lt;a href=&quot;https://f-droid.org/&quot;&gt;F-Droid&lt;/a&gt;, &lt;a href=&quot;https://obtainium.imranr.dev/&quot;&gt;Obtainium&lt;/a&gt;, or &lt;a href=&quot;https://accrescent.app/&quot;&gt;Accrescent&lt;/a&gt;, among other non-Google sources. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/i-would-rather-spend-an-evening-on-a-web-scraper-than-use-your-app/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Insert GIF of the Earl of Lemongrab screaming “UNACCEPTABLE!” &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/i-would-rather-spend-an-evening-on-a-web-scraper-than-use-your-app/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I&#39;m using the Eleventy &lt;a href=&quot;https://plug11ty.com/plugins/table-of-contents/&quot;&gt;table of contents plugin&lt;/a&gt; to generate a table of contents from the header elements in a post. Since it hasn&#39;t been updated since 2021, I&#39;ve tried directly using cheerio (which the plugin is based on), but wasn&#39;t having a lot of success. I figure playing with cheerio some more will help with that. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/i-would-rather-spend-an-evening-on-a-web-scraper-than-use-your-app/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;I%20Would%20Rather%20Spend%20an%20Evening%20on%20a%20Web%20Scraper%20than%20Use%20Your%20App&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>Databending Part 5—Listening to Telephone Codecs</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/05/databending-part-5/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/05/databending-part-5/</guid>
            <pubDate>Wed, 07 May 2025 14:16:00 GMT</pubDate>
            <description>One way to get more variety when transforming data into audio is to change the encoding. Today I&#39;m implementing the VOX ADPCM telephone codec—which I especially like—in Rust to accomplish this!</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/notes-photos.css&quot; /&gt;
&lt;p&gt;Today we&#39;ll be talking about the VOX or &lt;a href=&quot;https://en.wikipedia.org/wiki/Dialogic_ADPCM&quot;&gt;Dialogic ADPCM&lt;/a&gt; format—a lossy algorithm from &lt;a href=&quot;https://en.wikipedia.org/wiki/Dialogic_ADPCM&quot;&gt;Oki Electric&lt;/a&gt; for digital voice telephony—and using it to translate raw data (e.g., program files) into audio. As I mentioned in my &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/01/databending-part-1/&quot;&gt;first post&lt;/a&gt; on the topic, at a certain point,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;once you listen to the “sonified” data from enough files, commonalities start to become apparent. Many programs use some of the same [library] files, and…[even] differently-named library files sometimes contain similar elements—likely re-used code patterns, or further library code compiled in.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One way I&#39;ve found of getting more variety from the data is to change the sample format in which I import it. If I divide or group the raw bytes in different ways, or treat them as coming from audio files with different encodings, I can get different sound results from the exact same data. For example, here is the same data (&lt;code&gt;libicudata.73.1.dylib&lt;/code&gt; from the Calibre e-book manager macOS app) imported into Audacity first as 16-bit integer, then as VOX ADPCM:&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-4/libicudata.73.1%20i16.mp3&quot; title=&quot;libicudata.73.1 imported to Audacity as 16-bit audio&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-4/libicudata.73.1%20VOX%20ADPCM.mp3&quot; title=&quot;libicudata.73.1 imported to Audacity as VOX ADPCM audio&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;Both very cool, and both very different, despite coming from the same data! Today, let&#39;s talk about how ADPCM and VOX formats work; how to do this yourself in Audacity; and how I incorporated these formats into the &lt;a href=&quot;https://github.com/reillypascal/data2audio&quot;&gt;Rust tool&lt;/a&gt; I made in my &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-4/&quot;&gt;last post&lt;/a&gt; to automate the process of converting data to audio. Also check out the end of today&#39;s post for some fun stuff to look forward to!&lt;/p&gt;
&lt;h2 id=&quot;following-along-in-audacity&quot;&gt;Following along in Audacity&lt;/h2&gt;
&lt;p&gt;While I will be doing this in Rust, you can make the exact same sounds (minus the convenience of automation) in Audacity. See my &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/01/databending-part-1/&quot;&gt;first post&lt;/a&gt; on databending for a discussion of how to find the best files to use. Once you have the file you want to convert to audio,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;in Audacity, go to File &amp;gt; Import &amp;gt; Raw Data…, choose your file, and click “Open”&lt;/li&gt;
&lt;li&gt;in the settings menu that pops up, set encoding to “VOX ADPCM,” byte order to “default endianness,” channels to “1 channel (mono),” and sample rate to 44100 (or change sample rate to taste)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-adpcm%3F&quot;&gt;What is ADPCM?&lt;/h2&gt;
&lt;p&gt;Tan and Jiang &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; have a helpful discussion of the basics of ADPCM, or “adaptive differential pulse-code modulation.” First, with differential pulse-code modulation (the “non-adaptive” flavor),&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[the] general idea is to use past recovered values as the basis to predict the current input data and then encode the difference between the current input and the predicted input. (486)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In other words, if we can predict the output to within a decent approximation, all we need to store is the difference between our rough prediction and the actual output. This difference uses less data to store than the original signal, saving bandwidth. The following diagram illustrates the signal flow for the encoder (A) and decoder (B):&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-5/dpcm-block-diagram.webp&quot; alt=&quot;Differential pulse code modulation (DPCM) block diagram. A quantizer feeds back into a prediction of the output; the prediction is compared to the actual next sample; and the difference is used for the next prediction.&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;DPCM block diagram from Tan and Jiang&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that while we describe a “predictor,” there isn&#39;t anything fancy here—we simply “predict” that the current sample will equal the previous one and take the (quantized) difference between that and the actual current sample.&lt;/p&gt;
&lt;p&gt;The next diagram shows the adaptive version of the decoder as shown in the original VOX ADPCM paper from the Dialogic Corporation. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; The primary difference here is the addition of an adaptive scaling factor for the difference between prediction and actual value. This scaling factor is based on the amplitude of the incoming difference, and we will discuss the specifics of the scaling in the next section.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-5/vox-adpcm-block-diagram.webp&quot; alt=&quot;VOX ADPCM (adaptive differential pulse code modulation) decoder block diagram.&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;ADPCM decoder block diagram from the Dialogic Corporation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;vox&quot;&gt;VOX&lt;/h2&gt;
&lt;p&gt;There are a number of ADPCM algorithms—many different ways to adapt our step size based on the amplitude of the difference and/or prediction—and after testing some out while importing data as audio in Audacity, I decided VOX was by far my favorite. Unfortunately I wasn&#39;t able to find anything pre-existing in Rust for VOX—the &lt;a href=&quot;https://crates.io/crates/symphonia&quot;&gt;symphonia crate&lt;/a&gt; that was recommended to me only has &lt;a href=&quot;https://lib.rs/crates/symphonia-codec-adpcm#readme-support&quot;&gt;Microsoft and IMA flavors&lt;/a&gt; of ADPCM. Looks like I need to code it myself! You can find the resulting code &lt;a href=&quot;https://github.com/reillypascal/data2audio&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s a snippet of audio databent through my resulting VOX ADPCM implementation:&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-5/libQt5Core.5.mp3&quot; title=&quot;libQt5Core.5.dylib file databent through VOX ADPCM codec&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;The file is &lt;code&gt;libQt5Core.5.dylib&lt;/code&gt; which I &lt;em&gt;believe&lt;/em&gt; I pulled from DaVinci Resolve a week or two ago. Also, just as a check, here&#39;s a voice file (8 kHz sample rate) I encoded as VOX ADPCM with Audacity &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; and decoded with this Rust tool:&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-5/this-is-a-test.mp3&quot; title=&quot;me saying &#39;this is a test of my voice to import as VOX ADPCM&#39; at 8 kHz sample rate&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;Sounds just as expected—a bit crunchy and lo-fi like a telephone, but clear and comprehensible.&lt;/p&gt;
&lt;h2 id=&quot;reading-the-vox-spec&quot;&gt;Reading the VOX Spec&lt;/h2&gt;
&lt;p&gt;First, we need to calculate the step size &lt;code&gt;ss(n)&lt;/code&gt; and use that and the 4-bit input sample &lt;code&gt;L(n)&lt;/code&gt; to calculate the difference &lt;code&gt;d(n)&lt;/code&gt;. That difference plus the previous output &lt;code&gt;X(n-1)&lt;/code&gt; will give our 12-bit output value. Below is the pseudocode from the Dialogic paper for calculating &lt;code&gt;d(n)&lt;/code&gt; given a value of &lt;code&gt;ss(n)&lt;/code&gt; and an incoming sample. Note the values B3–B0—these refer to the 4 bits in the incoming sample, with B3 as the sign and the rest as the magnitude.&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;B2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;B1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;BO&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;B3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    then &lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make this calculation, we need to get the step size &lt;code&gt;ss(n)&lt;/code&gt;. The pseudocode for that is shown below:&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;M&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The paper includes a pair of lookup tables to efficiently calculate this value. Here they are as I use them in my Rust code. We use the 4-bit incoming value to look up an “adjustment factor” in the &lt;code&gt;ADPCM_INDEX_TABLE&lt;/code&gt;, and we move an index into the &lt;code&gt;VOX_STEP_TABLE&lt;/code&gt; table by that adjustment factor. This index is initialized to zero, giving the first value in that table—16.&lt;/p&gt;
&lt;div class=&quot;code-file&quot;&gt;vox.rs&lt;/div&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// duplicate values from spec; can index w/ whole nibble, incl sign bit (4th)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// increment up/down thru this table...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADPCM_INDEX_TABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...use (clamped) index table to index this array for step size&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VOX_STEP_TABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;49&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;34&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;37&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;55&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;66&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;73&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;88&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;107&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;118&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;130&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;143&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token number&quot;&gt;157&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;173&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;190&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;209&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;230&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;253&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;279&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;307&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;337&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;371&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;408&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;449&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token number&quot;&gt;494&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;544&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;598&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;658&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;724&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;796&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;876&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;963&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1060&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1166&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1282&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1411&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1552&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that incoming magnitudes (first 3 bits) below 4 cause the step size to decrease, and values 4 or greater cause it to increase. The values in &lt;code&gt;ADPCM_INDEX_TABLE&lt;/code&gt; are duplicated so I can use the whole 4-bit value (including bit 4, the sign bit) to index the table.&lt;/p&gt;
&lt;h2 id=&quot;implementing-vox-in-rust&quot;&gt;Implementing VOX in Rust&lt;/h2&gt;
&lt;p&gt;To start, I have a struct called &lt;code&gt;VoxState&lt;/code&gt; that stores the predictor and step index. Note in the diagram above that these two values are fed into single-sample delays (the blocks labeled “Z&lt;sup&gt;-1&lt;/sup&gt;”), &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; so having them stored in a struct allows us to maintain state between calls to the decoder function.&lt;/p&gt;
&lt;div class=&quot;code-file&quot;&gt;vox.rs&lt;/div&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;VoxState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    predictor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    step_index&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I implement a &lt;code&gt;vox_decode()&lt;/code&gt; function for the &lt;code&gt;VoxState&lt;/code&gt; struct, as shown below. We get the step size from last time around, then update the step size for next time. The sign is the 4th bit of the incoming nibble, and the magnitude is the lower 3 bits. We get the difference between the current value and the prediction from last time with the line &lt;code&gt;let mut delta = ((2 * (magnitude as i16) + 1) * step_size) &amp;gt;&amp;gt; 3;&lt;/code&gt;—we will come back to how this relates to the pseudocode in a bit.&lt;/p&gt;
&lt;p&gt;We either add or subtract the predictor and &lt;code&gt;delta&lt;/code&gt;, depending on the sign bit, and clamp the predictor to the range of a 12-bit signed integer. When we return this value from the function, we multiply it by 16, scaling it into the range of the 16 bit integer format of the .WAV file we&#39;ll write later. Before returning, we&#39;ll also update the struct&#39;s step index for next time around.&lt;/p&gt;
&lt;div class=&quot;code-file&quot;&gt;vox.rs&lt;/div&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VoxState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;vox_decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; in_nibble&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// get step size from last time&#39;s index before updating&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; step_size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VOX_STEP_TABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;step_index &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// use in_nibble to index into adpcm step table; add to step&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; step_index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;step_index &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADPCM_INDEX_TABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;in_nibble &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// clamp index to size of step table—for next time&lt;/span&gt;
        step_index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;step_index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// sign is 4th bit; magnitude is 3 LSBs&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; sign &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; in_nibble &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0b1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; magnitude &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; in_nibble &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0b0111&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// magnitude; after * 2 and &gt;&gt; 3, equivalent to scale of 3 bits in (ss(n)*B2)+(ss(n)/2*B1)+(ss(n)/4*BO) from pseudocode&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// + 1; after &gt;&gt; 3, corresponds to ss(n)/8 from pseudocode—bit always multiplies step, regardless of 3 magnitude bits on/off&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; delta &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;magnitude &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; step_size&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// last time&#39;s value&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; predictor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;predictor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// if sign bit (4th one) is set, value is negative&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; sign &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; delta &lt;span class=&quot;token operator&quot;&gt;*=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        predictor &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; delta&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// clamp output between 12-bit signed min/max value&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;predictor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;predictor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// update for next time through; ss(n+1) into z-1 from block diagram&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;step_index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; step_index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// return updated predictor, which is also saved for next time; X(n) into z-1&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// scale from 12-bit to 16-bit; 16 = 2^4, or 4 extra bits&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;predictor &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Returning to the main code file and picking up from &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-4/&quot;&gt;last time&lt;/a&gt;, here is how we use our new code. We&#39;ve opened a file as a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;, and we&#39;re storing the results of a &lt;code&gt;match&lt;/code&gt; expression in a &lt;code&gt;Vec&amp;lt;f64&amp;gt;&lt;/code&gt; (since the filtering will work better with floats). In the “arm” of the &lt;code&gt;match&lt;/code&gt; expression for the VOX format, we iterate over the imported &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;, and for each byte, we split the byte into two 4-bit “nibbles,” iterating over &lt;code&gt;[chunk &amp;gt;&amp;gt; 4, chunk &amp;amp; 0b1111].iter()&lt;/code&gt; and running &lt;code&gt;vox_state.vox_decode()&lt;/code&gt; for each nibble.&lt;/p&gt;
&lt;p&gt;In the diagram below from the spec, note that the highest 4 bits in a byte come first, so our first nibble is &lt;code&gt;chunk &amp;gt;&amp;gt; 4&lt;/code&gt;, which brings those bits down into the lowest 4 positions. &lt;code&gt;chunk &amp;amp; 0b1111&lt;/code&gt; keeps only the 4 lowest bits, giving us the second nibble in the byte.&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-5/vox-adpcm-nibbles.webp&quot; alt=&quot;A diagram of a byte, showing “sample N” as the highest 4 bits and ”sample N+1” as the lower 4 bits&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;VOX byte layout from the Dialogic Corporation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We push the decoded values into our output &lt;code&gt;Vec&amp;lt;f64&amp;gt;&lt;/code&gt;, ready for the next stage, which is filtering and writing to .WAV (see &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-4&quot;&gt;previous post&lt;/a&gt; for a discussion of that code).&lt;/p&gt;
&lt;div class=&quot;code-file&quot;&gt;main.rs&lt;/div&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// import file as Vec&amp;lt;u8&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error reading file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// need to filter as f64 anyway, so best to do in match arms here for consistency&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; converted_data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;format &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;SampleFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vox&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; output&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; vox_state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;vox&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;VoxState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        data
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// using for_each and...&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;for_each&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;chunk&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// start with highest 4 bits (by right-shifting)&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// then &amp;amp; 0x1111 then selects lowest 4&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; nibble &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;chunk &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chunk &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0b1111&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    output&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vox_state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;vox_decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nibble&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...returning outside of pipeline since we need to handle *two* nibbles per element in iter()&lt;/span&gt;
        output
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we discuss the challenges, just for funsies I put the compiled binary for my databending tool back through the tool (using our new VOX codec). Here&#39;s a segment of the result:&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-5/data2audio.mp3&quot; title=&quot;data2audio binary file databent through VOX ADPCM codec&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;h2 id=&quot;challenges&quot;&gt;Challenges&lt;/h2&gt;
&lt;p&gt;At this point, our code works! There were a few things in the VOX spec that tripped me up though, so let&#39;s talk about how I got my code working. First, when my attempt at implementing the spec gave me trouble, I looked at the source for FFmpeg, which Audacity uses—specifically the function &lt;code&gt;adpcm_ima_oki_expand_nibble()&lt;/code&gt; in &lt;code&gt;libavcodec/adpcm.c&lt;/code&gt;, line 553. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt; This is where I got the line &lt;code&gt;let mut delta = ((2 * (magnitude as i16) + 1) * step_size) &amp;gt;&amp;gt; 3;&lt;/code&gt; from &lt;code&gt;vox.rs&lt;/code&gt; above.&lt;/p&gt;
&lt;p&gt;Let&#39;s consider the line of pseudocode &lt;code&gt;d(n) = (ss(n)*B2)+(ss(n)/2*B1)+(ss(n)/4*BO)+(ss(n)/8)&lt;/code&gt;—this is how we combine the incoming magnitude and the step size to get the difference between the current and previous samples. B2, B1, and B0 are the three magnitude bits from the incoming nibble. If, for example, B1 is zero, &lt;code&gt;ss(n)/2*B1&lt;/code&gt; will divide by zero. Not only will we need to check whether each bit is zero or not, but division is more costly than the other arithmetic operations. However, we can think about this another way.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;(ss(n)*B2)+(ss(n)/2*B1)+(ss(n)/4*BO)+(ss(n)/8)&lt;/code&gt;, if we leave out the multiplication by &lt;code&gt;ss(n)&lt;/code&gt; for the time being, we have 1 or 0 times 1; 1 or 0 times 1/2; 1 or 0 times 1/4; and 1 times 1/8. That&#39;s just the ones place and first 3 binary floating point places. If we shift those values 3 places left, we have no more fractions/division, and if we shift the incoming 3 magnitude bits 1 place left (i.e., multiply by 2) and add one, our magnitude and the previous values we shifted line up the same way as before. We can multiply what we have now by the step size, and &lt;code&gt;&amp;gt;&amp;gt; 3&lt;/code&gt; “undoes” the left shift we did to get rid of the fraction. Thus &lt;code&gt;((2 * (magnitude as i16) + 1) * step_size) &amp;gt;&amp;gt; 3&lt;/code&gt; is equivalent to &lt;code&gt;(ss(n)*B2)+(ss(n)/2*B1)+(ss(n)/4*BO)+(ss(n)/8)&lt;/code&gt;, but we don&#39;t need to work around dividing by zero, and things are a bit faster to boot.&lt;/p&gt;
&lt;h2 id=&quot;looking-forward&quot;&gt;Looking Forward&lt;/h2&gt;
&lt;p&gt;Lately I&#39;ve been enjoying windytan (Oona Räisänen)&#39;s &lt;a href=&quot;https://www.windytan.com/2013/11/broadcast-messages-on-darc-side.html&quot;&gt;blog&lt;/a&gt;—a “blog about sound &amp;amp; signals” where she discusses a variety of telecommunications encoding formats, both in terms of their sound and decoding them. I got an &lt;a href=&quot;https://www.rtl-sdr.com/about-rtl-sdr/&quot;&gt;RTL-SDR&lt;/a&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/Software-defined_radio&quot;&gt;software-defined radio&lt;/a&gt; dongle back in 2020, and greatly enjoyed tracking down and decoding interesting signals. Now that I have more programming skills, I think I&#39;ll do more discussion of and coding with different telecommunications formats—both for radio, and for telephony, as I did today.&lt;/p&gt;
&lt;p&gt;One thing that &lt;a href=&quot;https://toot.cat/@EveHasWords/114377893125307935&quot;&gt;@EveHasWords&lt;/a&gt; mentioned recently and that I also saw &lt;a href=&quot;https://www.windytan.com/2012/08/vintage-bits-on-cassettes.html&quot;&gt;on windytan&#39;s blog&lt;/a&gt; is using cassette tapes to store digital data such as software or games. The general idea is that you modulate a tone to encode digital data, and then record that as audio on a regular cassette tape—&lt;a href=&quot;https://zeninstruments.blogspot.com/2021/10/manchester-decoder-and-cassette.html&quot;&gt;this person&lt;/a&gt; did it with an Arduino and Python, so that could be a good starting point for a fun project.&lt;/p&gt;
&lt;!-- At one point I even [figured out](https://hachyderm.io/@reillypascal/112747124169952464) how to run Rust on a BBC micro:bit (using [this book](https://docs.rust-embedded.org/discovery/microbit/)), so that could be another fun thing to use in the project. --&gt;
&lt;p&gt;You can follow the &lt;a href=&quot;https://reillyspitzfaden.com/feeds&quot;&gt;RSS feeds&lt;/a&gt; for this blog to see any future updates on such projects—hope to see you then!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;L. Tan and J. Jiang, &lt;em&gt;Digital Signal Processing: Fundamentals and Applications&lt;/em&gt;. Academic Press, 2018, pp. 486–496. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Dialogic Corporation, &lt;em&gt;Dialogic ADPCM Algorithm&lt;/em&gt;, 1988. [Online]. Available: &lt;a href=&quot;https://people.cs.ksu.edu/~tim/vox/dialogic_adpcm.pdf&quot;&gt;https://people.cs.ksu.edu/~tim/vox/dialogic_adpcm.pdf&lt;/a&gt;. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Here&#39;s a &lt;a href=&quot;https://forum.audacityteam.org/t/dialogic-vox-format/40080/2&quot;&gt;link&lt;/a&gt; to the Audacity forum explaining where to find the settings to do this. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;This notation comes from the idea of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Z-transform&quot;&gt;Z-transform&lt;/a&gt;. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;FFmpeg, &lt;em&gt;libavcodec/adpcm.c&lt;/em&gt;. [Online]. Available: &lt;a href=&quot;https://ffmpeg.org/doxygen/7.0/adpcm_8c_source.html#l00553&quot;&gt;https://ffmpeg.org/doxygen/7.0/adpcm_8c_source.html#l00553&lt;/a&gt;. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-5/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;Databending%20Part%205%E2%80%94Listening%20to%20Telephone%20Codecs&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>Databending Part 4—Data to Audio with a Rust Tool</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/05/databending-part-4/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/05/databending-part-4/</guid>
            <pubDate>Thu, 01 May 2025 21:24:00 GMT</pubDate>
            <description>Manually importing data as audio in Audacity sounds super cool but takes a while and slows down my composition. Today I&#39;m automating it in Rust!</description>
            <content:encoded>
            
            

          
          
          &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/prism-perf-custom.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://reillyspitzfaden.com/styles/code/code-tweaks.css&quot; /&gt;
&lt;p&gt;Earlier this year I &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/01/databending-part-1/&quot;&gt;wrote&lt;/a&gt; about how to import any file into Audacity and convert it to audio. Today I want to make the process less tedious, as well as get some practice with the &lt;a href=&quot;https://en.wikipedia.org/wiki/Rust_(programming_language)&quot;&gt;Rust&lt;/a&gt; programming language.&lt;/p&gt;
&lt;p&gt;When I first wrote about this process, I mentioned that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;once you listen to the “sonified” data from enough files, commonalities start to become apparent…[and] the process of finding these sounds is also fairly slow and painstaking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In addition to simply speeding up the search for interesting files, automation also makes it more practical to “audition” different &lt;em&gt;ways&lt;/em&gt; of importing a given interesting file. I usually convert the data into an audio file in which each “sample” is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Audio_bit_depth&quot;&gt;16-bit integer&lt;/a&gt;—in many cases, I find I like the sound result of this best. However, treating it as an 8-bit, 24-bit or 32-bit integer (or some of the more unique sample formats available in Audacity, such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Differential_pulse-code_modulation&quot;&gt;ADPCM&lt;/a&gt;) can give additional variety and get around the sonic commonalities I mentioned.&lt;/p&gt;
&lt;aside id=&quot;adpcm&quot; class=&quot;post-body-aside&quot;&gt;
&lt;p&gt;The ADPCM input format is still in the works, but just because I like to include audio at the start of things, I want to take a brief sidebar about why ADPCM could be cool. Audacity has the VOX or &lt;a href=&quot;https://en.wikipedia.org/wiki/Dialogic_ADPCM&quot;&gt;Dialogic ADPCM&lt;/a&gt; flavor as one of its import formats, and I&#39;ve had some interesting results importing data using it and similar formats.&lt;/p&gt;
&lt;p&gt;If my math is correct, the audio examples here should be from the same portion of the source data (the &lt;code&gt;libicudata.73.1&lt;/code&gt; library file from the macOS release of the &lt;a href=&quot;https://calibre-ebook.com/&quot;&gt;calibre&lt;/a&gt; e-book manager). The first one is imported in Audacity as signed 16-bit integer at 44.1 kHz sampling rate:&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-4/libicudata.73.1%20i16.mp3&quot; title=&quot;libicudata.73.1 imported to Audacity as 16-bit audio&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;The second is imported into Audacity as VOX ADPCM (also at 44.1 kHz):&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://reillyspitzfaden.com/media/blog/2025/05/databending-part-4/libicudata.73.1%20VOX%20ADPCM.mp3&quot; title=&quot;libicudata.73.1 imported to Audacity as VOX ADPCM audio&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;The VOX ADPCM file is 4x as long (since it only uses 4 bits per sample, instead of 16), and the result is &lt;em&gt;sort of&lt;/em&gt; like a time-stretched version of the 16-bit one, but the voice-focused algorithm of the VOX ADPCM format introduces new strange characteristics as well.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;Anyway, my Rust tool is still a work in progress, but I thought I&#39;d do a writeup of what I have so far. If you want to use it or just follow along, the code is available &lt;a href=&quot;https://github.com/reillypascal/data2audio&quot;&gt;here on GitHub&lt;/a&gt;—if you&#39;re comfortable using Rust&#39;s &lt;code&gt;cargo&lt;/code&gt; package manager, it should be usable, and I&#39;ll look into providing compiled releases at some point! You can hear the result of this tool here:&lt;/p&gt;
&lt;p&gt;&lt;audio controls=&quot;&quot; src=&quot;https://media.hachyderm.io/media_attachments/files/114/410/358/926/835/338/original/5a33b44d29ec6fba.mp3&quot; title=&quot;databent audio using my Rust tool&quot;&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;h2 id=&quot;importing-the-files&quot;&gt;Importing the Files&lt;/h2&gt;
&lt;p&gt;As a refresher on the databending process, I wrote in the first post that in digital audio files&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;we take repeated readings or “samples” of this rising and falling [air] pressure at very rapid intervals in time (the “sample rate”), and represent the height of the rising and falling line at each reading using an integer value.&lt;/p&gt;
&lt;p&gt;Since any computer file is just a list of [binary] numbers…we can take the list of numbers from any file and treat them as a list of amplitudes in an audio file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;First, we want to import files as a list of bytes, and we want to be able to traverse through a large folder of files, with possible sub-folders. The &lt;a href=&quot;https://crates.io/crates/walkdir&quot;&gt;walkdir&lt;/a&gt; Rust crate is useful for traversing directories, and &lt;a href=&quot;https://rust-lang-nursery.github.io/rust-cookbook/file/dir.html#recursively-find-all-files-with-given-predicate&quot;&gt;these examples&lt;/a&gt; from the Rust Cookbook are a good model. &lt;code&gt;WalkDir::new()&lt;/code&gt; returns a recursive iterator into the directory, and we can check if the metadata is good; check if the metadata says an entry is a file and is bigger than the minimum size we&#39;ve chosen (0 is the default); and if so, use &lt;code&gt;fs::read()&lt;/code&gt; to read in the file. &lt;code&gt;fs::read()&lt;/code&gt; returns a &lt;code&gt;Result&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt;, so we can use &lt;code&gt;.expect()&lt;/code&gt; to get the vector out of the &lt;code&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;walkdir&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WalkDir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// WalkDir &quot;walks&quot; recursively through a directory and all its subfolders&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// args.input is from CLI arguments via clap&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;WalkDir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter_map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;entry&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;for_each&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;entry&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// extract metadata from Result&amp;lt;T,E&gt; for each entry in dir&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;metadata&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// if it&#39;s a file and greater/equal to min size (from CLI args via clap)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; metadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; metadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;min &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// ...read in file as a vector of unsigned 8-bit integers&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error reading file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;converting-files-to-audio&quot;&gt;Converting Files to Audio&lt;/h2&gt;
&lt;p&gt;I&#39;m using the &lt;a href=&quot;https://crates.io/crates/clap&quot;&gt;clap&lt;/a&gt; crate to handle command-line arguments. I won&#39;t get into too much detail here, but just to cover where it shows up, &lt;code&gt;WalkDir::new(&amp;amp;args.input)&lt;/code&gt; above is taking an input path from these arguments, but that could be replaced with another source of &lt;code&gt;&amp;amp;str&lt;/code&gt;/&lt;code&gt;&amp;amp;String&lt;/code&gt; reading e.g., &lt;code&gt;&amp;quot;input&amp;quot;&lt;/code&gt;; &lt;code&gt;args.min&lt;/code&gt; above is an integer giving the minimum filesize in bytes, and defaulting to 0; and below, the user&#39;s choice of sample format is recorded as one of the options in my &lt;code&gt;SampleFormat&lt;/code&gt; enum, which derives from &lt;code&gt;clap&lt;/code&gt;&#39;s &lt;code&gt;ValueEnum&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;clap&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Parser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ValueEnum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(ValueEnum, Clone, Debug)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;SampleFormat&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Uint8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Int24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Int32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Picking up where we left off when importing the file, I can take that &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; holding our file data and, depending on the sample format, convert it to appropriately-sized values. If the contents of the “arms” of a Rust &lt;code&gt;match&lt;/code&gt; statement are an &lt;a href=&quot;https://doc.rust-lang.org/book/ch03-03-how-functions-work.html#statements-and-expressions&quot;&gt;expression&lt;/a&gt;, you can have something like &lt;code&gt;let converted_data: Vec&amp;lt;f64&amp;gt; = match args.format {}&lt;/code&gt;, and the variable &lt;code&gt;converted_data&lt;/code&gt; will hold the appropriate value, based on the arm chosen. In this case, simply not putting a semicolon after (e.g.) &lt;code&gt;data.chunks_exact(2).map().collect()&lt;/code&gt; causes that piece of code to be an expression.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; metadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; metadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...read in file as a vector of unsigned 8-bit integers&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error reading file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ---- CONVERT BASED ON SAMPLE FORMAT ----&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// need to filter as f64 anyway, so best to do in match arms here for consistency&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; converted_data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;format &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;SampleFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Uint8&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      data
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;chunk&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// bit-shift based on using 16-bit wav at output&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// need to do as 16-bit to avoid overflow in shift&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;chunk &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;SampleFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      data
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;chunks_exact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;chunks&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// from_le_bytes() takes array of bytes and converts to a single little-endian integer&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from_le_bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chunks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;try_into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Could not import as 16-bit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;SampleFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int24&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      data
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;chunks_exact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;chunks&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// get values from chunks_exact(3), put in array&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; low_part&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chunks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;try_into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Could not import as 24-bit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// no i24, so we add this 0x00 to fill out hi byte in i32&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; high_part&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// copy to &quot;joined&quot; from low/hi parts as slices&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; joined&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          joined&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;copy_from_slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;high_part&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          joined&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;copy_from_slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;low_part&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

          &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from_le_bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;joined&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;SampleFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int32&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      data
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;chunks_exact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;chunks&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// bit-shift based on using 16-bit wav at output&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from_le_bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chunks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;try_into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Could not import as 32-bit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the 16-, 24-, and 32-bit versions, I need the &lt;code&gt;core::slice::chunks_exact()&lt;/code&gt; function, which returns an iterator over a slice of the specified length. The function &lt;code&gt;from_le_bytes()&lt;/code&gt; takes in an array of bytes and converts them into a single little-endian number. The type (e.g., in &lt;code&gt;i32::from_le_bytes()&lt;/code&gt;) specifies which version of the function to use. For the 24-bit version, there is no 24-bit integer type, so I use a 32-bit integer and fill the upper byte with zeroes. For the 8-bit version, I simply get an iterator over the &lt;code&gt;data&lt;/code&gt; &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;. All of the &lt;code&gt;match&lt;/code&gt; “arms” use &lt;code&gt;.map()&lt;/code&gt; to process each value in the &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;, and &lt;code&gt;.collect()&lt;/code&gt; collects those processed values into a new &lt;code&gt;Vec&amp;lt;f64&amp;gt;&lt;/code&gt; for processing by the audio filter.&lt;/p&gt;
&lt;p&gt;Note that all the sample formats except 16-bit integer use the bit-shift operators (&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; or &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;) to scale the values to the range needed to output a 16-bit WAV file. 16 bits is plenty to get good-quality sound, and the input formats are primarily for the different sound results, so I&#39;m fine with converting everything to 16-bit at the end. I temporarily convert everything to floating point numbers for filtering because the filter math is nicer to work with that way.&lt;/p&gt;
&lt;h2 id=&quot;filtering&quot;&gt;Filtering&lt;/h2&gt;
&lt;p&gt;I wrote &lt;a href=&quot;https://github.com/reillypascal/rs_rust_audio&quot;&gt;the filter&lt;/a&gt; I&#39;m using here myself over summer 2024. Filter math gets &lt;em&gt;intense&lt;/em&gt; really fast (and I can only barely muddle through it myself!) so I won&#39;t go into it here, but I&#39;ll put some reading materials/references in the footnotes if you&#39;re interested in reading further. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-4/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-4/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; In short, I have a filter module called &lt;code&gt;biquad&lt;/code&gt;; &lt;code&gt;biquad::AudioFilter::new()&lt;/code&gt; creates a filter; &lt;code&gt;filter.calculate_filter_coeffs()&lt;/code&gt; sets it up; and &lt;code&gt;filter.process_sample()&lt;/code&gt; takes in the audio, one &lt;code&gt;f64&lt;/code&gt; sample at a time, returning another &lt;code&gt;f64&lt;/code&gt; on each pass. All this ends up cast as 16-bit integers in a &lt;code&gt;Vec&amp;lt;i16&amp;gt;&lt;/code&gt; to be written to the WAV file. Note that I multiply each sample by 0.4 before filtering—this is necessary because filtering out sub-audible noise results in higher peaks in the sound, so I need more headroom to compensate.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// make filter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; filter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;biquad&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AudioFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
filter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calculate_filter_coeffs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// vec in which to process sound&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; filtered_vec &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// filter audio&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; sample &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;converted_data &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; filtered_samp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; filter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;process_sample&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;sample &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  filtered_vec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filtered_samp &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;writing-to-wav&quot;&gt;Writing to WAV&lt;/h2&gt;
&lt;p&gt;I create a &lt;a href=&quot;https://doc.rust-lang.org/std/path/struct.PathBuf.html&quot;&gt;PathBuf&lt;/a&gt; from the output path selected by the user, which defaults to &lt;code&gt;output/&lt;/code&gt;. This uses the &lt;code&gt;clap&lt;/code&gt; crate (which again, I&#39;m not covering to save time), but &lt;code&gt;&amp;amp;args.output&lt;/code&gt; could be replaced with another source of &lt;code&gt;&amp;amp;str&lt;/code&gt;/&lt;code&gt;&amp;amp;String&lt;/code&gt; reading e.g., &lt;code&gt;&amp;quot;output&amp;quot;&lt;/code&gt;. &lt;code&gt;create_dir()&lt;/code&gt; uses &lt;code&gt;fs::create_dir_all()&lt;/code&gt;, which the &lt;a href=&quot;https://doc.rust-lang.org/beta/std/fs/fn.create_dir_all.html&quot;&gt;documentation&lt;/a&gt; says is equivalent to multiple &lt;code&gt;mkdir&lt;/code&gt; calls on a Unix-like system. I can then use &lt;code&gt;PathBuf::push()&lt;/code&gt; to add the file name (taken from the entry&#39;s path), and &lt;code&gt;PathBuf::set_extension()&lt;/code&gt; to change the previous file extension to &lt;code&gt;.wav&lt;/code&gt; before using my &lt;code&gt;write_file_as_wav()&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// args.output is from CLI arguments via clap&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; write_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PathBuf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;output&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// create output dir if doesn&#39;t exist - create_dir returns Result&amp;lt;T,E&gt;, so match it and print if error&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; out_dir &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;create_dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;output&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; out_dir &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;eprintln!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// entry.path().file_name() returns an Option, so if let Some() handles/extracts value&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  write_path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  write_path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_extension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;wav&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;write_file_as_wav&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filtered_vec&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; write_path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;create_dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;io&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// create_dir_all - like multiple mkdir calls&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create_dir_all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I use the &lt;a href=&quot;https://crates.io/crates/hound&quot;&gt;&lt;code&gt;hound&lt;/code&gt;&lt;/a&gt; crate to handle writing WAV files (see &lt;a href=&quot;https://docs.rs/hound/latest/hound/struct.WavWriter.html&quot;&gt;documentation&lt;/a&gt; for &lt;code&gt;hound&lt;/code&gt;&#39;s &lt;code&gt;WavWriter&lt;/code&gt; struct). In addition to the filename, &lt;code&gt;hound::WavWriter::create()&lt;/code&gt; requires a “spec” giving the number of channels, sample rate, sample &lt;a href=&quot;https://en.wikipedia.org/wiki/Audio_bit_depth&quot;&gt;bit depth&lt;/a&gt;, and the sample format (from the &lt;code&gt;hound::SampleFormat&lt;/code&gt; enum). Once I have an appropriate &lt;code&gt;WavWriter&lt;/code&gt; made, I write the file one sample at a time with the &lt;code&gt;hound::WavWriter::write_sample()&lt;/code&gt; method, after which I need to call &lt;code&gt;hound::WavWriter::finalize()&lt;/code&gt; to update the WAVE header with the final file size.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; hound&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;write_file_as_wav&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;path&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PathBuf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// write WAV file&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// spec&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; spec &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;hound&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WavSpec&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    channels&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    sample_rate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;44100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    bits_per_sample&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    sample_format&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;hound&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SampleFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// writer&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; writer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;hound&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WavWriter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spec&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Could not create writer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; t &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    writer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write_sample&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Could not write sample&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  writer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finalize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Could not finalize WAV file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;summary-and-future-goals&quot;&gt;Summary and Future Goals&lt;/h2&gt;
&lt;p&gt;To review:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We use the &lt;a href=&quot;https://crates.io/crates/walkdir&quot;&gt;walkdir&lt;/a&gt; crate and &lt;code&gt;fs::read()&lt;/code&gt; to recursively traverse the files in the folder and open each as a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://crates.io/crates/clap&quot;&gt;clap&lt;/a&gt; crate handles CLI arguments, including selecting the input sample format.&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;core::slice::chunks_exact()&lt;/code&gt; and (e.g.) &lt;code&gt;i16::from_le_bytes()&lt;/code&gt; to convert groupings of bytes into 16-, 24-, or 32-bit samples.&lt;/li&gt;
&lt;li&gt;We use a biquad &lt;a href=&quot;https://github.com/reillypascal/rs_rust_audio&quot;&gt;filter&lt;/a&gt; I wrote to cut out sub-audible frequencies, casting to/from 64-bit floats for the filtering.&lt;/li&gt;
&lt;li&gt;Finally, we use a &lt;code&gt;WavWriter&lt;/code&gt; struct from the &lt;a href=&quot;https://crates.io/crates/hound&quot;&gt;hound&lt;/a&gt; crate to write each WAV file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I mentioned the &lt;a href=&quot;https://en.wikipedia.org/wiki/Differential_pulse-code_modulation&quot;&gt;ADPCM&lt;/a&gt; sample format at the start, and one of my next goals is to include that option when importing files. The &lt;a href=&quot;https://lib.rs/crates/symphonia#readme-codecs-decoders&quot;&gt;symphonia&lt;/a&gt; Rust crate has an ADPCM decoder (sadly not the VOX version—symphonia &lt;a href=&quot;https://lib.rs/crates/symphonia-codec-adpcm#readme-support&quot;&gt;has Microsoft and IMA flavors&lt;/a&gt;). I&#39;ll need to do some poking around to figure out how to use it, but I definitely plan to do so in the near future.&lt;/p&gt;
&lt;p&gt;I hope to see you again soon!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot; /&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Steven W. Smith, “The Scientist and Engineer’s Guide to Digital Signal Processing,” accessed April 30, 2025, &lt;a href=&quot;https://www.dspguide.com/&quot;&gt;https://www.dspguide.com/&lt;/a&gt;. This one is a bit older (and includes some BASIC and FORTRAN examples), but it&#39;s freely available online, the math parts are very helpful, and I overall greatly appreciate that it targets those with less math experience, while still being thorough. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-4/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Will Pirkle, Designing Audio Effect Plugins in C++: For AAX, AU, and VST3 with DSP Theory (Routledge, 2019), &lt;a href=&quot;https://www.taylorfrancis.com/books/mono/10.4324/9780429490248/designing-audio-effect-plugins-pirkle&quot;&gt;https://www.taylorfrancis.com/books/mono/10.4324/9780429490248/designing-audio-effect-plugins-pirkle&lt;/a&gt;. Chapters 10–12 in this one have a good discussion of filter math, and I (very loosely) based my filter on some of the examples here. &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/05/databending-part-4/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;Databending%20Part%204%E2%80%94Data%20to%20Audio%20with%20a%20Rust%20Tool&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>IndieWeb Blog Carnival—“Renewal”</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/04/indieweb-blog-carnival-renewal/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/04/indieweb-blog-carnival-renewal/</guid>
            <pubDate>Fri, 18 Apr 2025 04:00:00 GMT</pubDate>
            <description>I often feel compelled to tweak and redesign my website at the expense of other things I value. I talk about reconnecting with what I most enjoy about composing and coding, and avoiding treating my leisure and projects as if I need to impress someone.</description>
            <content:encoded>
            
            

          
          
          &lt;p&gt;This month&#39;s IndieWeb blog carnival topic is “renewal”—here is Jamie Thingelstad&#39;s &lt;a href=&quot;https://www.thingelstad.com/2025/03/27/renewal-indieweb-carnival.html&quot;&gt;introductory post&lt;/a&gt; on the topic.&lt;/p&gt;
&lt;p&gt;Initially I wasn&#39;t sure if this topic would apply to anything on which I currently had the inspiration to write, but I read &lt;a href=&quot;https://hamatti.org/posts/resisting-the-urge-to-rewrite-the-website/&quot;&gt;Juha-Matti Santala&#39;s post&lt;/a&gt; for the carnival and something clicked for me. He writes about&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the often observed dilemma that (especially) we techies often run into: how to resist the urge to rewrite the website every time you start writing a new blog post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have a similar desire to tinker on and “renew” my site, and recently that has interfered not only with sitting down to write for the site, but with my project and leisure time more generally. In addition to my website tinkering, I&#39;m going to discuss my relationship more broadly to leisure and to projects that interest me.&lt;/p&gt;
&lt;h2 id=&quot;writing-about-projects&quot;&gt;Writing about Projects&lt;/h2&gt;
&lt;p&gt;I have received positive feedback for some of the writing I&#39;ve done here, and I find myself chasing the high of having someone else appreciate something I&#39;ve made. The kind of writing that I most want to do requires an additional step beforehand. I want to do write-ups of interesting code or music projects and that requires me to actually do a code or music project. Because the writing I&#39;m most proud of requires these extra steps, when I contemplate writing for my blog I&#39;m really thinking of two separate things—the project itself and then the writeup.&lt;/p&gt;
&lt;p&gt;Since this site is one of the more public-facing expressions of my skills and personality, small changes to it can feel like easy ways to seek validation of those skills. Rather than having to do a whole project and then still know I need to complete a writeup, if I add an interesting feature to the site, I can share about that on Mastodon or Bluesky and the feature is immediately legible to a viewer without needing much of a writeup to communicate it.&lt;/p&gt;
&lt;!-- This is one major motivator of tinkering on the site instead of working on less-visible music and code projects that would require a writeup to communicate about them, and to receive (hopefully positive) feedback. --&gt;
&lt;p&gt;A second motivator to endlessly tinker is that my compulsion to stare at and revisit my own site tends to reveal small “flaws” in it, and leaving things the way they are when they aren&#39;t “perfect” feels like a little itch in the back of my head. Even if I feel intimidated by the prospect of getting a personal project to the point it can be written up on my blog (and then actually doing the writeup), poring over my existing website and writing can let me revisit nice things that people said about specific aspects of it, but since looking at it naturally reveals any small flaws, I end up with a laundry list of things that could be tweaked, and that I might prefer were a little better.&lt;/p&gt;
&lt;h2 id=&quot;the-character-of-my-projects&quot;&gt;The Character of My Projects&lt;/h2&gt;
&lt;p&gt;In addition to the ease of receiving feedback on my site and the list of small flaws I accumulate, most of the other projects I want to do (audio software development, composition, sound design in Max/MSP, etc.) are more complex and/or subjective than site tinkering. My website already exists, and HTML/CSS are a little more forgiving than C++, so (for example) starting a brand new audio plugin or tweaking an existing one both feel more intimidating.&lt;/p&gt;
&lt;p&gt;Composing music (and since designing Max/MSP patches is part of composing for me, that too) also feels much more subjective than frontend development. If a new site feature does what it&#39;s supposed to and fits my existing visual design, I&#39;m happy with it. With composing, what constitutes “objectively good” music? It&#39;s at least my personal value that there is no truly objective measure of “good” and “bad,” “better” and “worse” art—see for example, &lt;a href=&quot;https://musictheoryswhiteracialframe.wordpress.com/2020/04/24/beethoven-was-an-above-average-composer-lets-leave-it-at-that/&quot;&gt;Philip Ewell&#39;s provocatively-titled article&lt;/a&gt; on how our exaltation of Beethoven is much more subjective and based on cultural biases than we might like to admit.&lt;/p&gt;
&lt;!-- Recognizing the subjective nature of our assessment of art is in some ways freeing—I can do whatever I want! However, I have a strong desire for what I write to be “objectively good” so I know I did a “good job” (whatever that means). In addition to a belief that our assessment of art is deeply subjective, I also enjoy writing “experimental” music that uses strange sounds and (at least attempts) to do something “new.” As with the subjectivity of art, this is freeing in a way, but given that I have a deep desire to be “good enough,” it also opens the door wide for anxiety about the quality of my work.  --&gt;
&lt;p&gt;Overall, it&#39;s much harder to get myself to start these more subjective and intimidating projects, and much easier to perseverate on tweaks to my site.&lt;/p&gt;
&lt;h2 id=&quot;instrumentalizing-leisure-and-creation&quot;&gt;Instrumentalizing Leisure and Creation&lt;/h2&gt;
&lt;p&gt;I found an essay by Anne Helen Petersen via &lt;a href=&quot;https://tracydurnell.com/2025/04/13/on-hobbies-and-the-difficulty-of-embracing-slowness/&quot;&gt;Tracy Durnell&#39;s blog&lt;/a&gt; that was helpful for me in thinking about how I approach all of my projects, both in coding and composing. Petersen &lt;a href=&quot;https://annehelen.substack.com/p/what-is-millennial-hobby-energy&quot;&gt;writes&lt;/a&gt; about the way she notices herself and other millennials approaching hobbies and leisure. This approach is&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;…big and ambitious. It’s swallowing. It’s barely keeping the impulse to optimize and monetize at bay. It’s not unique to millennials, but it is endemic among us.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;She notices she and her peers grew up feeling that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;if you’re doing something that’s not directly related to grades, then it should be extremely legible as a line on your college resume.&lt;/p&gt;
&lt;p&gt;If you did what we called an extra-curricular, it was less because it was fun, or because you &lt;em&gt;wanted to&lt;/em&gt;, but because “it looked good,” or communicated something “interesting” or “well-rounded” about your personality. […]&lt;/p&gt;
&lt;p&gt;It makes sense that whatever hobby we find we like doing…we struggle not to turn it into work. I don’t even mean getting paid for doing it, although we’ll get to that later. We make it hard because when something’s hard, and [we] complete it anyway, it feels like we’re “being productive.” And the more productive we are, the better we feel.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This essay resonated deeply with me and my ongoing search to find recreational activities that &lt;em&gt;don&#39;t&lt;/em&gt; feel like work, and with which I don&#39;t feel like I need to prove myself and do the hobby “well enough.”&lt;/p&gt;
&lt;p&gt;I first picked up coding in around 2021–2022, first in sparse explorations here and there, and then in mid 2022 (i.e., right after I finished my composition PhD) much more seriously. I had long been interested in pursuing it, but there was an additional thought—that because the market for college music teaching jobs is rough to say the least, having skills in tech would give me more opportunities for employment and stability. Even when I&#39;m explicitly trying to code for my own enjoyment only, it&#39;s hard to break away from the feeling that I need to “optimize” the topics I pursue for capitalist market logic.&lt;/p&gt;
&lt;!-- Early on in my coding journey I followed a bunch of Instagram accounts relating to learning to code (I&#39;ve since almost completely stopped using Instagram). I remember seeing one post in particular essentially communicating that (for self-taught devs), if you&#39;re too scattered with your coding studies you won&#39;t get hired. I was already primed to believe that my activities needed to be as “productive” and career-focused as possible, and somehow that one message wormed its way into my head and has continued to inform what I focus on in my coding, even when I&#39;m explicitly trying to code for my own enjoyment only. --&gt;
&lt;p&gt;Composing was not even an “extra-curricular,” it was just a “curricular” for me. Since all my degrees are in music composition, my skill at composition was central to my academic success, and while I greatly appreciate my teachers&#39; pushing me to think more deeply about how to communicate my ideas clearly in music, it&#39;s a pretty big transition to go from receiving weekly feedback in private lessons to being completely in charge of evaluating my own music.&lt;/p&gt;
&lt;p&gt;This difficulty extricating my favorite activities from ideas about “productivity” and capitalist utility further contributes to the anxiety around pursuing these activities, again, making small tweaks to my site much less intimidating by comparison.&lt;/p&gt;
&lt;h2 id=&quot;reconnecting-with-what-drew-me-here-in-the-first-place&quot;&gt;Reconnecting with What Drew Me Here in the First Place&lt;/h2&gt;
&lt;p&gt;What do I want to do with this information about how I approach the things I enjoy? First, I want to allow myself “mess around“ more. If something doesn&#39;t have immediate capitalist utility, that&#39;s OK—it&#39;s desirable, even! It sounds much more healthy to let myself do things simply because I feel like it. I want to share what I do in smaller, easier-to-create chunks, without worrying whether each chunk is “impressive” enough.&lt;/p&gt;
&lt;p&gt;I also want to allow myself to write about exactly what interests me, regardless of how I think it will be received. For example, I&#39;ve &lt;a href=&quot;https://reillyspitzfaden.com/posts/2024/11/connecting-notation-programs-to-maxmsp/&quot;&gt;written&lt;/a&gt; a &lt;a href=&quot;https://reillyspitzfaden.com/posts/2024/05/composition-journal/&quot;&gt;few posts&lt;/a&gt; about &lt;a href=&quot;https://reillyspitzfaden.com/posts/2024/02/composition-journal/&quot;&gt;sound design in Max/MSP&lt;/a&gt; and they seemed not to get as much attention as some of the other posts I&#39;ve done. This wasn&#39;t a surprise — Max/MSP is a little niche, and in contrast, some of my &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/01/databending-part-1/&quot;&gt;posts&lt;/a&gt; on &lt;a href=&quot;https://reillyspitzfaden.com/posts/2025/02/databending-part-2/&quot;&gt;databending&lt;/a&gt; have a lower bar of entry, only requiring basic use of the Audacity sound editor or a hex editor. It may also be that I had fewer people following me at the time that I posted about Max/MSP. Regardless, I don&#39;t want to allow chasing “likes” to dictate what I write, and I think it would be nice to write more about what I do with Max/MSP.&lt;/p&gt;
&lt;p&gt;Overall, I want to take a step back and reconnect with my interests in a way that affirms what I most enjoy about them and lets go of the need to impress or be “productive”—a renewal of sorts for the spring.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;IndieWeb%20Blog%20Carnival%E2%80%94%E2%80%9CRenewal%E2%80%9D&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item>
        <item>
            <title>IndieWeb Movie Club: “The Castle” (1997)</title>
            <author>reillypascal@gmail.com (Reilly Spitzfaden)</author>
            <link>https://reillyspitzfaden.com/posts/2025/04/indieweb-movie-club-the-castle-1997/</link>
            <guid>https://reillyspitzfaden.com/posts/2025/04/indieweb-movie-club-the-castle-1997/</guid>
            <pubDate>Sun, 13 Apr 2025 03:15:00 GMT</pubDate>
            <description>My watch and discussion of this month&#39;s IndieWeb Movie Club pick — it&#39;s nice to participate in a group blogging activity!</description>
            <content:encoded>
            
            

          
          
          &lt;p&gt;This month&#39;s &lt;a href=&quot;https://indieweb.org/IndieWeb_Movie_Club&quot;&gt;IndieWeb Movie Club&lt;/a&gt; is &lt;cite&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/The_Castle_(1997_Australian_film)&quot;&gt;The Castle&lt;/a&gt;&lt;/cite&gt; (1997), and Zachary wrote the introduction post &lt;a href=&quot;https://zacharykai.net/notes/iwmapr25&quot;&gt;here&lt;/a&gt;. Spoilers will follow in this post!&lt;/p&gt;
&lt;p&gt;The movie is an Australian comedy about the Kerrigan family whose house is set to be seized (along with those of their neighbors) in order to expand an airport. After a long string of legal mishaps, they win their case against the government and the airport&#39;s corporate backer, and are able to keep their home.&lt;/p&gt;
&lt;p&gt;The Kerrigan family is portrayed as “unsophisticated” and clueless, and their house not particularly “nice,” with the humor often coming from the disconnect between this and how happy they are with their house and lives.&lt;/p&gt;
&lt;h2 id=&quot;reactions&quot;&gt;Reactions&lt;/h2&gt;
&lt;p&gt;I watched this with my partner who works for the courts, and it was fun to see their reaction to the legal proceedings. For example, the incompetent small-time lawyer Dennis Denuto the Kerrigans and their neighbors bring on for the case frantically shuffles through his notes for uncomfortably long stretches; says he never learned Roman numerals (while trying to read the Australian constitution and devise his argument in real time); and repeatedly argues that the eviction goes against “the vibe” of the constitution. My partner commented that there sometimes really are “vibes-based” arguments made, and that they will definitely be using the phrase “do you have any legal precedent to support ‘the vibe’?”&lt;/p&gt;
&lt;p&gt;I don&#39;t get around to watching a ton of movies, and this was a fun, lighthearted one to help break out of that. I did start to find the humor repetitive and a bit of a bummer for a stretch in the middle, although the ending was uplifting—the family and their neighbors prevail, set a legal precedent, and Lawrence, the retired &lt;a href=&quot;https://en.wikipedia.org/wiki/Queen%27s_Counsel&quot;&gt;Queen&#39;s Counsel&lt;/a&gt; lawyer who ultimately takes and wins their case becomes a family friend.&lt;/p&gt;
&lt;p&gt;As a counter-example to illustrate what I think bummed me out, I like &lt;cite&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Bob%27s_Burgers&quot;&gt;Bob&#39;s Burgers&lt;/a&gt;&lt;/cite&gt; and the similarly-animated &lt;cite&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/The_Great_North&quot;&gt;The Great North&lt;/a&gt;&lt;/cite&gt;. Both shows have quirky families whose oddities are a source of comedy, but in these two shows, I feel like I&#39;m laughing &lt;em&gt;with&lt;/em&gt; rather than &lt;em&gt;at&lt;/em&gt; the families. The funny thing about the families is that they&#39;re odd in a way that&#39;s just &lt;em&gt;different&lt;/em&gt; rather than something I feel like I&#39;m supposed to look down on. They&#39;re also self-aware—the Belchers (&lt;cite&gt;Bob&#39;s Burgers&lt;/cite&gt;) and the Tobins (&lt;cite&gt;The Great North&lt;/cite&gt;) seem well aware they&#39;re odd, and they don&#39;t seem to have a problem with that.&lt;/p&gt;
&lt;p&gt;The narrative of &lt;cite&gt;The Castle&lt;/cite&gt; ultimately wants me to root for the Kerrigans, but it did get to the point that laughing at them started to feel mean-spirited. As their fortunes improved though, that feeling went away again, and overall I enjoyed the film a lot—just an interesting contrast I noticed with other things I like.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;I enjoyed participating in a group blogging activity. I haven&#39;t done a &lt;a href=&quot;https://indieweb.org/blog_carnival&quot;&gt;blog carnival&lt;/a&gt; or other such thing before. I&#39;m planning to do more of these—hope to see you then!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;mailto:reillypascal@gmail.com?subject=Re:%20&#39;IndieWeb%20Movie%20Club:%20%E2%80%9CThe%20Castle%E2%80%9D%20(1997)&#39;&quot;&gt;Reply via email&lt;/a&gt; :: &lt;a href=&quot;https://reillyspitzfaden.com/feeds/&quot;&gt;Subscribe to my other feeds&lt;/a&gt; :: &lt;a href=&quot;https://ko-fi.com/reillyspitzfaden&quot;&gt;Buy me a coffee&lt;/a&gt;&lt;/p&gt;
            </content:encoded>
          </item></channel>
</rss>