<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Beijaflor's blog RSS feed]]></title><description><![CDATA[Beijaflor's blog RSS feed]]></description><link>https://beijaflor.io</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 31 Jan 2024 09:48:44 GMT</lastBuildDate><item><title><![CDATA[cmakefmt]]></title><description><![CDATA[I have written an initial prototype of , a CMake files auto-formatter. The source code is available on GitHub at (https://github.com…]]></description><link>https://beijaflor.io/cmakefmt-01/</link><guid isPermaLink="false">https://beijaflor.io/cmakefmt-01/</guid><pubDate>Thu, 18 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have written an initial prototype of &lt;code class=&quot;language-text&quot;&gt;cmakefmt&lt;/code&gt;, a CMake files auto-formatter.&lt;/p&gt;
&lt;p&gt;The source code is available on GitHub at (&lt;a href=&quot;https://github.com/yamadapc/cmakefmt&quot;&gt;https://github.com/yamadapc/cmakefmt&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&quot;installing-from-source&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#installing-from-source&quot; aria-label=&quot;installing from source permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Installing from source&lt;/h2&gt;
&lt;p&gt;Using &lt;code class=&quot;language-text&quot;&gt;cargo&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cargo install cmakefmt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;examples&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#examples&quot; aria-label=&quot;examples permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Examples&lt;/h2&gt;
&lt;p&gt;These are interactive examples using a &lt;a href=&quot;https://github.com/yamadapc/cmakefmt_web&quot;&gt;WASM version of the tool&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;long-commands&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#long-commands&quot; aria-label=&quot;long commands permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Long commands&lt;/h3&gt;
&lt;p&gt;Long lines are broken-up into separate lines.&lt;/p&gt;
&lt;cmakefmt-editor height=&quot;150&quot;&gt;
set_property(
    TARGET test PROPERTY INTERFACE_LINK_LIBRARIES
    &quot;${DIR}/lib64/libcublas_static.a&quot;)
&lt;/cmakefmt-editor&gt;
&lt;h3 id=&quot;key-value-pair-behaviour&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#key-value-pair-behaviour&quot; aria-label=&quot;key value pair behaviour permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Key-value pair behaviour&lt;/h3&gt;
&lt;p&gt;Arguments with all upper-case letters are grouped together with the next
non-uppercase argument. So if there is space, upper-case arguments will be
printed as key-value pairs as follows.&lt;/p&gt;
&lt;cmakefmt-editor height=&quot;150&quot;&gt;
juce_add_gui_app( StoryBook PRODUCT_NAME &quot;BF StoryBook&quot; COMPANY_NAME &quot;Pedro Tacla Yamada&quot; BUNDLE_ID &quot;com.beijaflor.juce_storybook&quot;)
&lt;/cmakefmt-editor&gt;
&lt;h3 id=&quot;indentation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#indentation&quot; aria-label=&quot;indentation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Indentation&lt;/h3&gt;
&lt;p&gt;Nested blocks are indented by 2 spaces.&lt;/p&gt;
&lt;cmakefmt-editor height=&quot;150&quot;&gt;
function(foo)
    bar(  x
y z
    )
endfunction()
&lt;/cmakefmt-editor&gt;
&lt;h3 id=&quot;conditions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conditions&quot; aria-label=&quot;conditions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conditions&lt;/h3&gt;
&lt;p&gt;Condition nodes are grouped together.&lt;/p&gt;
&lt;cmakefmt-editor height=&quot;150&quot;&gt;
if(true AND (true AND false OR NOT true) OR true OR (true AND NOT (false OR true)) OR (true AND false OR NOT true) OR true OR (true AND NOT (false OR true)))
message(STATUS ok)
endif()
&lt;/cmakefmt-editor&gt;
&lt;h2 id=&quot;implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementation&quot; aria-label=&quot;implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementation&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;cmakefmt&lt;/code&gt; is written in Rust using &lt;code class=&quot;language-text&quot;&gt;nom&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;pretty&lt;/code&gt; combinator libraries.
It &lt;a href=&quot;https://github.com/yamadapc/cmakefmt/tree/master/src/parser&quot;&gt;parses the CMake document onto an AST using nom parser combinators&lt;/a&gt;
and then serialises it into a string using &lt;a href=&quot;https://github.com/yamadapc/cmakefmt/tree/master/src/pretty_printer&quot;&gt;pretty&lt;/a&gt;, which are combinators in the same style of &lt;a href=&quot;https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf&quot;&gt;“A pretty-printer”&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;playground&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#playground&quot; aria-label=&quot;playground permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Playground&lt;/h2&gt;
&lt;p&gt;Please try it out here or check-out the &lt;a href=&quot;https://github.com/yamadapc/cmakefmt&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;cmakefmt-editor height=&quot;800&quot;&gt;
&lt;/cmakefmt-editor&gt;</content:encoded></item><item><title><![CDATA[Drawing knobs]]></title><description><![CDATA[This is an exploration on knobs UIs. It uses JavaScript to render knobs on your
web browser and shows code, however it is intended to be a…]]></description><link>https://beijaflor.io/drawing-knobs/</link><guid isPermaLink="false">https://beijaflor.io/drawing-knobs/</guid><pubDate>Sat, 08 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is an exploration on knobs UIs. It uses JavaScript to render knobs on your
web browser and shows code, however it is intended to be a more general
look at knobs. Therefore, some concerns specific to the web platform may be
ignored.&lt;/p&gt;
&lt;p&gt;Knobs are a common control on music and audio applications, such as virtual
instruments, DAW (digital audio workstations) or audio effects plugins.&lt;/p&gt;
&lt;p&gt;This is done in resemblance to real world musical objects, which have knobs and
also because they are a very compact way of representing many different
parameters’ state in a way that can be glanced at quickly.&lt;/p&gt;
&lt;p&gt;&lt;knobs-gallery height=&quot;100px&quot;&gt;&lt;/knobs-gallery&gt;&lt;/p&gt;
&lt;h2 id=&quot;drawing-knobs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#drawing-knobs&quot; aria-label=&quot;drawing knobs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Drawing knobs&lt;/h2&gt;
&lt;p&gt;Let there be a canvas:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; canvas &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;canvas&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;
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;canvas&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; size &lt;span class=&quot;token operator&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 keyword&quot;&gt;const&lt;/span&gt; canvasSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; size &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; scaleFactor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; center &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvasSize &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 punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; radius &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;canvasSize &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&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;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvasSize&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvasSize&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style &lt;span class=&quot;token operator&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;width: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;size&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;px; height: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;size&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;px&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 keyword&quot;&gt;const&lt;/span&gt; ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2d&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These simple knob controls above are just arcs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderKnobArc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value &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 punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; revolution &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; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PI&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; totalSweepAngle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.75&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; revolution&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sweepAngle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; totalSweepAngle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; startAngle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.375&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; revolution&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; endAngle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; startAngle &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; sweepAngle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;beginPath&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;
  ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strokeStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lineWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; scaleFactor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;center&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; radius&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; startAngle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; endAngle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stroke&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;/div&gt;
&lt;h2 id=&quot;reacting-to-input&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reacting-to-input&quot; aria-label=&quot;reacting to input permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reacting to input&lt;/h2&gt;
&lt;p&gt;We must react to mouse input. The most natural would be to react to the angle
of the mouse relative to the knob:&lt;/p&gt;
&lt;p&gt;&lt;knob-angle-calc-demo height=&quot;300px&quot; step=&quot;1&quot;&gt;&lt;/knob-angle-calc-demo&gt;&lt;/p&gt;
&lt;p&gt;For this, we’ll use &lt;a href=&quot;https://en.wikipedia.org/wiki/Atan2&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;atan2&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; revolution &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PI&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.0&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; startAngle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.375&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; revolution&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sweepAngle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.75&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; revolution&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; angle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
  Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;atan2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    mouse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    mouse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Offset the angle so 0 is at startAngle&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; startAngle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This fixes the angle so it goes from 0 to 2.pi radius&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;angle &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&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 punctuation&quot;&gt;{&lt;/span&gt;
  angle &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; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PI&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; angle&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;// This truncates values under the knob so they either snap&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// to start or end. In reality we should cancel the gesture&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// so that there&apos;re no jumps.&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;angle &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; sweepAngle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  angle &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;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Above, we also rotate our reference points and clamp the results.&lt;/p&gt;
&lt;p&gt;By “clamping”, I mean what to do when the drag/mouse is outside of the expected
track.
In the code above, the angle resets to zero.
However that’s akward in practice and it’s best to ignore drags that are outside
of the track.&lt;/p&gt;
&lt;p&gt;You can see how clamping to 0 reacts to input here:&lt;/p&gt;
&lt;p&gt;&lt;knob-angle-calc-demo height=&quot;300px&quot; step=&quot;2&quot;&gt;&lt;/knob-angle-calc-demo&gt;&lt;/p&gt;
&lt;p&gt;If we fix that and also add drag tracking and calculate a value that corresponds
to the rotation:&lt;/p&gt;
&lt;p&gt;&lt;knob-angle-and-mouse height=&quot;300px&quot; step=&quot;3&quot;&gt;&lt;/knob-angle-calc-demo&gt;&lt;/p&gt;
&lt;p&gt;We now have an usable knob:&lt;/p&gt;
&lt;p&gt;&lt;working-knob height=&quot;200px&quot;&gt;&lt;/working-knob&gt;&lt;/p&gt;
&lt;h2 id=&quot;knobs-that-are-sliders&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#knobs-that-are-sliders&quot; aria-label=&quot;knobs that are sliders permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Knobs that are sliders&lt;/h2&gt;
&lt;p&gt;One alternative UI for changing numeric parameters, would be to use sliders:&lt;/p&gt;
&lt;p&gt;&lt;sliders-gallery&gt;&lt;/sliders-gallery&gt;&lt;/p&gt;
&lt;p&gt;These have the advantage of being more generally available and intuitive for
mouse users, but will take more space in one axis (horizontal / vertical).&lt;/p&gt;
&lt;p&gt;And interesting interaction pattern is a “slider knob”.
That is a knob which is interacted with as a slider:&lt;/p&gt;
&lt;p&gt;&lt;knob-slider&gt;&lt;/knob-slider&gt;&lt;/p&gt;
&lt;p&gt;In this case, the knob is simply a visual representation of the control.
Clicking it reveals a secondary interaction layer, which contains a slider.
The slider is positioned using the value and mouse position to show the user
what their next gestures will cause.&lt;/p&gt;
&lt;p&gt;&lt;knob-slider-layers&gt;&lt;/knob-slider-layers&gt;&lt;/p&gt;
&lt;p&gt;This has the advantage that the knob may be really small but still usable:&lt;/p&gt;
&lt;p&gt;&lt;knob-slider size=&quot;80&quot; knobscount=&quot;5&quot;&gt;&lt;/knob-slider&gt;&lt;/p&gt;
&lt;p&gt;The choice between horizontal and vertical backing sliders can be done on a case
by case basis, but most of the time the vertical slider would make the most
sense due to being analogue to mixer sliders.&lt;/p&gt;
&lt;p&gt;However, I find this interaction is a bit akward.
There’s a level of indirection between the visual representation and how to
manipulate its value, which I think makes it harder to learn.&lt;/p&gt;
&lt;h2 id=&quot;expanding-knobs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#expanding-knobs&quot; aria-label=&quot;expanding knobs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Expanding knobs&lt;/h2&gt;
&lt;p&gt;I’d like to explore expanding knobs GUI, where knobs would expand with
interactions on another document.&lt;/p&gt;
&lt;h2 id=&quot;that-is-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#that-is-it&quot; aria-label=&quot;that is it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;That is it&lt;/h2&gt;
&lt;p&gt;Pretty short, thanks for reading.&lt;/p&gt;
&lt;p&gt;You should find Swift, Rust (Skia), Flutter and JS implementations for knobs on
&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/&quot;&gt;https://github.com/yamadapc/augmented-audio/&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Wisual Logger]]></title><description><![CDATA[Logging is an important strategy for building observable software systems.
The practices and set-up for application logging often consist of…]]></description><link>https://beijaflor.io/wisual-logger/</link><guid isPermaLink="false">https://beijaflor.io/wisual-logger/</guid><pubDate>Mon, 20 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Logging is an important strategy for building observable software systems.
The practices and set-up for application logging often consist of a program
printing some of its operations onto a file, console or network such that later
on they can be inspected by a human or automated tools.&lt;/p&gt;
&lt;p&gt;This is particularly useful when the software mis-behaves and requires debugging
that would otherwise be hard to conduct on an opaque system.&lt;/p&gt;
&lt;p&gt;Often on back-end systems, logs will end-up, temporarily, on some tool that
can archive, search or visualise the output (Splunk, ELK).&lt;/p&gt;
&lt;p&gt;Logs should be complimentary to metrics or analytics and will also be useful
during the software development process in order to quickly provide some
feedback of internal behaviour.&lt;/p&gt;
&lt;p&gt;When developing back-end web services,
logging is essential in order to allow the
software to be operated by teams at scale.
This is partially due to such services running on closed environments, which can
not be easily probed otherwise.&lt;/p&gt;
&lt;p&gt;I believe this is in contrast with GUI applications and, in particular, browser
based GUIs.
While analytics, metrics and some form of automated application
monitoring are wide-spread amongst front-end application, logging from the
front-end does not seem to be fully exploited.&lt;/p&gt;
&lt;h2 id=&quot;wisuallogger&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#wisuallogger&quot; aria-label=&quot;wisuallogger permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@wisual/logger&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/packages/logger&quot;&gt;@wisual/logger on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I wrote a very simple logger for a side-project which tries to bring hierarchy
into browser logs.&lt;/p&gt;
&lt;p&gt;Usage is as follows:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; LoggerFactory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@wisual/logger&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; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; LoggerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MyLogger&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;

logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello world&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 literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&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;/div&gt;
&lt;p&gt;This will print:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;2022-06-20T10:48:47.470Z [app&gt;MyLogger] (info) Hello world [Object &gt;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Where ‘object’ is a table in Chrome inspector or a JSON string on Node.js.&lt;/p&gt;
&lt;h3 id=&quot;hierarchy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hierarchy&quot; aria-label=&quot;hierarchy permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hierarchy&lt;/h3&gt;
&lt;p&gt;Loggers can have contextual data attached to them:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; usersServiceLogger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;UsersService&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 literal-property property&quot;&gt;dbBackend&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;psql&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;flushTimeout&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&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 keyword&quot;&gt;const&lt;/span&gt; usersCacheLogger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; usersServiceLogger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;UsersCache&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 literal-property property&quot;&gt;cacheBackend&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redis&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The context will be merged with data of each log message.&lt;/p&gt;
&lt;h3 id=&quot;react-integration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#react-integration&quot; aria-label=&quot;react integration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;React integration&lt;/h3&gt;
&lt;p&gt;There are hooks provided for react integration:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react&quot;&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 punctuation&quot;&gt;{&lt;/span&gt; useLogger &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@wisual/logger&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MyComponent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; userId&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&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;// Creating our logger and properly setting its context&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MyComponent&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; userId &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;// Will log on render with proper context / names&lt;/span&gt;
  logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rendering&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;// This will `useEffect` to log &amp;amp; log everytime the argument object&apos;s values change&lt;/span&gt;
  logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Data has changed&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; data &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;// This is wrapping the children with this logger&apos;s context provider&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// calling `useLogger` on a child will use `MyComponent` logger as the parent.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;wrap&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;div &lt;span class=&quot;token operator&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;logger-sinks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#logger-sinks&quot; aria-label=&quot;logger sinks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Logger sinks&lt;/h3&gt;
&lt;p&gt;Sinks are implementors of the LoggerSink interface.&lt;/p&gt;
&lt;p&gt;Tree sinks are provided:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PrettyConsoleSink - Pretty print messages on Node.js&lt;/li&gt;
&lt;li&gt;PrettyBrowserSink - Pretty print messages on browser consoles&lt;/li&gt;
&lt;li&gt;DelegatingSink - Send messages to multiple sinks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There’s no filtering currently implemented.&lt;/p&gt;
&lt;h3 id=&quot;configuring-the-sink&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#configuring-the-sink&quot; aria-label=&quot;configuring the sink permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Configuring the sink&lt;/h3&gt;
&lt;p&gt;Use &lt;code class=&quot;language-text&quot;&gt;LoggerFactory.setSink&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;why-named-and-hierarchical-loggers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-named-and-hierarchical-loggers&quot; aria-label=&quot;why named and hierarchical loggers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why named and hierarchical loggers?&lt;/h2&gt;
&lt;p&gt;Large applications will benefit from being able to filter out logs based on
their place in the hierarchy.
For React applications the UI hierarchy can be indicative of ownership or package
boundaries.&lt;/p&gt;
&lt;h2 id=&quot;what-else&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-else&quot; aria-label=&quot;what else permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What else?&lt;/h2&gt;
&lt;p&gt;That is it.
This is a very simple package for now
and should grow with the needs of my project.&lt;/p&gt;
&lt;p&gt;All the best&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Parsing and serializing MIDI files in Rust]]></title><description><![CDATA[This is a super simple post about , an unpublished crate for MIDI
event and file parsing and serialization. The library: Parses standard…]]></description><link>https://beijaflor.io/midi-parsing/</link><guid isPermaLink="false">https://beijaflor.io/midi-parsing/</guid><pubDate>Tue, 03 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a super simple post about &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/data/augmented-midi&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-midi&lt;/code&gt;&lt;/a&gt;, an unpublished crate for MIDI
event and file parsing and serialization.&lt;/p&gt;
&lt;p&gt;The library:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parses standard MIDI files and messages&lt;/li&gt;
&lt;li&gt;Serializes MIDI messages&lt;/li&gt;
&lt;li&gt;Performs no allocations on parse (results are either owned or input references)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is part of the &lt;a href=&quot;https://github.com/yamadapc/augmented-audio&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-audio&lt;/code&gt; repository&lt;/a&gt;
and is used by &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/application/audio-processor-standalone&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-standalone&lt;/code&gt;&lt;/a&gt;
to feed MIDI input into &lt;a href=&quot;/blog/02-2022/rust-audio-experiments-4/&quot;&gt;generated processor CLIs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You implement the &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-traits&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;MIDIEventHandler&lt;/code&gt; traits&lt;/a&gt;
and wrap it with &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/application/audio-processor-standalone&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-standalone&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now you have a CLI that can process audio and MIDI, both in real-time and offline.&lt;/p&gt;
&lt;p&gt;When rendering offline, your CLI will be able to be process MIDI input:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cd ./crates/apps/synth
cargo run \
    --release -- \
    --input-file=../../../input-files/synthetizer-loop.mp3 \
    --output-file=synth.wav \
    --midi-input-file=../../augmented/data/augmented-midi/bach_846.mid&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This outputs &lt;code class=&quot;language-text&quot;&gt;synth.wav&lt;/code&gt; &lt;em&gt;(⚠️ level are off so this is loud)&lt;/em&gt;:&lt;/p&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;166&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allow=&quot;autoplay&quot; src=&quot;https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/1261439842%3Fsecret_token%3Ds-ADHwFU2e5FF&amp;color=%23ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;show_teaser=true&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;I wrote about what I’m trying with these abstractions in more detail on &lt;a href=&quot;/blog/02-2022/rust-audio-experiments-4/&quot;&gt;“Generic AudioProcessors in Rust”&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;usage-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#usage-example&quot; aria-label=&quot;usage example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Usage example&lt;/h2&gt;
&lt;p&gt;Using the &lt;code class=&quot;language-text&quot;&gt;TryFrom&lt;/code&gt; trait implementation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;let&lt;/span&gt; input_buffer &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;0x9_8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x3C&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x44&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; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MIDIMessage&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 operator&quot;&gt;&amp;amp;&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 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;try_from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input_buffer&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;unwrap&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 macro property&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
   midi_message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;token class-name&quot;&gt;MIDIMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NoteOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MIDIMessageNote&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      channel&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;
      note&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;
      velocity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;68&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;/div&gt;
&lt;p&gt;Using the &lt;code class=&quot;language-text&quot;&gt;nom&lt;/code&gt; functions, which are more verbose:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;augmented_midi&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;MIDIMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MIDIMessageNote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MIDIParseResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; parse_midi_event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParserState&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;// Initialize parser state. This is here to support rolling status on MIDI files.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Not relevant for single events.&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; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParserState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&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;// We&apos;ll parse this &amp;amp;[u8] buffer&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; input_buffer &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;0x9_8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x3C&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x44&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;// We parse a message borrowing from the input buffer. We could use `MIDIMessage&amp;lt;Vec&amp;lt;u8&gt;&gt;`&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// to allocate owned messages.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This will determine how variable size messages (SysEx) will be allocated.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Parsing is otherwise only using the stack.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; parse_result&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MIDIParseResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MIDIMessage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&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 operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;parse_midi_event&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;input_buffer&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; state&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 punctuation&quot;&gt;(&lt;/span&gt;_remaining_input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; midi_message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parse_result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&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 macro property&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;midi_message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MIDIMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NoteOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MIDIMessageNote&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; channel&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; note&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; velocity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;68&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 keyword&quot;&gt;match&lt;/span&gt; midi_message &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;MIDIMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NoteOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MIDIMessageNote&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; channel&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; note&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; velocity &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 macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\o/&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 comment&quot;&gt;// MIDIMessage::... variants contain all the messages&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;parser-combinators-code-classlanguage-textnomcode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#parser-combinators-code-classlanguage-textnomcode&quot; aria-label=&quot;parser combinators code classlanguage textnomcode permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Parser combinators: &lt;code class=&quot;language-text&quot;&gt;nom&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The parser is implemented using &lt;a href=&quot;https://github.com/Geal/nom&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;nom&lt;/code&gt;&lt;/a&gt; parser
combinators.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;nom&lt;/code&gt; not required for MIDI, which is a simple protocol, but made it easier to
go with a good structure from the start.&lt;/p&gt;
&lt;p&gt;We have some type aliases:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&apos;a&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 operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&apos;a&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 punctuation&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;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;MIDIParseResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Output&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;IResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&apos;a&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 class-name&quot;&gt;Output&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Out inputs are slices of bytes and our outputs are &lt;code class=&quot;language-text&quot;&gt;nom&lt;/code&gt; results, these are
&lt;code class=&quot;language-text&quot;&gt;Result&lt;/code&gt; types where success is a tuple of the remaining input and the parsed type
and failure is an error, that will reference the input position for the failure.&lt;/p&gt;
&lt;p&gt;Here’s the function to parse the MIDI file format from MIDI files’ header chunk
as an example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;parse_file_format&lt;/span&gt;&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 class-name&quot;&gt;Input&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 class-name&quot;&gt;MIDIParseResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MIDIFileFormat&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 punctuation&quot;&gt;(&lt;/span&gt;input&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 operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;be_u16&lt;/span&gt;&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 operator&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; format &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; format &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 operator&quot;&gt;=&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 class-name&quot;&gt;MIDIFileFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Single&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;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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 class-name&quot;&gt;MIDIFileFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Simultaneous&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;=&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 class-name&quot;&gt;MIDIFileFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sequential&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 class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MIDIFileFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Unknown&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;input&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 punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our function takes &lt;code class=&quot;language-text&quot;&gt;Input&lt;/code&gt;, which is just &lt;code class=&quot;language-text&quot;&gt;&amp;amp;[u8]&lt;/code&gt; and returns &lt;code class=&quot;language-text&quot;&gt;Result&amp;lt;(&amp;amp;[u8], MIDIFileFormat), Error&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It takes a stream of bytes, parses a &lt;code class=&quot;language-text&quot;&gt;MIDIFileFormat&lt;/code&gt; enum and returns it with
the remaining stream of bytes. Otherwise, it returns an error.&lt;/p&gt;
&lt;p&gt;MIDI events/files are very simple and compact, but there’s a bit of bit
fiddling. For example, most events have 3 bytes, with the first byte being both
the type of the event and sometimes what channel it refers to. The first 4 bits
of this status byte are the status and the last 4 bits might contain the channel
information.&lt;/p&gt;
&lt;p&gt;The crate is useful to avoid repeating this on application code. It uses
bit-masks and returns nice ‘rusty’ types other code can pattern match over.&lt;/p&gt;
&lt;p&gt;The entry-points to the library are the &lt;code class=&quot;language-text&quot;&gt;parse_midi_event&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;parse_midi_file&lt;/code&gt;
functions and the &lt;code class=&quot;language-text&quot;&gt;MIDIMessage&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;MIDIFile&lt;/code&gt; types.&lt;/p&gt;
&lt;p&gt;The functions and types are generic: &lt;code class=&quot;language-text&quot;&gt;MIDIMessage&amp;lt;Buffer: Borrow&amp;lt;[u8]&gt;&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Most messages have static sizes and will be stack-allocated. For example,
note-on messages always have 3 bytes while program change messages always have
2 bytes. The generic argument is used to determine how the variable size messages
are handled (just SysEx).&lt;/p&gt;
&lt;p&gt;If &lt;code class=&quot;language-text&quot;&gt;MIDIMessage&amp;lt;&amp;amp;&apos;a [u8]&gt;&lt;/code&gt; is parsed, the sys-ex messages will point to the
input buffer. On the other hand, if &lt;code class=&quot;language-text&quot;&gt;MIDIMessage&amp;lt;Vec&amp;lt;u8&gt;&gt;&lt;/code&gt; is parsed, the sys-ex
messages will be owned and the buffers copied into vecs.&lt;/p&gt;
&lt;p&gt;The same goes for parts of the MIDI file spec which require strings such as
chunks (this will only be used for unknown chunks found). Though these strings
will always only have 4 bytes so maybe this could be removed.&lt;/p&gt;
&lt;p&gt;Some nice Rust, here is SysEx parsing:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;parse_sysex_event&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Borrow&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 keyword&quot;&gt;u8&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 operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&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;// [1]&lt;/span&gt;
    input&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&apos;a&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;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MIDIParseResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MIDISysExEvent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Buffer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;                    &lt;span class=&quot;token comment&quot;&gt;// [2]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &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 operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;alt&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;tag&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;0xF7&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;tag&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;0xF0&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;input&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 keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bytes&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;take_till&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;b&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xF7&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;input&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 keyword&quot;&gt;let&lt;/span&gt; &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 operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1u8&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;input&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;
        input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;MIDISysExEvent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            message&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bytes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;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 comment&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 punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;The type is generic over a &lt;code class=&quot;language-text&quot;&gt;Buffer&lt;/code&gt; type argument, which will be the output
storage
a. we require this type to be borrow-able as a &lt;code class=&quot;language-text&quot;&gt;[u8]&lt;/code&gt; slice, so that
we can read it back in a standard way as a slice of bytes
b. we require that this type be convertible from our input type, which is
&lt;code class=&quot;language-text&quot;&gt;&amp;amp;[u8]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We return the input/output tuple result as before, lifetime annotations are
carried with the input type&lt;/li&gt;
&lt;li&gt;Conversion from the input slice into just a reference to it (no conversion)
or into a &lt;code class=&quot;language-text&quot;&gt;Vec&amp;lt;u8&gt;&lt;/code&gt; is handled by &lt;code class=&quot;language-text&quot;&gt;.into()&lt;/code&gt; calling the &lt;code class=&quot;language-text&quot;&gt;Into&lt;/code&gt; trait&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;serialization-cookie_factory&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#serialization-cookie_factory&quot; aria-label=&quot;serialization cookie_factory permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Serialization: cookie_factory&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rust-bakery/cookie-factory&quot;&gt;cookie-factory&lt;/a&gt; is used for
serializing MIDI events. Currently, only MIDI events can be serialized, rather
than the full MIDI standard files specification.&lt;/p&gt;
&lt;p&gt;This maps closely to the &lt;code class=&quot;language-text&quot;&gt;nom&lt;/code&gt; parser combinators. &lt;a href=&quot;https://www.mathematik.uni-marburg.de/~rendel/rendel10invertible.pdf&quot;&gt;I remember something about
isomorphic parser/pretty-printer code&lt;/a&gt;,
but this is a very tiny serializer.&lt;/p&gt;
&lt;h2 id=&quot;performance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance&quot; aria-label=&quot;performance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/augmented_midi_criterion_05_2022/report/index.html&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-midi&lt;/code&gt; is very fast, these are benchmarks.&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Generally:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Performance isn’t an issue for this&lt;/li&gt;
&lt;li&gt;No allocation is 1 order of magnitude faster than yes allocation 🤷‍&lt;/li&gt;
&lt;li&gt;My computer can parse billions of MIDI messages a second; so it doesn’t matter&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That is all! Thank you for reading and give me feedback on &lt;a href=&quot;https://twitter.com/yamadapc&quot;&gt;Twitter &lt;code class=&quot;language-text&quot;&gt;@yamadapc&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Continuous Looper]]></title><description><![CDATA[This is an April update on Augmented Audio Libraries,
my hobby libraries for audio programming in Rust (check previous updates). It is a…]]></description><link>https://beijaflor.io/rust-audio-experiments-5/</link><guid isPermaLink="false">https://beijaflor.io/rust-audio-experiments-5/</guid><pubDate>Tue, 12 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is an April update on &lt;a href=&quot;https://github.com/yamadapc/augmented-audio&quot;&gt;&lt;strong&gt;Augmented Audio Libraries&lt;/strong&gt;&lt;/a&gt;,
my hobby libraries for audio programming in Rust (&lt;a href=&quot;https://beijaflor.io/blog&quot;&gt;check previous updates&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;It is a special one because today I have released an initial version of
&lt;strong&gt;Continuous Looper&lt;/strong&gt; on the macOS App Store (iOS version coming &lt;del&gt;soon&lt;/del&gt; next).&lt;/p&gt;
&lt;p&gt;There’s a lot to do and fix, but I think publishing this post (and the app)
to get some feedback will help me.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/au/app/continuous-looper/id1616355791?mt=12&quot;&gt;&lt;strong&gt;You can download the app now here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this post, I’ll discuss how I have written this app, the approaches I have
been trying and challenges faced.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.83783783783784%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACbklEQVR42m2TWW/bMBCEHZ+6D0qiTkuyZFt2WudA7aZJEaRBEBRFULQP/f9/ZTpiDj8kDwOSOr7dnV0OwnKLjyTyDm7SIpx3uHp8guBqh3O4soSfVPDkHF5UwA0zKoUTJHCExOAjWFSdovl0wGr3Fe3nA5aHW9SbSzTbCxTtFi5BrzCnh4kYji9he9F7oKypaoP99wc8/PqL+5+/cXP3iG+3D3j68w/76zuCcvhR/gJ7zqyHWV5wBIpio8qsqhaxlNB1A5qmUwZs2+HZxGymqecyiiBlQoCkQlhuoGCWK47AortA2pyhPWtgCwuD4QRjQobTGWamjSk1mmoYTqYYmw5Mx4fMKiR5hUBm6mxRChhQ51c/0O2usd4vIZc1pppFXwRMy2ZkH5rl8gcXJrPtzw4z67Y7nF/uUTcrGDbf294xQ48d9bMWriuRL9bIiwpRJBGFEnGSqfN8TpU1yqpGFOcqyMywoVvMmDCD6zOw6BDkK2oJV0QosgJ13SJJC5Rlo3xdUP1+sVgRvIBlOc/esUyT2fUwg9UMeljEGUsXp5DzNSNajMwm0ODXxowmE0zoZ/98MjOUp/3eY3ARSLieUDAFFMwqI+zLzT3WuwPGNH7KbmqmBZ0aDUcYnJxAI9z1PNUkT4Qw+K5ZdtidnaMoK/WtAvZlirTh9Nf0seJoGJz6mIObwjBM2D7BXP2AEIMjxJnr95qmKYD+Evgtw4CN8JOFuk5+WiOUAbLlFnG9hsMP6k2pMuub02122O72WK9PlZ+6ab6BjiUrGLOLS3U/o5TTb9vKu5lmYjymBbShzzIMYwj6FkQJfD9g2fY74H83o0h8Vq9ihwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/b2fd5b5cfe0544c874b0d6f8091e4198/f51c2/01_cover.webp 148w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/66888/01_cover.webp 295w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/bc8a3/01_cover.webp 590w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/4ce34/01_cover.webp 885w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/f490a/01_cover.webp 1180w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/da28f/01_cover.webp 2880w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/b2fd5b5cfe0544c874b0d6f8091e4198/c5084/01_cover.png 148w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/60cc9/01_cover.png 295w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/6d370/01_cover.png 590w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/540ae/01_cover.png 885w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/2561a/01_cover.png 1180w,
/static/b2fd5b5cfe0544c874b0d6f8091e4198/f9c26/01_cover.png 2880w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/b2fd5b5cfe0544c874b0d6f8091e4198/6d370/01_cover.png&quot;
            alt=&quot;Continuous Looper screenshot&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;continuous-looper&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#continuous-looper&quot; aria-label=&quot;continuous looper permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Continuous Looper&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Continuous Looper&lt;/strong&gt; is a fully open-source 8-track live-looper and performance
sampler, written in Rust/Swift which features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;8-track looping/sequencing&lt;/li&gt;
&lt;li&gt;Step sequencing live after recording&lt;/li&gt;
&lt;li&gt;2 quantization modes&lt;/li&gt;
&lt;li&gt;Scenes and parameter locks&lt;/li&gt;
&lt;li&gt;Click and twist MIDI mapping to any parameter &amp;#x26; the record-button&lt;/li&gt;
&lt;li&gt;2 LFOs + 1 ADSR envelope per track&lt;/li&gt;
&lt;li&gt;Automatic slicing based on transient detection&lt;/li&gt;
&lt;li&gt;Basic pitch shifting (potentially dated phase-vocoder)&lt;/li&gt;
&lt;li&gt;Variable start/end/speed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since the scope got large, there are many rough edges! But it has worked well.&lt;/p&gt;
&lt;p&gt;The app/GUI source-code is available under the AGPLv3 license at &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/apps/looper/Sequencer&quot;&gt;augmented-audio/crates/apps/looper/Sequencer&lt;/a&gt;
and the engine source-code is available under the MIT license at &lt;a href=&quot;https://github.com/yamadapc/augmented-audio&quot;&gt;augmented-audio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Continuous Looper&lt;/strong&gt; is inspired by the great &lt;a href=&quot;https://www.beepstreet.com/ios/drambo&quot;&gt;Drambo iOS modular synth&lt;/a&gt;
and by &lt;a href=&quot;https://www.elektron.se/products/octatrack-mkii/&quot;&gt;Octatrack&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;demo-video&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#demo-video&quot; aria-label=&quot;demo video permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Demo video&lt;/h3&gt;
&lt;p&gt;Here is a tiny demo video.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Note: this is an iPhone video and audio is very low)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;lite-youtube videoid=&quot;gNnrHyl4mrg&quot; videotitle=&quot;Continuous Looper demo 1&quot; videoStartAt=&quot;32&quot;&gt;&lt;/lite-youtube&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;table-of-contents&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#table-of-contents&quot; aria-label=&quot;table of contents permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Table of contents&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#some-requirements-for-the-app&quot;&gt;Requirements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#architecture&quot;&gt;Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#rust-and-audio&quot;&gt;Rust and audio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;some-requirements-for-the-app&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#some-requirements-for-the-app&quot; aria-label=&quot;some requirements for the app permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Some requirements for the app&lt;/h2&gt;
&lt;p&gt;It’s good to mention a couple of requirements that guide the decisions I have
been taking and greatly increase complexity.&lt;/p&gt;
&lt;h3 id=&quot;real-time-audio&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#real-time-audio&quot; aria-label=&quot;real time audio permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Real-time audio&lt;/h3&gt;
&lt;p&gt;The app must render audio in real-time. At fixed rate intervals, our software’s
audio callback will be called with a buffer of numbers representing
an audio signal. Our software will have a capped amount of time
(roughly between 1ms-10ms) to finish. It must finish in this time or else
playback will audibly glitch.&lt;/p&gt;
&lt;p&gt;The code that runs in the audio-thread:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;should avoid variable size work&lt;/li&gt;
&lt;li&gt;should avoid locks&lt;/li&gt;
&lt;li&gt;should avoid allocation/de-allocation&lt;/li&gt;
&lt;li&gt;should not perform IO&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some good resources on this subject:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.rossbencina.com/code/real-time-audio-programming-101-time-waits-for-nothing&quot;&gt;Real-time audio programming 101: time waits for nothing - Ross Bencina&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=zrWYJ6FdOFQ&quot;&gt;Using Locks in Real-Time Audio Processing, Safely - Timur Doumler - ADC20&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=SJXGSJ6Zoro&quot;&gt;The Golden Rules of Audio Programming - Pete Goodliffe&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;interactive-gui&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#interactive-gui&quot; aria-label=&quot;interactive gui permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Interactive GUI&lt;/h3&gt;
&lt;p&gt;Second, we want the GUI to be highly interactive. The GUI should render
visualisations of the operations taking place in the audio side whenever
possible.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Note: this is a screen recording and much louder than the previous video)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;lite-youtube videoid=&quot;UYB0wuk0s18&quot; videotitle=&quot;Continuous Looper demo 2&quot; videoStartAt=&quot;32&quot;&gt;&lt;/lite-youtube&gt;&lt;/p&gt;
&lt;p&gt;The following data needs to constantly update on the UI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Global play-head position
&lt;ul&gt;
&lt;li&gt;The position of the track in beats at the center-top&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sequencer position
&lt;ul&gt;
&lt;li&gt;The current active step flashes in the sequencer view&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Looper play-head position
&lt;ul&gt;
&lt;li&gt;The position of each looper within its recording, represented by the green
line over the rendered audio wave and by the green fill over the track’s
button&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Last MIDI messages
&lt;ul&gt;
&lt;li&gt;This isn’t shown, but a monitor of previous events is displayed when the
MIDI button on the top-right is clicked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;looper-quantization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#looper-quantization&quot; aria-label=&quot;looper quantization permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Looper quantization&lt;/h3&gt;
&lt;p&gt;This is the main reason I wanted to write a looper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quantization&lt;/strong&gt; in live-loopers refers to automatic tempo correction of the
presses to the record/stop/play buttons. This is to aid musicians to record
loops in sync with some tempo.&lt;/p&gt;
&lt;p&gt;For example, there may be a drum track playing or another looped track.
Whenever a new loop is recorded it should have a length that makes sense in the
current tempo and time-signature (subject to configuration).&lt;/p&gt;
&lt;p&gt;Most software loopers I’ve tried only correct the musician being &lt;strong&gt;early&lt;/strong&gt;, but
don’t correct them being &lt;strong&gt;late&lt;/strong&gt;. I have hardware looper pedals which do both.&lt;/p&gt;
&lt;p&gt;If there’s a tempo track playing and a musician presses to start a recording at
beat 3.7, the looper will wait until the 1st beat of the next bar.
This is correcting for when the musician is &lt;strong&gt;early&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.432432432432435%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB7ElEQVR42pVS22/SYBTvf2zCYqI88saLbybOoTNEwDlZF0dMmLYCLbRSMLh28MBl5dYGAoJAKfeffEfBGeOMJ/nlO+c79wvnui4qlQosy0K320W73Uar1cJgMACjzWaD/yGu3+8jFouB53nE43GIokgyS8JotVoR1us1lsslvXcGZEaNRgPNZpPAeNu2Ua/XUa1WSTZNE7VajbqYzWZ3B2QtTadTjMcjOI6DyWRCGI/He5npR6MRFovFv1u2bQtm/WY/r93Mbs/uNm/oVygWi+h0OpRw57Oz4Yp1FVbX3Ln+PfVPB12IQf+cQ9uyqfI/KqzZV0ilRSgZFZIkIZvNUgWGYUDXdRQKBeINQ8cX4xra5QXyagZaPg9ZlqEoCkqlElXM9sHplQwO7t/DwwdeeDwe+P1+vD49xctQCMFgEIFAAOFwGJFIBEfPjxF5/Ajvzs/oEnw+H7xeL9lpmkYL426sEo5ePMNxMISnW4eTN1EktpWmJBmJZAofBAGSnIaYSCIWfw/hgoeqKlC0HMLbJCwYO7VyuYz5fA7O7dkYJM8wlM736H2MIht5gtzJIXKvDuntJaIYpnjSf0u/JX7aadHU1+vVr6UsRl8xvVbgFtU9mGx9EmBnRUJ7yzvbvxnpfugdPY3FsPfbwhh9B9s3GqbgsKjMAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/af79f98032db384bb761e4b30ca2f8f4/f51c2/snap-next.webp 148w,
/static/af79f98032db384bb761e4b30ca2f8f4/66888/snap-next.webp 295w,
/static/af79f98032db384bb761e4b30ca2f8f4/bc8a3/snap-next.webp 590w,
/static/af79f98032db384bb761e4b30ca2f8f4/4ce34/snap-next.webp 885w,
/static/af79f98032db384bb761e4b30ca2f8f4/f490a/snap-next.webp 1180w,
/static/af79f98032db384bb761e4b30ca2f8f4/08048/snap-next.webp 1400w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/af79f98032db384bb761e4b30ca2f8f4/c5084/snap-next.png 148w,
/static/af79f98032db384bb761e4b30ca2f8f4/60cc9/snap-next.png 295w,
/static/af79f98032db384bb761e4b30ca2f8f4/6d370/snap-next.png 590w,
/static/af79f98032db384bb761e4b30ca2f8f4/540ae/snap-next.png 885w,
/static/af79f98032db384bb761e4b30ca2f8f4/2561a/snap-next.png 1180w,
/static/af79f98032db384bb761e4b30ca2f8f4/3643c/snap-next.png 1400w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/af79f98032db384bb761e4b30ca2f8f4/6d370/snap-next.png&quot;
            alt=&quot;Snap next quantization diagram&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, if the musician is late, we’d also like to correct the tempo, by
assuming that the button had already been pressed (in the past) and then
continuing to operate.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.432432432432435%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB9UlEQVR42pVS7W/ScBDu32uCiTo/+NXExc+6D0KiTGCWl1XJtFaECpswKLQsjLlMx/uLCgOhlLaDBh77+2E1TKPxlzy56909z13uyhiGgW63i8FggPF4jH6/j16vh+FwSO1iscD/PIaIRaNRsCwLURQRi8XAcRwEQQDP85jNZrSQCBMsl8u/C+q6jkajgXa7jVqtRi1pUq/X0Ww20Wq1qE9qiO10OpjP55RMxK82YEhXXTegqiqFpmmYTCYUo9EI0+mUgvhOzpn6T9My1fo5PpyerHW8CucR39kpEd4X4/j08WxNnKl8laHqFw7ln0t3iJphopzYw+VktMZlKt0SeGEP8bcJeohUKoVisQhZllEoFJDL5aAoCgXxSUwhOaWIeGQHCYHHUalEd2tZFpjSeQbXb1zDxq3bcLlcuLe5CTYYxFOfD0+8Xjxyu+Hz++EPBOD2eODd3obPzoW45/Dcv4s7Gzfx2K6TJAmmaYJp9yoIRoLY5V4gGArjFf8a2ZyEfF6iE6XTaXuqPDKHWbxLpnDwPm3HMpBkBQdvXiLKRXCYzaJh/wHk+szlxWdoyTCm+7s/8S3BQg48QHFnC0fPtqAEHtKYlgpjkgxRqCILs9dyFvvrytZUhVE9hlkr/8CJ/V3Gl+P8CuWV1aurnAPCsTT1t2N+B8oXGPrDvIsMAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/1c99ae9211b522df4aee9a3a4d3093de/f51c2/snap-closest.webp 148w,
/static/1c99ae9211b522df4aee9a3a4d3093de/66888/snap-closest.webp 295w,
/static/1c99ae9211b522df4aee9a3a4d3093de/bc8a3/snap-closest.webp 590w,
/static/1c99ae9211b522df4aee9a3a4d3093de/4ce34/snap-closest.webp 885w,
/static/1c99ae9211b522df4aee9a3a4d3093de/f490a/snap-closest.webp 1180w,
/static/1c99ae9211b522df4aee9a3a4d3093de/08048/snap-closest.webp 1400w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/1c99ae9211b522df4aee9a3a4d3093de/c5084/snap-closest.png 148w,
/static/1c99ae9211b522df4aee9a3a4d3093de/60cc9/snap-closest.png 295w,
/static/1c99ae9211b522df4aee9a3a4d3093de/6d370/snap-closest.png 590w,
/static/1c99ae9211b522df4aee9a3a4d3093de/540ae/snap-closest.png 885w,
/static/1c99ae9211b522df4aee9a3a4d3093de/2561a/snap-closest.png 1180w,
/static/1c99ae9211b522df4aee9a3a4d3093de/3643c/snap-closest.png 1400w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/1c99ae9211b522df4aee9a3a4d3093de/6d370/snap-closest.png&quot;
            alt=&quot;Snap closest quantization diagram&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;These two quantization strategies are implemented and selectable on
&lt;strong&gt;Continuous Looper&lt;/strong&gt; and there is some more around implementation later on.&lt;/p&gt;
&lt;h2 id=&quot;architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#architecture&quot; aria-label=&quot;architecture permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Architecture&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;IMPORTANT:&lt;/strong&gt; I am not a professional audio developer&lt;/p&gt;
&lt;p&gt;Please give me feedback! :)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Continuous Looper&lt;/strong&gt;’s engine is built using the &lt;code class=&quot;language-text&quot;&gt;Rust&lt;/code&gt; crates I have been
building on &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-audio&lt;/code&gt;&lt;/a&gt;.
It benefits from several crates in the rust ecosystem such as &lt;a href=&quot;https://docs.rs/petgraph/latest/petgraph/&quot;&gt;petgraph&lt;/a&gt;,
&lt;a href=&quot;https://github.com/RustAudio/cpal&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cpal&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/glowcoil/basedrop&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;basedrop&lt;/code&gt;&lt;/a&gt;,
&lt;a href=&quot;https://github.com/actix/actix/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;actix&lt;/code&gt;&lt;/a&gt; and more (&lt;code class=&quot;language-text&quot;&gt;ringbuf&lt;/code&gt;, etc. the list
of dependencies is very long).&lt;/p&gt;
&lt;p&gt;The GUI is built using Swift, AppKit (and UIKit at some point) and SwiftUI.&lt;/p&gt;
&lt;p&gt;Originally a simpler looper GUI was built using &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.05405405405405%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB6ElEQVR42p2Sy27TQBSGU9SKBWp9iT2ecWynbXB8H5y4BEpVpIhKWbBBlbroggchgjdjwQtgwauAuhz9nLGTEm4bRvo04zlnPs0548GAxuXlSqyWq+Tq5VX8N65f3xLX8b/iyxfLRDsGm3GQJvJjOq2QT+W3bFrd7ZLG5V0tn3bo9e/xbCq/54nENC4+kWtfC40oitrJZAKXMeW6Ln6BMTiO0/FHrEexPucruQ57YRi2URiAe67i3KPZg2AuOBfgNAvOITy9z2iPd7M39mndfStBZ4Ig6IWGYZh0qDVPchhJoyw+wuH5GxjxDEPLhDmRsMLHsEQAKziF7TLYnNYRh3Wcwh46yvZD8CDcEQreWskcRnmhTEp6tHwLI1vApjKPZq86qXmc0d5z2J7o1tZJqC8AmwlljRPw6PSncCREy6kPzBkqRmW6owhiaFCZVJIf9KXrUkXfAo03FrpFunzlUS8D37/voZnneVvXNcqqUlUlIYlycYFyNseTswXmz87RNA2qqkKW5aD8e7I8V/VshjRNtfCoe5QkST5LKVEUhSrLEgUlViTUsnpLcwYd25VtUPosCb9sb/jQtu0bevYPNL8j1hpGCMYIZ8062Jpy1tv4DvrMe/p9brRLC/eIB5uf8uA/2d849n4AWgrh4pinQSgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/2ba68ebf7054c95f5faae2d401495d12/f51c2/looper-screenshot.webp 148w,
/static/2ba68ebf7054c95f5faae2d401495d12/66888/looper-screenshot.webp 295w,
/static/2ba68ebf7054c95f5faae2d401495d12/bc8a3/looper-screenshot.webp 590w,
/static/2ba68ebf7054c95f5faae2d401495d12/4ce34/looper-screenshot.webp 885w,
/static/2ba68ebf7054c95f5faae2d401495d12/f490a/looper-screenshot.webp 1180w,
/static/2ba68ebf7054c95f5faae2d401495d12/7ef45/looper-screenshot.webp 1624w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/2ba68ebf7054c95f5faae2d401495d12/c5084/looper-screenshot.png 148w,
/static/2ba68ebf7054c95f5faae2d401495d12/60cc9/looper-screenshot.png 295w,
/static/2ba68ebf7054c95f5faae2d401495d12/6d370/looper-screenshot.png 590w,
/static/2ba68ebf7054c95f5faae2d401495d12/540ae/looper-screenshot.png 885w,
/static/2ba68ebf7054c95f5faae2d401495d12/2561a/looper-screenshot.png 1180w,
/static/2ba68ebf7054c95f5faae2d401495d12/fc0cd/looper-screenshot.png 1624w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/2ba68ebf7054c95f5faae2d401495d12/6d370/looper-screenshot.png&quot;
            alt=&quot;Looper Iced GUI screen-shot&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I will likely still use &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; to ship a simplified VST.&lt;/p&gt;
&lt;p&gt;Swift/AppKit/UIKit/SwiftUI are more mature and allow iOS support, so this
version of the app uses them.&lt;/p&gt;
&lt;h3 id=&quot;containers-or-components&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#containers-or-components&quot; aria-label=&quot;containers or components permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Containers or components&lt;/h3&gt;
&lt;p&gt;Here’s a “Container diagram” (I’m likely misusing the &lt;a href=&quot;https://c4model.com/&quot;&gt;C4 model&lt;/a&gt;
in my context):&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 120.94594594594594%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAYAAAD6S912AAAACXBIWXMAAAsTAAALEwEAmpwYAAAC/0lEQVR42q2Ve0hTURzH91d/BP1VBP0RFVEQBUmUERLRg+ifCCqLXhCISJKJlkUWkinaU0Qj0iIUtYcZRYI9tafO5XpoztnUXcO5zT1qecfu7mvfzj3bXbu6wEk/+HLu5dzzOd9zfr9zrg5xQpZDkEMhTCd0+M8RBYYijjheQG5lM2pajGG3CTrVxS5Tif4fLuhWZmFdWoVmooSAUgTW9mkQa9IrMXf7eczfWYI9BfX4NR5ICEyBoiTTl9KbL6BbkoEFu0oxa/NZzEjORa9lVLOCKToMA1v0ZixKvYCl+y5h4e5SbDpWDY/Pn7jD2FAHymQSWZYibVgJA4PBIBFHoZIkwu9nIYoighwHn883HYfEXUgKu1QVca1oKk41wNoOF/Iarch/yCC/yYoTjQyMDBvt53keNpsNHo8HbrcbLpeLtrGTaIB5DxhsKetD6o3vVBuvmPD4i0f1TrfEZDLBYDDAaDRSWa1Wsj1SfGDOfQari3uwt8qCHdfMSCrsxqPP3knArq4uClNahmEmA9WCqHpjR2bdAHE6jJNNw8hsGETn0HgUKAgC7HY7xsbGqJxOJ20nL5lmVYrMRDafZBghmT7zBBLrQE2Skv1/JkWZwev1IhAIULifZcFxATpQSQDLsppBEoFxHBe32GMuBxlBQYQUUlyJ9HzzogQhxh1dNjmmIulTvhUl4nRCGWmSUtE6isO3LUivsSCjbhAHq814Z/lb0P12P1KKOrG+2ICUwg6sPafH/uvdYDlR3TktMPuuFRsu9+LArQEiC5JLvqHR4CD2efK1iPdmF2YfacWK0+1Uy059QNKZdnhYPj4w644Vq4q6sa28D1vLTFhe8BVNRne0DvQDP6E79AxzMtswL+s1Zqa9xOLjb+FlhfjAex/dKGoewdXnoyh/ZcfFpzb02PzR/hEvh+x6M3IUNZhxtLYPJU+GEBCk6B7H/AKA8YCA30SCKGPiTT7lG1spVuWMKq2qYJCnWZbi/P2U94nSAJXDrVa/0xmufofDEa7JacQfOIX5xhxAHBkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/b0a5f57122a811c2940ea1b9138506b7/f51c2/container-diagram.webp 148w,
/static/b0a5f57122a811c2940ea1b9138506b7/66888/container-diagram.webp 295w,
/static/b0a5f57122a811c2940ea1b9138506b7/bc8a3/container-diagram.webp 590w,
/static/b0a5f57122a811c2940ea1b9138506b7/4ce34/container-diagram.webp 885w,
/static/b0a5f57122a811c2940ea1b9138506b7/f490a/container-diagram.webp 1180w,
/static/b0a5f57122a811c2940ea1b9138506b7/ff2d6/container-diagram.webp 1754w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/b0a5f57122a811c2940ea1b9138506b7/c5084/container-diagram.png 148w,
/static/b0a5f57122a811c2940ea1b9138506b7/60cc9/container-diagram.png 295w,
/static/b0a5f57122a811c2940ea1b9138506b7/6d370/container-diagram.png 590w,
/static/b0a5f57122a811c2940ea1b9138506b7/540ae/container-diagram.png 885w,
/static/b0a5f57122a811c2940ea1b9138506b7/2561a/container-diagram.png 1180w,
/static/b0a5f57122a811c2940ea1b9138506b7/f2be5/container-diagram.png 1754w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/b0a5f57122a811c2940ea1b9138506b7/6d370/container-diagram.png&quot;
            alt=&quot;Container diagram&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;User&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Some musician that wants to use the app&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous macOS/iOS App&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;GUI, which users will interact with&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LooperEngine&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Audio engine, which communicates with the app through a C API&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MIDI controllers&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System Audio/MIDI APIs&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next, I will go over details, starting with high-level notes, then talking about
the GUI and moving on to details of specifics of the rust implementation.&lt;/p&gt;
&lt;h3 id=&quot;system-audiomidi-apis---api-wrappers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#system-audiomidi-apis---api-wrappers&quot; aria-label=&quot;system audiomidi apis   api wrappers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;System Audio/MIDI APIs - API wrappers&lt;/h3&gt;
&lt;p&gt;For &lt;code class=&quot;language-text&quot;&gt;5&lt;/code&gt;, the system APIs are wrapped by &lt;code class=&quot;language-text&quot;&gt;cpal&lt;/code&gt; and &lt;a href=&quot;https://github.com/Boddlnagg/midir/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;midir&lt;/code&gt;&lt;/a&gt;
for audio and MIDI support, respectively.&lt;/p&gt;
&lt;p&gt;Both of these are wrapped by the higher-level &lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; trait
abstraction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.rs/audio-processor-traits/1.0.0-alpha.5/audio_processor_traits/&quot;&gt;audio-processor-traits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.rs/audio-processor-standalone/1.0.0-alpha.9/audio_processor_standalone/&quot;&gt;audio-processor-standalone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.rs/audio-processor-standalone-midi/latest/audio_processor_standalone_midi/&quot;&gt;audio-processor-standalone-midi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These higher-level abstractions allow the looper engine (&lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt;) to work
drop-in in a hosted scenario, such as within a VST. They currently
use traits for &lt;code class=&quot;language-text&quot;&gt;AudioBuffer&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;MidiMessageLike&lt;/code&gt; so that buffers and messages
could be compatible with both rust and VST C API representations.&lt;/p&gt;
&lt;p&gt;The ‘application code’ uses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/blob/master/crates/augmented/audio/audio-processor-traits/src/lib.rs&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/blob/master/crates/augmented/audio/audio-processor-traits/src/audio_buffer.rs&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;AudioBuffer&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/blob/master/crates/augmented/audio/audio-processor-traits/src/midi.rs&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;MidiEventHandler&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then, the crates in &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/application&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;crates/augmented/application&lt;/code&gt;&lt;/a&gt;
provide standalone and hosted helpers to run our processors as an online CLI,
offline file processor or VST plugin.&lt;/p&gt;
&lt;p&gt;The gist is audio-buffers and MIDI messages are abstract and so are audio
processing nodes. However, I now have the impression converting into a common
type before doing audio processing could have been a better option than making
all implementations support abstract types.&lt;/p&gt;
&lt;h3 id=&quot;looperengineswift-integration---rust--swift-integration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#looperengineswift-integration---rust--swift-integration&quot; aria-label=&quot;looperengineswift integration   rust  swift integration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LooperEngine/Swift integration - Rust &amp;#x26; Swift integration&lt;/h3&gt;
&lt;p&gt;Most of the &lt;strong&gt;Continuous Looper&lt;/strong&gt; integration issues are in the bridge between &lt;strong&gt;Continuous macOS/iOS App&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;
and &lt;strong&gt;LooperEngine&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Communication is done through a C bridge. C API bindings are generated with
&lt;code class=&quot;language-text&quot;&gt;cbindgen&lt;/code&gt;. Universal binaries are built with &lt;code class=&quot;language-text&quot;&gt;cargo-lipo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The bridge is a good candidate for review, because it uses too many strategies
at the same time.&lt;/p&gt;
&lt;p&gt;Data is exchanged between Rust/Swift in 4 ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Polling&lt;/strong&gt;: GUI &lt;strong&gt;poll&lt;/strong&gt;/queries for state from the audio engine
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;for example: play-head state or shared read-only references to audio buffers&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Push from GUI&lt;/strong&gt;: GUI dispatches changes to some object
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;for example: a float/int/binary/enum parameter&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dispatch from GUI&lt;/strong&gt;: GUI dispatches a command
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;for example: “start recording” when the record button is clicked&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dispatch from engine&lt;/strong&gt;: LooperEngine dispatches an event
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;for example: MIDI/IO events happened&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At the moment, most data is copied in both Swift and Rust sides. These two must
sync and things would be a lot simpler if this wasn’t the case.&lt;/p&gt;
&lt;p&gt;I believe this will start to be a bigger problem when persistence is added,
which allow certain more complex structures to be changed from the Rust side.
These changes will need to be pushed/queried somehow.&lt;/p&gt;
&lt;p&gt;Additionally, there’s shared memory in the case of audio-buffers, where Swift
just references the same audio-buffer used in the looper. This is fine thanks to
reference counting, some safer wrappers around the C API and because the looper
buffer will be pre-allocated to a maximum capacity at the start.&lt;/p&gt;
&lt;h4 id=&quot;polling-for-changes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#polling-for-changes&quot; aria-label=&quot;polling for changes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Polling for changes&lt;/h4&gt;
&lt;p&gt;For some data, the GUI poll the audio-thread every &lt;code class=&quot;language-text&quot;&gt;1/60&lt;/code&gt; seconds.
Though the data could be pushed from the rust side instead I think generally
the strategy of polling audio-thread state makes sense.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/05a3b2f9d97290248d0576012f581f23/playhead-demo.gif&quot; alt=&quot;Play-head demo video&quot;&gt;&lt;/p&gt;
&lt;p&gt;The play-head for example (our position within the song or loop measured in
samples/beats/seconds) updates at least every audio callback (1-10ms).
Since this may update at much higher frequency than our GUI and represents time
ticking, we can poll for it at the UI refresh rate we want.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 4.72972972972973%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAYAAADeko4lAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAN0lEQVR42mPQ0tT4LyLI+Z+BgeE/IyMjBmZmZv7PzcP1n4sLUw0TExNYTERE5L+UlNR/Xl7e/wBSkhWsi+tbDAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/bdcac8455511a223cb4dcbe0d072967e/f51c2/topbar.webp 148w,
/static/bdcac8455511a223cb4dcbe0d072967e/66888/topbar.webp 295w,
/static/bdcac8455511a223cb4dcbe0d072967e/bc8a3/topbar.webp 590w,
/static/bdcac8455511a223cb4dcbe0d072967e/4ce34/topbar.webp 885w,
/static/bdcac8455511a223cb4dcbe0d072967e/f490a/topbar.webp 1180w,
/static/bdcac8455511a223cb4dcbe0d072967e/d1d4a/topbar.webp 1478w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/bdcac8455511a223cb4dcbe0d072967e/c5084/topbar.png 148w,
/static/bdcac8455511a223cb4dcbe0d072967e/60cc9/topbar.png 295w,
/static/bdcac8455511a223cb4dcbe0d072967e/6d370/topbar.png 590w,
/static/bdcac8455511a223cb4dcbe0d072967e/540ae/topbar.png 885w,
/static/bdcac8455511a223cb4dcbe0d072967e/2561a/topbar.png 1180w,
/static/bdcac8455511a223cb4dcbe0d072967e/02a0d/topbar.png 1478w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/bdcac8455511a223cb4dcbe0d072967e/6d370/topbar.png&quot;
            alt=&quot;Top navigation bar screenshot&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Some data can be polled at lower frequencies, such as the CPU indicator on the
top-right (&lt;a href=&quot;#cpu-metering&quot;&gt;there’s a section about the CPU meter later&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;One downside of SwiftUI in this context is that since our data will change on
every frame, I imagine a lot of work performed by the library to optimise
performance by avoiding invalidating a view is wasted.&lt;/p&gt;
&lt;h4 id=&quot;push-from-gui&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#push-from-gui&quot; aria-label=&quot;push from gui permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Push from GUI&lt;/h4&gt;
&lt;p&gt;SwiftUI provides an &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; class, which allows views to update
automatically when some properties change. This is similar to
&lt;a href=&quot;https://mobx.js.org/README.html&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;mobx&lt;/code&gt;&lt;/a&gt; and really nice to use.&lt;/p&gt;
&lt;p&gt;The bridging code benefits by listening to changes to these objects and pushing
them into the rust engine.&lt;/p&gt;
&lt;p&gt;Pushing observable object changes out of the GUI into Rust could look something
like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StartParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableObject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@Published&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StartParameterView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// this view will update whenever StartParameter::value changes&lt;/span&gt;
    &lt;span class=&quot;token attribute atrule&quot;&gt;@ObservedObject&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; startParameter&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StartParameter&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&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 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;// Set-up a subscription to the value field and push it into rust&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// on changes&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;syncStart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parameter&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StartParameter&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 class-name&quot;&gt;AnyCancellable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; parameter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;receiveValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;looper_engine__set_start_parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&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;/div&gt;
&lt;p&gt;However, this is not super manageable for a larger nº of parameters, so the
solution used is moving towards a slightly more generic binding.&lt;/p&gt;
&lt;p&gt;This also stops working once there’s communication in the other direction
(Rust -&gt; Swift). The users may map MIDI controllers to parameters, so at any
point a MIDI event could be changing a parameter without the GUI knowing.&lt;/p&gt;
&lt;p&gt;We want the GUI to update when a MIDI controller’s knob is mapped to a parameter,
and it’s moved.&lt;/p&gt;
&lt;p&gt;At the moment this is addressed with polling the Rust looper state from the
GUI. There’s an &lt;code class=&quot;language-text&quot;&gt;EntityId&lt;/code&gt;, which all GUI controls and parameters have and all
entities* are pushed into and polled from the rust side.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;*all entities that are MIDI/LFO/Step/Scene mappable (knobs and some buttons)&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;swiftui-performance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#swiftui-performance&quot; aria-label=&quot;swiftui performance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SwiftUI Performance&lt;/h4&gt;
&lt;p&gt;I’ve found a number of bottlenecks related to the GUI and this set-up (but I
think it is just generally really hard to build this sort of GUI and be
performant whatever the technology).&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; GUI, though simpler and ‘inefficient’ (&lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; must re-layout and draw
everything if anything wants to update), consumed a lot less CPU. I’m afraid my
app might be too CPU intensive for some lower-end computers or iPads - this might
go into another post.&lt;/p&gt;
&lt;p&gt;In particular:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setting a &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; property will trigger updates even if the value
doesn’t change, this is a massive CPU sink, so we can’t do it
&lt;ul&gt;
&lt;li&gt;Instead, we read the property before setting it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reading the &lt;code class=&quot;language-text&quot;&gt;@Published&lt;/code&gt; property in an &lt;code class=&quot;language-text&quot;&gt;ObservableObject&lt;/code&gt; is super expensive
compared to reading a normal property
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/blob/master/crates/apps/looper/Sequencer/SequencerUI/Sources/SequencerUI/store/FastPublished.swift&quot;&gt;Instead, I am using &lt;code class=&quot;language-text&quot;&gt;@FastPublished&lt;/code&gt;, a custom property wrapper that copies the value to a normal field, so it’s faster to read&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Updating SwiftUI views at a high rate is a major performance bottleneck, so
the views that will update frequently (step sequencer buttons and knobs) use
AppKit instead of SwiftUI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have profiled my SwiftUI issues and found some accessibility code + forced
layout are the culprit. Setting a fixed size for the window improved things
partially, but using AppKit was the better escape hatch.&lt;/p&gt;
&lt;p&gt;There is still a lot of room to improve the GUI performance.&lt;/p&gt;
&lt;h4 id=&quot;callbacks-from-rust-to-swift&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#callbacks-from-rust-to-swift&quot; aria-label=&quot;callbacks from rust to swift permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Callbacks from rust to swift&lt;/h4&gt;
&lt;p&gt;Regarding how to call into Swift from Rust, Nick Wilcox has written great
documentation on how to achieve this here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nickwilcox.com/blog/recipe_swift_rust_callback/&quot;&gt;https://www.nickwilcox.com/blog/recipe_swift_rust_callback/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is pretty much the set-up used. You can check the source code &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/apps/looper/looper-processor/src/c_api&quot;&gt;here&lt;/a&gt;
and &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/apps/looper/Sequencer/SequencerEngine/foreign_callbacks&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;swift-frameworks-packages-and-apps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#swift-frameworks-packages-and-apps&quot; aria-label=&quot;swift frameworks packages and apps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Swift frameworks, packages and apps&lt;/h3&gt;
&lt;p&gt;The following is a diagram of some views / components in the Swift GUI side.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.810810810810814%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABsElEQVR42qVS20ojQRT0k10QRUHwL3zzxV3xFok+iQ8iIsYriqIiumaNidHESTLJZLqnb+U5PTGXNYvCNhympudMddWpHgEt11ffXf/qH/Ef3WDZTmcttlg5a+KkILskXbK+viGE7tPpnrBtMX/YQC4v/Lv9u8+5rwmVcag0NVrSehVNYSCVTQmt9dIasaEe5XuHEvLJNv0Hv980JrJlbN9GCMjy1HoZ2YtWR2Has3AUYHq9iBc6uDuyoQrpqbRFQzgI5aBNiiNpBlxEiUWdVCpN+x9K+gnzgcLsziuuy8LPbW6vgotSTLYd5nNvOMi3B2a4e9fCr8OAiB1qkcJk5hE/c5UeYbGhsXgakl1FiiyWCF+Vpf8hQymfFkTXGq/9hzbWzkO0yUVISmc2Clg6rvYIecilegJt2Z71ONFsm3AtgSQcE76vCIQUEvcVa+zGDA9l67qJ8cwTnknp8Z8YP5afcF9VuHlJMEr48lniMUgwtlpA7iGiMCwmsyVsXqVhGdtT7wn5itRp+JxiQleBg+CbokgAY97TnX1WzgpDGg0H90mhEAJciZSQVNoY/M96B4VdnP3t5FMWAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/63a5584460653d09a58d83a644febead/f51c2/swift-ui-components.webp 148w,
/static/63a5584460653d09a58d83a644febead/66888/swift-ui-components.webp 295w,
/static/63a5584460653d09a58d83a644febead/bc8a3/swift-ui-components.webp 590w,
/static/63a5584460653d09a58d83a644febead/4ce34/swift-ui-components.webp 885w,
/static/63a5584460653d09a58d83a644febead/f490a/swift-ui-components.webp 1180w,
/static/63a5584460653d09a58d83a644febead/3ac28/swift-ui-components.webp 4870w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/63a5584460653d09a58d83a644febead/c5084/swift-ui-components.png 148w,
/static/63a5584460653d09a58d83a644febead/60cc9/swift-ui-components.png 295w,
/static/63a5584460653d09a58d83a644febead/6d370/swift-ui-components.png 590w,
/static/63a5584460653d09a58d83a644febead/540ae/swift-ui-components.png 885w,
/static/63a5584460653d09a58d83a644febead/2561a/swift-ui-components.png 1180w,
/static/63a5584460653d09a58d83a644febead/d2538/swift-ui-components.png 4870w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/63a5584460653d09a58d83a644febead/6d370/swift-ui-components.png&quot;
            alt=&quot;SwiftUI component diagram&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Just some tiny things worth mentioning.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Store&lt;/code&gt;: We hold all state under a ‘store’ object which makes it a bit easier
to pass it around or mock it out.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;SwiftSequencerEngine&lt;/code&gt;: There’s no code calling into native Rust directly in the
GUI itself. There are multiple packages in the XCode project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the GUI exists in a cross-platform Swift package &lt;code class=&quot;language-text&quot;&gt;SequencerUI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;the rust library is linked against a separate &lt;code class=&quot;language-text&quot;&gt;framework&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The reason for this is to be able to support multiple platforms (iOS/macOS).
Also, having a simple &lt;code class=&quot;language-text&quot;&gt;SequencerUI&lt;/code&gt; swift package that doesn’t link to anything
fancy allows normal XCode tooling such as previews or Swift specific unit-tests
to work normally and faster.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;SequencerUI&lt;/code&gt; declares a &lt;code class=&quot;language-text&quot;&gt;SequencerEngine&lt;/code&gt; protocol/interface, which the
&lt;code class=&quot;language-text&quot;&gt;SequencerEngine.framework&lt;/code&gt; frameworks for macOS/iOS (which are exactly the same
source, but different build configurations) implement in &lt;code class=&quot;language-text&quot;&gt;SequencerEngineImpl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As mentioned, most data is copied from the engine into the &lt;code class=&quot;language-text&quot;&gt;Store&lt;/code&gt; objects,
except for audio buffers which are shared.&lt;/p&gt;
&lt;p&gt;For audio buffers, another protocol is declared in &lt;code class=&quot;language-text&quot;&gt;SequencerUI&lt;/code&gt; and the engine
implements this protocol for some C &lt;code class=&quot;language-text&quot;&gt;struct&lt;/code&gt; pointer wrappers, which are
essentially wrappers on top of reference counted &lt;code class=&quot;language-text&quot;&gt;AudioBuffer&lt;/code&gt; rust objects.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;SequencerUI&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;SequencerEngine.framework&lt;/code&gt; are then consumed by the
application projects themselves for iOS/macOS.&lt;/p&gt;
&lt;p&gt;I have not released the iOS app, because there are currently issues with certain
IO channel configurations; but the app runs (with Rust!) fine on iOS, and it
is going to be great.&lt;/p&gt;
&lt;h3 id=&quot;rust-components&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rust-components&quot; aria-label=&quot;rust components permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rust components&lt;/h3&gt;
&lt;p&gt;All the audio processing code I’m writing is following a “shared handle”
pattern:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 116.21621621621622%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAAsTAAALEwEAmpwYAAACZElEQVR42q1VW28SQRTmJxvFFrTVB+ODMdHoHyj6oI0xhaTYNKil8ZKYArVqvGIpSFnA7XKH3dmd2c8zU3dhy4I0cZJv58zZs9+cmXPZCM4xXHcaZ0cE/3kECAVtKWhXQQ8f3ppmm7vomQJ9CUsomXE3nFC6L8LOoDY6nT/WGG7vdHDvVQ/3X/dx62UHuYrpfx9CiLmEuV8MV9M6LqzlcPHBPpZTdbw9YgGbMSH+TbhXsRB/1sJq+jeupZtY2jTwpmTOIJzjoafu0b2VThyUW5zg4JDk9pDPuEMC94IwgbBrdQVXUJ4JdzbhLA+n81EQofAzY4rQ0x3RETLfRsj+ZMgWGTJfR6h3x0eqdRy8+MHU+52iheffLZQNezrKnmfZ4gixdBurm3V16VLer1o+oYzyUrKO5SeHiD0tIbrRoKBY00HxFrtFE/GtLq5slLGSqlJEO1OEMlWij77gskSyQWkTQui5+0ljWNvr4/H7oUKCZBlVbxR1Gw/zQ6wfWFj/YCKRG+Bznc1LbHfi4iniDoPjOKfvyAUpS507YcfnRZmLcbpI2bQsIrF9Y9tmSicCdudIG4tO2+zZ0PuOQqNrUzM4U0VibnMIllqegnAz08Kd3Q7uEm5sGzioBWuXiwW6jWfzrsIQT2m4lCggmsgjltRQOLYDNgtVik9YtnB9u01eGgorW20UqsE0Ca0Ub/RNKvaBrWZOjXPABI7bDjSqFgkpDyzhE0qymUdWUSQS5oi/TcJd4L/iBlJIEXrKMHDO4SjQJoRFmkdE13XUajU0m01omqbmRqMBXT9RaylLnWEYC3WiP1BO681Un/NoAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/df53782c0a9f200a401205bacd167ebb/f51c2/example-audio-processor.webp 148w,
/static/df53782c0a9f200a401205bacd167ebb/66888/example-audio-processor.webp 295w,
/static/df53782c0a9f200a401205bacd167ebb/bc8a3/example-audio-processor.webp 590w,
/static/df53782c0a9f200a401205bacd167ebb/4ce34/example-audio-processor.webp 885w,
/static/df53782c0a9f200a401205bacd167ebb/f490a/example-audio-processor.webp 1180w,
/static/df53782c0a9f200a401205bacd167ebb/a311b/example-audio-processor.webp 1858w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/df53782c0a9f200a401205bacd167ebb/c5084/example-audio-processor.png 148w,
/static/df53782c0a9f200a401205bacd167ebb/60cc9/example-audio-processor.png 295w,
/static/df53782c0a9f200a401205bacd167ebb/6d370/example-audio-processor.png 590w,
/static/df53782c0a9f200a401205bacd167ebb/540ae/example-audio-processor.png 885w,
/static/df53782c0a9f200a401205bacd167ebb/2561a/example-audio-processor.png 1180w,
/static/df53782c0a9f200a401205bacd167ebb/bf7a7/example-audio-processor.png 1858w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/df53782c0a9f200a401205bacd167ebb/6d370/example-audio-processor.png&quot;
            alt=&quot;Example audio processor diagram&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every audio processor is a struct with a “process buffer” method&lt;/li&gt;
&lt;li&gt;This method takes a mutable reference to the processor&lt;/li&gt;
&lt;li&gt;The audio-thread owns the processor&lt;/li&gt;
&lt;li&gt;Any state that must be changed from other threads is done via a handle
&lt;ul&gt;
&lt;li&gt;A handle is a reference counted shared thread-safe struct (using atomics)
that both the processor and GUI can read/write from&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In code, this looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;let&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MultiTrackLooper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&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; processor_handle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MultiTrackLooperHandle&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;
        processor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handle&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;clone&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;// send the processor to the audio-thread&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; _audio_handles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;audio_processor_start_with_midi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    processor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;audio_garbage_collector&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handle&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;// the handle can change things and dispatch events&lt;/span&gt;
processor_handle
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_source_parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LooperId&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 punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SourceParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Speed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.0&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;// open the GUI with the handle now&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This introduces indirection to access anything on the handle (which is all
processor state a lot of the time). I haven’t measured the performance impact in
a larger example such as the looper.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 113.51351351351352%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAAsTAAALEwEAmpwYAAACWUlEQVR42q1VTWsTURSd3+bGlRtx5U5Bse6KO7EqiLjQiljQhVBbUkGxLoTGYimJuNGFto01SdPEVmwmzdfMZGImnXkfc3zz8SYTmqRJ8ZLLvW9yOTnv3HszCmLmxvy0puA/WwRoORzlFkWlHcSuOHvGXRfi48cgH81fkd/nqwR3knXcWi5iZqWBfI2EgP1y8HEBs4cEM0kNt9+XcXNFQ67aDyjtRECZtI8YdgSrQp348a/NI2aDALnb8/hvKIhfxeXgpOvHYTzGZmh0GX5UCDLCt1SCljhjyAg51MWngonVbQPJjI6sagW1bkzDn0LDG+9UTC/lML1cQbZKj2koa7UOxYXnBZyby+PMoywefChH7Pu6fO9jE/eTKu6uar6OwwB1i+Lii12cf7aDs09ymF1Te4Cy2KZcFArvBtG7Vk/fmHMOQihqpgPVCNzs0iFN8XJOQzaDxSeEwLbtEXMYJrU2RaroIF2ysb5ri3PQlGaHIVVy8HmfYq1whMNWT4pBe69IjTKqg2tLe5h6uY2riX3RaSdslnie+IWp+QwuL5SwWbYHDvyxTSnWHTxOaXiarmNWxGIjYLKnUf/5XLqBh+tNMfjOaECpIRNiMyZcVFLGfPGllizMxx5sF/3r43Lmu8x5mE8MKGtV8feV+NrEm28GFr808Ucn0RxOBBg16MDClYUCri9mcWm+gO+/OxG7sXeZMheEBVqpJsPrTQtvtyy82rBwYLDJGHrWdRgsm/VdPb5ykt2JgF43pXsd9aLXZeafQ20neHkphmFA13XIaJqmn1uWdaqX1D8ByfEF5OrAWAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/5e7cb43cb9ba64114fefdca0f54986e8/f51c2/looper-engine-diagram.webp 148w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/66888/looper-engine-diagram.webp 295w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/bc8a3/looper-engine-diagram.webp 590w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/4ce34/looper-engine-diagram.webp 885w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/f490a/looper-engine-diagram.webp 1180w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/40086/looper-engine-diagram.webp 2516w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/5e7cb43cb9ba64114fefdca0f54986e8/c5084/looper-engine-diagram.png 148w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/60cc9/looper-engine-diagram.png 295w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/6d370/looper-engine-diagram.png 590w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/540ae/looper-engine-diagram.png 885w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/2561a/looper-engine-diagram.png 1180w,
/static/5e7cb43cb9ba64114fefdca0f54986e8/1a2d7/looper-engine-diagram.png 2516w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/5e7cb43cb9ba64114fefdca0f54986e8/6d370/looper-engine-diagram.png&quot;
            alt=&quot;Looper engine diagram&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That’s how the looper is structured: there is a &lt;code class=&quot;language-text&quot;&gt;LooperEngine&lt;/code&gt; struct created
by the Swift GUI that starts a couple of different background workers,
among which is the &lt;code class=&quot;language-text&quot;&gt;audio_processor_standalone&lt;/code&gt; instance.
&lt;code class=&quot;language-text&quot;&gt;audio_processor_standalone&lt;/code&gt; is the audio/MIDI threads.&lt;/p&gt;
&lt;p&gt;The audio/MIDI threads own the processor and reference shared state
the GUI can read and update (through special APIs).&lt;/p&gt;
&lt;p&gt;MIDI events might arrive in a different thread and are pushed via a lock-free
queue to the audio-thread. I think we could handle MIDI events out of the
audio-thread, but that’s how things are done right now.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;MultiTrackLooper&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;MultiTrackLooperHandle&lt;/code&gt; are where all the magic happens,
which leads us to…&lt;/p&gt;
&lt;h2 id=&quot;rust-and-audio&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rust-and-audio&quot; aria-label=&quot;rust and audio permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rust and audio&lt;/h2&gt;
&lt;h3 id=&quot;multi-track-looper-audio-graph&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#multi-track-looper-audio-graph&quot; aria-label=&quot;multi track looper audio graph permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Multi-track looper audio graph&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;MultiTrackLooper&lt;/code&gt; processing has the following topology:&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
  &lt;img alt=&quot;Multi-track looper audio graph&quot; style=&quot;max-height: 300px&quot; src=&quot;https://mermaid.ink/img/pako:eNptkkFrwyAYhv-KfOcm0BxzGBSade062q07bCQ9SDSLrNFgdDAk_30Gl2hCPYg-76s8oAZKQSik8CVxW6P3bcGRHRuz561WvduF8yaKHrL8KERLJVpfHUQW7vIzU2UddTWr1CJ7zDP-Q2_2zAw_5VlV0VJ1M_phdphxtO492puTVs7n32KgL6NGckW--7zwSIKrD94jxMfJI6SfziO57zFZnEaLOI5Dj_PCY4h9-upN5sHb5DLnF2dj4T0fWEFDZYMZsU9phkIBqqYNLSC1S4LldwEFH3q6JVjRjDAlJKQVvnV0BVgrcfnlJaRKajqWtgzbb9E42P8BFBij3g&quot;&gt;
&lt;/div&gt;
&lt;p&gt;Each track has, in series, a single-track looper, pitch-shifter, ADSR envelope,
effects (which are not in the published version of the app, as they have some
issues) and gain. This graph of processors is built using &lt;code class=&quot;language-text&quot;&gt;audio-processor-graph&lt;/code&gt;
which is a partial audio graph implementation in the repository.&lt;/p&gt;
&lt;p&gt;The looper is by far the largest processor in this chain, since a lot of the
other nodes are very simple. Currently, the “effects” node is a sub-graph.&lt;/p&gt;
&lt;h3 id=&quot;looper-states&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#looper-states&quot; aria-label=&quot;looper states permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Looper states&lt;/h3&gt;
&lt;p&gt;The looper can be in different states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Empty&lt;/li&gt;
&lt;li&gt;Recording&lt;/li&gt;
&lt;li&gt;Playing&lt;/li&gt;
&lt;li&gt;Paused&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Three additional states are used to represent overdubbing and the cases where
playing/recording are scheduled.&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
  &lt;img alt=&quot;Looper state diagram&quot; style=&quot;max-height: 300px&quot; src=&quot;https://mermaid.ink/img/pako:eNp9kT0PgjAQhv9Kc6OBxbGDk-xGRutwoScQaTH9MCGE_27lQxGCN7333Nu3uVwLWS0JOFiHjo4l5gZV_NwLzUJddlcWxweWqIdrBtTLHp4pq40sdT4MPm2aFSR9RZJ_GbMT3ExJfx1r_vfTfniqsNkaobdT9Ghb816vk8ZmtteUsNhqHbBYaknnVjYroSECRUZhKcNt2vdrAa4gRQJ4kBLNXYDQXfD5hwynS2TpagP8hpWlCNC7Om10BtwZT5NpvO_o6l6rmKb6&quot;&gt;
&lt;/div&gt;
&lt;p&gt;Each of these needs special treatment of both the actions (what transition to
trigger on interaction) and more importantly on how they render/handle the audio
callback.&lt;/p&gt;
&lt;h3 id=&quot;always-recording-buffer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#always-recording-buffer&quot; aria-label=&quot;always recording buffer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Always recording buffer&lt;/h3&gt;
&lt;p&gt;We want to support “snap closest” behaviour:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.432432432432435%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB9UlEQVR42pVS7W/ScBDu32uCiTo/+NXExc+6D0KiTGCWl1XJtFaECpswKLQsjLlMx/uLCgOhlLaDBh77+2E1TKPxlzy56909z13uyhiGgW63i8FggPF4jH6/j16vh+FwSO1iscD/PIaIRaNRsCwLURQRi8XAcRwEQQDP85jNZrSQCBMsl8u/C+q6jkajgXa7jVqtRi1pUq/X0Ww20Wq1qE9qiO10OpjP55RMxK82YEhXXTegqiqFpmmYTCYUo9EI0+mUgvhOzpn6T9My1fo5PpyerHW8CucR39kpEd4X4/j08WxNnKl8laHqFw7ln0t3iJphopzYw+VktMZlKt0SeGEP8bcJeohUKoVisQhZllEoFJDL5aAoCgXxSUwhOaWIeGQHCYHHUalEd2tZFpjSeQbXb1zDxq3bcLlcuLe5CTYYxFOfD0+8Xjxyu+Hz++EPBOD2eODd3obPzoW45/Dcv4s7Gzfx2K6TJAmmaYJp9yoIRoLY5V4gGArjFf8a2ZyEfF6iE6XTaXuqPDKHWbxLpnDwPm3HMpBkBQdvXiLKRXCYzaJh/wHk+szlxWdoyTCm+7s/8S3BQg48QHFnC0fPtqAEHtKYlgpjkgxRqCILs9dyFvvrytZUhVE9hlkr/8CJ/V3Gl+P8CuWV1aurnAPCsTT1t2N+B8oXGPrDvIsMAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/1c99ae9211b522df4aee9a3a4d3093de/f51c2/snap-closest.webp 148w,
/static/1c99ae9211b522df4aee9a3a4d3093de/66888/snap-closest.webp 295w,
/static/1c99ae9211b522df4aee9a3a4d3093de/bc8a3/snap-closest.webp 590w,
/static/1c99ae9211b522df4aee9a3a4d3093de/4ce34/snap-closest.webp 885w,
/static/1c99ae9211b522df4aee9a3a4d3093de/f490a/snap-closest.webp 1180w,
/static/1c99ae9211b522df4aee9a3a4d3093de/08048/snap-closest.webp 1400w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/1c99ae9211b522df4aee9a3a4d3093de/c5084/snap-closest.png 148w,
/static/1c99ae9211b522df4aee9a3a4d3093de/60cc9/snap-closest.png 295w,
/static/1c99ae9211b522df4aee9a3a4d3093de/6d370/snap-closest.png 590w,
/static/1c99ae9211b522df4aee9a3a4d3093de/540ae/snap-closest.png 885w,
/static/1c99ae9211b522df4aee9a3a4d3093de/2561a/snap-closest.png 1180w,
/static/1c99ae9211b522df4aee9a3a4d3093de/3643c/snap-closest.png 1400w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/1c99ae9211b522df4aee9a3a4d3093de/6d370/snap-closest.png&quot;
            alt=&quot;Snap closest quantization diagram&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And, in fact, we also want to support this when &lt;strong&gt;starting&lt;/strong&gt; to record/play.&lt;/p&gt;
&lt;p&gt;In this case, the looper will receive a start recording command after it was
supposed to have started.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.1891891891892%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAACOElEQVR42q2TS28SURiG+a0u3Ltw5cqF0cTUNtouNDRqIq1oaQSKsTQIBmyhtEogw3Ab6HAvA7TDgFwG+jgzhhZiV8YveXPyncw8533PxcYtdXV1xWw241/Kxn+uJeB4PKJQKLD70UM8fkK/r1nz0+nU0ty5OZr9eDy+np/LthhP62kIYgrX4RoFKUc6naZcLltjqVRCkiQEQSCfz5NKpQgEAmQyGbLZrNX3er1lh/1+n0atTjj9gYvLLoV8wfpBFEULYsKSyeR1X6vVkGXZSmUubEWOx+Msyu3f45XzCT7fHjs7O7jdbvx+P+FwmEQiQTQatYCmK9PAZDJZOkCb0+nE1Pb2Nl6Ph8cvn3H/0R3W1lax2+04HA5cLhehUMiCFItFqtUq7XabVquFqqoW1Nw6C+j1ejHlMWAHhpOV1y948PQuGxvrbBrArTkwGCRrxJeseDLNZtOCapqGrus3Dk2QKTPa/pd9Vt6s83D1HvZNO+8cWwZslz2fj0gkQi6XQzIclisVzs9bdNqdv4HmKnMpisJR5ph04oTGmcR55QylXqHTrHOhNFE7CgP1ktFAYzwcoBvXbKobcRf38OZ1/JmUhANGn54zOXjLJPQe/dCDfrLPNPmNWTaGKsQo/zyiWxRRa2cMugrT0fAGOL+Q85MSpRiloJNa9DPVuJ/qaYBGKkJTjBmQFMNGGbUu01caDNUuk199ZouRF9+vWRlVJigeEpN+EM4e81UI890Yg0KEUkte+va2+g1ib7QRle2CUAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/ec9a9e27ea4858da5fecc78307304d22/f51c2/snap-next-2.webp 148w,
/static/ec9a9e27ea4858da5fecc78307304d22/66888/snap-next-2.webp 295w,
/static/ec9a9e27ea4858da5fecc78307304d22/bc8a3/snap-next-2.webp 590w,
/static/ec9a9e27ea4858da5fecc78307304d22/4ce34/snap-next-2.webp 885w,
/static/ec9a9e27ea4858da5fecc78307304d22/9b20f/snap-next-2.webp 904w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/ec9a9e27ea4858da5fecc78307304d22/c5084/snap-next-2.png 148w,
/static/ec9a9e27ea4858da5fecc78307304d22/60cc9/snap-next-2.png 295w,
/static/ec9a9e27ea4858da5fecc78307304d22/6d370/snap-next-2.png 590w,
/static/ec9a9e27ea4858da5fecc78307304d22/540ae/snap-next-2.png 885w,
/static/ec9a9e27ea4858da5fecc78307304d22/91af2/snap-next-2.png 904w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/ec9a9e27ea4858da5fecc78307304d22/6d370/snap-next-2.png&quot;
            alt=&quot;Snap next quantization diagram&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For this, we need to have an always recording buffer (the &lt;code class=&quot;language-text&quot;&gt;ScratchPad&lt;/code&gt;). The
scratch-pad will be a circular buffer for the last few seconds which will always
be recording.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/768c123ef6683189bd84ed51423630df/scratch-pad4.gif&quot; alt=&quot;scratch-pad animated diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then, when recording is triggered, we can either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;store what our “write cursor” position within the scratch-pad is&lt;/li&gt;
&lt;li&gt;or copy the last few seconds into another buffer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Currently, &lt;strong&gt;Continuous Looper&lt;/strong&gt; has a pretty small 10s limit on loop length, so it
uses the scratch-pad to record and copies to another buffer only when the
recording is ended.&lt;/p&gt;
&lt;h3 id=&quot;quantization-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#quantization-implementation&quot; aria-label=&quot;quantization implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Quantization implementation&lt;/h3&gt;
&lt;h4 id=&quot;keeping-track-of-time-play-head&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keeping-track-of-time-play-head&quot; aria-label=&quot;keeping track of time play head permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Keeping track of time: Play-head&lt;/h4&gt;
&lt;p&gt;I didn’t know of &lt;a href=&quot;https://www.youtube.com/watch?v=kwnjF4U8_I0&amp;#x26;t=1893s&quot;&gt;Vlad Voina’s “Loopers and bloopers” talk&lt;/a&gt;
when I implemented this, but gives some ideas of things I could be fixing next.&lt;/p&gt;
&lt;p&gt;In order to quantize we first need to be able to keep track of the play-head
position, as represented in previous tempo diagrams.&lt;/p&gt;
&lt;p&gt;The play-head is our position within the song. This should be a value we
can convert into/from samples, beats and seconds and that represents the
timestamp within the song of the play-head (the playback).&lt;/p&gt;
&lt;p&gt;In &lt;code class=&quot;language-text&quot;&gt;augmented-audio&lt;/code&gt;, I’ve implemented a basic hosted &lt;code class=&quot;language-text&quot;&gt;PlayHead&lt;/code&gt; data structure
in &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/blob/843f78265a464a7f95ed052784994513f25accfd/crates/augmented/data/augmented-playhead/src/lib.rs&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented_playhead&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The gist is that we keep track of the current &lt;code class=&quot;language-text&quot;&gt;sample&lt;/code&gt; and then calculate our
position in seconds/beats based on the tempo and sample-rate:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;let&lt;/span&gt; elapsed_samples &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_elapsed_samples_from_somewhere_pass_it_in&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; elapsed_secs &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;1.0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; sample_rate&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;num_samples &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 keyword&quot;&gt;let&lt;/span&gt; beats_per_second &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tempo_in_bpm &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 operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60.0&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; elapsed_beats &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; beats_per_second &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; elapsed_secs&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All units can be converted to one another. One important thing to do is to use
double precision, because there will be serious inaccuracies if &lt;code class=&quot;language-text&quot;&gt;f32&lt;/code&gt; is used
after a certain song length.&lt;/p&gt;
&lt;p&gt;Vlad Voina’s talk linked above mentions the size of the &lt;code class=&quot;language-text&quot;&gt;elapsed_samples&lt;/code&gt; cursor,
as just incrementing it will eventually overflow. Generally it’s not an issue
for this use-case.&lt;/p&gt;
&lt;p&gt;In order to tempo-sync with a VST host, the &lt;strong&gt;Continuous Looper&lt;/strong&gt; engine is using a
&lt;code class=&quot;language-text&quot;&gt;TimeInfoProvider&lt;/code&gt; abstraction, which wraps both a ‘stand-alone’ play-head and a
‘hosted’ implementation. &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/blob/master/crates/apps/looper/looper-processor/src/audio/time_info_provider/mod.rs#L95-L103&quot;&gt;The source code is here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The idea is we will tick and control the tempo when running in an app, but when
running inside a VST we will call back into the host via the VST API to get the
current play-head. I will extract this out of the looper application for re-use
later.&lt;/p&gt;
&lt;h4 id=&quot;implementing-quantization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementing-quantization&quot; aria-label=&quot;implementing quantization permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementing quantization&lt;/h4&gt;
&lt;p&gt;Once we know the play-head position, quantization is some calculations, which
depend on the time-signature, our position in beats and (for snap closest) a
mistake correction threshold.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// This code is subject to the MIT LICENSE terms in https://github.com/yamadapc/augmented-audio/blob/master/crates/apps/looper/looper-processor/src/audio/loop_quantization.rs#L89-L114&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This returns a positive nº to the next 1 beat&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;snap_next_beat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;quantization_beats&lt;span class=&quot;token punctuation&quot;&gt;:&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; position_beats&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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;f32&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; f_beats &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; quantization_beats &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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;position_beats &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; f_beats&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;ceil&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; f_beats
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This returns either a positive or negative nº to the closest 1 beat&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;snap_closest_beat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    quantization_beats&lt;span class=&quot;token punctuation&quot;&gt;:&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;
    tempo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    threshold_ms&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    position_beats&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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; beats_per_ms &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tempo &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60_000.0&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; threshold_beats &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; beats_per_ms &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; threshold_ms&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; f_beats &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; quantization_beats &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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; ratio &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; position_beats &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; f_beats&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lower &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ratio&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&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; f_beats&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; upper &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ratio&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ceil&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; f_beats&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[allow(clippy::float_equality_without_abs)]&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;lower &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; position_beats&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;abs&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; threshold_beats&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;f32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EPSILON&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lower
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        upper
    &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;/div&gt;
&lt;p&gt;Since we have our scratch-pad, the looper can handle negative quantization
offsets. I imagine similar quantization logic would work for other applications.&lt;/p&gt;
&lt;h3 id=&quot;testing-audio&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#testing-audio&quot; aria-label=&quot;testing audio permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Testing audio&lt;/h3&gt;
&lt;p&gt;In order to test audio processors I am trying a couple of approaches.&lt;/p&gt;
&lt;p&gt;Again, I’m not an audio developer (and there’s a lot to fix in the repo!), but
maybe it can be useful.&lt;/p&gt;
&lt;h4 id=&quot;normal-unit-testing-with-mock-buffers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#normal-unit-testing-with-mock-buffers&quot; aria-label=&quot;normal unit testing with mock buffers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Normal unit-testing with mock buffers&lt;/h4&gt;
&lt;p&gt;For looper logic which just records and reproduces samples, we can unit-test
using small made-up (&lt;code class=&quot;language-text&quot;&gt;1,2,3&lt;/code&gt;) buffers and check that the output makes sense after
certain operations.&lt;/p&gt;
&lt;p&gt;This is regular unit-testing as on any domain.&lt;/p&gt;
&lt;h4 id=&quot;property-based-testing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#property-based-testing&quot; aria-label=&quot;property based testing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Property based testing&lt;/h4&gt;
&lt;p&gt;On some small pieces, I’ve experimented with tests such as “feed a sine-wave
into this DSP code and check that the output has the same RMS as the input”.&lt;/p&gt;
&lt;p&gt;I imagine with more analysis properties this would be more meaningful.&lt;/p&gt;
&lt;p&gt;I have drafted some analysis code on &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-analysis&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-analysis&lt;/code&gt;&lt;/a&gt;,
but there isn’t a lot of usage in tests, and I’d need to experiment more.&lt;/p&gt;
&lt;h4 id=&quot;visualization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#visualization&quot; aria-label=&quot;visualization permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Visualization&lt;/h4&gt;
&lt;p&gt;This isn’t a proper test, but very often, it’s helpful to visualise what the
signal is doing, so I used &lt;code class=&quot;language-text&quot;&gt;plotters&lt;/code&gt; and sometimes &lt;code class=&quot;language-text&quot;&gt;piet&lt;/code&gt; to generate charts.&lt;/p&gt;
&lt;p&gt;The analysis crate above is a good example of doing this, but there are several
in the repository.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-testing-helpers&lt;/code&gt; exposes a function to make it dead simple to
plot a vector of floats and have a look at the signal:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// this macro returns a relative path to the CARGO_MANIFEST_DIR&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;relative_path!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/lib.rs&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;// will create `src/lib.rs--chart_name.png&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;draw_vec_chart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;chart_name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; my_vector&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;/div&gt;
&lt;h4 id=&quot;snapshot-testing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#snapshot-testing&quot; aria-label=&quot;snapshot testing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Snapshot testing&lt;/h4&gt;
&lt;p&gt;Since we get free CLIs from our abstraction code, the repository has a
couple of snapshot tests which run in CI and are documented in &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/blob/master/docs/monorepo-tooling/SNAPSHOT_TESTING.md&quot;&gt;SNAPSHOT_TESTING.md&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is just rendering some audio through each effect and checking hashes match
what had been committed.&lt;/p&gt;
&lt;p&gt;Maybe comparing properties or visualisations here would be more meaningful and
less brittle.&lt;/p&gt;
&lt;p&gt;However, this approach and the others increase my level of confidence I’m not
breaking things when making changes.&lt;/p&gt;
&lt;h3 id=&quot;testing-allocations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#testing-allocations&quot; aria-label=&quot;testing allocations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Testing allocations&lt;/h3&gt;
&lt;p&gt;All audio-thread code is trying to avoid (de)allocations, which increases
complexity quite a lot. This is very hard to avoid in the Rust ecosystem with
many 3rd party libraries that provide lock-free or immutable data-structures
allocating on reads.&lt;/p&gt;
&lt;p&gt;In order to unit-test that allocations aren’t being made the &lt;a href=&quot;https://docs.rs/assert_no_alloc&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;assert_no_alloc&lt;/code&gt; crate&lt;/a&gt;
is of great help. It replaces the global allocator so that sections of the source
can disable allocations (causing the program to print a warning or crash).&lt;/p&gt;
&lt;p&gt;On unit-tests this can be used to test sections are allocation free.&lt;/p&gt;
&lt;h3 id=&quot;sequencer-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sequencer-implementation&quot; aria-label=&quot;sequencer implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Sequencer implementation&lt;/h3&gt;
&lt;p&gt;My sequencer implementation is based on detecting when the play-head has
changed from one beat to another. A “step-tracker” struct keeps track of the
last position in beats. When a new position is received, it checks if there was
a trigger.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;let&lt;/span&gt; prev_position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;- prev beat is 0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; curr_position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;- curr beat is 1&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// trigger beat 1.0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The active steps, along with parameter lock information are kept on a shared
vector pointer.&lt;/p&gt;
&lt;p&gt;When the GUI wants to add a step, it’ll copy the vector, make changes and then
atomically swap the old pointer with the new. This is done using
&lt;code class=&quot;language-text&quot;&gt;basedrop::SharedCell&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When both threads are done with the old data it’ll be released on a background
thread.&lt;/p&gt;
&lt;p&gt;I wanted to use immutable data-structures for this case, but there should be
negligible gain at this point because the sequence can only be 16 steps.&lt;/p&gt;
&lt;h3 id=&quot;parameter-storage---lfos-parameter-locks-sequencing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#parameter-storage---lfos-parameter-locks-sequencing&quot; aria-label=&quot;parameter storage   lfos parameter locks sequencing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Parameter storage - LFOs, parameter locks, sequencing&lt;/h3&gt;
&lt;p&gt;Parameters, LFO mapping and parameter locks work in a similar way.&lt;/p&gt;
&lt;p&gt;There’s a “ParametersMap” struct, which is an array of all parameters and their
values, combined with a HashMap that locates a typed (enum) parameter ID into
its position.&lt;/p&gt;
&lt;p&gt;These positions and array are pre-calculated because we know all possible
parameters ahead of time. This likely won’t work moving forward.&lt;/p&gt;
&lt;p&gt;For each step and scene there’s a similar map. The steps only hold parameters
that have been locked.&lt;/p&gt;
&lt;p&gt;On each audio callback we apply the parameter locks, scenes and so on.&lt;/p&gt;
&lt;p&gt;We have a scratch buffer of parameters, onto which we load ‘user parameters’ the
user has set through the GUI. Next, we override these with the scenes (performing
linear interpolation according to the slider position). Third, we apply parameter
locks if there’s an active step with them. Forth, we apply LFO modulation.&lt;/p&gt;
&lt;p&gt;Finally, all parameters are “flushed” from the scratch parameters buffer to
their corresponding handles (currently this is just the looper handle).
Once there are effects and more mappable processors they would be flushed to
each of these.&lt;/p&gt;
&lt;p&gt;This scratch stage is necessary because scenes, locks and modulation introduce
a mismatch between what the user has set on the GUI (the ‘user value’) and what
the value should be at &lt;em&gt;this current point in time&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I think this has room for improvement because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The current approach uses a &lt;code class=&quot;language-text&quot;&gt;HashMap&lt;/code&gt; for a pre-determined set of items
&lt;ul&gt;
&lt;li&gt;We could be using just an index and a Vec&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We have to manually flush parameters from this storage into the handles,
which centralises a lot of mapping code in the main &lt;code class=&quot;language-text&quot;&gt;MultiTrackLooper&lt;/code&gt;
processor&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Thank you for reading my post!&lt;/p&gt;
&lt;p&gt;I really hope you got something out of it.&lt;/p&gt;
&lt;p&gt;I will continue to develop &lt;strong&gt;Continuous Looper&lt;/strong&gt; and libraries in &lt;code class=&quot;language-text&quot;&gt;augmented-audio&lt;/code&gt; with
goal #1 being to learn and have fun. (&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/apps/looper/Sequencer&quot;&gt;augmented-audio/crates/apps/looper/Sequencer&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;If you have feedback, please reach out, and I will be super happy to chat.&lt;/p&gt;
&lt;p&gt;All the best, &lt;a href=&quot;https://twitter.com/yamadapc&quot;&gt;@yamadapc&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/rust/comments/u1xhvc/continuous_looper_deep_dive_into_an_opensource/&quot;&gt;Reddit post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;other-links&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#other-links&quot; aria-label=&quot;other links permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Other links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/RustAudio/&quot;&gt;RustAudio&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/RustAudio/cpal&quot;&gt;cpal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.researchgate.net/publication/220723752_A_Transient_Detection_Algorithm_for_Audio_Using_Iterative_Analysis_of_STFT&quot;&gt;A Transient Detection Algorithm for Audio Using Iterative Analysis of STFT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Audio-Effects-Theory-Implementation-Application/dp/1466560282&quot;&gt;Audio Effects: Theory, Implementation and Application 1st Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aVLRUyPBBJk&quot;&gt;Real-time Audio Programming with Bela&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=kwnjF4U8_I0&amp;#x26;t=1893s&quot;&gt;Vlad Voina - Loopers and bloopers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Augmented Audio - Generic AudioProcessors in Rust]]></title><description><![CDATA[This is a February update on Augmented Audio Libraries,
my hobby libraries for audio programming in Rust. Note: Most of this is unpublished…]]></description><link>https://beijaflor.io/rust-audio-experiments-4/</link><guid isPermaLink="false">https://beijaflor.io/rust-audio-experiments-4/</guid><pubDate>Thu, 24 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a February update on &lt;a href=&quot;https://github.com/yamadapc/augmented-audio&quot;&gt;&lt;strong&gt;Augmented Audio Libraries&lt;/strong&gt;&lt;/a&gt;,
my hobby libraries for audio programming in Rust.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Most of this is unpublished to &lt;code class=&quot;language-text&quot;&gt;crates.io&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is 3rd in a series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://beijaflor.io/blog/07-2021/rust-audio-experiments-2/&quot;&gt;Test Plugin Host: An early VST host to aid prototyping of plugins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beijaflor.io/blog/01-2022/rust-audio-experiments-3/&quot;&gt;Simple Metronome: Implementing a metronome with Rust and Flutter&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;overview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#overview&quot; aria-label=&quot;overview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Overview&lt;/h2&gt;
&lt;p&gt;In this post I’ll try to write about some ideas around generating CLIs and GUIs.&lt;/p&gt;
&lt;p&gt;I’ll share the &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-traits&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;AudioProcessorHandle&lt;/code&gt; traits&lt;/a&gt;
and some of &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/application/audio-processor-standalone&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio_processor_standalone&lt;/code&gt; usage&lt;/a&gt; in conjunction with these.&lt;/p&gt;
&lt;h2 id=&quot;audioprocessor-implementing-a-bitcrusher&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#audioprocessor-implementing-a-bitcrusher&quot; aria-label=&quot;audioprocessor implementing a bitcrusher permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AudioProcessor: Implementing a BitCrusher&lt;/h2&gt;
&lt;p&gt;We’d like to implement a bit-crusher, so some sample-rate reduction distortion
effect.&lt;/p&gt;
&lt;p&gt;I’m using an algorithm I found on the internet, which works by implementing
sample-and-hold of the input signal.&lt;/p&gt;
&lt;p&gt;The algorithm is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// src/lib.rs&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;process&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Given sample_rate as samples per second&lt;/span&gt;
  sample_rate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Given bit_rate as output samples per second&lt;/span&gt;
  bit_rate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Given buffer as the mono input/output buffer of samples&lt;/span&gt;
  buffer&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 punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;f32&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; buffer_size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; buffer&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;
  &lt;span class=&quot;token comment&quot;&gt;// We&apos;ll samples every `sample_rate / bit_rate` indexes&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 punctuation&quot;&gt;(&lt;/span&gt;sample_rate &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; bit_rate&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;usize&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; sample_index &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;

  &lt;span class=&quot;token comment&quot;&gt;// For each index in the buffer&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; sample_index &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; buffer_size &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Take the first sample&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; first_sample &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;sample_index&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; limit_index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sample_index &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 punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buffer_size&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;// Hold it for `sample_rate / bit_rate` steps&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; sample_index &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; limit_index &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; first_sample&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      sample_index &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 punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cool. We have our processor which is super simple, but it has a few minor
issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It only supports mono buffers&lt;/li&gt;
&lt;li&gt;Multichannel buffers would need to not be interleaved (where each
multichannel frame happens one after the other with &lt;code class=&quot;language-text&quot;&gt;[l1, r1, l2, r2, l3, r3]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;We can’t hear it&lt;/li&gt;
&lt;li&gt;We can’t play with the parameters on a GUI&lt;/li&gt;
&lt;li&gt;We can’t load it in a DAW and play with it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ll address each of these.&lt;/p&gt;
&lt;h3 id=&quot;implementing-code-classlanguage-textaudioprocessorcode---generic-buffers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementing-code-classlanguage-textaudioprocessorcode---generic-buffers&quot; aria-label=&quot;implementing code classlanguage textaudioprocessorcode   generic buffers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementing &lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; - Generic buffers&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-traits&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; trait&lt;/a&gt;
aims to provide a way to implement audio-processors
against a general interface.&lt;/p&gt;
&lt;p&gt;There are two methods to implement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;prepare&lt;/code&gt; - Here we’ll receive audio-settings like sample-rate and prepare our
processor for playback&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;process&lt;/code&gt; - Here we’ll receive an audio buffer and process it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First let’s make this into a &lt;code class=&quot;language-text&quot;&gt;struct&lt;/code&gt; and declare some imports we’ll need:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// src/lib.rs&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;audio_processor_traits&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;AudioProcessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessorSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioBuffer&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;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;BitCrusherProcessor&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 keyword&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  bit_rate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BitCrusherProcessor&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;default&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;-&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;
    &lt;span class=&quot;token keyword&quot;&gt;Self&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.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bit_rate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11025.0&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;/div&gt;
&lt;p&gt;We’ll store parameters in the struct, so they can be changed externally (this
will change slightly later). Also, we need imports for &lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt;,
&lt;code class=&quot;language-text&quot;&gt;AudioProcessorSettings&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;AudioBuffer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can now declare the &lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; impl:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;AudioProcessor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BitCrusherProcessor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;SampleType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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;prepare&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; settings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessorSettings&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;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sample_rate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sample_rate&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 keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BufferType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioBuffer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SampleType&lt;/span&gt; &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;&lt;span class=&quot;token class-name&quot;&gt;SampleType&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 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;
    data&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 class-name&quot;&gt;BufferType&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; buffer_size &lt;span class=&quot;token operator&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;num_samples&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; step_size &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 keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sample_rate &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;bit_rate&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;usize&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; sample_index &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;

    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; sample_index &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; buffer_size &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; first_index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sample_index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; limit_index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sample_index &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 punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buffer_size&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;while&lt;/span&gt; sample_index &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; limit_index &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 comment&quot;&gt;// **************************************************&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// **************************************************&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// HERE - This is the only real change&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// **************************************************&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; channel_index &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;num_channels&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; out &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&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;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;channel_index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; first_index&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;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;channel_index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sample_index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; out&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 comment&quot;&gt;// **************************************************&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// **************************************************&lt;/span&gt;

        sample_index &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 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;/div&gt;
&lt;p&gt;In our &lt;code class=&quot;language-text&quot;&gt;prepare&lt;/code&gt; method we use the &lt;code class=&quot;language-text&quot;&gt;sample_rate&lt;/code&gt; provided, which will match
whatever output device we’ll use this processor with.&lt;/p&gt;
&lt;p&gt;On &lt;code class=&quot;language-text&quot;&gt;process&lt;/code&gt; we receive an &lt;code class=&quot;language-text&quot;&gt;AudioBuffer&lt;/code&gt;. The &lt;code class=&quot;language-text&quot;&gt;AudioBuffer&lt;/code&gt; is an abstract
multichannel buffer, and it may be several types.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-traits&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-traits&lt;/code&gt; crate&lt;/a&gt;
provides implementations for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interleaved samples&lt;/li&gt;
&lt;li&gt;Non-interleaved&lt;/li&gt;
&lt;li&gt;Helpers for VST interop&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally, the &lt;code class=&quot;language-text&quot;&gt;SampleType = f32&lt;/code&gt; is declared as part of the processor
specification. If we wanted we could declare both &lt;code class=&quot;language-text&quot;&gt;f32&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;f64&lt;/code&gt; processors
with generics.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;AudioBuffer&lt;/code&gt; provides a handful of methods, here we use &lt;code class=&quot;language-text&quot;&gt;get&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt;,
&lt;code class=&quot;language-text&quot;&gt;num_samples&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;num_channels&lt;/code&gt; which should have clear names for what they do.&lt;/p&gt;
&lt;p&gt;I’ve found Rust can do better optimisations with iterators, so &lt;code class=&quot;language-text&quot;&gt;AudioBuffer&lt;/code&gt;
provides &lt;code class=&quot;language-text&quot;&gt;frames&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;frames_mut&lt;/code&gt; which is an iterator over the multichannel
frames and will work for different layouts at 0 cost in the cases where one
should benefit from these. They are the preferred method of processing, but in
this case things are simpler with &lt;code class=&quot;language-text&quot;&gt;get&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt; as we want to index into the
buffer.&lt;/p&gt;
&lt;p&gt;Processing code has only changed in an inner &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; loop;
we’ll do the sample-and-hold for each channel in the input buffer.&lt;/p&gt;
&lt;h3 id=&quot;listening-to-our-code-classlanguage-textaudioprocessorcode---generic-cli&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#listening-to-our-code-classlanguage-textaudioprocessorcode---generic-cli&quot; aria-label=&quot;listening to our code classlanguage textaudioprocessorcode   generic cli permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Listening to our &lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; - Generic CLI&lt;/h3&gt;
&lt;p&gt;At this point, we can listen to our code. We’ll create an &lt;code class=&quot;language-text&quot;&gt;examples&lt;/code&gt; directory
and add &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/application/audio-processor-standalone&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio_processor_standalone&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Don’t run this without reading on&lt;/strong&gt; ⚠️&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// examples/bitcrusher.rs&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;bitcrusher&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BitCrusherProcessor&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 keyword&quot;&gt;let&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BitCrusherProcessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&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 namespace&quot;&gt;audio_processor_standalone&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;audio_processor_main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;processor&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;/div&gt;
&lt;p&gt;What &lt;code class=&quot;language-text&quot;&gt;audio_processor_standalone::audio_processor_main&lt;/code&gt; will do is run a little
CLI program with our processor.&lt;/p&gt;
&lt;p&gt;If I run this: &lt;code class=&quot;language-text&quot;&gt;cargo run --example bitcrusher -- --help&lt;/code&gt;, I’ll get the
following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-standalone

USAGE:
    bitcrusher [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -i, --input-file &amp;lt;INPUT_PATH&gt;      An input audio file to process
    -o, --output-file &amp;lt;OUTPUT_PATH&gt;    If specified, will render offline into this file (WAV)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;If I run it without options, it’ll run “online” processing on the system’s
default audio input/output (this may cause feedback issues)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We also get &lt;code class=&quot;language-text&quot;&gt;--input-file&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;--output-file&lt;/code&gt; which we can use to process
audio files offline.&lt;/p&gt;
&lt;p&gt;These are two independent methods of running the processor:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPAL online rendering
&lt;ul&gt;
&lt;li&gt;We can run the processor on default input/output &amp;#x26; plug in a guitar if we
want&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CLI offline rendering
&lt;ul&gt;
&lt;li&gt;We can generate output from input files and run analysis or tests over it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;listening-to-our-code-classlanguage-textaudioprocessorcode---generic-vst&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#listening-to-our-code-classlanguage-textaudioprocessorcode---generic-vst&quot; aria-label=&quot;listening to our code classlanguage textaudioprocessorcode   generic vst permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Listening to our &lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; - Generic VST&lt;/h3&gt;
&lt;p&gt;If you wanted to just plug this into a DAW now you can do it.&lt;/p&gt;
&lt;p&gt;Add another &lt;code class=&quot;language-text&quot;&gt;examples/bitcrusher_vst.rs&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// examples/bitcrusher_vst.rs&lt;/span&gt;

&lt;span class=&quot;token namespace&quot;&gt;audio_processor_standalone&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token macro property&quot;&gt;standalone_vst!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BitCrusherProcessor&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;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;examples/bitcrusher_vst&lt;/code&gt; should be configured to compile as:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[[example]]
name = &quot;bitcrusher_vst&quot;
crate-type = [&quot;cdylib&quot;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the &lt;code class=&quot;language-text&quot;&gt;augmented-audio&lt;/code&gt; repository (this tooling isn’t installable otherwise)
we can generate a proper VST bundle for macOS (the &lt;code class=&quot;language-text&quot;&gt;Info.plist&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;.vst&lt;/code&gt;
directory structure) by marking this crate as having a VST example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[package.metadata.augmented]
vst_examples = [&quot;bitcrusher_vst&quot;]
private = true&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And then running &lt;code class=&quot;language-text&quot;&gt;dev.sh&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;./scripts/dev.sh build --crate ./path-to-bitcrusher&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will output a VST under &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;generic-gui&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#generic-gui&quot; aria-label=&quot;generic gui permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Generic GUI&lt;/h3&gt;
&lt;p&gt;Now we want a GUI for the processor, so we can twist a knob and change our
parameter. In order to do this, we’ll need to introduce an “audio processor handle”.&lt;/p&gt;
&lt;p&gt;The audio processor handle is a pattern I’ve introduced which consists of the
following idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our audio processor struct is a mutable stateful object owned by the
audio-thread&lt;/li&gt;
&lt;li&gt;If we want a GUI thread, the GUI thread will hold a “handle” for the processor&lt;/li&gt;
&lt;li&gt;The handle will be a shared reference counted struct that uses atomics for
thread-safety&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;extract-parameters-into-a-handle&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#extract-parameters-into-a-handle&quot; aria-label=&quot;extract parameters into a handle permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Extract parameters into a handle&lt;/h4&gt;
&lt;p&gt;So first we’ll extract the parameters from processor onto handle:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// src/lib.rs&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;audio_garbage_collector&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;Shared&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; make_shared&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;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;audio_processor_traits&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;AtomicF32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessorSettings&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;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;BitCrusherHandle&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 class-name&quot;&gt;AtomicF32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  bit_rate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AtomicF32&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;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandle&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;default&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;-&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;
    &lt;span class=&quot;token keyword&quot;&gt;Self&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 class-name&quot;&gt;AtomicF32&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 number&quot;&gt;44100.0&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;
      bit_rate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AtomicF32&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 number&quot;&gt;11025.00&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 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;BitCrusherProcessor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  handle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandle&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 keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BitCrusherProcessor&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;default&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;-&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;
    &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; handle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make_shared&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&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;/div&gt;
&lt;p&gt;There are 3 parts I want to clarity in this snippet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;audio_processor_traits::AtomicF32&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AtomicF32&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;AtomicF64&lt;/code&gt; are conveniently exported from
&lt;code class=&quot;language-text&quot;&gt;audio_processor_traits&lt;/code&gt;. On release builds these compile to the same code as
using normal floats, and they’re an unfortunate need to make rust compiler
happy with changing floats in multiple threads.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Shared&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Shared&lt;/code&gt; is re-exported from &lt;a href=&quot;https://github.com/glowcoil/basedrop&quot;&gt;basedrop&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;basedrop&lt;/code&gt; provides a couple of smart pointers that will not de-allocate on the
current thread. The idea here is that if we &lt;code class=&quot;language-text&quot;&gt;drop&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;BitCrusherProcessor&lt;/code&gt; on the
audio-thread (in the case of a dynamic processor graph) we’d not want any
de-allocations to happen on the audio-thread.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;basedrop&lt;/code&gt; will instead push the de-allocation onto a queue.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-garbage-collector&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio_garbage_collector&lt;/code&gt; is a wrapper on top of &lt;code class=&quot;language-text&quot;&gt;basedrop&lt;/code&gt; which provides a standard background thread for running the collection.&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;make_shared&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;make_shared&lt;/code&gt; will create a smart &lt;code class=&quot;language-text&quot;&gt;Shared&lt;/code&gt; pointer using the default global GC
thread. So
&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-garbage-collector&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio_garbage_collector&lt;/code&gt;&lt;/a&gt;
will start a global GC thread &amp;#x26; this
&lt;code class=&quot;language-text&quot;&gt;Shared&lt;/code&gt; is associated with it. With raw &lt;code class=&quot;language-text&quot;&gt;basedrop&lt;/code&gt; we’d have to set this up
manually on different places.&lt;/p&gt;
&lt;h4 id=&quot;implement-a-generic-handle&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implement-a-generic-handle&quot; aria-label=&quot;implement a generic handle permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implement a generic handle&lt;/h4&gt;
&lt;p&gt;I won’t go over the changes required to the &lt;code class=&quot;language-text&quot;&gt;process&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;prepare&lt;/code&gt; methods as
these changes are mechanical.&lt;/p&gt;
&lt;p&gt;In order to generate a GUI we need to have a way of introspecting into the
available parameters at runtime.&lt;/p&gt;
&lt;p&gt;This will be done by implementing &lt;code class=&quot;language-text&quot;&gt;AudioProcessorHandle&lt;/code&gt;. The way to do this is
to create a &lt;code class=&quot;language-text&quot;&gt;newtype&lt;/code&gt; around our smart-pointer:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// src/generic_handle.rs&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;audio_garbage_collector&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Shared&lt;/span&gt;&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;audio_processor_traits&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;parameters&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;AudioProcessorHandle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FloatType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParameterSpec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParameterType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParameterValue&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;use&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandle&lt;/span&gt;&lt;span class=&quot;token punctuation&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;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;BitCrusherHandleRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandle&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 keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandleRef&lt;/span&gt; &lt;span class=&quot;token punctuation&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;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inner&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandle&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;-&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;
    &lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandleRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inner&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;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessorHandle&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandleRef&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;parameter_count&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;self&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;usize&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 keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_parameter_spec&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;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _index&lt;span class=&quot;token punctuation&quot;&gt;:&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;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParameterSpec&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ParameterSpec&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 string&quot;&gt;&quot;Bit rate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;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 class-name&quot;&gt;ParameterType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FloatType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        range&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;100.0&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;&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 function&quot;&gt;sample_rate&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;
        step&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&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 keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_parameter&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;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _index&lt;span class=&quot;token punctuation&quot;&gt;:&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;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ParameterValue&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 class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ParameterValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Float&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      value&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;&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 function&quot;&gt;bit_rate&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 keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;set_parameter&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;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _index&lt;span class=&quot;token punctuation&quot;&gt;:&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; request&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParameterValue&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;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;ParameterValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Float&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request &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;&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 function&quot;&gt;set_bit_rate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&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;/div&gt;
&lt;p&gt;This is some basic boilerplate that can be generated at some point.&lt;/p&gt;
&lt;p&gt;Once we have this, by convention, we’ll declare a &lt;code class=&quot;language-text&quot;&gt;generic_handle&lt;/code&gt; method on
our processor:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// src/lib.rs&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BitCrusherProcessor&lt;/span&gt; &lt;span class=&quot;token punctuation&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;generic_handle&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;self&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;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessorHandle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;BitCrusherHandleRef&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 keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&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;/div&gt;
&lt;p&gt;And now we can add a GUI example&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;// examples/bitcrusher_gui.rs&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 keyword&quot;&gt;let&lt;/span&gt; handle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessorHandleRef&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arc&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;processor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generic_handle&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;// This actually starts the audio-thread and audio&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; _audio_handles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;audio_processor_standalone&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;audio_processor_start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;processor&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;// This opens the GUI&lt;/span&gt;
  &lt;span class=&quot;token namespace&quot;&gt;audio_processor_standalone&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;gui&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handle&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;/div&gt;
&lt;p&gt;The VST can also change:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;bitcrusher&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BitCrusherProcessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;audio_processor_standalone&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token macro property&quot;&gt;generic_standalone_vst!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BitCrusherProcessor&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;/div&gt;
&lt;p&gt;With all of this we should get the following on the screen and processing the
default input/output device audio:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 106.75675675675676%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAABYlAAAWJQFJUiTwAAABl0lEQVR42sWVy07CQBSGQdSkuMYNDZbLgnY6GCjQNpaLMZCGYMQo+gJeEtYu8el0SeR5jJTjOWPBiixqIXGSr5ec+f/+nXZmYrHvFo9Fb/FfN7zAZctoPJpVZxSk7XRG7eYXjt0erdbtqvNA2qXXcDhMCENm3J2Yp2DXmnO71gLCqjah1x3AoH8Dl+e30D3rg2k0wK9TvzlpjnnlnjyEV6vV2sXrfUVRnhjjwJj+rmnsY4Gqaj8I1qivrnMgLXrsCS8/YTKTyYwZY6BpmodASDxd10GW5TF6SMIrYPgc1ZC0/2tYLBZBVdXtGJKgXq9DpWKAX49uSKk452DbNliWJcw3TqgyHY6OTSjk8+te++8JS6USdNweuK4L5XJZjOfGH4VzXRhvPIbBpFv7ypH/Q5o+fpoZ4oVkRiH8qZdcTTimccIniqeGxCMNaZcJsdHhIJVKXWez2VdcOSbIGzINnKdr7ul6ksvlXkhLHsjOYoGVEAVpIhfIVUiob8PXSsFVW6TEdihJUhqRQ5ImTTDd6laQiMhyT/kEd0if6mWkWpQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/c5842598dfdde2e0aeb0363eafab22ac/f51c2/screenshot.webp 148w,
/static/c5842598dfdde2e0aeb0363eafab22ac/66888/screenshot.webp 295w,
/static/c5842598dfdde2e0aeb0363eafab22ac/bc8a3/screenshot.webp 590w,
/static/c5842598dfdde2e0aeb0363eafab22ac/4291a/screenshot.webp 824w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/c5842598dfdde2e0aeb0363eafab22ac/c5084/screenshot.png 148w,
/static/c5842598dfdde2e0aeb0363eafab22ac/60cc9/screenshot.png 295w,
/static/c5842598dfdde2e0aeb0363eafab22ac/6d370/screenshot.png 590w,
/static/c5842598dfdde2e0aeb0363eafab22ac/a0e79/screenshot.png 824w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/c5842598dfdde2e0aeb0363eafab22ac/6d370/screenshot.png&quot;
            alt=&quot;screenshot&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;⚠️ &lt;strong&gt;This runs online, control your volume &amp;#x26; so on, there may be mistakes above&lt;/strong&gt; ⚠️&lt;/p&gt;
&lt;h2 id=&quot;thats-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#thats-it&quot; aria-label=&quot;thats it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;That’s it&lt;/h2&gt;
&lt;p&gt;For the proper bitcrusher source-code see &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/08166ea31f80894dcce81d95baaed46ecd7c3782/crates/augmented/audio/audio-processor-bitcrusher&quot;&gt;crates/augmented/audio/audio-processor-bitcrusher&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;custom-gui&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-gui&quot; aria-label=&quot;custom gui permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom GUI&lt;/h2&gt;
&lt;p&gt;Custom GUI can be done easily as well and is done for other processors in the
repository.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/apps/looper&quot;&gt;See the looper for an example&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Its GUI looks like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.05405405405405%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB6ElEQVR42p2Sy27TQBSGU9SKBWp9iT2ecWynbXB8H5y4BEpVpIhKWbBBlbroggchgjdjwQtgwauAuhz9nLGTEm4bRvo04zlnPs0548GAxuXlSqyWq+Tq5VX8N65f3xLX8b/iyxfLRDsGm3GQJvJjOq2QT+W3bFrd7ZLG5V0tn3bo9e/xbCq/54nENC4+kWtfC40oitrJZAKXMeW6Ln6BMTiO0/FHrEexPucruQ57YRi2URiAe67i3KPZg2AuOBfgNAvOITy9z2iPd7M39mndfStBZ4Ig6IWGYZh0qDVPchhJoyw+wuH5GxjxDEPLhDmRsMLHsEQAKziF7TLYnNYRh3Wcwh46yvZD8CDcEQreWskcRnmhTEp6tHwLI1vApjKPZq86qXmc0d5z2J7o1tZJqC8AmwlljRPw6PSncCREy6kPzBkqRmW6owhiaFCZVJIf9KXrUkXfAo03FrpFunzlUS8D37/voZnneVvXNcqqUlUlIYlycYFyNseTswXmz87RNA2qqkKW5aD8e7I8V/VshjRNtfCoe5QkST5LKVEUhSrLEgUlViTUsnpLcwYd25VtUPosCb9sb/jQtu0bevYPNL8j1hpGCMYIZ8062Jpy1tv4DvrMe/p9brRLC/eIB5uf8uA/2d849n4AWgrh4pinQSgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/2ba68ebf7054c95f5faae2d401495d12/f51c2/looper-screenshot.webp 148w,
/static/2ba68ebf7054c95f5faae2d401495d12/66888/looper-screenshot.webp 295w,
/static/2ba68ebf7054c95f5faae2d401495d12/bc8a3/looper-screenshot.webp 590w,
/static/2ba68ebf7054c95f5faae2d401495d12/4ce34/looper-screenshot.webp 885w,
/static/2ba68ebf7054c95f5faae2d401495d12/f490a/looper-screenshot.webp 1180w,
/static/2ba68ebf7054c95f5faae2d401495d12/7ef45/looper-screenshot.webp 1624w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/2ba68ebf7054c95f5faae2d401495d12/c5084/looper-screenshot.png 148w,
/static/2ba68ebf7054c95f5faae2d401495d12/60cc9/looper-screenshot.png 295w,
/static/2ba68ebf7054c95f5faae2d401495d12/6d370/looper-screenshot.png 590w,
/static/2ba68ebf7054c95f5faae2d401495d12/540ae/looper-screenshot.png 885w,
/static/2ba68ebf7054c95f5faae2d401495d12/2561a/looper-screenshot.png 1180w,
/static/2ba68ebf7054c95f5faae2d401495d12/fc0cd/looper-screenshot.png 1624w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/2ba68ebf7054c95f5faae2d401495d12/6d370/looper-screenshot.png&quot;
            alt=&quot;looper screenshot&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;All the best&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Simple Metronome]]></title><description><![CDATA[On this post, I’ll discuss some work on the Augmented Audio Libraries
and particularly go over a “metronome” app written using Rust and…]]></description><link>https://beijaflor.io/rust-audio-experiments-3/</link><guid isPermaLink="false">https://beijaflor.io/rust-audio-experiments-3/</guid><pubDate>Tue, 11 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On this post, I’ll discuss some work on the &lt;a href=&quot;https://github.com/yamadapc/augmented-audio&quot;&gt;&lt;strong&gt;Augmented Audio Libraries&lt;/strong&gt;&lt;/a&gt;
and particularly go over a “metronome” app written using &lt;strong&gt;Rust and Flutter&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;goals-primer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#goals-primer&quot; aria-label=&quot;goals primer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Goals primer&lt;/h2&gt;
&lt;p&gt;Before anything else:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;These are hobby projects&lt;/li&gt;
&lt;li&gt;I’m not a professional &lt;em&gt;audio&lt;/em&gt; software developer
&lt;ul&gt;
&lt;li&gt;take information with a grain of salt&lt;/li&gt;
&lt;li&gt;any feedback is great&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;augmented-audio-libraries&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#augmented-audio-libraries&quot; aria-label=&quot;augmented audio libraries permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;em&gt;Augmented Audio Libraries&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;While I would like to go over a metronome, it’s built around the
&lt;a href=&quot;https://github.com/yamadapc/augmented-audio&quot;&gt;&lt;strong&gt;Augmented Audio Libraries&lt;/strong&gt;&lt;/a&gt;.
They are a set of building blocks for &lt;strong&gt;audio development in Rust&lt;/strong&gt;, building on
top of great tooling from the &lt;a href=&quot;https://github.com/RustAudio/&quot;&gt;&lt;strong&gt;RustAudio&lt;/strong&gt;&lt;/a&gt;
GitHub organization, such as &lt;a href=&quot;https://github.com/RustAudio/cpal&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cpal&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://github.com/RustAudio/vst-rs&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;vst-rs&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The initial driver for them was the &lt;strong&gt;Test Plugin Host&lt;/strong&gt;, a VST host for development
with support for hot-reload of in-development plugins. You can read about it here:
&lt;a href=&quot;https://beijaflor.io/blog/07-2021/rust-audio-experiments-2/&quot;&gt;“Test Plugin Host: An early VST plugin host to aid quick prototyping of audio plugins written in Rust”&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’ve done some explorations with web and native GUI which I walk over above and
was now trying out Flutter for the audio application space.&lt;/p&gt;
&lt;p&gt;It’s still missing a lot of real-world testing and features, but there are &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented&quot;&gt;quite
a few utilities which could deserve posts of their own&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audio Processors&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-traits&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-traits&lt;/code&gt;: Traits for declaring audio processors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/application/audio-processor-standalone&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-standalone&lt;/code&gt;: Stand-alone audio processor application generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-garbage-collector&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-garbage-collector&lt;/code&gt;: Background reference counting de-allocation for offloading alloc/de-alloc from the audio thread (wrapper on top of &lt;code class=&quot;language-text&quot;&gt;basedrop&lt;/code&gt;)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-utility&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-utility&lt;/code&gt;: Gain, pan, mono/stereo audio processors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-file&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-file&lt;/code&gt;: MP3 input file processor and WAV output file processor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-time&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-time&lt;/code&gt;: Delay effect audio processor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-analysis&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-analysis&lt;/code&gt;: RMS &amp;#x26; FFT analysis processors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/audio-processor-graph&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-graph&lt;/code&gt;: Audio graphs implementation for audio processor traits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Synthesizer building blocks&lt;/strong&gt;: Envelope, Filters &amp;#x26; Oscillators
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/dsp/dsp-filters&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-dsp-filters&lt;/code&gt;: RBJ filters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/oscillator&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-oscillator&lt;/code&gt;: Generic oscillator struct with a couple of wave forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/audio/adsr-envelope&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;adsr-envelope&lt;/code&gt;: ADSR envelope implementation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data structures&lt;/strong&gt;: Lock-free queue, MIDI, play-head
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/data/atomic-queue&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;atomic-queue&lt;/code&gt;: Multi-producer, multi-consumer bounded lock-free queue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/data/augmented-midi&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-midi&lt;/code&gt;: MIDI event/file parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/data/augmented-playhead&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-playhead&lt;/code&gt;: Audio PlayHead representation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of the repository is MIT licensed, but hasn’t been published into crates.io
yet as it’s alpha quality. There are quite a few interesting discussions to
tackle in terms of how to build GUIs or how to best represent audio processors
(and audio buffers) as abstract objects in Rust.&lt;/p&gt;
&lt;h2 id=&quot;the-metronome&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-metronome&quot; aria-label=&quot;the metronome permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Metronome&lt;/h2&gt;
&lt;div style=&quot;max-width: 150px; margin-left: auto; margin-right: auto; margin-bottom: 15px&quot;&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 195.94594594594594%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAnCAYAAAAPZ2gOAAAACXBIWXMAABYlAAAWJQFJUiTwAAAF20lEQVR42q2W23MTVRzHFxVffNFnxxkfdMbxf/CGPCjji844OmOp10FghBZBhgedAk4rBQrVVizV4gVLLWmhTSjQQtO0uTWFNA1J2pDUQm16wd5z2Vuy+/V3djdpehkEZWe+s+f8zu989vz2d/a3h3N2BOFzR+Fz3IS/J4wbPUMY6BmkdgghRxiDZAt1D2KI2kxh8gvbyWYL6WJjtkFE2nwI1zvAeW0B8DY30pGr6LvdhisT/QAP+H0BOHrc6Ov1wt8fQK/Tg/7rfq0fGBiEkBAhJCXwCQmpuAhRymDEegOc2zoAJb4ICHOQxDnwchyKnIbMC+AX45CSSUgpHnyc7skUROrLqRTSkgyFpLK7IIFd0U4fOH/XdVjHgNdMwJvNQIEFmEwC1yaBlihwPkKr/RtYoDmWYV2tpFkBCE0DzTfJJ6zCRT5DV73gwt19+MYFcPtUPHxQxUMkJz3gTAj42gkcdADnaFJ0Diglv/Je3TY0A7QReL8dKHOoOEn+gcvXCGjrwyFy4vYD6w8Dj5RDA54dAo6QvdwNmKM68KgHqLym28KzwOUR4BA9pMKt4lQYuHGpD1yw04PTRH+iEnjqBPBsLRBhzn8CP1B+vvcCtr+AKXoNtQPAj6QassfigCsGVNN4jVdByygQvOQB57zo0V5oSgYoaRDTWheKCqQVXazNrmw/o+h91fCR07ohdLGXgG19WHmpK/qKomjSCGtcqmIA29wEtHhyEHUN2L1cOeAFArpaPWs6KYaTKIq4desWYrGY1mb2TCazJjBodtEKzauBKoWmGuHJsoyxsTFEIhHE4/Hcw9S88HPAVicBTb3LXlzWcXx8HLOzsxAEAS6XCzMzM5rd7XYjGo0u880BW+hbtu/pNmJccuB5HkVFRTCZTOjo6EB1dTWqqqrQ2dmJiooKlJWVYWpqaikaAxg4bwfX82EX8rOShXo8HtTX12ttv9+PxsZG1NTUYGJiAhaLBXa7fSn87ArPdRPwE9syYDYZXq9Xg7BQi4uLkaSiwB7Awq+rq8PAwIAR7tIKg80E7P7YZgzoUoxdfPv2KAKBINrb21FaWgqr1Yrp6WlUVlaioaFh7aQ02cBd+WD5CtfcZ3mTV2Y4HxgwEfDi+zawXTVPRXWBSdA1z+58RgOwrZNOZ7T9xyLQvxw1p4ys78ugqQtcGwHpM8YkbbEppsSSJuclSJJEsHRub2YyyiqlJb0ABM8S0FJ495Dn5+cxMjKi7ctEIrHqFWh9o1oE/iBga2F3DpgNR9W3pXYJtCcZcHh4GHNzc3cFBhus4MxGyLF5BSkxo6WavbNshclPArvn27PKyEbIZ6z6Chl/gVexmEghSWExYPadZYuBnhAlZ8+XkjaS8jsBz+WFzCaxBNx3+cqG/CsBmwt7ctVX/S/FMB/4CwGbCu3/urHvGfhzJwE3P0DgKQKe3ex4cMCfroJrLHBqHPb1sGRl/2z3I1k2NnYtAc8UuHNJ+b8rDJygpDQU9KKdTgDl9AV+S9FXrtBxhz7GdMy+epzNOdKloIkY/u+o2px+z4MNdFrgtgMPfU73XXkqpuPJF8CTpXSyKAEe3avb8n3WsTnbFTx9nP43h+knVfWuGxtPknEbAZlTEWmnMZHa6/cAzx8DnjsKPLbPGC9e0jo2ZxsBK+hMVEpHkbJ3HPiNqvmWJhU7W1XsvqDiq3a9zbSDtLVZxaekz1p0W5FZxa4LQLFFb281KTjuA/rK6PRV8hbbh1QMVHZolDA8I+FKWNbaKnTbcrHCwWPhziidYqe1vpoRtYw6S+h8uP9tOzJqGkmenBURtqiMLy9TIUhLEMTVEunEmqAiMjgURmx8QrOlkiLh6NBZQifYvZu6yImHLAm4syjAFxMxOiMiMCEiRT95URRo0pJESUQimcDk5AQV3zltnKcjMxU92Hb3g3vjcTO2vmDGtg0mbHm5ER+92IgdL5lQsqEFBzeSXs2qNacD1D9A4+WvtKL29fOo22TG6U0dOPWMFf8A13cBjsYlxfAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source srcset=&quot;/static/f3db7b473a62bc871dff5172f935b9e0/f51c2/light.webp 148w,
/static/f3db7b473a62bc871dff5172f935b9e0/66888/light.webp 295w,
/static/f3db7b473a62bc871dff5172f935b9e0/bc8a3/light.webp 590w,
/static/f3db7b473a62bc871dff5172f935b9e0/828c8/light.webp 641w&quot; sizes=&quot;(max-width: 590px) 100vw, 590px&quot; type=&quot;image/webp&quot;&gt;
          &lt;source srcset=&quot;/static/f3db7b473a62bc871dff5172f935b9e0/c5084/light.png 148w,
/static/f3db7b473a62bc871dff5172f935b9e0/60cc9/light.png 295w,
/static/f3db7b473a62bc871dff5172f935b9e0/6d370/light.png 590w,
/static/f3db7b473a62bc871dff5172f935b9e0/243e6/light.png 641w&quot; sizes=&quot;(max-width: 590px) 100vw, 590px&quot; type=&quot;image/png&quot;&gt;
          &lt;img class=&quot;gatsby-resp-image-image&quot; src=&quot;/static/f3db7b473a62bc871dff5172f935b9e0/6d370/light.png&quot; alt=&quot;Simple Metronome&quot; title=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;&gt;
        &lt;/picture&gt;
    &lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Simple Metronome&lt;/strong&gt; is the app above and should be published soon. You can find
the entire source code at &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/apps/metronome&quot;&gt;the metronome subdirectory in augmented audio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;metronome&lt;/strong&gt; is a practice aid for musicians. To practice timing, a musician
will set a certain BPM on the metronome and start it. It should then produce
some kind of sound on every beat the musician can use as a reference. In
&lt;strong&gt;Simple Metronome&lt;/strong&gt; this sound is a synthesized beep.&lt;/p&gt;
&lt;p&gt;The app is written using Flutter and Rust. The GUI and business logic
(state/persistence) are written in Dart and drive the Rust audio back-end.&lt;/p&gt;
&lt;p&gt;The Rust side of things exposes methods using &lt;a href=&quot;https://github.com/fzyzcjy/flutter_rust_bridge&quot;&gt;flutter_rust_bridge&lt;/a&gt;,
a high-level codegen solution for Dart/Rust interop. A static library is
compiled from the Rust side, exposing functions to dart. The library is linked
onto the macOS and iOS apps (and other platforms would work similarly, but
haven’t been tested).&lt;/p&gt;
&lt;p&gt;Since this is a very simple audio app, only 3 pieces of data need to be
communicated between the GUI and Audio Thread:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tempo parameter (a float)&lt;/li&gt;
&lt;li&gt;current beat (a float)&lt;/li&gt;
&lt;li&gt;volume parameter (a float)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this, Rust exposes callbacks which set atomic values onto the audio
processor handle.&lt;/p&gt;
&lt;h2 id=&quot;playing-back-a-sine-wave-with-augmented&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#playing-back-a-sine-wave-with-augmented&quot; aria-label=&quot;playing back a sine wave with augmented permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Playing back a Sine Wave with Augmented&lt;/h2&gt;
&lt;p&gt;Let me show what a sine wave generator looks like using augmented.&lt;/p&gt;
&lt;p&gt;First, we declare our audio processor struct.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;augmented_oscillator&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Oscillator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Default)]&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;OscillatorProcessor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  oscillator&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Oscillator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;f32&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, implement the &lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; trait:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;audio_processor_traits&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;AudioProcessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessorSettings&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;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OscillatorProcessor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;SampleType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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;prepare&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; settings&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioProcessorSettings&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;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;oscillator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_sample_rate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sample_rate&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 keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BufferType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioBuffer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SampleType&lt;/span&gt; &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;&lt;span class=&quot;token class-name&quot;&gt;SampleType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&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;&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;
    buffer&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 class-name&quot;&gt;BufferType&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; frame &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frames_mut&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; out &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;oscillator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next_sample&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;for&lt;/span&gt; sample &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; frame &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; out&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;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; trait requires two methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;prepare&lt;/code&gt; will receive the audio settings such as sample rate, so the processor
can set up internal state&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;process&lt;/code&gt; will receive a mutable, generic audio buffer implementation, which
allows processing decoupled from sample layout&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The 2nd detail is up to discussion. I’ve found that some audio buffer
indirection may cause serious performance cost due to missed optimisations, and it
might not be worth to have it instead of simple sample slices.&lt;/p&gt;
&lt;p&gt;However, some kind of ‘process callback context’ is required, as the processor
needs to know the nº of input/output channels.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AudioBuffer::frames_mut&lt;/code&gt; returns an iterator of the channel chunks; each frame
is a sample pair (for stereo audio). We simply tick the oscillator struct and
set it as the output.&lt;/p&gt;
&lt;h3 id=&quot;main-function&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#main-function&quot; aria-label=&quot;main function permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Main function&lt;/h3&gt;
&lt;p&gt;Once this is set up, a command-line app can be generated with
&lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/master/crates/augmented/application/audio-processor-standalone&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio_processor_standalone&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;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 keyword&quot;&gt;let&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OscillatorProcessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&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 namespace&quot;&gt;audio_processor_standalone&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;audio_processor_main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;processor&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;/div&gt;
&lt;p&gt;The command-line supports both “online” (using your input/output devices) and
“offline” (into a file) rendering.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;audio-processor-standalone

USAGE:
    my-crate [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -i, --input-file &amp;lt;INPUT_PATH&gt;              An input audio file to process
        --midi-input-file &amp;lt;MIDI_INPUT_FILE&gt;    If specified, this MIDI file will be passed through the processor
    -o, --output-file &amp;lt;OUTPUT_PATH&gt;            If specified, will render offline into this file (WAV)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It can also read an input audio file and feed it into the processor as well as
read an input MIDI file.&lt;/p&gt;
&lt;p&gt;In order to process MIDI &lt;code class=&quot;language-text&quot;&gt;audio_processor_main_with_midi&lt;/code&gt; should be used and the
struct needs to implement the &lt;code class=&quot;language-text&quot;&gt;MidiEventHandler&lt;/code&gt; trait.&lt;/p&gt;
&lt;h3 id=&quot;embedding-into-an-app&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#embedding-into-an-app&quot; aria-label=&quot;embedding into an app permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Embedding into an app&lt;/h3&gt;
&lt;p&gt;On top of a command-line, &lt;code class=&quot;language-text&quot;&gt;audio_processor_standalone&lt;/code&gt; can also be used to
embed an audio-thread into another app. For this, the &lt;code class=&quot;language-text&quot;&gt;standalone_start&lt;/code&gt;
function is used. The library will return a set of “handles”, objects that
should be held onto (the audio thread will stop when they are dropped).&lt;/p&gt;
&lt;h2 id=&quot;audio-processor-handle-pattern&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#audio-processor-handle-pattern&quot; aria-label=&quot;audio processor handle pattern permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Audio Processor Handle Pattern&lt;/h2&gt;
&lt;p&gt;I found a little pattern for sharing state between GUI and audio thread, which
is the “audio processor handle” pattern.&lt;/p&gt;
&lt;p&gt;Each &lt;code class=&quot;language-text&quot;&gt;AudioProcessor&lt;/code&gt; declares its shared state as a handle, which is
thread-safe (uses atomics or other real-time safe synchronization primitives).
This handle uses a shared pointer which is reference counted &amp;#x26; de-allocated on a
background thread.&lt;/p&gt;
&lt;p&gt;The GUI thread will copy the handle &amp;#x26; keep a reference to it, while the audio
processor will be moved into the audio thread.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;audio_processor_traits&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AtomicF32&lt;/span&gt;&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;audio_garbage_collector&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;Shared&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; make_shared&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;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;MyProcessor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  handle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyProcessorHandle&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 keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;MyProcessorHandle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  volume&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AtomicF32&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;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;example&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; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyProcessor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    handle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make_shared&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyProcessorHandle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      volume&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AtomicF32&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 number&quot;&gt;0.0&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 keyword&quot;&gt;let&lt;/span&gt; handle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; processor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&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;// now the processor can be moved to the audio thread.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// the handle can be shared between many threads &amp;amp; must be thread safe&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Simple Metronome&lt;/strong&gt; will soon be available for free on macOS and iOS stores.&lt;/p&gt;
&lt;p&gt;Its source code and building blocks will continue to be available, and I intend
to write more simple/complex apps in order to stress them.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/yamadapc&quot;&gt;Please reach out with any feedback, questions or comments on Twitter - @yamadapc&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All the best.&lt;/p&gt;
&lt;div style=&quot;max-width: 150px; margin-left: auto; margin-right: auto; margin-bottom: 15px&quot;&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 195.2702702702703%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAnCAYAAAAPZ2gOAAAACXBIWXMAABYlAAAWJQFJUiTwAAAFxklEQVR42q2XWU9bRxTH5/r62uB9xQveMTY2ZrHBjjEEY8KSRlSFEEggZGmVEFRKItI1QYQ8tGmbkBC1SR+6oKZKE7WqKvVTpB8gfW4/BA+t1OrfM/casIGSquXhr5k5d+5vzjkzd3zM+p3fIef8HFnrZyiYH6JoeYDDzgcoWO+jy3IX3ZZVst+h/iqJWvNt5EndtnsoVKjX/j5K1rtgRd89XD3yCq5NDeL0xQHkzhxFX+4IxkdP4ez0BUyfPI/TZZ08cQbTk+cxOT6DYs8git0D5XYIfb0l9DW8Dpb3XkdPOITBhB35tAMNzU5EvEG0NTejsyVFakY6lUKmLYP2ZAKZVJLaJsQaGhELRxAL+klBRMNeZOqPg7Xoz8HVdhj1F5/AOf0Voq99DXdDCkzjglCXBXN0gBkCYIIEZm+jcQbMSTZJB6Zzg7kPQSAxSxhBXQ8B1ccRnroJ/WOA3fsdhkdAcPgCmG8IQn4JLPcuWHwCzOgD63wLrOMqhUV2WxzMX5L7QvYdAp1D0FgEa9eOwzd2EzWfEnBlA7r7gK//Ipi3BKFzESxzGaxxVAGm58Ha5gj8Jpg1Rov2Uv8qhPQCWPMUgqY+mlMzgYbx66hbB7S3/4D7CyDcf5ZC6YbQcVmBRI5ReHUEu0SaVRYxh8A8eepfgdBOm9E0jpDxMFjWfgkWhwP+zpfgSQ/D3zEEg9EIptISxElyKPliDKzGClZL41o75VQFJmrkscAXk9QI6QtgBc8byuT/IYGp5DZsIGCvj+IXyKgSt8THymShrJ1joWosMHEbWApc3nd1URThoJRwaTSafT2McOBA+Mqek1QqZRKHxONxZDIZ2Gw2BSAIsnYCGzhwMLy3hzqdThbvR6NRmEwmxYtIBF6vdwu8C9hlG6e8CVUTuFZXVzEzM4NCoYClpSXMz8+jvb0dN27ckMdOp3Pb20pgh/5oxSZsq1gsYnZ2dsurhYUFeczDHx4elrWZmk1gVAYajlUBN71MJBKYm5uT+ysrK3C5XJicnEQ2m8Xo6ChKpdJuoLESyEHc/fJmhOkm6aOXRkZGsL6+Lrc+nw/Ly8tYXFyEVqvdtSmNHJjUDZJX+x9ctVqN2tpauc9BkiTteWxkYLSGXKezxrSmammMUOss8uaMjY1tHSGeEh4mP58qlSJRVBaIKUC6IbjB4IZg9GyJ1ThgdEewsbGBtbU1+QVJ0pC30i5pJCX8RtOmh+WQVbS6qBKqUuDxeBAKheRjotfr9/1S4uZND4Xtr4Pnq3IyDzOZTMpnsPLs/SOwoaYIUeLfqIil5Zv45flzmM3mrc3gudr81DZzt1M87ApgL0QNz4EOi29fw8/PnsFI9yEH8N2Uc0RebudPvUsaSbk0mixdCpAJB3MfJi35CmA5rJ03yb8CCgqw2XrogDysAmoPHxiwhQMjBwm05TmQfu35haBS/2cJorLLrRwY5sAXeKgq60UetnFgSKK43a1gqVNgCSp2kuO7FS8rscez5HGo+LuhbmRsdA5DIhU+09+DrVEp8sGfYB9iW7cA8WPA/xCo+wSouaPYquf8BRW3v/crcu5BKmHEFggzP1ChVAH8aBsoEbDvKZD7BrCsKTZhJ/A2tdd+Q95DQLsYo6KIiqPTP4FNfAth4jGEsS/BJp+QnioaL2vi6bbtBD2fUKSa+hFs+BYKdjoxbn0H6mxG1FlqUWeSYPcEYE4eUcZluaykct9Jctv0SER8CNc7lOcWHSwWEQOeASqgDJ3wBYKkCPweFxWfwzCcekS/HwH4/CGyV6veH0QwHEW+qwctrWn4gxEE6F1XwIshL/0+efRZ+P318Pu88NFEd6oHnqYOuJM51FOp6/OTnZ7ztp7k9XkIGECqtRmxpkZ4/R7Z7gjYcdQzQiFLPXAZUnAYYnAYY7CZU6izZuC35hAypxExpRE1dSBm6kTclEWT6RCS5i76zLrRZu1FztJH/xgG0G97Ga+aF/E3RVwt0IbNbPQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source srcset=&quot;/static/efc50abe06f6b0fac9fbff00c8e11774/f51c2/dark.webp 148w,
/static/efc50abe06f6b0fac9fbff00c8e11774/66888/dark.webp 295w,
/static/efc50abe06f6b0fac9fbff00c8e11774/bc8a3/dark.webp 590w,
/static/efc50abe06f6b0fac9fbff00c8e11774/e3893/dark.webp 643w&quot; sizes=&quot;(max-width: 590px) 100vw, 590px&quot; type=&quot;image/webp&quot;&gt;
          &lt;source srcset=&quot;/static/efc50abe06f6b0fac9fbff00c8e11774/c5084/dark.png 148w,
/static/efc50abe06f6b0fac9fbff00c8e11774/60cc9/dark.png 295w,
/static/efc50abe06f6b0fac9fbff00c8e11774/6d370/dark.png 590w,
/static/efc50abe06f6b0fac9fbff00c8e11774/45cbc/dark.png 643w&quot; sizes=&quot;(max-width: 590px) 100vw, 590px&quot; type=&quot;image/png&quot;&gt;
          &lt;img class=&quot;gatsby-resp-image-image&quot; src=&quot;/static/efc50abe06f6b0fac9fbff00c8e11774/6d370/dark.png&quot; alt=&quot;Simple Metronome&quot; title=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;&gt;
        &lt;/picture&gt;
    &lt;/span&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Test Plugin Host]]></title><description><![CDATA[Over the last several weeks, I’ve been locked-down at home. A new cluster of the
Delta variant has resulted in a lockdown of Sydney, as well…]]></description><link>https://beijaflor.io/rust-audio-experiments-2/</link><guid isPermaLink="false">https://beijaflor.io/rust-audio-experiments-2/</guid><pubDate>Sat, 24 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the last several weeks, I’ve been locked-down at home. A new cluster of the
Delta variant has resulted in a lockdown of Sydney, as well as other parts of
Australia.&lt;/p&gt;
&lt;p&gt;With nothing else to do, I started exploring building audio apps with Rust.&lt;/p&gt;
&lt;p&gt;My main goal was to learn and have fun, but I had a specific product in mind
driving my exploration, which has now reached a minimally useful version.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio&quot;&gt;All the code I’m writing is MIT licensed and available on GitHub here.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It still needs a lot of polish and testing &amp;#x26; I’m just learning as I go, as I’m
not a professional audio developer.&lt;/p&gt;
&lt;p&gt;However, I want to share its existence with the world and also share some
thoughts from my first development attempts.&lt;/p&gt;
&lt;h2 id=&quot;plugin-host&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#plugin-host&quot; aria-label=&quot;plugin host permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Plugin Host&lt;/h2&gt;
&lt;div style=&quot;max-width: 150px; margin-left: auto; margin-right: auto; margin-bottom: 15px&quot;&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 512px; &quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAACEElEQVR42q2UzcthURzHr/e3WDwLTymSZGcjG+WllJKXjSzJStkpxoTBYxJZEAkRIrKw9dc9PWP6zjlnzp1MdzKYWXy83Ht+3/v9fX/nHkEQBDOhTPhCqBHqD1LjtZ8IrwShQgDhwr+fQaylokKL//kgfH+SD67RFPiHxKFMJnvGoVTwlhC9d33/6vflpkO60GQyQS6XS4qVSiVeXl7uc6jX62G322E0GjEej2GxWJiAwWCATqeD1WqF0+lELpeDVquFw+EArZEIcieXfr+P9XqNRqOBdruNTqeDVCqFer3OOJ1OmM1mKBaL6Ha72O/3aDabrJZr/BRUKBTs4nw+R6VSYaK9Xg/D4ZCJLhYLVKtVHA4HJBIJDAYD1kE0GsV0OmW1PJLfBZfLJTabDTweDyKRCHw+H3OazWYxmUxwPp8xGo0QDAYRj8fhcrkQi8WkLXP1C83GbDYjmUyy/K6HkU6nkc/n4fV6oVarWaZ/HYo4VZrTbrdDOBxGIBBg7bndbpTLZYRCIaxWK7RaLbZW7O6PgiqV6pdgoVBg7dPiTCbDHnA8HtlwaNb0YXStONCbDukg/H4/y69UKmG73bJ26XTpgGq1GtsJd70pFJqPRqNhe43mRfcf3Y82m41do52IGd8leI3ommclef0eFvyXw+Gd8O1J3rlGgwp+/o8O6WEtWPgx/pXwRmg/yBuvpRqWH6ol2Oy+RyjRAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source srcset=&quot;/static/2ffd88043569291834f15e420e9fa5ff/f51c2/icon.webp 148w,
/static/2ffd88043569291834f15e420e9fa5ff/66888/icon.webp 295w,
/static/2ffd88043569291834f15e420e9fa5ff/2bf0f/icon.webp 512w&quot; sizes=&quot;(max-width: 512px) 100vw, 512px&quot; type=&quot;image/webp&quot;&gt;
          &lt;source srcset=&quot;/static/2ffd88043569291834f15e420e9fa5ff/c5084/icon.png 148w,
/static/2ffd88043569291834f15e420e9fa5ff/60cc9/icon.png 295w,
/static/2ffd88043569291834f15e420e9fa5ff/06341/icon.png 512w&quot; sizes=&quot;(max-width: 512px) 100vw, 512px&quot; type=&quot;image/png&quot;&gt;
          &lt;img class=&quot;gatsby-resp-image-image&quot; src=&quot;/static/2ffd88043569291834f15e420e9fa5ff/06341/icon.png&quot; alt=&quot;Plugin Host Icon&quot; title=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;&gt;
        &lt;/picture&gt;
    &lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;The product I had in mind was &lt;code class=&quot;language-text&quot;&gt;plugin-host&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;plugin-host-gui&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plugin Host&lt;/strong&gt; is a testing utility for VST plug-ins.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.72972972972973%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAACXElEQVR42p2TW6/SQBDHmxheQMKlgLRc2tLDrVzaprSERJFbDNeH82g8D6Kf1gcfz4l+DbWJWthxZqEI5CRemvwyOzu7/5ntzgqOJEVvX7lvPK/3zjbMrW0ibXvbM3snZi9n2/Hz8cVciGu6b52u897pOK8Nw3gqiHGxbpudn47jQLPeZDf6DVT1KmiKCiqiqRroFR10rcLHj8C4VdRvuVyuI6RSqVa90fiB6sz13J1lWfumYewVVdkXioW9JMt7SZIO0FiWfvsHdgiUy+WvsixbXBC/oNvtwnA4ZJ7nAY1d14V2uw20GBdeUCgUoFgshjCylUrFVxTFEmKxWKtUKgUIUJUk0ul0ACvlttFoQL1e5zSbTY6maZDP53kyhB2T+tls1hIikUgLqwwQwAwMM502kyVR0zTBtm2epFarAW6EdDodwsjifj8ajR4qFEUxQGgho8UhmUyGktBxQNd1PqY5WnsGI4uifiKRuBQMg+dQ5clkkkPj6/g/C4Y8UtmloHgmiMcL6IjYRwwhC0cf/uTTbyIrpkU/Eb0SpGxhJUcf/sLnFRbVop+VswfBXq8XTCYTull+y9SLo9GI3zK1SL/f5z7dMMUHgwH1LPfxstigPwDn1vPlJfYh/kxjNpt9X6/XbLVaBePxeLdYLHbL5ZKDQrv5fM4hn+JhjNZhIcF6sWaDuxdf1LuqyV8KVhZQ31Fj46she4IafTqdwmaz4f51HP3Dnobh86eHN1PBiY+48RO+lnucfMCndQE+rQeM8fF1HP17tJ/RfojH41UBvydICnmG5P4T2psmrV8f4HhyVegEhQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/0c3a179734be8e685bed27ecff17f4e7/f51c2/screenshot.webp 148w,
/static/0c3a179734be8e685bed27ecff17f4e7/66888/screenshot.webp 295w,
/static/0c3a179734be8e685bed27ecff17f4e7/bc8a3/screenshot.webp 590w,
/static/0c3a179734be8e685bed27ecff17f4e7/4ce34/screenshot.webp 885w,
/static/0c3a179734be8e685bed27ecff17f4e7/bdd56/screenshot.webp 1136w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/0c3a179734be8e685bed27ecff17f4e7/c5084/screenshot.png 148w,
/static/0c3a179734be8e685bed27ecff17f4e7/60cc9/screenshot.png 295w,
/static/0c3a179734be8e685bed27ecff17f4e7/6d370/screenshot.png 590w,
/static/0c3a179734be8e685bed27ecff17f4e7/540ae/screenshot.png 885w,
/static/0c3a179734be8e685bed27ecff17f4e7/46e29/screenshot.png 1136w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/0c3a179734be8e685bed27ecff17f4e7/6d370/screenshot.png&quot;
            alt=&quot;screenshot&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;text-align: center; font-style: italic; margin-bottom: 15px&quot;&gt;
  Screenshot of Plugin Host&apos;s iced based GUI
&lt;/div&gt;
&lt;p&gt;It comes in two flavours: as a &lt;strong&gt;Command-line Tool&lt;/strong&gt; and as a &lt;strong&gt;GUI&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The workflow is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start &lt;code class=&quot;language-text&quot;&gt;plugin-host&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open an input audio file with it&lt;/li&gt;
&lt;li&gt;Open your plugins dylib with it&lt;/li&gt;
&lt;li&gt;Develop your plugin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plugin host will host your plug-in and loop the audio file over it. Whenever
you recompile your dylib, plugin host will reload your VST and its editor
and restart audio playback with the new version.&lt;/p&gt;
&lt;p&gt;Doing proper “hot-reload” with fade-ins/outs and without restarting the
audio-thread may come at some point soon.&lt;/p&gt;
&lt;h3 id=&quot;very-quick-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#very-quick-demo&quot; aria-label=&quot;very quick demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Very quick demo&lt;/h3&gt;
&lt;p&gt;Here’s a 1-minute demo. Imagine I’m working on a new looper VST, and I’m trying
to build a nice audio visualization chart.&lt;/p&gt;
&lt;p&gt;I started with a blue color, but now I’m not quite sure. So I make some changes
and re-compile:&lt;/p&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;iframe src=&quot;https://www.youtube.com/embed/EeLlckBa8y8?start=13&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt; &lt;/iframe&gt; &lt;/div&gt;
&lt;p&gt;For this small VST, most builds should complete in a couple of seconds, which is
an awesome dev-loop for a Rust based plug-in. I could try things out, see how
they feel and continue.&lt;/p&gt;
&lt;h3 id=&quot;more-features-id-want-to-add&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#more-features-id-want-to-add&quot; aria-label=&quot;more features id want to add permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;More features I’d want to add&lt;/h3&gt;
&lt;h4 id=&quot;input--output&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#input--output&quot; aria-label=&quot;input  output permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Input &amp;#x26; Output&lt;/h4&gt;
&lt;p&gt;For now, the host only supports looping over a file, but I’ll add support for
processing normal input soon. Then, one should be able to write some
audio-processing code, a GUI, maybe plug their guitar in and see how things
feel.&lt;/p&gt;
&lt;p&gt;The CLI version of the host supports generating an output &lt;code class=&quot;language-text&quot;&gt;.wav&lt;/code&gt; file and
outputting some basic performance diagnostics, but that has not been added to
the GUI yet.&lt;/p&gt;
&lt;h4 id=&quot;protecting-your-ears-and-speakers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#protecting-your-ears-and-speakers&quot; aria-label=&quot;protecting your ears and speakers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Protecting your ears and speakers&lt;/h4&gt;
&lt;p&gt;It’s quite a bad idea to hot-reload audio-processing code with your speakers
turned up as you may damage your ears or speakers depending on what type of
mistake you make.&lt;/p&gt;
&lt;p&gt;In order to mitigate this, I’d like to add a limiter and similar protections
onto the host, so we could iterate without fear.&lt;/p&gt;
&lt;h4 id=&quot;visualization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#visualization&quot; aria-label=&quot;visualization permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Visualization&lt;/h4&gt;
&lt;p&gt;For now, the host only draws a very basic RMS chart at the bottom &amp;#x26; a broken
(not using dBs) volume indicator. It’d be great to provide more tools that’d be
commonly useful for developing audio processors, such as a spectrogram.&lt;/p&gt;
&lt;h4 id=&quot;volume-control--fixes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#volume-control--fixes&quot; aria-label=&quot;volume control  fixes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Volume control &amp;#x26; fixes&lt;/h4&gt;
&lt;p&gt;This is a basic thing that is missing.&lt;/p&gt;
&lt;h3 id=&quot;augmented-audio&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#augmented-audio&quot; aria-label=&quot;augmented audio permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Augmented Audio&lt;/h3&gt;
&lt;p&gt;I’m pushing these things to the &lt;a href=&quot;https://github.com/yamadapc/augmented-audio&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-audio&lt;/code&gt;&lt;/a&gt;
repository on GitHub.&lt;/p&gt;
&lt;p&gt;Ultimately, I’d very much like to have some kind of &lt;em&gt;framework-y&lt;/em&gt; selection of
solutions for common audio application problems in Rust.&lt;/p&gt;
&lt;p&gt;Some examples of “solutions” that need to be selected (or built):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Testing tools - Such as &lt;code class=&quot;language-text&quot;&gt;plugin-host&lt;/code&gt;, but also unit-testing approaches
like snapshot testing or property testing over the audio&lt;/li&gt;
&lt;li&gt;Reference counting background GC - I’m using a &lt;a href=&quot;https://github.com/glowcoil/basedrop&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;basedrop&lt;/code&gt;&lt;/a&gt;
wrapper to avoid de-allocation on the audio-thread &amp;#x26; have a WIP alternative. New
things that I think are missing in both: metrics, more rigid scheduling of
collection, more tests.&lt;/li&gt;
&lt;li&gt;GUI toolkit&lt;/li&gt;
&lt;li&gt;Audio processor abstraction &amp;#x26; audio graph implementation&lt;/li&gt;
&lt;li&gt;Stand-alone &amp;#x26; multiple plug-in format packaging&lt;/li&gt;
&lt;li&gt;Filters &amp;#x26; common effects&lt;/li&gt;
&lt;li&gt;Guidelines of how to handle shared audio state - e.g. “ProcessorHandle”
structs?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There’s a ton of amazing work on the &lt;a href=&quot;https://github.com/RustAudio/&quot;&gt;RustAudio&lt;/a&gt;
GitHub organisation which is super helpful for me as well as other &lt;em&gt;framework-y&lt;/em&gt;
projects such as &lt;a href=&quot;https://github.com/wrl/baseplug&quot;&gt;baseplug&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-audio&lt;/code&gt; is my own exploration on this space, leveraging some tooling
from &lt;code class=&quot;language-text&quot;&gt;RustAudio&lt;/code&gt; &amp;#x26; several crates I’m trying to write or port from C++.&lt;/p&gt;
&lt;h3 id=&quot;web-gui-earlier-steps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#web-gui-earlier-steps&quot; aria-label=&quot;web gui earlier steps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Web GUI: Earlier steps&lt;/h3&gt;
&lt;p&gt;The initial idea for these libraries and for &lt;strong&gt;Plugin Host GUI&lt;/strong&gt; was to explore
using Web GUI for the audio apps.&lt;/p&gt;
&lt;p&gt;I’ve now deprecated this work and will replace all web bits with native GUI.
Notes on it will continue to live on &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/blob/master/docs/WEB_GUI.md&quot;&gt;WEB_GUI.md&lt;/a&gt;
and the &lt;code class=&quot;language-text&quot;&gt;legacy-web-gui&lt;/code&gt; branch of the repository.&lt;/p&gt;
&lt;p&gt;However, it’s still an interesting little experiment, as I did get it to work
and perform acceptably.&lt;/p&gt;
&lt;p&gt;There were two parts of this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;plugin-host-gui&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Equivalent to the GUI you saw above, but built using &lt;code class=&quot;language-text&quot;&gt;tauri&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;tasv2&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;A tremolo plug-in built with a React.js front-end hosted on a &lt;code class=&quot;language-text&quot;&gt;WkWebView&lt;/code&gt;
and communicating through messages with the Rust audio processing parts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I won’t discuss the &lt;code class=&quot;language-text&quot;&gt;tauri&lt;/code&gt; app, though you may check the markdown file above
for notes including how I was trying to get around bottlenecks for the volume
meter.&lt;/p&gt;
&lt;h3 id=&quot;web-front-ends-for-vst-plug-ins&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#web-front-ends-for-vst-plug-ins&quot; aria-label=&quot;web front ends for vst plug ins permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Web front-ends for VST plug-ins&lt;/h3&gt;
&lt;div style=&quot;max-width: 300px; margin-left: auto; margin-right: auto; margin-bottom: 15px&quot;&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 91.8918918918919%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAABYlAAAWJQFJUiTwAAADcElEQVR42m1UXY8URRTtvzDjbHdXV3fPdPf0x3z0TM8us9mwJsiyi8bsEmXZhDc1Bh/0QfwD/AzejBFICJr4IhgRhgSNQiQx6JsfAaO7JmIMPsjz8Z7qERfDw0lV3Tp17rm3uto6eewYvr10EZ9fuYrzl9/DR59ewp3ZTXx57Tpuz27gxpVP8OH5C/jg/XOYfXwZtyT21fXZE/jis2v47tZtnH7zLVhn3jkNfHMHv/9wDze/n+Huva/xaO8B/vx1F3/t7eHB/fu4K2Titx9/MrGHu7tP4I+ffwH+foR3z56FFYQhxtNl9HoFFgcVxv0SRVHIumcw6PdQjUYGw8EAfYntBznkl8MSSRzDarVasBcW0Gg00GjW0E4LgVpAq9lE03bQ9NtoyEhOk7F9aLWeQeCS78B1XViO4yAUl1y4SqHb1ugEGr7nIu9G0EkBpTx4SQ4v7EC5jqyVgecpZJEPLWPS9hH6moI2gsCHLQ5CrRAHHmzHhSMI0wJxqGVdZ1dxbpJyzv1uyMRKKrRNMh22RVA2A98Hnabijpkd24bqxHCCDpLAlUNeHQvaUFHXzCmUhF6dTOCnPWg/gOX6IXScou05UmpNYIkqzkwSPT/IxDzIuKu0uJOS6VbEXbqLu9BKemhLICyG0gNtDpCgJAETudIOlh+LYOhLabJni1gnyxFpx6xNC6S/WqogLNrvpQn8bg5HnLntWMpNjJi5qDnYDl96HEglaZZCtYVjKpG+6sBUorVciqfoQC7FE4dRagRZ6n6YNohoIi0heNNOGAs/M5XY9oLcuGdgkRwEdXnKHPzvs/g/XFcuzK1v2fAMt/7cfLlY45CTxcVFjOQlcByPx2ZOcD6ZTGQcoSzLxzHyqmqC0ZzLPcZivhQKkrSysoKdnR1Mp1P0+30M5JkdWFrC8e0TeGl7B5VwGK+qCltbW9jc3DQi5BHUiKIIFm3SBclpmiLPMjMvy6E4qNBd28aJN97GxpE15PJmh8OhcLrm/Rbz9VMFuckg7XM+KHIMps+id+S4+QryucDq6ipOvnpK1vJjeJogS16S0vI8wyuvvY7VQ2s4urGO9fV1jMRlcWgLy89t4MUXnpd2LGM8HGCy8TLKg4cldtQkoCBNJUlS/234C8qkVAY4MhvBeZpE4iw3zf+3LYzl8i2SQ9eMUYNfwj9OHkb/bnOr8gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source srcset=&quot;/static/21fc21b93e413e6c9a2e3cc2daa6da44/f51c2/tremolo-screenshot.webp 148w,
/static/21fc21b93e413e6c9a2e3cc2daa6da44/66888/tremolo-screenshot.webp 295w,
/static/21fc21b93e413e6c9a2e3cc2daa6da44/bc8a3/tremolo-screenshot.webp 590w,
/static/21fc21b93e413e6c9a2e3cc2daa6da44/ec4d5/tremolo-screenshot.webp 626w&quot; sizes=&quot;(max-width: 590px) 100vw, 590px&quot; type=&quot;image/webp&quot;&gt;
          &lt;source srcset=&quot;/static/21fc21b93e413e6c9a2e3cc2daa6da44/c5084/tremolo-screenshot.png 148w,
/static/21fc21b93e413e6c9a2e3cc2daa6da44/60cc9/tremolo-screenshot.png 295w,
/static/21fc21b93e413e6c9a2e3cc2daa6da44/6d370/tremolo-screenshot.png 590w,
/static/21fc21b93e413e6c9a2e3cc2daa6da44/ce661/tremolo-screenshot.png 626w&quot; sizes=&quot;(max-width: 590px) 100vw, 590px&quot; type=&quot;image/png&quot;&gt;
          &lt;img class=&quot;gatsby-resp-image-image&quot; src=&quot;/static/21fc21b93e413e6c9a2e3cc2daa6da44/6d370/tremolo-screenshot.png&quot; alt=&quot;Plugin Host Icon&quot; title=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;&gt;
        &lt;/picture&gt;
    &lt;/span&gt;
&lt;/div&gt;
&lt;div style=&quot;text-align: center; font-style: italic; margin-bottom: 15px&quot;&gt;
  Screenshot of the tremolo&apos;s React.js based GUI
&lt;/div&gt;
&lt;p&gt;The incomplete web based plug-in idea would work in the following manner.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.21621621621621%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACRUlEQVR42nWSW2/TQBBG+///CrwiBA/w2ILUW0raXBrHSXy/27trO3bswyQFUZF2pdFIo90z38x+F/w943hKtc7xN09UicNeRbAv6HRMGW9pCp+hTjmYmKHJjo/4/1zodsDsR9puOBX2TUngPEtY6CJkbFNUFmBZFmkas28NnUSjEsbhcA68XRt+zDLWQU0cx6SRS5FF+GFEVSQEmzmeu0WbmrKqqLsDB+m9r0uGQ38O3MYddrQnqXqGYaDRGa3JqMpc1Fa0uStKEyrTUOkaXbeYuqYxhdzvX23sZfyLf+yXwrGz0RVaG+paxlOh1AqiJGPn+ihjKMsSXb4z8ltAVRXyqMAYTV8ForrAjQtsNyLXLbppTwq1qlBKURTlqclxwjeAxSlqo2T5il4FGJXx7MQ8WS6T+wWPD2siz8UPHAlf9qvp+o5hfAeoZJxMumqVc9CR2CUg2q1RyTHbhBv7ZKOJdcvN4prJ6p6FM2fmTs+Bvdhmr8Qe8jG9wIc6RkchvuWzW25ZPa6Itgm57zPzplzNrvh++43Lx0uu7Z/nQA7yk5lD6FoUsXPyoRY7ec8Jm7nH8sHCWYbEO59tssYvA3HJmkDyMlycAwdRqBYPZL/uUPMpXemTez7OIiRcpyzvbeY3Nv5qKzbK6fuePM8YDyNxHrwC/vHRoFK2nz8x/fgB5+sXcmeJt7bZbVycnYs99VjduQL3xLuitIxIZUVJGRPk3rnCsa3oBNJsFpJX8kspJklwF56oEqVzl9XEInP8Y/szH/4GUSrfjET6Og0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/4234132d16a6f94fb7ecf7daf7906a27/f51c2/web-gui-diagram.webp 148w,
/static/4234132d16a6f94fb7ecf7daf7906a27/66888/web-gui-diagram.webp 295w,
/static/4234132d16a6f94fb7ecf7daf7906a27/bc8a3/web-gui-diagram.webp 590w,
/static/4234132d16a6f94fb7ecf7daf7906a27/4ce34/web-gui-diagram.webp 885w,
/static/4234132d16a6f94fb7ecf7daf7906a27/f490a/web-gui-diagram.webp 1180w,
/static/4234132d16a6f94fb7ecf7daf7906a27/95226/web-gui-diagram.webp 3404w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/4234132d16a6f94fb7ecf7daf7906a27/c5084/web-gui-diagram.png 148w,
/static/4234132d16a6f94fb7ecf7daf7906a27/60cc9/web-gui-diagram.png 295w,
/static/4234132d16a6f94fb7ecf7daf7906a27/6d370/web-gui-diagram.png 590w,
/static/4234132d16a6f94fb7ecf7daf7906a27/540ae/web-gui-diagram.png 885w,
/static/4234132d16a6f94fb7ecf7daf7906a27/2561a/web-gui-diagram.png 1180w,
/static/4234132d16a6f94fb7ecf7daf7906a27/07938/web-gui-diagram.png 3404w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/4234132d16a6f94fb7ecf7daf7906a27/6d370/web-gui-diagram.png&quot;
            alt=&quot;web gui diagram&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;vst-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#vst-layer&quot; aria-label=&quot;vst layer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;VST layer&lt;/h4&gt;
&lt;p&gt;Rust exposes VST APIs for an audio host to call using the &lt;a href=&quot;https://github.com/RustAudio/vst-rs&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;rust-vst&lt;/code&gt;&lt;/a&gt;
crate.&lt;/p&gt;
&lt;p&gt;It exposes callbacks to process audio, as well as callbacks to create its
“editor”.&lt;/p&gt;
&lt;p&gt;For the very simple web plugin prototype I built, a shared &lt;code class=&quot;language-text&quot;&gt;AudioParameterStore&lt;/code&gt;
object held all parameter states and definitions (value &amp;#x26; instructions to render
on the GUI).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;PluginParameterLike&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;name&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;self&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 class-name&quot;&gt;String&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;label&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;self&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 class-name&quot;&gt;String&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;text&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;self&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 class-name&quot;&gt;String&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;value&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;self&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;f32&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;set_value&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;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;can_be_automated&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;self&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;bool&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;value_range&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;self&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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;f32&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;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;value_type&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;self&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 class-name&quot;&gt;ParameterType&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;value_precision&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;self&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;u32&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;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ParameterRef&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;dyn&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PluginParameterLike&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;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ParameterStoreInner&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    parameters&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ParameterId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParameterRef&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;
    parameter_ids&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 class-name&quot;&gt;ParameterId&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;gui-thread&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gui-thread&quot; aria-label=&quot;gui thread permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GUI Thread&lt;/h4&gt;
&lt;p&gt;The host will call into the VST passing a pointer for the platform-specific view
the plugin GUI should attach itself to.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;GenericParametersEditor&lt;/code&gt; struct creates a webview and a transport layer to
communicate with it. Internally this uses &lt;code class=&quot;language-text&quot;&gt;webview-holder&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;darwin-webkit&lt;/code&gt;
crates which are just low-level wrappers to the WebKit Objective-C API.&lt;/p&gt;
&lt;h4 id=&quot;transport-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#transport-layer&quot; aria-label=&quot;transport layer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Transport layer&lt;/h4&gt;
&lt;p&gt;In order to exchange messages with JavaScript, there’s an abstract transport
layer. Two &lt;code class=&quot;language-text&quot;&gt;WebviewTransport&lt;/code&gt; implementations were available.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;WkMessageHandlerTransport&lt;/code&gt; uses &lt;a href=&quot;https://developer.apple.com/documentation/webkit/wkscriptmessagehandler&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;WkScriptMessageHandler&lt;/code&gt;s&lt;/a&gt;
to exchange messages with JavaScript. This is a webkit API for attaching native
callbacks for JavaScript to post messages into. The ‘Rust to JavaScript’ side is
achieved by evaluating a JavaScript function call that broadcasts messages onto
a global &lt;code class=&quot;language-text&quot;&gt;EventEmitter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;WebSocketsTransport&lt;/code&gt; uses websockets to send/receive messages into JavaScript.&lt;/p&gt;
&lt;p&gt;The reason I implemented both transports is &lt;code class=&quot;language-text&quot;&gt;WebSockets&lt;/code&gt; is more flexible, since
it allows for the plugin front-end to run in Chrome or over the network. At the
same time, I &lt;strong&gt;expected&lt;/strong&gt; that &lt;code class=&quot;language-text&quot;&gt;WkMessageHandlerTransport&lt;/code&gt; would be more
efficient.&lt;/p&gt;
&lt;p&gt;However, I never benchmarked one transport against the other, so I don’t know if
there are performance benefits from one or another. It’d be interesting to see
if &lt;code class=&quot;language-text&quot;&gt;WkMessageHandler&lt;/code&gt;s are more efficient or the other way around.&lt;/p&gt;
&lt;p&gt;I think, in theory, one benefit of using the native API is one could avoid
serialization of the messages and communicate via JavaScript objects. However, in
my transport implementation messages are always serialised to JSON.&lt;/p&gt;
&lt;p&gt;Another benefit is not having to run a websockets server, which does bring
concerns around security and bloat.&lt;/p&gt;
&lt;h4 id=&quot;javascript-side&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#javascript-side&quot; aria-label=&quot;javascript side permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JavaScript side&lt;/h4&gt;
&lt;p&gt;JavaScript has counter-parts to the transport layer which handle the different
message transport options.&lt;/p&gt;
&lt;p&gt;As mentioned, we may develop the front-end in Google Chrome or, for example, use
it as a remote control, but when packaged we may use the native transport.&lt;/p&gt;
&lt;p&gt;The tremolo is a super simple front-end written in React.js. It subscribes-to
and publishes parameter changes as well as rendering knobs dynamically based on
what parameters the Rust side sends to it.&lt;/p&gt;
&lt;h4 id=&quot;command-handler&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#command-handler&quot; aria-label=&quot;command handler permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Command handler&lt;/h4&gt;
&lt;p&gt;When messages are received from JavaScript they’re pushed into a queue. A
&lt;code class=&quot;language-text&quot;&gt;tokio&lt;/code&gt; task pops messages and actually performs the state updates or broadcast.&lt;/p&gt;
&lt;p&gt;The command handler keeps a reference to the parameters-store and handles messages
to set parameters as well as broadcasting initial parameter state for the GUI
to render when it’s created.&lt;/p&gt;
&lt;p&gt;When the React.js front-end is mounted on the DOM, it’ll publish a message
signaling it’s ready for which the command handler will respond with definitions
of all the knobs it should render.&lt;/p&gt;
&lt;p&gt;This might be a bit more complex than it had to, but it allowed for very loosely
coupled components (transport is just something that pushes messages onto a
channel, command handler is application specific). Even though I won’t be using
the web front-end bits for now, the building blocks could be adapted for
other use-cases.&lt;/p&gt;
&lt;h3 id=&quot;issues--benefits-with-web-front-ends&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#issues--benefits-with-web-front-ends&quot; aria-label=&quot;issues  benefits with web front ends permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Issues &amp;#x26; benefits with web front-ends&lt;/h3&gt;
&lt;p&gt;There are several issues and benefits of web front-ends for VST plug-ins.&lt;/p&gt;
&lt;p&gt;Here’s a quick list of some down-sides and comments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Much more complexity
&lt;ul&gt;
&lt;li&gt;Runtime &amp;#x26; build-time infrastructure to support it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Worse performance
&lt;ul&gt;
&lt;li&gt;All messages must be serialized and some rendering tasks are less efficient
on the Web&lt;/li&gt;
&lt;li&gt;However, there are several rendering tasks which are very efficient on the
web, and it’s a very featured platform&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Worse memory usage?
&lt;ul&gt;
&lt;li&gt;Having a completely separate GUI would mean a lot of data is duplicated&lt;/li&gt;
&lt;li&gt;It could be the case comparing with an optimised native GUI that memory
usage is worse with a browser, but it’s not really the case comparing my
simple tremolo with &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; based plug-ins&lt;/li&gt;
&lt;li&gt;The React.js plug-in actually consumes significantly less memory (several
times less) than a similar &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; counterpart&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Not cross-platform (yet)
&lt;ul&gt;
&lt;li&gt;Since I’m just using raw &lt;code class=&quot;language-text&quot;&gt;WkWebView&lt;/code&gt; bindings, this is not cross-platform at
all (though once would use &lt;code class=&quot;language-text&quot;&gt;tauri&lt;/code&gt;’s building blocks if they support the
plugin use-case at some point - currently, their libraries require that the
event-loop is owned by &lt;code class=&quot;language-text&quot;&gt;tao&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, the reason I’m not going to continue pushing this side is because I’d
really like to do basic audio-visualisation in the plug-ins, and I’ve found that
&lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; has quite good performance at doing that &amp;#x26; would be able to integrate
onto a custom rendering pipeline if I need to.&lt;/p&gt;
&lt;p&gt;I think for plug-ins that do not intend to do audio-visualisation, if there was
a cross-platform wrapper supporting event callbacks in/out of JavaScript it
could be a potential option.&lt;/p&gt;
&lt;p&gt;For a professional application, I’d really consider building on top of the web
platform a safer bet than adopting Rust native GUI toolkits, as unfortunate as
that may be. As long as the project can survive the short-comings of message
passing and the extra complexity, it’d be something to consider.&lt;/p&gt;
&lt;p&gt;It really depends on the application and its constraints.&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-texticedcode-and-code-classlanguage-textdruidcode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#code-classlanguage-texticedcode-and-code-classlanguage-textdruidcode&quot; aria-label=&quot;code classlanguage texticedcode and code classlanguage textdruidcode permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;druid&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;I did a very lousy benchmark of &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;druid&lt;/code&gt; drawing the input signal as a
line.&lt;/p&gt;
&lt;p&gt;For this, &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; could stay with only around 5% CPU usage to render (not at
60fps), while &lt;code class=&quot;language-text&quot;&gt;druid&lt;/code&gt; saturated the CPU to do a similar thing.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.72972972972973%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAABrElEQVR42rWTPU/CYBDHHwZDYsLsonUSvwjfwOAgurLwCdjZWHBgYOYLsKAJkWACUhPeEqIxKYS0vBQaS1oxTVtKOa+lRSUYJOIl/6R9Lvd7rnf/Eub6xss935+/jthLeTQKrdNToxF6Qb2vz1+8CcKVxPNn1Wp1nySTySPD0FVYxBy2DNM07Rpd16VcLndC4vH4sTGbuUDTgf5aCLRqQENgNpv1k0QiQRmGoe2iwyVwOp3+HxADXDkF8PVsNe82oWnaAphKpahut4sj0O3bEO6SQVVVkGUZFEX51tXKhfbDcDiUIpGInwQCASoYDGo0TUOv15vXajXo9/tQLBahXC4Dbg7q9TrwPA+dTgeazSYUCgVAiwDO3uZb55lMRiKE+Ek4HKZisZjGMAyIojhvt9swmUyg1WoBy7JQqVRgMBjAeDwGQRDsMwvAcZz9BRbQyuXzecnn823esuOKjUtZztAC4ovmJt3Z/LSIjUv5Lx8qjusN1GwbYRhOh+Lnr7eDDhEo46ZPSTQaPUAf3qFVaLRNCfWwpUpY+4ibv02n04doHeJB7aG8f5TF8HwAK7o6DAnLy5cAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/5d84c6594991488757f1bd06c99e8927/f51c2/iced-viz-screenshot.webp 148w,
/static/5d84c6594991488757f1bd06c99e8927/66888/iced-viz-screenshot.webp 295w,
/static/5d84c6594991488757f1bd06c99e8927/bc8a3/iced-viz-screenshot.webp 590w,
/static/5d84c6594991488757f1bd06c99e8927/4ce34/iced-viz-screenshot.webp 885w,
/static/5d84c6594991488757f1bd06c99e8927/bdd56/iced-viz-screenshot.webp 1136w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/5d84c6594991488757f1bd06c99e8927/c5084/iced-viz-screenshot.png 148w,
/static/5d84c6594991488757f1bd06c99e8927/60cc9/iced-viz-screenshot.png 295w,
/static/5d84c6594991488757f1bd06c99e8927/6d370/iced-viz-screenshot.png 590w,
/static/5d84c6594991488757f1bd06c99e8927/540ae/iced-viz-screenshot.png 885w,
/static/5d84c6594991488757f1bd06c99e8927/46e29/iced-viz-screenshot.png 1136w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/5d84c6594991488757f1bd06c99e8927/6d370/iced-viz-screenshot.png&quot;
            alt=&quot;iced viz screenshot&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I sure that this may be due to my incorrect usage of &lt;code class=&quot;language-text&quot;&gt;druid&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I’m trying to use the widget API to draw charts (and doing it without much
performance considerations). When profiling, most of the time is spent within
&lt;code class=&quot;language-text&quot;&gt;CoreGraphics&lt;/code&gt; calls, so if drawing back-ends are switchable (I have not checked)
this could potentially be worked around.&lt;/p&gt;
&lt;p&gt;Even so, the results of this spike, combined with existing support
from &lt;a href=&quot;https://github.com/BillyDM/iced_baseview&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;iced_baseview&lt;/code&gt;&lt;/a&gt; to render
&lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; apps inside plug-ins led me to pick &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; performance is not 100% free on &lt;code class=&quot;language-text&quot;&gt;plugin-host&lt;/code&gt; as I had to
change some internal &lt;code class=&quot;language-text&quot;&gt;lyon&lt;/code&gt; calls to get the &lt;code class=&quot;language-text&quot;&gt;plugin-host-gui2&lt;/code&gt; audio chart to
perform better. I’ve taken notes here: &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/wiki/Volume-Charts---iced-lyon-performance&quot;&gt;https://github.com/yamadapc/augmented-audio/wiki/Volume-Charts---iced-lyon-performance&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;plugin-host-code-classlanguage-texticedcode-gui-notes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#plugin-host-code-classlanguage-texticedcode-gui-notes&quot; aria-label=&quot;plugin host code classlanguage texticedcode gui notes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Plugin host &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; GUI notes&lt;/h3&gt;
&lt;p&gt;So far I’ve found &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; a very nice library to use, it’s very easy to use and
solves several problems out-of-the-box with good documentation and working
examples.&lt;/p&gt;
&lt;p&gt;Some things that I’d put on a wish-list:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;iced&lt;/code&gt; is a great open-source project, that I’m sure has been built
with care (who knows if not by someone else in lock-down!), and these are
things I think need to be solved, but not complaints or demands or anything
like that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Documentation on how to structure a larger app&lt;/li&gt;
&lt;li&gt;Support for avoiding re-rendering/re-drawing&lt;/li&gt;
&lt;li&gt;Accessibility&lt;/li&gt;
&lt;li&gt;Context menus&lt;/li&gt;
&lt;li&gt;Multiple windows&lt;/li&gt;
&lt;li&gt;Gradients &amp;#x26; shadows - &lt;a href=&quot;https://github.com/yamadapc/iced&quot;&gt;I started drafting gradient support here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;High memory usage&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yamadapc/augmented-audio&quot;&gt;Check-out the &lt;code class=&quot;language-text&quot;&gt;plugin-host-gui2&lt;/code&gt; source code if you’re interested.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rust-audio.discourse.group/t/announcement-initial-test-plugin-host-implementation/436&quot;&gt;I’ve started a discourse topic here as well for discussion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Reach out to me on the &lt;code class=&quot;language-text&quot;&gt;Rust Audio&lt;/code&gt; chat if you’d like information, have feedback
or just want to try these things out. I’m going as &lt;code class=&quot;language-text&quot;&gt;@yamadapc&lt;/code&gt; there.&lt;/p&gt;
&lt;p&gt;I’m also on &lt;a href=&quot;https://twitter.com/yamadapc&quot;&gt;https://twitter.com/yamadapc&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’ll try to write some thoughts next on the implementation details of
&lt;code class=&quot;language-text&quot;&gt;plugin-host&lt;/code&gt; as well as just keep pushing forward with it.&lt;/p&gt;
&lt;p&gt;All the best.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Rust Audio Experiments: 1 - Logging]]></title><description><![CDATA[This is (hopefully) the first in a series of posts I want to write while I try
to use Rust for audio development. In this post I’d like to…]]></description><link>https://beijaflor.io/rust-audio-experiments-1/</link><guid isPermaLink="false">https://beijaflor.io/rust-audio-experiments-1/</guid><pubDate>Fri, 16 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is (hopefully) the first in a series of posts I want to write while I try
to use Rust for audio development. In this post I’d like to share some thoughts
around building Rust audio plugins locally.&lt;/p&gt;
&lt;p&gt;I’ll show:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The set-up &amp;#x26; libraries for logging from a VST to a file&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;logging-from-the-vst&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#logging-from-the-vst&quot; aria-label=&quot;logging from the vst permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Logging from the VST&lt;/h1&gt;
&lt;p&gt;Audio plug-ins will be loaded into a host application as a dynamic library and
because of this, logging to standard output will not be effective (as the host’s
standard output is not available while doing manual testing).&lt;/p&gt;
&lt;p&gt;In order to log from the VST, we’ll log to a file.&lt;/p&gt;
&lt;h2 id=&quot;rust-logging-facade&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rust-logging-facade&quot; aria-label=&quot;rust logging facade permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rust logging facade&lt;/h2&gt;
&lt;p&gt;Rust has the standard &lt;a href=&quot;https://crates.io/crates/log&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;log&lt;/code&gt;&lt;/a&gt; crate to be used as
a logging facade. Logs can be added to the code as follows:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;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 namespace&quot;&gt;log&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token macro property&quot;&gt;error!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error msg&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 namespace&quot;&gt;log&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token macro property&quot;&gt;warn!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Warning msg&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 namespace&quot;&gt;log&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token macro property&quot;&gt;info!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Info msg&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 namespace&quot;&gt;log&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token macro property&quot;&gt;debug!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Debug msg&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 namespace&quot;&gt;log&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token macro property&quot;&gt;trace!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Trace msg&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;/div&gt;
&lt;p&gt;Adding &lt;code class=&quot;language-text&quot;&gt;log&lt;/code&gt; statements to the code will not output anywhere and needs a logger
implementation to be configured.&lt;/p&gt;
&lt;h2 id=&quot;log4rs-set-up-to-log-to-a-file&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#log4rs-set-up-to-log-to-a-file&quot; aria-label=&quot;log4rs set up to log to a file permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;log4rs set-up to log to a file&lt;/h2&gt;
&lt;p&gt;For this use-case I’ve found &lt;code class=&quot;language-text&quot;&gt;log4rs&lt;/code&gt; provided a simple enough set-up. The
plug-in will call a &lt;code class=&quot;language-text&quot;&gt;configure_logging()&lt;/code&gt; function, which will try to initialize
the logger.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;configure_logging&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root_config_path&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 class-name&quot;&gt;Path&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 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;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;log-destination&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#log-destination&quot; aria-label=&quot;log destination permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Log destination&lt;/h3&gt;
&lt;p&gt;We’ll write logs to two destinations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Standard output&lt;/li&gt;
&lt;li&gt;A log file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ll rotate the log file when it reaches a certain size and ensure the
destination directory exists before starting.&lt;/p&gt;
&lt;h3 id=&quot;creating-the-root-configurationlogging-directory&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#creating-the-root-configurationlogging-directory&quot; aria-label=&quot;creating the root configurationlogging directory permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Creating the root configuration/logging directory&lt;/h3&gt;
&lt;p&gt;Some other part of our application will give us the logging directory, which may
be in a &lt;code class=&quot;language-text&quot;&gt;.&lt;/code&gt; directory of the user’s HOME or, for example, in
&lt;code class=&quot;language-text&quot;&gt;/Library/Application Support&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;ensure_logging_directory&lt;/code&gt; function will make sure the root path exists:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;ensure_logging_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root_config_path&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 class-name&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;-&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 class-name&quot;&gt;PathBuf&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 namespace&quot;&gt;std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&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;root_config_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 function&quot;&gt;map_err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LoggingSetupError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CreateLogDirectoryError&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;root_config_path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_path_buf&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;/div&gt;
&lt;p&gt;It returns a &lt;code class=&quot;language-text&quot;&gt;Result&lt;/code&gt;, which is a type I’ll define soon.&lt;/p&gt;
&lt;h3 id=&quot;configuring-logging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#configuring-logging&quot; aria-label=&quot;configuring logging permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Configuring logging&lt;/h3&gt;
&lt;p&gt;To configure logging we’ll set up two &lt;code class=&quot;language-text&quot;&gt;Appenders&lt;/code&gt; which log to stdout &amp;#x26; the file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&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;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;configure_logging&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root_config_path&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 class-name&quot;&gt;Path&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 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 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 keyword&quot;&gt;let&lt;/span&gt; log_dir &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ensure_logging_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root_config_path&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 keyword&quot;&gt;let&lt;/span&gt; log_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; log_dir&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&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 punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; logfile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RollingFileAppender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&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;encoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Box&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 class-name&quot;&gt;PatternEncoder&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 string&quot;&gt;&quot;{d} [{l}] {M}:{L} - {m} - tid:{T}:{t} pid:{P}\n&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 function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            log_path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Box&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 class-name&quot;&gt;CompoundPolicy&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 class-name&quot;&gt;Box&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 class-name&quot;&gt;SizeTrigger&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 number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&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;Box&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 class-name&quot;&gt;DeleteRoller&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 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 function&quot;&gt;map_err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LoggingSetupError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FileAppenderError&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 keyword&quot;&gt;let&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&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;appender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Appender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&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;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;logfile&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&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;logfile&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;appender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Appender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&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;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;stdout&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&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 class-name&quot;&gt;ConsoleAppender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&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;build&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 function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Root&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&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;appender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;logfile&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;appender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;stdout&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;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LevelFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Info&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 namespace&quot;&gt;log4rs&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init_config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&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;/div&gt;
&lt;h3 id=&quot;errors&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#errors&quot; aria-label=&quot;errors permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Errors&lt;/h3&gt;
&lt;p&gt;We’ll use &lt;code class=&quot;language-text&quot;&gt;thiserror&lt;/code&gt; to handle errors. It’ll join the 4 types of errors that
may happen during this process into one type:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&gt;&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(thiserror::Error, Debug)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&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;LoggingSetupError&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[error(&lt;span class=&quot;token string&quot;&gt;&quot;Failed to create logging directory&quot;&lt;/span&gt;)]&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;CreateLogDirectoryError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&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;Error&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 attribute attr-name&quot;&gt;#[error(&lt;span class=&quot;token string&quot;&gt;&quot;Failed to set-up log-file appender&quot;&lt;/span&gt;)]&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;FileAppenderError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&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;Error&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 attribute attr-name&quot;&gt;#[error(&lt;span class=&quot;token string&quot;&gt;&quot;Failed to set-up logging configuration&quot;&lt;/span&gt;)]&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;LogConfigError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[from]&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConfigErrors&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 attribute attr-name&quot;&gt;#[error(&lt;span class=&quot;token string&quot;&gt;&quot;Failed to set logger configuration&quot;&lt;/span&gt;)]&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;SetLoggerError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[from]&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SetLoggerError&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;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition 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 class-name&quot;&gt;T&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;std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;result&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 class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggingSetupError&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h3&gt;
&lt;p&gt;See &lt;a href=&quot;https://github.com/yamadapc/augmented-audio/tree/0d8aa108d8a36bf7690d47c72f38534e0b0a0608/crates/audio-plugin-logger/src&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;augmented-audio/crates/audio-plugin-logger&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[FileSaver - v1.1.1]]></title><description><![CDATA[FileSaver is now “Parallel Disk Scanner” and is available for sale on the app store. Since I got my latest MacBook, I can only go for a…]]></description><link>https://beijaflor.io/filesaver-freeing-up-space/</link><guid isPermaLink="false">https://beijaflor.io/filesaver-freeing-up-space/</guid><pubDate>Mon, 08 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;FileSaver is now “Parallel Disk Scanner” and is available for sale on the app store.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div style=&quot;max-width: 150px; margin-left: auto; margin-right: auto; margin-bottom: 15px&quot;&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEUElEQVR42m1VbVMTVxSOtn+ljDptf0xr22l/g4wzCNQZPziOY5HO2HbqWG0VdUAgyJuVANEoSYBASCIE0xYtCW9iQkiQ7G7u7t37sk/PUmmx5c48OSd7zvPsnnvPng0EDizg/FH6OfK3bx4DRJPBRc8Wc+M+fB9KnPFjbwlHABwNHLb2AwBvqAsRTFaFO7gD/LgJXMx7eyDf868lK8KtcxEEjIaD3EPE2KeFutztLAKtz5jXlDJVa9pSZzOWPps2dWvKUE1zhmrJMM/PKVhyFx7/5B1Rct7bs8r+4qXpqfasi3MzprqUYmhLM1yas/DNMwdt84Isx+UFgcvzDs4lDMoVeGlqRdzPD2oFwM0TJVtXf8i4aE9Y+rskwxXC5YSFb+cELkaKOD+QwYX702RTuBDewPdpifZpS/ucIlNV0jj+T8lSy2CoAFyJ1eW1BMO1aeb76F9U6JpYRvdIDMl8EZlSHanVbfSOx9E2mMP1pKA8S44SlzR635ZsfbRUFM71CRu34sy7Gbfxc4xheIHj93wVifgklFLwqB6pPGhytmzg2mAUV0MbuD0tvJ+Iu1QSDuB+GFCKt6RXgI5HddUdtdFNwTuP6yiUNbK558ivUJAWlwLC88BsjpWyxGi2iKv3kuiJS+JayteglmoO1JnoncwCwXBdDURsDBKCYYZ8UWMuk0GpXIamXJeeUngajiOw9trB5IsabnTGMBBxKd9SU4sAaXUHajV3MjYN/Dpm65GwgxBhOMSwvgXM53LIFwp7T+gIAZcOlDGO1yWJifQmOu4mMfpYEZepWIK60nCjAcOQ8dlJ4NEI05FRB5ExB+GHNtY2JSqGgcdPn4ILuScqaSel9FAuCdzriWIouImJsCSurZKkYRpiIsDqPLiUBGLDTE09dDA14iA6yFCpAulcFs3NLQhHIljdeIXtN2+wsrqB0NATDNz+DYmQxBTd3Oe+mKPXoi56AoIOpZQDZoO2Sj/gSA05yIWAJ+PzaDx9CtFEEqWaieRCFtPpDDLJeRRiW0g/UEgPO8gQx+f6GrTLzX7bfFxdkXwx6GJxwPb+GNJ4eD2DU6cbEU3PglOpFh2GTe1SJ9+oaKyPC2T7bTwf4ATH87nVFcH9FtzrRSFE7+4MsHxPymf0kp5pbEGoI4Eynf7qDMPqrE3WxnrKwZ/jDEtdNpb7OJbvc7zodKQxS/srqNz9ZaJ6XO2oSrnfQ/6OqRc7NlHs1li77WD9Lv8Xdzg2Ol1s9rh41etivdPR5X4NtaMrJirH3hkOlrK+0kXtmUGN8k1XbXU52O52D0eX65V+cZRBuShBE/fLd4cD8L5va27tM1GUNURpv25Jz7jBlUHiRgfXRoerjZv0/4ar/BhiVGZJ7ZrcPHlQ438zcRvbDZzxPvlSCVArYIzQR7hPGKdGpGt+TDAeNLD9waED9oCoP9L3PgFVVE9w2K3c5EG548Zdgu/bnv21H/tv/v76C0bxtbGPU7H0AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source srcset=&quot;/static/d20809f87a188065c0e002e49ec29ca2/f51c2/logo.webp 148w,
/static/d20809f87a188065c0e002e49ec29ca2/66888/logo.webp 295w,
/static/d20809f87a188065c0e002e49ec29ca2/bc8a3/logo.webp 590w,
/static/d20809f87a188065c0e002e49ec29ca2/4ce34/logo.webp 885w,
/static/d20809f87a188065c0e002e49ec29ca2/d71bc/logo.webp 1024w&quot; sizes=&quot;(max-width: 590px) 100vw, 590px&quot; type=&quot;image/webp&quot;&gt;
          &lt;source srcset=&quot;/static/d20809f87a188065c0e002e49ec29ca2/c5084/logo.png 148w,
/static/d20809f87a188065c0e002e49ec29ca2/60cc9/logo.png 295w,
/static/d20809f87a188065c0e002e49ec29ca2/6d370/logo.png 590w,
/static/d20809f87a188065c0e002e49ec29ca2/540ae/logo.png 885w,
/static/d20809f87a188065c0e002e49ec29ca2/42a19/logo.png 1024w&quot; sizes=&quot;(max-width: 590px) 100vw, 590px&quot; type=&quot;image/png&quot;&gt;
          &lt;img class=&quot;gatsby-resp-image-image&quot; src=&quot;/static/d20809f87a188065c0e002e49ec29ca2/6d370/logo.png&quot; alt=&quot;FileSaver Logo&quot; title=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;&gt;
        &lt;/picture&gt;
    &lt;/span&gt;
  &lt;div style=&quot;text-align: center; font-style: italic; margin-top: 15px&quot;&gt;
    FileSaver logo
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Since I got my latest MacBook, I can only go for a couple of months before it
starts running out of free disk space. It’s a pretty bad situation where to save
a bit on SSD size, I have to spend my time cleaning-up large files like music
samples, VMs, &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;So then I decided to save a lot of time 😂 and instead of buying one of the
available “disk cleaning” solutions, I spent some time trying some open-source
software and &lt;em&gt;eventually&lt;/em&gt; decided to write &lt;strong&gt;FileSaver&lt;/strong&gt;, a fast multithreaded
disk usage scanner.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.16216216216216%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACp0lEQVR42nWRy08aURSHtS01dcP/oE2aiiDMk4cgigoamy5qUmLronXRZU3/kjYuSCgV4sJYGlPbtCsaHonRDT4iREZ5DA8BpSBSxAS9zumdSUu0jTf5cnJncr+c3zltbfgAQLtYw2GQZbOcO5GJr8diXDAej4ei0WhoNxoJ8Xwy9PMwH8qkU/4Yx4V3dnbeXH177dhs/T2MiTEOm63WgD+QDYc3wO8PCNvbEdjc2ISvvjVYWFmFL6tJ2M3V0enpL+C4vQ9/hLf+9bWPj1l/GE1mYCgD0rL9SKUkEaFmparqJZBSSaH73Qo08diO0gdHzUKhAFtbW+9vEt4x6AeDSgUBKiVxMWAaAprSA0logSJ1Egy+9ylJsD+dgmKxgIrFoih03Sg0GQeDahUFhIZF5gGLJPwrk4S0ARQKDUxO2uHw6BCVSiWIRCLOG4U61hDE8XBXLMLy/4RSx2oank1NQ4rn0UE+D2vr6wv47V2fzyf3er0i91rCEYs1OGKxgbqPQjSlvR4Xd9eLx+F49xbiezFIpzOQyWQgkUg0kslkPpfLHeBT4zjO0xIytD4oSnBsSUiRLNCkVoLFwp6HfeD9tAy1Wg3E+ZXLZahWq1Cv18VvSBAEyOfz36/McCgw+cQOep35AosFkurHGCUYxigoetTC0tJHOD6uSN3hjkQE3J2ARRdnZ2eQSqW+XYk8Fpx+/gJGR8YRTeL56caA1E9gHoHWMCqOAhYXlwS84cv9/f1LHFeqIul0+rzRaADP8y3hbZY1rAwP25p4IWW8nKqqV9NCo6aq3V0PqsvLn8+bzSaIG65UKlL8k5MTESlyNpttRW7v6OjowpWWyWRkZ2fnNeRyOYn/EbOzs9Nzc3OvHQ7HjNPpnHG73RLz8/MvPR7PK5fLZRFlvwGA29fQbAUG1wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/1a328a047fdea2232098bfb75ca11aab/f51c2/screenshot.webp 148w,
/static/1a328a047fdea2232098bfb75ca11aab/66888/screenshot.webp 295w,
/static/1a328a047fdea2232098bfb75ca11aab/bc8a3/screenshot.webp 590w,
/static/1a328a047fdea2232098bfb75ca11aab/4ce34/screenshot.webp 885w,
/static/1a328a047fdea2232098bfb75ca11aab/f490a/screenshot.webp 1180w,
/static/1a328a047fdea2232098bfb75ca11aab/12be9/screenshot.webp 2300w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/1a328a047fdea2232098bfb75ca11aab/c5084/screenshot.png 148w,
/static/1a328a047fdea2232098bfb75ca11aab/60cc9/screenshot.png 295w,
/static/1a328a047fdea2232098bfb75ca11aab/6d370/screenshot.png 590w,
/static/1a328a047fdea2232098bfb75ca11aab/540ae/screenshot.png 885w,
/static/1a328a047fdea2232098bfb75ca11aab/2561a/screenshot.png 1180w,
/static/1a328a047fdea2232098bfb75ca11aab/90a52/screenshot.png 2300w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/1a328a047fdea2232098bfb75ca11aab/6d370/screenshot.png&quot;
            alt=&quot;FileSaver screenshot&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;On my MacBook, &lt;strong&gt;scanning 5.3 million files takes 100 seconds, with around 51.000
files/second of throughput&lt;/strong&gt;. The compromise is high memory usage of around 2GB.&lt;/p&gt;
&lt;h3 id=&quot;what-i-used-to-do-with-coreutils&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-i-used-to-do-with-coreutils&quot; aria-label=&quot;what i used to do with coreutils permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What I used to do with coreutils&lt;/h3&gt;
&lt;p&gt;I used to do the following command combination to find sizes:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Find file sizes&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;du&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-h&lt;/span&gt; ./directory &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 comment&quot;&gt;# Sort by size&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# (just sort on linux, requires gnu coreutils on OSX)&lt;/span&gt;
    gsort &lt;span class=&quot;token parameter variable&quot;&gt;-h&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 comment&quot;&gt;# Put on a pager&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;less&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This works fine except that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It’s slow&lt;/li&gt;
&lt;li&gt;It’s not interactive&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-classlanguage-textduccode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#code-classlanguage-textduccode&quot; aria-label=&quot;code classlanguage textduccode permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;duc&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;I’ve also tried a great alternative to the above, called &lt;a href=&quot;http://duc.zevv.nl/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;duc&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;duc&lt;/code&gt; is a great open-source utility written in C that performs this task a bit
faster and with interactive options. It can also generate an index that can be
re-used, so we don’t need to scan all files when looking for places to clean.&lt;/p&gt;
&lt;p&gt;Even so, what I found was that on my filled-up disk, it was really slow to
generate the index for the whole thing, including system directories like
&lt;code class=&quot;language-text&quot;&gt;/Library&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;/usr/local&lt;/code&gt;. As a developer, a lot of the trash was around
those.&lt;/p&gt;
&lt;h3 id=&quot;parallel-size-scanning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#parallel-size-scanning&quot; aria-label=&quot;parallel size scanning permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Parallel size scanning&lt;/h3&gt;
&lt;p&gt;I profiled &lt;code class=&quot;language-text&quot;&gt;duc&lt;/code&gt; and found that it’s single threaded nature was a bit
bottleneck. Writes to the index blocked scanning file sizes and the calculations
to aggregate sizes also probably compromised throughput.&lt;/p&gt;
&lt;p&gt;I ran over this issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Some thoughts about indexing, filtering, parallelization, etc
&lt;a href=&quot;https://github.com/zevv/duc/issues/205&quot;&gt;https://github.com/zevv/duc/issues/205&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The idea that I could write a multithreaded program and get a massive
performance boost stuck with me.&lt;/p&gt;
&lt;p&gt;I considered contributing to &lt;code class=&quot;language-text&quot;&gt;duc&lt;/code&gt;, but it had two major flaws for me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It’s GPL licensed&lt;/li&gt;
&lt;li&gt;It’s written in C&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The 2nd point is just because, knowing a bit of C++ I didn’t see the point of
going to C.&lt;/p&gt;
&lt;p&gt;I then designed and iterated on a parallel scanning architecture / aggregation
algorithm that achieves pretty good results in terms of throughput.&lt;/p&gt;
&lt;h2 id=&quot;problem-domain-and-aggregation-overview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem-domain-and-aggregation-overview&quot; aria-label=&quot;problem domain and aggregation overview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem domain and aggregation overview&lt;/h2&gt;
&lt;p&gt;The problem we have to solve is that of finding directory sizes based on all
their children’s sizes.&lt;/p&gt;
&lt;p&gt;Given a certain &lt;code class=&quot;language-text&quot;&gt;path&lt;/code&gt; to scan, we want to
&lt;a href=&quot;https://linux.die.net/man/2/lstat&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;lstat&lt;/code&gt;&lt;/a&gt; it. This will give us its size and
file type. If this &lt;code class=&quot;language-text&quot;&gt;path&lt;/code&gt; is a directory, we then want to scan all its children.&lt;/p&gt;
&lt;p&gt;This path will have “completed” once all its children have completed or if
&lt;code class=&quot;language-text&quot;&gt;lstat&lt;/code&gt; says it isn’t a directory.&lt;/p&gt;
&lt;p&gt;So we have to continuously scan children and as they come in, update the parent
path’s completed children count (and do this recursively up).&lt;/p&gt;
&lt;p&gt;To support better UX and performance, whenever a path is completed, its delta
update in size is carried up each of its parents.&lt;/p&gt;
&lt;p&gt;For example, imagine we have the following directory structure:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;content
└── blog
    └── filesaver-freeing-up-space
        ├── index.md
        └── logo.png&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;We’ll start the scan at &lt;code class=&quot;language-text&quot;&gt;/content&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Since it’s a directory we read its children&lt;/li&gt;
&lt;li&gt;Its size and 1 child count is stored&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We do the same at &lt;code class=&quot;language-text&quot;&gt;/content/blog&lt;/code&gt; and then &lt;code class=&quot;language-text&quot;&gt;/content/blog/filesaver-freeing-up-space&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;At this point our state looks like:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content&lt;/code&gt; - 1 pending child path&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content/blog&lt;/code&gt; - 1 pending child path&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content/blog/filesaver-freeing-up-space&lt;/code&gt; - 2 pending child paths&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We scan &lt;code class=&quot;language-text&quot;&gt;index.md&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content/blog/filesaver-freeing-up-space/index.md&lt;/code&gt; - is completed + 100KB&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content/blog/filesaver-freeing-up-space&lt;/code&gt; - 1 pending child paths (+ 100KB)&lt;/li&gt;
&lt;li&gt;Other paths’ pending counts are kept, but their total sizes are updated&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content/blog&lt;/code&gt; - 1 pending child paths (+ 100KB)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content&lt;/code&gt; - 1 pending child paths (+ 100KB)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We scan &lt;code class=&quot;language-text&quot;&gt;logo.png&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content/blog/filesaver-freeing-up-space/logo.png&lt;/code&gt; - is completed + 300KB&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content/blog/filesaver-freeing-up-space&lt;/code&gt; - is completed (+ 300KB)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content/blog&lt;/code&gt; - is completed (+ 300KB)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/content&lt;/code&gt; - is completed (+ 300KB)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#architecture&quot; aria-label=&quot;architecture permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Architecture&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;FileSaver&lt;/strong&gt; is a simple program, but it’s structured. The architecture is as
follows.&lt;/p&gt;
&lt;div style=&quot;background-color: white; border-radius: 6px; padding: 15px; margin-bottom: 15px&quot;&gt;
  &lt;img alt=&quot;FileSaver architecture diagram&quot; src=&quot;/f1ab00fdeb1d2b33269baaae28cda711/diagram.svg&quot;&gt;
&lt;/div&gt;
&lt;h3 id=&quot;components&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#components&quot; aria-label=&quot;components permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Components&lt;/h3&gt;
&lt;ol start=&quot;0&quot;&gt;
&lt;li&gt;Lock based queue&lt;/li&gt;
&lt;li&gt;Worker Threads&lt;/li&gt;
&lt;li&gt;Reader Thread&lt;/li&gt;
&lt;li&gt;UI Thread&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;queue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#queue&quot; aria-label=&quot;queue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Queue&lt;/h4&gt;
&lt;p&gt;We pass data between threads using an uninteresting lock based queue, plus one
extra lock on the size/pending state, since the UI thread will read it while its
being processed.&lt;/p&gt;
&lt;p&gt;For this we have &lt;a href=&quot;https://yamadapc.github.io/filesaver/classfilesaver_1_1data_1_1_work_queue.html&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;filesaver::data::WorkQueue&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;worker-threads&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#worker-threads&quot; aria-label=&quot;worker threads permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Worker Threads&lt;/h4&gt;
&lt;p&gt;The workers work with two queues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;workQueue&lt;/strong&gt; provides incoming paths to scan&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;resultQueue&lt;/strong&gt; has entries pushed onto it, with their sizes, types and child
paths attached&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The worker will enqueue any children it finds.&lt;/p&gt;
&lt;h4 id=&quot;reader-thread&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reader-thread&quot; aria-label=&quot;reader thread permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reader thread&lt;/h4&gt;
&lt;p&gt;The reader thread performs the aggregation as described in the aggregation
overview. The state gets stored in a giant hash-map (&lt;code class=&quot;language-text&quot;&gt;std::unordered_map&lt;/code&gt;)
mapping paths to their sizes and number of pending children.&lt;/p&gt;
&lt;h4 id=&quot;ui&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ui&quot; aria-label=&quot;ui permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UI&lt;/h4&gt;
&lt;p&gt;The UI uses AppKit and is written in Objective-C++. It polls the state for
the directories that are visible on the screen.&lt;/p&gt;
&lt;h2 id=&quot;distribution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#distribution&quot; aria-label=&quot;distribution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Distribution&lt;/h2&gt;
&lt;p&gt;For now, &lt;strong&gt;FileSaver&lt;/strong&gt; is available from its
&lt;a href=&quot;https://github.com/yamadapc/filesaver&quot;&gt;GitHub page&lt;/a&gt;. The project has structure
that should make it convenient to include it as a library onto a different
codebase. Though there could still be improvements for this use-case like
organizing public headers and the build system, it goes most of the way and
should have good DoxyGen docs.&lt;/p&gt;
&lt;p&gt;The code is distributed under the &lt;strong&gt;MIT license&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/filesaver&quot;&gt;&lt;strong&gt;Download FileSaver&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>