<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://octetta.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://octetta.com/" rel="alternate" type="text/html" /><updated>2026-11-07T16:42:50+01:00</updated><id>https://octetta.com/feed.xml</id><title type="html">octetta</title><subtitle>I like to think about things and then build them.</subtitle><entry><title type="html">Beyond AMY part 1 : ROTOTEM</title><link href="https://octetta.com/2025/06/01/rototem.html" rel="alternate" type="text/html" title="Beyond AMY part 1 : ROTOTEM" /><published>2025-06-01T10:00:00+02:00</published><updated>2025-06-01T10:00:00+02:00</updated><id>https://octetta.com/2025/06/01/rototem</id><content type="html" xml:base="https://octetta.com/2025/06/01/rototem.html"><![CDATA[<p>I really love AMY and you can read some of the reasons why in earlier posts.</p>

<p>For the past few years, I’ve prototyped a REPL for synth sounds and pattern
playback and want to document things before I forget where I’ve been while
talking about where I’m going.</p>

<p>A buddy of mine was listening to me rant about my work and invented the name
“Rototem” that’s evocative of a tower with spinning things on it. This conceptually
matches what I want to make and use… a funny hybrid between a CLI for sound
with a pattern player included.</p>

<p>Rototem was a proof of concept of this using AMY as the sound back-end and a REPL
of my creation, and an expansion of AMY’s “wire protocol”.</p>

<p>I’ve made several videos of me playing with this at YouTube</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code></code></pre></div></div>]]></content><author><name></name></author><category term="opensource" /><summary type="html"><![CDATA[I really love AMY and you can read some of the reasons why in earlier posts.]]></summary></entry><entry><title type="html">zoe : The Zip-on-Executable Library</title><link href="https://octetta.com/2023/05/02/zip-on-executable.html" rel="alternate" type="text/html" title="zoe : The Zip-on-Executable Library" /><published>2023-05-02T10:00:00+02:00</published><updated>2023-05-02T10:00:00+02:00</updated><id>https://octetta.com/2023/05/02/zip-on-executable</id><content type="html" xml:base="https://octetta.com/2023/05/02/zip-on-executable.html"><![CDATA[<p>Have you ever needed to package a set of files with a binary executable, but
found yourself struggling to keep track of multiple files and directories?
Or perhaps you’ve wanted to create self-extracting archives that can be run
without needing an external program? If so, then <code class="language-plaintext highlighter-rouge">zoe</code> may be just what you
need.</p>

<h3 id="intro">Intro</h3>

<p><code class="language-plaintext highlighter-rouge">zoe</code> is a simple C library that allows you to access a zip file that
has been appended to an executable. This means that your binary can include
all the files it needs to function, without needing to rely on external files
or archives. And because the zip file is appended to the end of the
executable, it’s all contained within a single file. Underneath <code class="language-plaintext highlighter-rouge">zoe</code> is the
wonderful <a href="https://github.com/richgel999/miniz">miniz</a> library should you
need to perform more technical <code class="language-plaintext highlighter-rouge">zip</code> file work.</p>

<p>For my first use case, I created <code class="language-plaintext highlighter-rouge">zoe</code> to help me package sound performance
and audio samples with my <code class="language-plaintext highlighter-rouge">rototem</code> sound tool. Using <code class="language-plaintext highlighter-rouge">zoe</code>, <code class="language-plaintext highlighter-rouge">rototem</code> can now
find and read the files contained within the appended zip file, without
needing to reference any external file. This made it much easier to distribute
the <code class="language-plaintext highlighter-rouge">rototem</code>, as I only needed to send a single file.</p>

<p>But the usefulness of <code class="language-plaintext highlighter-rouge">zoe</code> doesn’t stop there. Because the zip file is attached
to the executable, you can use command-line utilities like the <code class="language-plaintext highlighter-rouge">zip</code> tool to
add, remove, or update the files within the archive. This makes it easy to keep
your files up-to-date, and allows users of the binary to do so themselves.</p>

<h3 id="library-overview">Library overview</h3>

<p>Here’s a run-down of the <code class="language-plaintext highlighter-rouge">zoe</code> library’s functions:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="o">*</span><span class="nf">zoe_self</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">exename</span><span class="p">);</span>
<span class="n">zoe_t</span> <span class="o">*</span><span class="nf">zoe_open</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">zipname</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">zoe_close</span><span class="p">(</span><span class="n">zoe_t</span> <span class="o">*</span><span class="n">zoe</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">zoe_files</span><span class="p">(</span><span class="n">zoe_t</span> <span class="o">*</span><span class="n">zoe</span><span class="p">);</span>
<span class="kt">char</span> <span class="o">*</span><span class="nf">zoe_name_at</span><span class="p">(</span><span class="n">zoe_t</span> <span class="o">*</span><span class="n">zoe</span><span class="p">,</span> <span class="kt">int</span> <span class="n">i</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">zoe_find</span><span class="p">(</span><span class="n">zoe_t</span> <span class="o">*</span><span class="n">zoe</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">);</span>
<span class="n">zoe_file_t</span> <span class="o">*</span><span class="nf">zoe_fopen_at</span><span class="p">(</span><span class="n">zoe_t</span> <span class="o">*</span><span class="n">zoe</span><span class="p">,</span> <span class="kt">int</span> <span class="n">i</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">zoe_fclose</span><span class="p">(</span><span class="n">zoe_file_t</span> <span class="o">*</span><span class="n">zfile</span><span class="p">);</span>
<span class="cp">#define ZOE_FILE(z) (z-&gt;mem)
</span></code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">zoe_self</code> resolves a full pathname to the exename provided. This enables <code class="language-plaintext highlighter-rouge">zoe</code> to open the attached zip file regardless of where it’s located and without troubles due to symbolic filesystem links.</li>
  <li><code class="language-plaintext highlighter-rouge">zoe_open</code> finds the appended zip file and returns a <code class="language-plaintext highlighter-rouge">zoe_t</code> structure, which is used for further operations.</li>
  <li><code class="language-plaintext highlighter-rouge">zoe_close</code> frees any allocations created by <code class="language-plaintext highlighter-rouge">zoe_open</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">zoe_files</code> returns the number of files in the appended zip file.</li>
  <li><code class="language-plaintext highlighter-rouge">zoe_name_at</code> returns the filename using an index number (0 to the number returned from <code class="language-plaintext highlighter-rouge">zoe_files()-1</code>).</li>
  <li><code class="language-plaintext highlighter-rouge">zoe_find</code> returns the index of a file with the given name, or <code class="language-plaintext highlighter-rouge">-1</code> if not found.</li>
  <li><code class="language-plaintext highlighter-rouge">zoe_fopen_at</code> opens a file in the appended zip using an index number and returns a <code class="language-plaintext highlighter-rouge">zoe_file_t</code> structure that contains a <code class="language-plaintext highlighter-rouge">FILE</code> pointer that can be used with the C buffered I/O library, along with the provided <code class="language-plaintext highlighter-rouge">ZOE_FILE</code> macro.</li>
  <li><code class="language-plaintext highlighter-rouge">zoe_fclose</code> closes the file opened by <code class="language-plaintext highlighter-rouge">zoe_fopen_at</code>.</li>
</ul>

<h3 id="usage">Usage</h3>

<p>Using <code class="language-plaintext highlighter-rouge">zoe</code> is straightforward. Simply call <code class="language-plaintext highlighter-rouge">zoe_open</code> with a full path to
your executable), and <code class="language-plaintext highlighter-rouge">zoe</code> will validate and open the appended zip file.
You can then use the other functions to retrieve information about the
files in the archive, or to open and read the files themselves.</p>

<h4 id="clone-zoe-and-build-it-like-this">Clone <code class="language-plaintext highlighter-rouge">zoe</code> and build it like this</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/octetta/zoe.git
<span class="nb">pushd </span>zoe<span class="p">;</span> make<span class="p">;</span> <span class="nb">popd</span>
</code></pre></div></div>

<h4 id="create-examplec-for-learning-like">Create <code class="language-plaintext highlighter-rouge">example.c</code> for learning like</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// example.c</span>
<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">"zoe/zoe.h"</span><span class="cp">
</span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
    <span class="n">zoe_t</span> <span class="o">*</span><span class="n">z</span> <span class="o">=</span> <span class="n">zoe_open</span><span class="p">(</span><span class="n">zoe_self</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]));</span>
    <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">zoe_files</span><span class="p">(</span><span class="n">z</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"%d files in %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"#%d -&gt; %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">zoe_name_at</span><span class="p">(</span><span class="n">z</span><span class="p">,</span> <span class="n">i</span><span class="p">));</span>
    <span class="n">zoe_close</span><span class="p">(</span><span class="n">z</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="build-and-run-the-example-without-any-attached-zip-file-like">Build and run the example without any attached zip file like</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gcc example.c zoe/libzoe.a <span class="nt">-o</span> example
<span class="nv">$ </span>./example
0 files <span class="k">in</span> ./example
</code></pre></div></div>

<h4 id="add-a-starting-zip-file-like">Add a starting <code class="language-plaintext highlighter-rouge">.zip</code> file like</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"this is the first archive file"</span> <span class="o">&gt;</span> first
<span class="nv">$ </span>zip first.zip first
  adding: first <span class="o">(</span>deflated 6%<span class="o">)</span>
<span class="nv">$ </span><span class="nb">cat </span>example first.zip <span class="o">&gt;</span> example.zip
<span class="nv">$ </span><span class="nb">chmod</span> +x example.zip
<span class="nv">$ </span>./example.zip
1 files <span class="k">in</span> ./example.zip
<span class="c">#0 -&gt; first (size:31)</span>
</code></pre></div></div>

<h4 id="add-new-file-to-the-examplezip-binary-like">Add new file to the <code class="language-plaintext highlighter-rouge">example.zip</code> binary like</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>zip <span class="nt">-A</span> example.zip example.c
Zip entry offsets appear off by 113576 bytes - correcting...
  adding: example.c <span class="o">(</span>deflated 40%<span class="o">)</span>
<span class="nv">$ </span>./example.zip
2 files <span class="k">in</span> ./example.zip
<span class="c">#0 -&gt; first (size:31)</span>
<span class="c">#1 -&gt; example.c (size:358)</span>
</code></pre></div></div>

<h4 id="update-an-existing-file-in-examplezip-like">Update an existing file in <code class="language-plaintext highlighter-rouge">example.zip</code> like</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"updating the first archive file"</span> <span class="o">&gt;</span> first
<span class="nv">$ </span>zip <span class="nt">-A</span> example.zip first
Zip entry offsets <span class="k">do </span>not need adjusting
updating: first <span class="o">(</span>deflated 3%<span class="o">)</span>
<span class="nv">$ </span>./example.zip
2 files <span class="k">in</span> ./example.zip
<span class="c">#0 -&gt; first (size:32)</span>
<span class="c">#1 -&gt; example.c (size:358)</span>
</code></pre></div></div>

<h4 id="remove-a-file-from-examplezip-like">Remove a file from <code class="language-plaintext highlighter-rouge">example.zip</code> like</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>zip <span class="nt">-A</span> <span class="nt">-d</span> example.zip example.c
Zip entry offsets <span class="k">do </span>not need adjusting
deleting: example.c
<span class="nv">$ </span>./example.zip
1 files <span class="k">in</span> ./example.zip
<span class="c">#0 -&gt; first (size:32)</span>
</code></pre></div></div>

<h3 id="where-to-find-out-more-about-zoe">Where to find out more about <code class="language-plaintext highlighter-rouge">zoe</code></h3>

<p><code class="language-plaintext highlighter-rouge">zoe</code> is on my GitHub page at https://github.com/octetta/zoe.git .</p>

<p>If you want to discuss <code class="language-plaintext highlighter-rouge">zoe</code>, DISCORD INFO HERE.</p>

<h3 id="wrapping-up">Wrapping up</h3>

<p>Overall, I’m happy with the results of my work on <code class="language-plaintext highlighter-rouge">zoe</code>. It’s a
lightweight and versatile library that has already made my life much easier.</p>

<p>If you’re looking for a way to package files with your executable I highly
recommend giving <code class="language-plaintext highlighter-rouge">zoe</code> a try.</p>]]></content><author><name></name></author><category term="opensource" /><summary type="html"><![CDATA[Have you ever needed to package a set of files with a binary executable, but found yourself struggling to keep track of multiple files and directories? Or perhaps you’ve wanted to create self-extracting archives that can be run without needing an external program? If so, then zoe may be just what you need.]]></summary></entry><entry><title type="html">Command History With `bestline`</title><link href="https://octetta.com/2023/05/01/command-history.html" rel="alternate" type="text/html" title="Command History With `bestline`" /><published>2023-05-01T10:00:00+02:00</published><updated>2023-05-01T10:00:00+02:00</updated><id>https://octetta.com/2023/05/01/command-history</id><content type="html" xml:base="https://octetta.com/2023/05/01/command-history.html"><![CDATA[<p>As an interlude from talking about Elixir and sound generation, I thought
I’d mention something I’ve been hacking on.</p>

<p>If you remember from the article
<a href="https://octetta.com/2023/03/13/elixir-and-amy.html">Elixir + AMY = BEEP!</a>,
I made a CLI tool to use for learning AMY’s
wire protocol that lets you type in a string, hit enter, and have
AMY make sound from that string.</p>

<p>That CLI tool was using C’s standard I/O library, which if you didn’t already
know, doesn’t have cursor handling or text editing abilities at all.</p>

<p>In the open source world, the well-known option to enable editing is
<a href="https://en.wikipedia.org/wiki/GNU_Readline">GNU Readline</a>, which is
perfectly capable, but as is with many GNU libraries, is a bit large
for what it does.</p>

<p>Another option is <a href="https://github.com/antirez/linenoise">antirez’ linenoise library</a>,
which in character for many things antirez has created is minimal, and beautiful.</p>

<p>The option I wound up working with is <a href="https://github.com/jart/bestline">jart’s bestline library</a>,
which is inspired by anterez’ but with jart’s extraordinary eye for completeness and
minimal resulting binary size.</p>

<p>There’s not much to say with regards to how I use <code class="language-plaintext highlighter-rouge">bestline</code> in my code, as it’s a
straightforward pattern like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">"bestline.h"</span><span class="cp">
</span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="o">*</span><span class="n">l</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">l</span> <span class="o">=</span> <span class="n">bestline</span><span class="p">(</span><span class="s">"r&gt; "</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">l</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"YOU TYPED -&gt; %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">l</span><span class="p">);</span>
        <span class="n">free</span><span class="p">(</span><span class="n">l</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It’s notable that this pattern, at least in single process / single-threaded programs,
means the code it’s linked with can’t respond to events while it’s in the
<code class="language-plaintext highlighter-rouge">bestline</code> function.</p>

<p>I chatted with jart about this and she suggested an elegant way to add this
functionality to <code class="language-plaintext highlighter-rouge">bestline</code> that would allow appearingly parallel functions, but in the end
I chose to add a function pointer / callback style to allow use of a custom <code class="language-plaintext highlighter-rouge">read</code>
function that’s responsible for demultiplexing input.</p>

<p>The new function I added to <code class="language-plaintext highlighter-rouge">bestline</code> is:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">bestlineUserIO</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">userReadFn</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">userWriteFn</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">userPollFn</span><span class="p">);</span>
</code></pre></div></div>

<p>Which gives you hooks into the read, write, and poll functions used by <code class="language-plaintext highlighter-rouge">bestline</code>.</p>

<p>I added a demo of this in the <code class="language-plaintext highlighter-rouge">bestline</code> codebase called <code class="language-plaintext highlighter-rouge">multi.c</code> which multiplexes
console input with a UDP socket listener by replacing the <code class="language-plaintext highlighter-rouge">userReadFn</code> with my
custom version wherein I use the <code class="language-plaintext highlighter-rouge">poll</code> function.</p>

<p>Check it out if you have similar needs.</p>]]></content><author><name></name></author><category term="opensource" /><summary type="html"><![CDATA[As an interlude from talking about Elixir and sound generation, I thought I’d mention something I’ve been hacking on.]]></summary></entry><entry><title type="html">Elixir and Ports</title><link href="https://octetta.com/2023/03/19/elixir-and-ports.html" rel="alternate" type="text/html" title="Elixir and Ports" /><published>2023-03-19T09:00:00+01:00</published><updated>2023-03-19T09:00:00+01:00</updated><id>https://octetta.com/2023/03/19/elixir-and-ports</id><content type="html" xml:base="https://octetta.com/2023/03/19/elixir-and-ports.html"><![CDATA[<p><em>Last week I wrote about using Elixir Ports to talk to the 
<a href="https://youtu.be/yL1ERELng3o" target="_blank" rel="noopener noreferrer">AMY</a>
sound engine.</em></p>

<p>You can read about that <a href="https://octetta.com/2023/03/13/elixir-and-amy.html">here</a>,
but as a short recap,
<em>Ports</em> allow Elixir to run and talk to external programs
such as <code class="language-plaintext highlighter-rouge">amy-message</code> through standard input and output channels.</p>

<p>With a Port created as <code class="language-plaintext highlighter-rouge">p</code>, this code:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">send</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="p">{</span><span class="n">self</span><span class="p">(),</span> <span class="p">{</span><span class="ss">:command</span><span class="p">,</span> <span class="s2">"v5w7p60"</span><span class="p">}})</span>
</code></pre></div></div>

<p>..will write <code class="language-plaintext highlighter-rouge">v5w7p60</code> to the input channel of <code class="language-plaintext highlighter-rouge">amy-message</code>.</p>

<div class="mermaid">
graph LR;
  A[Elixir]
  B[amy-message]
  A--"v5w7p60"--&gt;B;
</div>
<script async="" src="https://unpkg.com/mermaid@8.2.3/dist/mermaid.min.js"></script>

<p><em>This week, I’ll focus on getting responses from <code class="language-plaintext highlighter-rouge">amy-message</code> that we can use in
our code.</em></p>

<p>The Port module uses Elixir’s mailboxes to communicate with programs it starts.
This was demonstrated in the use of the <code class="language-plaintext highlighter-rouge">send</code> function above.</p>

<p>To get something back from a Port, we need to use the complimentary <code class="language-plaintext highlighter-rouge">receive</code> function, as:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">9</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">r</span> <span class="o">=</span> <span class="k">receive</span> <span class="k">do</span> <span class="n">msg</span> <span class="o">-&gt;</span> <span class="n">msg</span> <span class="k">end</span>
<span class="p">{</span><span class="c1">#Port&lt;0.6&gt;, {:data, ": "}}</span>
</code></pre></div></div>

<div class="mermaid">
graph LR;
  A[Elixir]
  B[amy-message]
  B--":&nbsp;"--&gt;A;
</div>

<p>In this example, the resulting <code class="language-plaintext highlighter-rouge">r</code> is a tuple containing
values telling us where the message came from and a tuple
containing an atom <code class="language-plaintext highlighter-rouge">:data</code> and the actual data <code class="language-plaintext highlighter-rouge">": "</code> sent by <code class="language-plaintext highlighter-rouge">amy-message</code>.</p>

<p>We can use pattern-matching to extract the actual data from this tuple like:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="p">{</span><span class="ss">:data</span><span class="p">,</span> <span class="n">s</span><span class="p">}}</span> <span class="o">=</span> <span class="n">r</span>
<span class="p">{</span><span class="c1">#Port&lt;0.6&gt;, {:data, ": "}}</span>
<span class="n">iex</span><span class="p">(</span><span class="mi">11</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">s</span>
<span class="s2">": "</span>
</code></pre></div></div>

<p>FYI, <code class="language-plaintext highlighter-rouge">s</code> contains <code class="language-plaintext highlighter-rouge">": "</code>, which is the prompt string <code class="language-plaintext highlighter-rouge">amy-message</code> wrote to indicate
it’s waiting for a new incoming command string.</p>

<p>There’s one thing to be cautious about with <code class="language-plaintext highlighter-rouge">receive</code> though.
If for some reason <code class="language-plaintext highlighter-rouge">amy-message</code> doesn’t
send a response, <code class="language-plaintext highlighter-rouge">receive</code> will wait forever for a response, making Elixir’s
<code class="language-plaintext highlighter-rouge">iex</code> appear to lock up (although the BEAM-VM is actually running just fine).</p>

<p>To safeguard against this, use <code class="language-plaintext highlighter-rouge">after</code> with the <code class="language-plaintext highlighter-rouge">receive</code> function and specify
the number of milliseconds to wait for a message, like this:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">r</span> <span class="o">=</span> <span class="k">receive</span> <span class="k">do</span> <span class="n">msg</span> <span class="o">-&gt;</span> <span class="n">msg</span> <span class="k">after</span> <span class="mi">1_000</span> <span class="o">-&gt;</span> <span class="ss">:timeout</span> <span class="k">end</span>
<span class="ss">:timeout</span>
</code></pre></div></div>

<p>…here we see the <code class="language-plaintext highlighter-rouge">:timeout</code> atom was returned after 1,000 milliseconds of nothing
in the mailbox.</p>

<p>To make the timeout condition easier to work with,
change the return structure to mimic a normal mailbox message by returning
a tuple containing a <code class="language-plaintext highlighter-rouge">:timeout</code> atom and an empty string:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">r</span> <span class="o">=</span> <span class="k">receive</span> <span class="k">do</span> <span class="n">msg</span> <span class="o">-&gt;</span> <span class="n">msg</span> <span class="k">after</span> <span class="mi">1_000</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="n">self</span><span class="p">(),</span> <span class="p">{</span><span class="ss">:timeout</span><span class="p">,</span> <span class="s2">""</span><span class="p">}}</span> <span class="k">end</span>
<span class="p">{</span><span class="c1">#PID&lt;0.108.0&gt;, {:timeout, ""}}</span>
<span class="n">iex</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">s</span><span class="p">}}</span> <span class="o">=</span> <span class="n">r</span>                                                      
<span class="p">{</span><span class="c1">#PID&lt;0.108.0&gt;, {:timeout, ""}}</span>
<span class="n">iex</span><span class="p">(</span><span class="mi">15</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">s</span>
<span class="s2">""</span>
</code></pre></div></div>

<p>Now that we have these basic elements in place, more complex
interactions with <code class="language-plaintext highlighter-rouge">amy-message</code> are possible.</p>

<p>A simple example would be to ask <code class="language-plaintext highlighter-rouge">amy-message</code> for its <em>sysclock</em> (
the number of milliseconds the AMY sound engine has been running) by
sending <code class="language-plaintext highlighter-rouge">@c</code>, then capturing the response:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">send</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="p">{</span><span class="n">self</span><span class="p">(),</span> <span class="p">{</span><span class="ss">:command</span><span class="p">,</span> <span class="s2">"@c"</span><span class="p">}})</span>                                  
<span class="p">{</span><span class="c1">#PID&lt;0.108.0&gt;, {:command, "@c"}}</span>
<span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">r</span> <span class="o">=</span> <span class="k">receive</span> <span class="k">do</span> <span class="n">msg</span> <span class="o">-&gt;</span> <span class="n">msg</span> <span class="k">after</span> <span class="mi">1_000</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="n">self</span><span class="p">(),</span> <span class="p">{</span><span class="ss">:timeout</span><span class="p">,</span> <span class="s2">""</span><span class="p">}}</span> <span class="k">end</span>
<span class="p">{</span><span class="c1">#Port&lt;0.7&gt;, {:data, "13531\n: "}}</span>
<span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="p">{</span><span class="ss">:data</span><span class="p">,</span> <span class="n">s</span><span class="p">}}</span> <span class="o">=</span> <span class="n">r</span>
<span class="p">{</span><span class="c1">#Port&lt;0.7&gt;, {:data, "13531\n: "}}</span>
</code></pre></div></div>

<p>There’s a little more work to do, as <code class="language-plaintext highlighter-rouge">s</code> contains two lines of text: the
sysclock number as a string, a return character <code class="language-plaintext highlighter-rouge">\n</code>, and the prompt character.</p>

<p>We handle this by splitting <code class="language-plaintext highlighter-rouge">s</code> into an array <code class="language-plaintext highlighter-rouge">a</code>, using <code class="language-plaintext highlighter-rouge">"\n"</code> to signal what to split on:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">18</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">a</span> <span class="o">=</span> <span class="no">String</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="p">[</span><span class="s2">"13531"</span><span class="p">,</span> <span class="s2">": "</span><span class="p">]</span>
</code></pre></div></div>

<p>Then convert the sysclock element of <code class="language-plaintext highlighter-rouge">a</code> into a number using a pipeline:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">21</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">sysclock</span> <span class="o">=</span> <span class="n">a</span> <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">at</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="no">String</span><span class="o">.</span><span class="n">to_integer</span>
<span class="mi">13531</span>
</code></pre></div></div>

<p>Now <code class="language-plaintext highlighter-rouge">sysclock</code> is a number where can perform math functions.</p>

<p>Knowing <em>sysclock</em> inside a running AMY engine will be useful as a way
to synchronize audio and events with AMY engines running on other devices.</p>

<p><em>We now have the basic structure for interacting with things started with the Port module!</em></p>

<p>One last thing to show is how to stop <code class="language-plaintext highlighter-rouge">amy-message</code> in a controlled manner:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">send</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="p">{</span><span class="n">self</span><span class="p">(),</span> <span class="p">{</span><span class="ss">:command</span><span class="p">,</span> <span class="s2">":q"</span><span class="p">}})</span>
</code></pre></div></div>

<p>…upon which, no trace should be left running.</p>

<p>You can verify this by:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pgrep -laf amy-message
</code></pre></div></div>

<p>I think we’ve covered enough for now, I certainly had to refer to the Elixir documents
<a href="https://elixir-lang.org/docs.html">here</a> to write this and hope it was useful for you.</p>

<p><em>Next time, I’ll present a module to simplify these interactions and show a
very simple event sequencer.</em></p>

<p>Until then, enjoy a few live-coding sessions showing new features
I’ve added to the AMY codebase:</p>

<style>
.iframe-container{
  position: relative;
  width: 100%;
  padding-bottom: 56.25%; 
  height: 0;
}
.iframe-container iframe{
  position: absolute;
  top:0;
  left: 0;
  width: 100%;
  height: 100%;
}
</style>

<p><br /></p>

<p><b><em>Capture a PCM patch, modify it, and store to a user PCM patch…</em></b></p>

<div class="iframe-container">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/yLpVCpF2w2I" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
</div>

<p><br /></p>

<p><b><em>Wave sequencing and the resonant low-pass filter…</em></b></p>

<div class="iframe-container">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Dy4pd1_UFSo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
</div>]]></content><author><name></name></author><category term="elixir" /><summary type="html"><![CDATA[Last week I wrote about using Elixir Ports to talk to the AMY sound engine.]]></summary></entry><entry><title type="html">Elixir + AMY = BEEP!</title><link href="https://octetta.com/2023/03/13/elixir-and-amy.html" rel="alternate" type="text/html" title="Elixir + AMY = BEEP!" /><published>2023-03-13T09:00:00+01:00</published><updated>2023-03-13T09:00:00+01:00</updated><id>https://octetta.com/2023/03/13/elixir-and-amy</id><content type="html" xml:base="https://octetta.com/2023/03/13/elixir-and-amy.html"><![CDATA[<p>As I continue my learning experience with Elixir, having a fun project to stay motivated is always a good idea.</p>

<p>I saw an interesting project called AMY on
<a href="https://news.ycombinator.com/item?id=34841489" target="_blank" rel="noopener noreferrer">Hacker News</a>
for sound generation (which has NOTHING to do with Elixir, BTW).</p>

<p>AMY provides a software audio synthesizer with 64 simultaneous “voices” (aka oscillators) that can generate
sound via several different methods:</p>

<ul>
  <li>Periodic pure waveforms</li>
  <li>Short audio samples</li>
  <li>FM/DX7 style synthesizer patches</li>
  <li>Cool stuff I don’t quite understand yet with “partials”</li>
</ul>

<p>For more details, the AMY github project is at
<a href="https://github.com/bwhitman/amy" target="_blank" rel="noopener noreferrer">github</a>
and has all the details.</p>

<p>AMY’s authors (
<a href="https://notes.variogram.com/" target="_blank" rel="noopener noreferrer">Brian Whitman</a>
and
<a href="https://research.google/people/DanEllis/" target="_blank" rel="noopener noreferrer">DAn Ellis</a>
) use it for another interesting project called the 
<a href="https://notes.variogram.com/2022/09/23/alles-amy/" target="_blank" rel="noopener noreferrer">Alles mesh synthesizer</a>
.</p>

<p>AMY and Alles are loving nods to sound experiments of the past, one of which I’d heard
about (Atari’s 1980’s
<a href="https://en.wikipedia.org/wiki/Atari_AMY" target="_blank" rel="noopener noreferrer">AMY sound chip</a>
) and the other I’d not (Hal Alles’ 1970’s
<a href="https://en.wikipedia.org/wiki/Bell_Labs_Digital_Synthesizer" target="_blank" rel="noopener noreferrer">Bell Labs Digital Synthesizer</a>
), but apparently the Atari AMY chip was inspired by the Alles synthesizer.</p>

<p>While Brian’s and DAn’s projects aren’t direct copies of their namesakes, the
inspiration is evident the more I learn.</p>

<p>Anyway, after playing with AMY (the software package) for several days, I wanted to get back to the Elixir part of this project,
and thought it would be interesting to connect the two.</p>

<p>Looking at the existing AMY code, there are three obvious methods that Elixir might be able to use for control.</p>

<ul>
  <li>A standalone test tool using the C-API that generates three tones using data structures and function calls.</li>
  <li>A WASM module with the entire C-API, used by their demo webpage.</li>
  <li>A Python module with wrappers for the C-API.</li>
</ul>

<p>None of these methods quite fit my mental picture, with the closest being the Python module.</p>

<p>This was slightly more complicated than I wanted however, and would require me to write data structure conversion
and method invocation code.</p>

<p>Rather, I wanted something that was somewhat like
a black-box, kind of how the POKEY chip of the Atari 400 or SID chip of the Commodore 64
worked:</p>

<h4 id="joe-in-1981"><em>Joe in 1981</em></h4>

<div class="mermaid">
graph LR;
  A[Atari 400 basic]
  B[POKEY sound chip]
  C[square wave sounds]
  A-- "magic <br />POKE" --&gt;B;
  B--&gt;C;
</div>
<script async="" src="https://unpkg.com/mermaid@8.2.3/dist/mermaid.min.js"></script>

<h4 id="joe-in-1982"><em>Joe in 1982</em></h4>

<div class="mermaid">
graph LR;
  A[Commodore 64 basic &amp;<br />6502 code]
  B[SID sound chip]
  C[more kinds of wave sounds]
  A-- "write to magic<br />addresses" --&gt;B;
  B--&gt;C;
</div>

<h4 id="joe-in-2023"><em>Joe in 2023</em></h4>

<div class="mermaid">
graph LR;
  A[Elixir code]
  B[AMY audio <br />synthesizer library]
  C[even more <br />amazing sounds]
  A-- some kind of <br />magic dust --&gt;B;
  B--&gt;C;
</div>

<p>One enticing option the API exposes is a function for what they call a
“wire protocol” that doesn’t require sharing complex data structures or functions.</p>

<p>The wire protocol uses short ASCII strings to control AMY, which the authors describe as a
way to “make using AMY from any sort of environment as easy as possible”.</p>

<p>Wire protocol messages are handled via a function named <code class="language-plaintext highlighter-rouge">amy_play_message</code> that accepts a
<code class="language-plaintext highlighter-rouge">char *</code> parameter.</p>

<p>Seems like this was exactly the magic dust I was looking for!</p>

<p>Using this, my strategy is to create a standalone binary to pass wire protocol via
standard input into the <code class="language-plaintext highlighter-rouge">amy_play_message</code> function.</p>

<div class="mermaid">
sequenceDiagram
participant iex as Iex shell
participant amy as amy-message
participant out as speaker
iex -&gt;&gt;+ amy : launch binary as child process
amy --&gt;&gt; iex : open stdin and stdout channels
iex -&gt;&gt; amy : send "v0w0f440l1" to stdout
amy --&gt;&gt; iex : send response
amy --&gt;&gt; out : generate 440 Hz sine wave
</div>

<p>I contributed the <code class="language-plaintext highlighter-rouge">amy-message</code> code to the AMY github codebase and have been using it to learn
the protocol and the behavior of AMY’s voices.</p>

<p>I have an uncommited change that enables command-line history and editing when <code class="language-plaintext highlighter-rouge">amy-message</code> is run from a shell;
reach out if you’d like more details.</p>

<p>Another rationale for creating <code class="language-plaintext highlighter-rouge">amy-message</code> was to use it as a “port driver” inside Elixir.</p>

<p>Port drivers are discussed at
<a href="https://hexdocs.pm/elixir/main/Port.html" target="_blank" rel="noopener noreferrer">hex.pm</a>,
but the short version is Elixir runs an external program uses stdin/stdout as a communication channel.</p>

<p>Port drivers provide a relatively safe way to extend Elixir
functionality without introducing unsafe code into Elixir’s BEAM VM.</p>

<p>Since AMY and the associated sound libraries start threads and have some amount of dynamic memory allocation
that might be “dangerous”, I feel
better about isolating things in a child process to keep BEAM safe.</p>

<p>With this work in place, it’s just a matter of putting the <code class="language-plaintext highlighter-rouge">amy-message</code> binary somewhere
Elixir can find it, and sending wire procotol strings through <code class="language-plaintext highlighter-rouge">Port</code> functions.</p>

<ul>
  <li>Here’s a summary of use</li>
</ul>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Port</span><span class="o">.</span><span class="n">open</span><span class="p">({</span><span class="ss">:spawn</span><span class="p">,</span> <span class="s2">"./amy-message -l"</span><span class="p">},</span> <span class="p">[</span><span class="ss">:binary</span><span class="p">])</span>
<span class="n">flush</span><span class="p">()</span> <span class="c1"># see the available audio backends</span>
<span class="n">p</span> <span class="o">=</span> <span class="no">Port</span><span class="o">.</span><span class="n">open</span><span class="p">({</span><span class="ss">:spawn</span><span class="p">,</span> <span class="s2">"./amy-message -d 0"</span><span class="p">},</span> <span class="p">[</span><span class="ss">:binary</span><span class="p">])</span>
<span class="n">send</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="p">{</span><span class="n">self</span><span class="p">(),</span> <span class="p">{</span><span class="ss">:command</span><span class="p">,</span> <span class="s2">"v5w7p60"</span><span class="p">}})</span>  <span class="c1"># set osc5 to PCM "bird" patch</span>
<span class="n">send</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="p">{</span><span class="n">self</span><span class="p">(),</span> <span class="p">{</span><span class="ss">:command</span><span class="p">,</span> <span class="s2">"v5l1"</span><span class="p">}})</span>  <span class="c1"># play osc5</span>
</code></pre></div></div>

<ul>
  <li>Here’s a video of me building and running it…</li>
</ul>

<style>
.iframe-container{
  position: relative;
  width: 100%;
  padding-bottom: 56.25%; 
  height: 0;
}
.iframe-container iframe{
  position: absolute;
  top:0;
  left: 0;
  width: 100%;
  height: 100%;
}
</style>

<div class="iframe-container">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/8tnYzZzLVKI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
</div>

<ul>
  <li>
    <p>Here’s a hastily written web app inspired from an AMY example to try the wire protocol.</p>
  </li>
  <li>
    <p><a href="https://octetta.com/bob.html" target="_blank" rel="noopener noreferrer">Joe’s AMY explorer</a></p>
  </li>
</ul>

<p>After all this, it might seem like very little Elixir learning happened, which is kind of true, but
this is organically growing towards an audio project for Elixir I hope to write more of and about.</p>

<p><em>So stay tuned…</em></p>

<hr />

<p>Notes:</p>

<p>AMY currently runs on Windows, macOS, and Linux systems (including Raspberry Pi’s), so this
same approach should work equally well on these platforms. I’ve mostly run on x86_64 Linux, but lightly
tested on macOS and a Raspberry Pi 3B.</p>

<p>AMY also targets ESP32 and other minimal horsepower platforms. It’s conceivable that a variation
of the port example above could send wire protocol via UART or Bluetooth, or something else.</p>

<hr />

<p>Links:</p>

<ul>
  <li><a href="https://notes.variogram.com/amy/" target="_blank" rel="noopener noreferrer">Brian Whitman’s web AMY example</a></li>
  <li><a href="https://youtu.be/l0wVHq_8Zic" target="_blank" rel="noopener noreferrer">Interview with an Atari AMY chip developer Tom Zimmerman</a></li>
  <li><a href="https://youtu.be/NChqEEz31eE" target="_blank" rel="noopener noreferrer">Laurie Spiegel playing a Alles synthesizer in 1977</a></li>
  <li><a href="https://youtu.be/t-up62BP8WY" target="_blank" rel="noopener noreferrer">Judy Jackson playing a Alles synthesizer in 2016</a></li>
</ul>]]></content><author><name></name></author><category term="elixir" /><summary type="html"><![CDATA[As I continue my learning experience with Elixir, having a fun project to stay motivated is always a good idea.]]></summary></entry><entry><title type="html">Experiments in blogging</title><link href="https://octetta.com/2022/12/07/experiments-in-blogging.html" rel="alternate" type="text/html" title="Experiments in blogging" /><published>2022-12-07T21:00:00+01:00</published><updated>2022-12-07T21:00:00+01:00</updated><id>https://octetta.com/2022/12/07/experiments-in-blogging</id><content type="html" xml:base="https://octetta.com/2022/12/07/experiments-in-blogging.html"><![CDATA[<p>I decided to try a different platform that Wordpress for this site.</p>

<p>I won’t try to justify the exact reasons why I’m not using WP, but instead
will list what I wanted:</p>

<p><a href="" target="_blank" rel="noopener noreferrer"></a></p>

<ul rel="noopener noreferrer">
  <li>Native use of <a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#GitHub-flavored-markdown" rel="noopener noreferrer">Markdown</a></li>
  <li>Static rendering of the site</li>
  <li>Easy to backup to <a href="https://github.com" target="_blank" rel="noopener noreferrer">github</a></li>
  <li>Preprocessors for code highlighting and text-as-diagrams</li>
</ul>

<p>I tried several based on
<a href="https://rust-lang.github.io/mdBook" target="_blank" rel="noopener noreferrer">Rust</a>,
<a href="https://gohugo.io" target="_blank" rel="noopener noreferrer">Go</a>, and even
<a href="https://dalgona.github.io/Serum" target="_blank" rel="noopener noreferrer">Elixir</a>, but wound up
choosing the Ruby tool <a href="https://jekyllrb.com" target="_blank" rel="noopener noreferrer">Jekyll</a>.</p>

<p><img src="https://upload.wikimedia.org/wikipedia/commons/0/09/Wordpress-Logo.svg" alt="" /></p>]]></content><author><name></name></author><category term="heroes" /><summary type="html"><![CDATA[I decided to try a different platform that Wordpress for this site.]]></summary></entry><entry><title type="html">That time I met Forrest M. Mims III</title><link href="https://octetta.com/2022/12/03/that-time-i-met-forrest-mims.html" rel="alternate" type="text/html" title="That time I met Forrest M. Mims III" /><published>2022-12-03T21:00:00+01:00</published><updated>2022-12-03T21:00:00+01:00</updated><id>https://octetta.com/2022/12/03/that-time-i-met-forrest-mims</id><content type="html" xml:base="https://octetta.com/2022/12/03/that-time-i-met-forrest-mims.html"><![CDATA[<p>Back in 2014, I treated myself to a VIP pass to Moogfest (I’ll write about
that another day because it was AWESOME in so many ways).</p>

<p>One talk I was super excited to attend was Forrest M. Mims III’s but realized
at the last minute that it overlapped a builder session I’d already paid to attend.</p>

<p>If you don’t know the name, he’s pretty much a hero in the hobby electronics
world. He had a monthly column in one of the magazines I treasured in the 70’s (Popular
Electronics) in addition a whole series of books and kits sold at Radio Shack.</p>

<p>His books have this distinct hand-written style:</p>

<p><img src="/images/forrest-timer.webp" alt="" /></p>

<p>(snipped from his book <a href="https://amzn.to/3ukA86y" target="_blank" rel="noopener noreferrer">Getting Started in Electronics</a>)</p>

<p>Back to the Moogfest part of this, I snuck out of the builder session
to see if I could sneak a peek at the beginning of his talk and to my
surprise, he was standing outside the venue waiting to get set up.</p>

<p>He was super nice and chatty.</p>

<p>Apparently his wife Minnie is his “official” photographer and she volunteered to take this
picture of us.</p>

<p><img src="/images/forrest.webp" alt="" /></p>

<p>He’s still active with atmospheric observations on his <a href="https://www.facebook.com/forrest.mimsiii" target="_blank" rel="noopener noreferrer">Facebook</a> page.</p>]]></content><author><name></name></author><category term="heroes" /><summary type="html"><![CDATA[Back in 2014, I treated myself to a VIP pass to Moogfest (I’ll write about that another day because it was AWESOME in so many ways).]]></summary></entry><entry><title type="html">Fixing ssh after updating to Ubuntu 22.04</title><link href="https://octetta.com/2022/12/01/ubuntu-upgrade.html" rel="alternate" type="text/html" title="Fixing ssh after updating to Ubuntu 22.04" /><published>2022-12-01T21:00:00+01:00</published><updated>2022-12-01T21:00:00+01:00</updated><id>https://octetta.com/2022/12/01/ubuntu-upgrade</id><content type="html" xml:base="https://octetta.com/2022/12/01/ubuntu-upgrade.html"><![CDATA[<p>After I updated from Ubuntu 20.04 to 22.04, <code class="language-plaintext highlighter-rouge">ssh</code>-ing into my
web-host via key-auth yielded this odd error:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scp A-FILE <span class="nv">$HOST</span>:
Unable to negotiate with <span class="nv">$HOST</span> port 22:
  no matching host key <span class="nb">type </span>found. Their offer: ssh-rsa,ssh-dss
</code></pre></div></div>

<p>A bit of googling showed other folks having the same problem fixed it
the following way:</p>

<ul>
  <li>Add these lines at the top of <code class="language-plaintext highlighter-rouge">.ssh/config</code></li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HostKeyAlgorithms ssh-rsa
PubkeyAcceptedKeyTypes ssh-rsa
</code></pre></div></div>

<ul>
  <li>For each host in <code class="language-plaintext highlighter-rouge">.ssh/config</code> that complains when you <code class="language-plaintext highlighter-rouge">ssh</code> or <code class="language-plaintext highlighter-rouge">scp</code>:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>host <span class="nv">$HOST</span>
    <span class="nb">hostname</span> <span class="nv">$HOST</span>.com
    user <span class="nv">$USER</span>
    HostKeyAlgorithms +ssh-rsa,ssh-dss
</code></pre></div></div>]]></content><author><name></name></author><category term="ubuntu" /><summary type="html"><![CDATA[After I updated from Ubuntu 20.04 to 22.04, ssh-ing into my web-host via key-auth yielded this odd error:]]></summary></entry><entry><title type="html">Elixir gen_tcp example</title><link href="https://octetta.com/2022/11/29/elixir-gen-tcp.html" rel="alternate" type="text/html" title="Elixir gen_tcp example" /><published>2022-11-29T00:00:00+01:00</published><updated>2022-11-29T00:00:00+01:00</updated><id>https://octetta.com/2022/11/29/elixir-gen-tcp</id><content type="html" xml:base="https://octetta.com/2022/11/29/elixir-gen-tcp.html"><![CDATA[<p>Quick and dirty… open two shell <code class="language-plaintext highlighter-rouge">iex</code> sessions:</p>

<p>In first session (this will be the server):</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">socket</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:gen_tcp</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">3000</span><span class="p">,</span> <span class="p">[</span><span class="ss">:binary</span><span class="p">,</span> <span class="ss">active:</span> <span class="no">false</span><span class="p">])</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="c1">#Port&lt;0.6&gt;}</span>
<span class="n">iex</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">data</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:gen_tcp</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<p>From the second session:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">server</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:gen_tcp</span><span class="o">.</span><span class="n">connect</span><span class="p">({</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">},</span> <span class="mi">3000</span><span class="p">,</span> <span class="p">[])</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="c1">#Port&lt;0.6&gt;}</span>
<span class="n">iex</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">&gt;</span> <span class="ss">:gen_tcp</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="s2">"hello"</span><span class="p">)</span>
<span class="ss">:ok</span>
</code></pre></div></div>

<p>Back in the first session:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">data</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:gen_tcp</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> 
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="s2">"hello"</span><span class="p">}</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="elixir" /><category term="networking" /><summary type="html"><![CDATA[Quick and dirty… open two shell iex sessions:]]></summary></entry><entry><title type="html">Elixir gen_udp example</title><link href="https://octetta.com/2022/11/28/elixir-gen-udp.html" rel="alternate" type="text/html" title="Elixir gen_udp example" /><published>2022-11-28T00:00:00+01:00</published><updated>2022-11-28T00:00:00+01:00</updated><id>https://octetta.com/2022/11/28/elixir-gen-udp</id><content type="html" xml:base="https://octetta.com/2022/11/28/elixir-gen-udp.html"><![CDATA[<p>Quick and dirty UDP server/client… open two shell <code class="language-plaintext highlighter-rouge">iex</code> sessions:</p>

<p>In first session (this will be the server):</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">socket</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:gen_udp</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="mi">3000</span><span class="p">,</span> <span class="p">[</span><span class="ss">:binary</span> <span class="p">,</span> <span class="ss">active:</span> <span class="no">false</span><span class="p">])</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="c1">#Port&lt;0.7&gt;}</span>
</code></pre></div></div>

<p>From the second session:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">socket</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:gen_udp</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="mi">3001</span><span class="p">)</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="c1">#Port&lt;0.8&gt;}</span>
<span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="ss">:gen_udp</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">},</span> <span class="mi">3000</span><span class="p">,</span> <span class="s2">"yo"</span><span class="p">)</span>
<span class="ss">:ok</span>
</code></pre></div></div>

<p>Back in the first session:</p>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">data</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:gen_udp</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">socket</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="p">{</span> <span class="p">{</span><span class="mi">127</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">},</span> <span class="mi">3001</span><span class="p">,</span> <span class="s2">"yo"</span><span class="p">}</span> <span class="p">}</span>
</code></pre></div></div>

<div class="plantuml"><svg contentStyleType='text/css' height='105px' preserveAspectRatio='none' style='width:65px;height:105px;background:#FFFFFF;' version='1.1' viewBox='0 0 65 105' width='65px' xmlns:xlink='http://www.w3.org/1999/xlink' xmlns='http://www.w3.org/2000/svg' zoomAndPan='magnify'><defs/><g><line style='stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;' x1='16' x2='16' y1='36.0986' y2='70.0986'/><line style='stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;' x1='48' x2='48' y1='36.0986' y2='70.0986'/><rect fill='#E2E2F0' height='30.0986' rx='2.5' ry='2.5' style='stroke:#181818;stroke-width:0.5;' width='22' x='5' y='5'/><text fill='#000000' font-family='sans-serif' font-size='14' lengthAdjust='spacing' textLength='8' x='12' y='25.1318'>a</text><rect fill='#E2E2F0' height='30.0986' rx='2.5' ry='2.5' style='stroke:#181818;stroke-width:0.5;' width='22' x='5' y='69.0986'/><text fill='#000000' font-family='sans-serif' font-size='14' lengthAdjust='spacing' textLength='8' x='12' y='89.2305'>a</text><rect fill='#E2E2F0' height='30.0986' rx='2.5' ry='2.5' style='stroke:#181818;stroke-width:0.5;' width='22' x='37' y='5'/><text fill='#000000' font-family='sans-serif' font-size='14' lengthAdjust='spacing' textLength='8' x='44' y='25.1318'>b</text><rect fill='#E2E2F0' height='30.0986' rx='2.5' ry='2.5' style='stroke:#181818;stroke-width:0.5;' width='22' x='37' y='69.0986'/><text fill='#000000' font-family='sans-serif' font-size='14' lengthAdjust='spacing' textLength='8' x='44' y='89.2305'>b</text><polygon fill='#181818' points='36,48.0986,46,52.0986,36,56.0986,40,52.0986' style='stroke:#181818;stroke-width:1.0;'/><line style='stroke:#181818;stroke-width:1.0;' x1='16' x2='42' y1='52.0986' y2='52.0986'/></g></svg></div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/0.4.0/mermaid.full.min.js"></script>

<div class="mermaid">
graph TD;
  A--&gt;B;
  A--&gt;C;
  B--&gt;D;
  C--&gt;D;
<div>
</div></div>]]></content><author><name></name></author><category term="elixir" /><category term="networking" /><summary type="html"><![CDATA[Quick and dirty UDP server/client… open two shell iex sessions:]]></summary></entry></feed>