<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://devtails.xyz/feed.xml" rel="self" type="application/atom+xml" /><link href="https://devtails.xyz/" rel="alternate" type="text/html" /><updated>2025-01-28T22:46:13+00:00</updated><id>https://devtails.xyz/feed.xml</id><title type="html">dev/tails</title><subtitle>Thoughts, stories, and tutorials about software development</subtitle><author><name>{&quot;email&quot;=&gt;&quot;adam@devtails.xyz&quot;}</name><email>adam@devtails.xyz</email></author><entry><title type="html">The Maze v0.0.0</title><link href="https://devtails.xyz/the-maze" rel="alternate" type="text/html" title="The Maze v0.0.0" /><published>2025-01-28T00:00:00+00:00</published><updated>2025-01-28T00:00:00+00:00</updated><id>https://devtails.xyz/the-maze</id><content type="html" xml:base="https://devtails.xyz/the-maze"><![CDATA[<p>It’s been nearly a year since my last post. I’ve hesitated to write because I get so caught up in what I should say that I never actually start. This time, though, I opened a blank text editor with a single goal: to write about how developing a product feels like navigating a maze.</p>

<p>The empty title field stares back at me, almost daring me to stop before I’ve even begun. “Don’t bother writing this,” it seems to say, “until you’ve figured out exactly what it is you want to say.” And maybe it’s right—this first version probably won’t be worth sharing. I might never share it. But even so, I’m pressing forward, trying to let the words flow and quiet the editor in my head.</p>

<p>Here’s the gist: building software is like walking through a maze. Somewhere at its center lies the “perfect product.” You take one path that seems promising — it leads closer to the center — but it ends in a dead end. You try again, another route, and another. Each brings you closer, but none gets you all the way there.</p>

<p>The real challenge comes when you need to leave a local maximum, a spot that feels good enough but isn’t quite it. If your product already has users, the stakes are higher. You can’t just let things get worse while you course-correct. Instead, you have to split your focus: keep everything working as it is while figuring out a new path that takes you closer to the elusive center.</p>

<p>It reminds me of the maze in Westworld — a symbol of self-discovery and mastery, where every twist and turn is part of the journey toward something greater. Like that maze, product development isn’t just about reaching the center; it’s about learning, adapting, and finding the courage to keep moving forward, even when the path isn’t clear.</p>]]></content><author><name>adam</name></author><category term="tails" /><summary type="html"><![CDATA[It’s been nearly a year since my last post. I’ve hesitated to write because I get so caught up in what I should say that I never actually start. This time, though, I opened a blank text editor with a single goal: to write about how developing a product feels like navigating a maze.]]></summary></entry><entry><title type="html">How to Create a Waveform Video From Audio Using HTML Canvas and JavaScript</title><link href="https://devtails.xyz/@adam/how-to-create-waveform-video-from-audio-using-html-canvas-and-javascript" rel="alternate" type="text/html" title="How to Create a Waveform Video From Audio Using HTML Canvas and JavaScript" /><published>2024-02-16T00:00:00+00:00</published><updated>2024-02-16T00:00:00+00:00</updated><id>https://devtails.xyz/@adam/how-to-create-waveform-video-from-audio</id><content type="html" xml:base="https://devtails.xyz/@adam/how-to-create-waveform-video-from-audio-using-html-canvas-and-javascript"><![CDATA[<p>This tutorial covers how to draw an audio waveform on an HTML canvas using JavaScript.  Once you understand the code below you can export the canvas to a video file by following <a href="/adam/how-to-save-html-canvas-to-mp4-using-web-codecs-api">How to Save HTML Canvas to Mp4 Using WebCodecs API 10x Faster Than Realtime in the Browser</a> or <a href="@adam/how-to-record-html-canvas-using-mediarecorder-and-export-as-video">How to Record HTML Canvas using MediaRecorder and Export as Video</a>.  The first option renders more quickly, but you may find the canvas recording option has more consistent browser support.</p>

<h2 id="demo">Demo</h2>

<p>Below is an example of what this will look like:</p>

<video src="/assets/video/reel-generator-sample.mp4" controls="" height="400px"></video>

<p>The above was created using kaizen.place’s <a href="https://kaizen.place/reel-generator?utm_medium=blog&amp;utm_source=devtails.xyz&amp;utm_content=textlink">Audio to Waveform Video Reel Generator</a>. This post is a simplified version of this tool to showcase how to convert audio data into a visual waveform representation.</p>

<h2 id="the-code">The Code</h2>

<h3 id="create-indexhtml">Create index.html</h3>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- index.html --&gt;</span>
<span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span>Audio to Waveform Video<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"index.js"</span> <span class="na">defer</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
  
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<h3 id="create-indexjs">Create index.js</h3>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">run</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">audioBuffer</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">loadAndDecodeAudio</span><span class="p">(</span><span class="dl">"</span><span class="s2">sample.mp3</span><span class="dl">"</span><span class="p">);</span>

  <span class="c1">// For simplicity, only using the first channel of data</span>
  <span class="kd">const</span> <span class="nx">channelData</span> <span class="o">=</span> <span class="nx">audioBuffer</span><span class="p">.</span><span class="nx">getChannelData</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>

  <span class="c1">// This tracks the maximum average seen across all chunks</span>
  <span class="kd">let</span> <span class="nx">max</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="c1">// How many milliseconds a chunk represents</span>
  <span class="kd">const</span> <span class="nx">msPerChunk</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>

  <span class="c1">// How many data points will be included in each chunk</span>
  <span class="kd">const</span> <span class="nx">chunkSize</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="nx">audioBuffer</span><span class="p">.</span><span class="nx">sampleRate</span> <span class="o">*</span> <span class="nx">msPerChunk</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">);</span>

  <span class="c1">// To get the average we need to sum up all values in the chunk</span>
  <span class="kd">let</span> <span class="nx">chunkTotalValue</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="c1">// As we compute chunk averages store them in this array</span>
  <span class="kd">let</span> <span class="nx">chunkAverages</span> <span class="o">=</span> <span class="p">[];</span>

  <span class="c1">// This primarily helps cover the final case where a chunk has fewer values</span>
  <span class="c1">// than the chunk size</span>
  <span class="kd">let</span> <span class="nx">currentChunkSize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">audioBuffer</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Channel data will be between -1 and 1</span>
    <span class="c1">// Absolute value ensures negatives don't just cancel out positives</span>
    <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="nx">channelData</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>

    <span class="nx">currentChunkSize</span><span class="o">++</span><span class="p">;</span>

    <span class="nx">chunkTotalValue</span> <span class="o">+=</span> <span class="nx">value</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="nx">i</span> <span class="o">%</span> <span class="nx">chunkSize</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">i</span> <span class="o">===</span> <span class="nx">audioBuffer</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">chunkAverage</span> <span class="o">=</span> <span class="nx">chunkTotalValue</span> <span class="o">/</span> <span class="nx">currentChunkSize</span><span class="p">;</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">chunkAverage</span> <span class="o">&gt;</span> <span class="nx">max</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">max</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="nx">chunkAverages</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">chunkAverage</span><span class="p">);</span>
      <span class="nx">chunkTotalValue</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="nx">currentChunkSize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// Use the max average we found to normalize the averages to be between 0 and 1</span>
  <span class="kd">const</span> <span class="nx">normalizedChunkValues</span> <span class="o">=</span> <span class="nx">chunkAverages</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">avg</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">avg</span> <span class="o">/</span> <span class="nx">max</span><span class="p">;</span>
  <span class="p">});</span>

  <span class="c1">// Create a canvas and add to the document to draw on</span>
  <span class="kd">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="mi">720</span><span class="p">;</span>
  <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="mi">1280</span><span class="p">;</span>

  <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">canvas</span><span class="p">);</span>

  <span class="kd">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span>

  <span class="nx">render</span><span class="p">({</span>
    <span class="nx">canvas</span><span class="p">,</span>
    <span class="nx">ctx</span><span class="p">,</span>
    <span class="nx">normalizedChunkValues</span><span class="p">,</span>
    <span class="na">startTime</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">(),</span>
    <span class="nx">msPerChunk</span><span class="p">,</span>
  <span class="p">});</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">render</span><span class="p">({</span> <span class="nx">canvas</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">,</span> <span class="nx">normalizedChunkValues</span><span class="p">,</span> <span class="nx">startTime</span><span class="p">,</span> <span class="nx">msPerChunk</span> <span class="p">})</span> <span class="p">{</span>
  <span class="c1">// The elapsedTime allows us to know how far into the audio we are</span>
  <span class="kd">const</span> <span class="nx">elapsedTime</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> <span class="o">-</span> <span class="nx">startTime</span><span class="p">;</span>

  <span class="c1">// Clear the entire canvas to remove any drawings from previous frame</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">#000000</span><span class="dl">"</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</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="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">#FFFFFF</span><span class="dl">"</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">barWidth</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">barSpacing</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">maxBarHeight</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">normalizedChunkValues</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// normalizedChunkValues will be a float 0-1 - a percentage of max amplitude</span>
    <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">normalizedChunkValues</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
    <span class="c1">// The highest amplitude part of audio will get a bar at the max height</span>
    <span class="kd">const</span> <span class="nx">barHeight</span> <span class="o">=</span> <span class="nx">maxBarHeight</span> <span class="o">*</span> <span class="nx">value</span><span class="p">;</span>

    <span class="c1">// This moves the bars based on how much time has passed</span>
    <span class="kd">const</span> <span class="nx">xOffset</span> <span class="o">=</span> <span class="p">(</span><span class="nx">elapsedTime</span> <span class="o">/</span> <span class="nx">msPerChunk</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="nx">barWidth</span> <span class="o">+</span> <span class="nx">barSpacing</span><span class="p">);</span>

    <span class="c1">// Spaces out the bars</span>
    <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="nx">i</span> <span class="o">*</span> <span class="p">(</span><span class="nx">barWidth</span> <span class="o">+</span> <span class="nx">barSpacing</span><span class="p">)</span> <span class="o">-</span> <span class="nx">xOffset</span><span class="p">;</span>

    <span class="c1">// Centers the bars to the middle of the canvas</span>
    <span class="kd">const</span> <span class="nx">y</span> <span class="o">=</span> <span class="p">(</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">-</span> <span class="nx">barHeight</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>

    <span class="c1">// Draws the bar at the calculated position and size</span>
    <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">barWidth</span><span class="p">,</span> <span class="nx">barHeight</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="c1">// Calls this function again at the start of the next frame</span>
  <span class="c1">// Typically this is 60fps, but will depend on the display rate of your monitor</span>
  <span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">render</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="p">...</span><span class="nx">arguments</span><span class="p">));</span>
<span class="p">}</span>

<span class="c1">// Helper function to fetch and decode from a URL</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nx">loadAndDecodeAudio</span><span class="p">(</span><span class="nx">audioURL</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">audioURL</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">arrayBuffer</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">arrayBuffer</span><span class="p">();</span>
  <span class="k">return</span> <span class="nx">decodeAudioData</span><span class="p">(</span><span class="nx">arrayBuffer</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Decodes the ArrayBuffer into an AudioBuffer</span>
<span class="c1">// This gives access to the raw channel data which we use to generate the waveform</span>
<span class="c1">// https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer</span>
<span class="c1">// https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/decodeAudioData</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nx">decodeAudioData</span><span class="p">(</span><span class="nx">arrayBuffer</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">audioContext</span> <span class="o">=</span> <span class="k">new</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">AudioContext</span> <span class="o">||</span>
      <span class="nb">window</span><span class="p">.</span><span class="nx">webkitAudioContext</span><span class="p">)();</span>
    <span class="nx">audioContext</span><span class="p">.</span><span class="nx">decodeAudioData</span><span class="p">(</span><span class="nx">arrayBuffer</span><span class="p">,</span> <span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">);</span>
  <span class="p">});</span>
<span class="p">}</span>

<span class="nx">run</span><span class="p">();</span>
</code></pre></div></div>

<h3 id="try-it-out">Try it out</h3>

<h4 id="add-samplemp3">Add sample.mp3</h4>

<p>You can use whatever audio file you like.  If you don’t have one you can grab a sample one from <a href="https://file-examples.com/index.php/sample-audio-files/sample-mp3-download/">here</a>.</p>

<h4 id="run-http-server">Run http-server</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm i <span class="nt">-g</span> http-server
http-server <span class="nb">.</span>
</code></pre></div></div>

<h4 id="open-browser">Open Browser</h4>

<p>Head to <a href="http://localhost:8080">http://localhost:8080</a> and you should see the canvas animating through the waveform generated from the audio file.</p>

<h3 id="next-steps">Next Steps</h3>

<p>This creates a nice animation in the browser, but if you want to send this somewhere it is very likely you want to download this as a video file (specifically mp4).  You can either follow <a href="/adam/how-to-save-html-canvas-to-mp4-using-web-codecs-api">How to Save HTML Canvas to Mp4 Using WebCodecs API 10x Faster Than Realtime in the Browser</a> or <a href="@adam/how-to-record-html-canvas-using-mediarecorder-and-export-as-video">How to Record HTML Canvas using MediaRecorder and Export as Video</a> to take this canvas drawing and export a video file.</p>

<p>Alternatively, if it suits your needs you can use the kaizen.place <a href="https://kaizen.place/reel-generator?utm_medium=blog&amp;utm_source=devtails.xyz&amp;utm_content=textlink">Audio to Waveform Video Reel Generator</a>.</p>]]></content><author><name>adam</name></author><category term="dev" /><category term="js" /><summary type="html"><![CDATA[Tutorial for how to render a waveform animation using the audio data from a sound file in the browser.]]></summary></entry><entry><title type="html">How to Save HTML Canvas to Mp4 Using WebCodecs API 10x Faster Than Realtime in the Browser</title><link href="https://devtails.xyz/adam/how-to-save-html-canvas-to-mp4-using-web-codecs-api" rel="alternate" type="text/html" title="How to Save HTML Canvas to Mp4 Using WebCodecs API 10x Faster Than Realtime in the Browser" /><published>2024-02-13T00:00:00+00:00</published><updated>2024-02-13T00:00:00+00:00</updated><id>https://devtails.xyz/adam/how-to-save-html-canvas-to-mp4-using-web-codecs-api</id><content type="html" xml:base="https://devtails.xyz/adam/how-to-save-html-canvas-to-mp4-using-web-codecs-api"><![CDATA[<h2 id="the-problem">The Problem</h2>

<p>In working on an Audio <a href="https://kaizen.place/reel-generator">Waveform Reel Generator</a> for <a href="https://kaizen.place/">kaizen.place</a>, I found a few resources to record a canvas using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder">MediaRecorder</a> API [<a href="https://julien-decharentenay.medium.com/how-to-save-html-canvas-animation-as-a-video-421157c2203b">1</a>][<a href="https://blog.theodo.com/2023/03/saving-canvas-animations/">2</a>], a method used for the initial implementation (I wrote about it <a href="https://devtails.xyz/@adam/how-to-record-html-canvas-using-mediarecorder-and-export-as-video">here</a>). Unfortunately, the only browser that seemed to record directly to <code class="language-plaintext highlighter-rouge">mp4</code> was Safari.  Chrome would record as a <code class="language-plaintext highlighter-rouge">webm</code> file which Instagram doesn’t support.</p>

<p>We were able to bridge the gap by using <a href="https://github.com/ffmpegwasm/ffmpeg.wasm">ffmpeg.wasm</a> to convert our webm file to an mp4.  However, on top of a 60 second recording session, we now also had another step in the pipeline and a 30 MB download, slowing things down even more.  With some careful codec selection, it was possible in some browsers to record webm with a codec that could simply be copied over to an mp4 container, which brought things to a somewhat acceptable level.</p>

<p>The MediaRecorder quality wasn’t great and I knew the drawing operations couldn’t be taking more than a millisecond, which meant most of the time was spent just waiting. I knew there must be a better way.</p>

<h2 id="the-solution">The Solution</h2>

<p>The code presented below uses the <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API">WebCodecs API</a> and the <a href="https://github.com/Vanilagy/mp4-muxer">mp4-muxer</a> npm package to encode a video from a canvas source up to 10 times faster than realtime.  A working demo of this code can be found <a href="https://canvas-to-mp4-main-dev-tails.engram.sh/">here</a>.</p>

<h2 id="initialize-project">Initialize Project</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm init
</code></pre></div></div>

<h2 id="install-mp4-muxer">Install <a href="https://github.com/Vanilagy/mp4-muxer/tree/main">mp4-muxer</a></h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm i mp4-muxer
</code></pre></div></div>

<h2 id="create-indexhtml">Create Index.html</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;html <span class="nv">lang</span><span class="o">=</span><span class="s2">"en"</span><span class="o">&gt;</span>
&lt;<span class="nb">head</span><span class="o">&gt;</span>
  &lt;meta <span class="nv">charset</span><span class="o">=</span><span class="s2">"UTF-8"</span><span class="o">&gt;</span>
  &lt;meta <span class="nv">name</span><span class="o">=</span><span class="s2">"viewport"</span> <span class="nv">content</span><span class="o">=</span><span class="s2">"width=device-width, initial-scale=1.0"</span><span class="o">&gt;</span>
  &lt;title&gt;Canvas To MP4&lt;/title&gt;
  &lt;script <span class="nv">src</span><span class="o">=</span><span class="s2">"index.js"</span><span class="o">&gt;</span>&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
  
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div></div>

<h2 id="install-esbuild">Install esbuild</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm i esbuild
</code></pre></div></div>

<h2 id="update-build-command-in-packagejson">Update Build Command in package.json</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"scripts"</span>: <span class="o">{</span>
  <span class="s2">"build"</span>: <span class="s2">"esbuild src/index.ts --bundle --outfile=public/index.js"</span>
<span class="o">}</span>,
</code></pre></div></div>

<h2 id="add-javascript">Add JavaScript</h2>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="o">*</span> <span class="k">as</span> <span class="nx">Mp4Muxer</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">mp4-muxer</span><span class="dl">"</span><span class="p">;</span>

<span class="k">async</span> <span class="kd">function</span> <span class="nx">run</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OffscreenCanvas</span><span class="p">(</span><span class="mi">720</span><span class="p">,</span> <span class="mi">1280</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span>
    <span class="c1">// This forces the use of a software (instead of hardware accelerated) 2D canvas</span>
    <span class="c1">// This isn't necessary, but produces quicker results</span>
    <span class="na">willReadFrequently</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="c1">// Desynchronizes the canvas paint cycle from the event loop</span>
    <span class="c1">// Should be less necessary with OffscreenCanvas, but with a real canvas you will want this</span>
    <span class="na">desynchronized</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
  <span class="p">});</span>

  <span class="kd">const</span> <span class="nx">fps</span> <span class="o">=</span> <span class="mi">30</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">duration</span> <span class="o">=</span> <span class="mi">60</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">numFrames</span> <span class="o">=</span> <span class="nx">duration</span> <span class="o">*</span> <span class="nx">fps</span><span class="p">;</span>

  <span class="kd">let</span> <span class="nx">muxer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Mp4Muxer</span><span class="p">.</span><span class="nx">Muxer</span><span class="p">({</span>
    <span class="na">target</span><span class="p">:</span> <span class="k">new</span> <span class="nx">Mp4Muxer</span><span class="p">.</span><span class="nx">ArrayBufferTarget</span><span class="p">(),</span>

    <span class="na">video</span><span class="p">:</span> <span class="p">{</span>
      <span class="c1">// If you change this, make sure to change the VideoEncoder codec as well</span>
      <span class="na">codec</span><span class="p">:</span> <span class="dl">"</span><span class="s2">avc</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">width</span><span class="p">:</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span>
      <span class="na">height</span><span class="p">:</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
    <span class="p">},</span>

    <span class="c1">// mp4-muxer docs claim you should always use this with ArrayBufferTarget</span>
    <span class="na">fastStart</span><span class="p">:</span> <span class="dl">"</span><span class="s2">in-memory</span><span class="dl">"</span><span class="p">,</span>
  <span class="p">});</span>

  <span class="kd">let</span> <span class="nx">videoEncoder</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VideoEncoder</span><span class="p">({</span>
    <span class="na">output</span><span class="p">:</span> <span class="p">(</span><span class="nx">chunk</span><span class="p">,</span> <span class="nx">meta</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">muxer</span><span class="p">.</span><span class="nx">addVideoChunk</span><span class="p">(</span><span class="nx">chunk</span><span class="p">,</span> <span class="nx">meta</span><span class="p">),</span>
    <span class="na">error</span><span class="p">:</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">),</span>
  <span class="p">});</span>

  <span class="c1">// This codec should work in most browsers</span>
  <span class="c1">// See https://dmnsgn.github.io/media-codecs for list of codecs and see if your browser supports</span>
  <span class="nx">videoEncoder</span><span class="p">.</span><span class="nx">configure</span><span class="p">({</span>
    <span class="na">codec</span><span class="p">:</span> <span class="dl">"</span><span class="s2">avc1.42001f</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">width</span><span class="p">:</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span>
    <span class="na">height</span><span class="p">:</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
    <span class="na">bitrate</span><span class="p">:</span> <span class="mi">500</span><span class="nx">_000</span><span class="p">,</span>
    <span class="na">bitrateMode</span><span class="p">:</span> <span class="dl">"</span><span class="s2">constant</span><span class="dl">"</span><span class="p">,</span>
  <span class="p">});</span>

  <span class="c1">// Loops through and draws each frame to the canvas then encodes it</span>
  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">frameNumber</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">frameNumber</span> <span class="o">&lt;</span> <span class="nx">numFrames</span><span class="p">;</span> <span class="nx">frameNumber</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">drawFrameToCanvas</span><span class="p">({</span>
      <span class="nx">ctx</span><span class="p">,</span>
      <span class="nx">canvas</span><span class="p">,</span>
      <span class="nx">frameNumber</span><span class="p">,</span>
      <span class="nx">numFrames</span>
    <span class="p">});</span>
    <span class="nx">renderCanvasToVideoFrameAndEncode</span><span class="p">({</span>
      <span class="nx">canvas</span><span class="p">,</span>
      <span class="nx">videoEncoder</span><span class="p">,</span>
      <span class="nx">frameNumber</span><span class="p">,</span>
      <span class="nx">fps</span>
    <span class="p">})</span>
  <span class="p">}</span>

  <span class="c1">// Forces all pending encodes to complete</span>
  <span class="k">await</span> <span class="nx">videoEncoder</span><span class="p">.</span><span class="nx">flush</span><span class="p">();</span>

  <span class="nx">muxer</span><span class="p">.</span><span class="nx">finalize</span><span class="p">();</span>

  <span class="kd">let</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="nx">muxer</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">buffer</span><span class="p">;</span>
  <span class="nx">downloadBlob</span><span class="p">(</span><span class="k">new</span> <span class="nx">Blob</span><span class="p">([</span><span class="nx">buffer</span><span class="p">]));</span>
<span class="p">}</span>

<span class="c1">// Animates a red box moving from top left to top right of screen</span>
<span class="kd">function</span> <span class="nx">drawFrameToCanvas</span><span class="p">({</span> <span class="nx">canvas</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">,</span> <span class="nx">frameNumber</span><span class="p">,</span> <span class="nx">numFrames</span> <span class="p">})</span> <span class="p">{</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">white</span><span class="dl">"</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</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="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>

  <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="p">(</span><span class="nx">frameNumber</span> <span class="o">/</span> <span class="nx">numFrames</span><span class="p">)</span> <span class="o">*</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>

  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">red</span><span class="dl">"</span><span class="p">;</span>
  <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">async</span> <span class="kd">function</span> <span class="nx">renderCanvasToVideoFrameAndEncode</span><span class="p">({</span>
  <span class="nx">canvas</span><span class="p">,</span>
  <span class="nx">videoEncoder</span><span class="p">,</span>
  <span class="nx">frameNumber</span><span class="p">,</span>
  <span class="nx">fps</span><span class="p">,</span>
<span class="p">})</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">frame</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VideoFrame</span><span class="p">(</span><span class="nx">canvas</span><span class="p">,</span> <span class="p">{</span>
    <span class="c1">// Equally spaces frames out depending on frames per second</span>
    <span class="na">timestamp</span><span class="p">:</span> <span class="p">(</span><span class="nx">frameNumber</span> <span class="o">*</span> <span class="mi">1</span><span class="nx">e6</span><span class="p">)</span> <span class="o">/</span> <span class="nx">fps</span><span class="p">,</span>
  <span class="p">});</span>

  <span class="c1">// The encode() method of the VideoEncoder interface asynchronously encodes a VideoFrame</span>
  <span class="nx">videoEncoder</span><span class="p">.</span><span class="nx">encode</span><span class="p">(</span><span class="nx">frame</span><span class="p">);</span>

  <span class="c1">// The close() method of the VideoFrame interface clears all states and releases the reference to the media resource.</span>
  <span class="nx">frame</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">downloadBlob</span><span class="p">(</span><span class="nx">blob</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">url</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">createObjectURL</span><span class="p">(</span><span class="nx">blob</span><span class="p">);</span>
  <span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">a</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">a</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">none</span><span class="dl">"</span><span class="p">;</span>
  <span class="nx">a</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">url</span><span class="p">;</span>
  <span class="nx">a</span><span class="p">.</span><span class="nx">download</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">animation.mp4</span><span class="dl">"</span><span class="p">;</span>
  <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
  <span class="nx">a</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
  <span class="nb">window</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">revokeObjectURL</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
<span class="p">}</span>

<span class="nx">run</span><span class="p">();</span>
</code></pre></div></div>

<h2 id="test-it-out-with-http-server">Test it Out With http-server</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm i http-server
</code></pre></div></div>

<h3 id="add-start-script-to-packagejson">Add Start Script to package.json</h3>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http-server public"</span><span class="w">
</span><span class="p">}</span><span class="err">,</span><span class="w">
</span></code></pre></div></div>

<h3 id="run-server">Run Server</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm start
</code></pre></div></div>

<p>By default, should be accessible at <a href="http://127.0.0.1:8080">http://127.0.0.1:8080</a></p>

<h2 id="resources">Resources</h2>

<p>[1] <a href="https://blog.theodo.com/2023/03/saving-canvas-animations/">Saving Canvas Animations With Media Recorder</a></p>

<p>[2] <strong><a href="https://julien-decharentenay.medium.com/how-to-save-html-canvas-animation-as-a-video-421157c2203b">How to save html canvas animation as a video</a></strong></p>]]></content><author><name>adam</name></author><category term="dev" /><category term="js" /><summary type="html"><![CDATA[After using the MediaRecorder to capture a canvas as a video, I figured there was a better way.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devtails.xyz/assets/img/canvas-to-mp4.png" /><media:content medium="image" url="https://devtails.xyz/assets/img/canvas-to-mp4.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">2024-01-01 Update</title><link href="https://devtails.xyz/adam/2024-01-01-update" rel="alternate" type="text/html" title="2024-01-01 Update" /><published>2024-01-01T00:00:00+00:00</published><updated>2024-01-01T00:00:00+00:00</updated><id>https://devtails.xyz/adam/update</id><content type="html" xml:base="https://devtails.xyz/adam/2024-01-01-update"><![CDATA[<p>It’s the start of the new year, and like most other people I’m rethinking a lot of things and how to improve.  Last year I purchased adamberg.blog and considered using that for more consistent updates that were less SEO focused and more stream of consciousness.  I never really stuck with it, but every time I did write a post there I got big value out of it.  My domain subscription lapsed and so I was left with wondering whether I renew or just put that on this site.</p>

<p>Ultimately, I’d like to be writing a lot more consistently and I think doing so in one place is the most likely way to successfully do this.  I write so infrequently that I don’t have a good routine.  There are certain things I like about this Jekyll blog, but things like adding screenshots is such a hassle that I procrastinate certain posts because I know it will be too painful.  Then I convince myself that I can just build something to make this more tolerable.  I already have, and it’s called “hyde”.  I’m still a bit lost as to whether it’s supposed to replace jekyll or simply work in tandem with it.  The name is apt because I literally feel like Dr. Jekyll getting angry and turning into Hyde.</p>

<p>These update posts are for those curious about what I’m currently working on and wanting a deeper look into the thoughts behind things.  My very first post on adamberg.blog was about how I wanted and needed to get off of Twitter.  It ended up taking me most of the year to finally make that leap.  I found it very difficult to not open Twitter several times a day, despite never really enjoying the experience.  I would much rather spend my time creating than consuming and so having a space that only lets me do one of those things is incredibly valuable to me.</p>

<h2 id="kaizen">Kaizen</h2>

<p>This year <a href="https://kaizen.place">kaizen.place</a> will be a key focus for me.  My good friend and co-founder, Ben, is coming on full-time and we will be learning and exploring how to make this a success for music creators and as a business.  This journey will inevitably have lots of twists and turns, but I’m looking forward to the adventure.</p>

<h2 id="youtube">YouTube</h2>

<p>I am again and again reminded that crafting good YouTube tutorials is where I should be focusing energy.  My tutorial on <a href="https://www.youtube.com/watch?v=8nOi-0kBv2Y">Writing a Wav File in C</a> picked up 10,000 views over the holidays and led to 233 subscribers so far.  And I feel like it’s just getting started.  There are so many things I want to put together, but I need to hone my focus and align goals of different projects.  This most likely means continuing to focus on audio programming topics to be able to showcase the evolution of these projects on my <a href="https://kaizen.place/music-tails">music.tails Kaizen account</a>.</p>

<h2 id="instagram">Instagram</h2>

<p>This is probably the hardest one for me.  I would like to be consistent in posting, particularly on my <a href="https://www.instagram.com/music.tails/">music.tails</a> account.  I find so many barriers and excuses for not posting here.  What I really need to do is set up some kind of schedule where I block off time to make/prepare content.  e.g. I have a bunch of footage from my first band performance from last year that has never seen the light of day.  Going through and cutting out clips is just enough work and far enough outside my comfort zone that I continue to defer doing this.</p>

<h2 id="solo-live-music-performance">Solo Live Music Performance</h2>

<p>In early February, I plan to put together a performance of some songs I have been learning to sing and play on piano.  I’m hoping to lock down a date and venue this week so I can stop delaying this.</p>

<h2 id="thrashing">Thrashing</h2>

<p>The word thrashing came up recently as a computer term mapped to the human brain.  I have definitely been thrashing the last little while.  I have so many things I want to do and others that I need to do and others yet that others want me to do.  This year, I want to be more mindful of this and do a better job of prioritizing and probably delegating.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I’ve already spent longer on this than I would have liked, but at least I made it.  Especially early on, I’d like to attempt to do this daily.  Essentially like a public journal.  I think this would help me narrow down focus and force some accountability on myself.  If I can get it down to 15-20 minutes of writing or less should be esay enough to have part of a morning routine.</p>]]></content><author><name>adam</name></author><category term="updates" /><category term="c++" /><summary type="html"><![CDATA[It’s the start of the new year, and like most other people I’m rethinking a lot of things and how to improve. Last year I purchased adamberg.blog and considered using that for more consistent updates that were less SEO focused and more stream of consciousness. I never really stuck with it, but every time I did write a post there I got big value out of it. My domain subscription lapsed and so I was left with wondering whether I renew or just put that on this site.]]></summary></entry><entry><title type="html">Getting Started With PineTime Watch</title><link href="https://devtails.xyz/adam/2023-pinetime-getting-started" rel="alternate" type="text/html" title="Getting Started With PineTime Watch" /><published>2023-12-31T00:00:00+00:00</published><updated>2023-12-31T00:00:00+00:00</updated><id>https://devtails.xyz/adam/pinetime-getting-started</id><content type="html" xml:base="https://devtails.xyz/adam/2023-pinetime-getting-started"><![CDATA[<p>When I first got this watch I was excited, but was immediately overwhelmed by the documentation to the point that I thought I wouldn’t actually be able to reprogram it.  After pushing through I eventually narrowed down the information into what worked for me.  This post will go over how to build and install your own custom version of <a href="https://github.com/InfiniTimeOrg/InfiniTime.git">InfiniTime</a> onto your PineTime watch.</p>

<h2 id="clone-infinitime-repo">Clone InfiniTime Repo</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/InfiniTimeOrg/InfiniTime.git
<span class="nb">cd </span>InfiniTime
git submodule update <span class="nt">--init</span>
</code></pre></div></div>

<h2 id="build-project-with-docker">Build Project With Docker</h2>

<p>This uses their image from Docker Hub that has all the dependencies you need to compile the source. See further explanation <a href="https://github.com/InfiniTimeOrg/InfiniTime/blob/main/doc/buildWithDocker.md">here</a></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">--rm</span> <span class="nt">-it</span> <span class="nt">-v</span> <span class="k">${</span><span class="nv">PWD</span><span class="k">}</span>:/sources <span class="nt">--user</span> <span class="si">$(</span><span class="nb">id</span> <span class="nt">-u</span><span class="si">)</span>:<span class="si">$(</span><span class="nb">id</span> <span class="nt">-g</span><span class="si">)</span> infinitime/infinitime-build
</code></pre></div></div>

<h2 id="flash-using-openocd-and-stlink">Flash Using OpenOCD and STLink</h2>

<p>Official docs <a href="https://github.com/InfiniTimeOrg/InfiniTime/blob/main/doc/openOCD.md">here</a></p>

<h3 id="install-openocd">Install openocd</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>openocd
</code></pre></div></div>

<h3 id="create-file-openocd-stlinkocd">Create File openocd-stlink.ocd</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>source [find interface/stlink.cfg]

gdb_flash_program enable
gdb_breakpoint_override hard

source [find target/nrf52.cfg]
</code></pre></div></div>

<h3 id="create-file-flash_bootloader_appocd">Create File flash_bootloader_app.ocd</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>init

program ./build/output/pinetime-mcuboot-app-image-1.13.0.bin verify 0x00008000

reset
</code></pre></div></div>

<h3 id="connect-stlinkv2">Connect STLinkV2</h3>

<p>See screenshots <a href="https://github.com/InfiniTimeOrg/InfiniTime/blob/main/doc/openOCD.md#connect-the-stlinkv2-to-the-pinetime">here</a> for correct orientation.  Pins should be connected to SWCLK, SWDIO, GND, and 3.3V on the ST-Link V2 side of things.  The wire coloring seems to be standard, so you should have:</p>

<p>Brown - SWCLK
Red - SWDIO
White - GND
Black - 3.3V</p>

<p>When plugging pins into PineTime, orient the programmable pins so they are on the far side from you and then the red pin should go in the left most pin.</p>

<p>I tend to plug pins into the PineTime first before plugging in the USB portion.  If you flip the watch face over and rest it on the pins it seems to hold itself up while applying pressure on the pins so you can actually see what’s happening without</p>

<h3 id="flash-using-openocd">Flash Using OpenOCD</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openocd -f ./openocd-stlink.ocd -f ./flash_bootloader_app.ocd
</code></pre></div></div>

<h2 id="flashing-pinetime-over-the-air-ota">Flashing PineTime Over-The-Air (OTA)</h2>

<p>This appears to be mostly smartphone driven, which is a bit confusing to imagine what kind of workflow one would use with that.  I managed to install <a href="https://apps.a pple.com/ca/app/nrf-connect-for-mobile/id1054362403">nRF Connect Mobile</a> on my M1 Mac and started an OTA update.  It took a very look time and then at 99% said there was an errror.  The STLink method is so much faster and reliable that I probably won’t explore this any further.</p>

<h2 id="infinitime-simulator">Infinitime Simulator</h2>

<p><a href="https://github.com/InfiniTimeOrg/InfiniSim">InfiniSim</a> seems to expect that you will be running on Linux.  I imagine it should be possible to get working on Mac, but the number of hours I have fought SDL2 and CMake to work correctly, I decided to just try it on Ubuntu.  Following their listed instructions on Ubuntu I was mostly able to get things going.  <code class="language-plaintext highlighter-rouge">lv_img_conv.py</code> gave a “SyntaxError: invalid syntax” at line 163 <code class="language-plaintext highlighter-rouge">match args.color_format:</code>.  I ended up just modifying the file to remove the match statement altogether and forcing one of the conditions.  I assume it’s a python version issue, but didn’t have the energy to figure out what the problem was.  Never ceases to amaze me how difficult reproducible builds are…</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone <span class="nt">--recursive</span> https://github.com/InfiniTimeOrg/InfiniSim.git
<span class="nb">cd </span>InfiniSim
git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>

<span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> cmake libsdl2-dev g++ npm libpng-dev
npm <span class="nb">install </span>lv_font_conv@1.5.2

cmake <span class="nt">-S</span> <span class="nb">.</span> <span class="nt">-B</span> build
cmake <span class="nt">--build</span> build <span class="nt">-j4</span>
</code></pre></div></div>]]></content><author><name>adam</name></author><category term="dev" /><category term="c++" /><summary type="html"><![CDATA[When I first got this watch I was excited, but was immediately overwhelmed by the documentation to the point that I thought I wouldn’t actually be able to reprogram it. After pushing through I eventually narrowed down the information into what worked for me. This post will go over how to build and install your own custom version of InfiniTime onto your PineTime watch.]]></summary></entry><entry><title type="html">Advent of Code (AoC) 2023 Day 3 - Gear Ratios - Solution in JavaScript (JS)</title><link href="https://devtails.xyz/adam/2023-advent-of-code-day-3" rel="alternate" type="text/html" title="Advent of Code (AoC) 2023 Day 3 - Gear Ratios - Solution in JavaScript (JS)" /><published>2023-12-03T00:00:00+00:00</published><updated>2023-12-03T00:00:00+00:00</updated><id>https://devtails.xyz/adam/aoc</id><content type="html" xml:base="https://devtails.xyz/adam/2023-advent-of-code-day-3"><![CDATA[<p>Solution for Advent of Code (AoC) 2023 <a href="https://adventofcode.com/2023/day/3">Day 3: Gear Ratios</a> in JavaScript:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">fs</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">fs</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">log</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">console</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">lines</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">./day3.txt</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">encoding</span><span class="p">:</span> <span class="dl">"</span><span class="s2">utf-8</span><span class="dl">"</span> <span class="p">}).</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="se">\n</span><span class="dl">"</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">isDigit</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="sr">/</span><span class="se">[\d]</span><span class="sr">/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">c</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">part1</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">[</span><span class="nx">rowIndex</span><span class="p">,</span> <span class="nx">line</span><span class="p">]</span> <span class="k">of</span> <span class="nx">lines</span><span class="p">.</span><span class="nx">entries</span><span class="p">())</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">partNumber</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">startColIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">[</span><span class="nx">colIndex</span><span class="p">,</span> <span class="nx">c</span><span class="p">]</span> <span class="k">of</span> <span class="nx">line</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">''</span><span class="p">).</span><span class="nx">entries</span><span class="p">())</span> <span class="p">{</span>

      <span class="k">if</span> <span class="p">(</span><span class="nx">isDigit</span><span class="p">(</span><span class="nx">c</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">partNumber</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">startColIndex</span> <span class="o">=</span> <span class="nx">colIndex</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="nx">partNumber</span> <span class="o">+=</span> <span class="nx">c</span><span class="p">;</span>
      <span class="p">}</span>

      <span class="kd">const</span> <span class="nx">isEndOfPartNumber</span> <span class="o">=</span> <span class="o">!</span><span class="nx">isDigit</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="nx">colIndex</span> <span class="o">===</span> <span class="nx">line</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">isEndOfPartNumber</span> <span class="o">&amp;&amp;</span> <span class="nx">partNumber</span><span class="p">)</span> <span class="p">{</span>

        <span class="kd">const</span> <span class="nx">startRowSearchIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">rowIndex</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">endRowSearchIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">rowIndex</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">lines</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">startColSearchIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">startColIndex</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">endColSearchIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">colIndex</span><span class="p">,</span> <span class="nx">line</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>

        <span class="kd">let</span> <span class="nx">symbolFound</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
        <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="nx">startRowSearchIndex</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;=</span> <span class="nx">endRowSearchIndex</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
          <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">j</span> <span class="o">=</span> <span class="nx">startColSearchIndex</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&lt;=</span> <span class="nx">endColSearchIndex</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="sr">/</span><span class="se">[^\d\.]</span><span class="sr">/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">lines</span><span class="p">[</span><span class="nx">i</span><span class="p">][</span><span class="nx">j</span><span class="p">]))</span> <span class="p">{</span>
              <span class="nx">symbolFound</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
            <span class="p">}</span>
          <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="p">(</span><span class="nx">symbolFound</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">sum</span> <span class="o">+=</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">partNumber</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="nx">partNumber</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="nx">log</span><span class="p">(</span><span class="nx">sum</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">part2</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">gearMap</span> <span class="o">=</span> <span class="p">{};</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">[</span><span class="nx">rowIndex</span><span class="p">,</span> <span class="nx">line</span><span class="p">]</span> <span class="k">of</span> <span class="nx">lines</span><span class="p">.</span><span class="nx">entries</span><span class="p">())</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">partNumber</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">startColIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">[</span><span class="nx">colIndex</span><span class="p">,</span> <span class="nx">c</span><span class="p">]</span> <span class="k">of</span> <span class="nx">line</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">''</span><span class="p">).</span><span class="nx">entries</span><span class="p">())</span> <span class="p">{</span>

      <span class="k">if</span> <span class="p">(</span><span class="nx">isDigit</span><span class="p">(</span><span class="nx">c</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">partNumber</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">startColIndex</span> <span class="o">=</span> <span class="nx">colIndex</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="nx">partNumber</span> <span class="o">+=</span> <span class="nx">c</span><span class="p">;</span>
      <span class="p">}</span>

      <span class="kd">const</span> <span class="nx">isEndOfPartNumber</span> <span class="o">=</span> <span class="o">!</span><span class="nx">isDigit</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="nx">colIndex</span> <span class="o">===</span> <span class="nx">line</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">isEndOfPartNumber</span> <span class="o">&amp;&amp;</span> <span class="nx">partNumber</span><span class="p">)</span> <span class="p">{</span>

        <span class="kd">const</span> <span class="nx">startRowSearchIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">rowIndex</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">endRowSearchIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">rowIndex</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">lines</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">startColSearchIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">startColIndex</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">endColSearchIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">colIndex</span><span class="p">,</span> <span class="nx">line</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>

        <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="nx">startRowSearchIndex</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;=</span> <span class="nx">endRowSearchIndex</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
          <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">j</span> <span class="o">=</span> <span class="nx">startColSearchIndex</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&lt;=</span> <span class="nx">endColSearchIndex</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="sr">/</span><span class="se">\*</span><span class="sr">/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">lines</span><span class="p">[</span><span class="nx">i</span><span class="p">][</span><span class="nx">j</span><span class="p">]))</span> <span class="p">{</span>
              <span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="s2">`</span><span class="p">${</span><span class="nx">i</span><span class="p">}</span><span class="s2">-</span><span class="p">${</span><span class="nx">j</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span>
              <span class="k">if</span> <span class="p">(</span><span class="nx">gearMap</span><span class="p">[</span><span class="nx">key</span><span class="p">])</span> <span class="p">{</span>
                <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">gearMap</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">*</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">partNumber</span><span class="p">);</span>
              <span class="p">}</span>

              <span class="nx">gearMap</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">partNumber</span><span class="p">);</span>
            <span class="p">}</span>
          <span class="p">}</span>
        <span class="p">}</span>

        <span class="nx">partNumber</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="nx">log</span><span class="p">(</span><span class="nx">sum</span><span class="p">)</span>
<span class="p">}</span>

<span class="nx">part1</span><span class="p">();</span>
<span class="nx">part2</span><span class="p">();</span>
</code></pre></div></div>]]></content><author><name>adam</name></author><category term="dev" /><category term="js" /><summary type="html"><![CDATA[Solution for Advent of Code (AoC) 2023 Day 3 - Gear Ratios - Solution in JavaScript (JS)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devtails.xyz/assets/img/advent-of-code-2023-day-3-gear-ratios-in-javascript.png" /><media:content medium="image" url="https://devtails.xyz/assets/img/advent-of-code-2023-day-3-gear-ratios-in-javascript.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Advent of Code (AoC) 2023 Day 2 - Cube Conundrum - Solution in JavaScript (JS)</title><link href="https://devtails.xyz/adam/2023-advent-of-code-day-two" rel="alternate" type="text/html" title="Advent of Code (AoC) 2023 Day 2 - Cube Conundrum - Solution in JavaScript (JS)" /><published>2023-12-02T00:00:00+00:00</published><updated>2023-12-02T00:00:00+00:00</updated><id>https://devtails.xyz/adam/aoc</id><content type="html" xml:base="https://devtails.xyz/adam/2023-advent-of-code-day-two"><![CDATA[<p>Solution for Advent of Code (AoC) 2023 <a href="https://adventofcode.com/2023/day/2">Day 2: Cube Conundrum</a> in JavaScript:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">fs</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">fs</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">log</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">console</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">lines</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">./day2.txt</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">encoding</span><span class="p">:</span> <span class="dl">"</span><span class="s2">utf-8</span><span class="dl">"</span> <span class="p">}).</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="se">\n</span><span class="dl">"</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">part1</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">maxDict</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">red</span><span class="p">:</span> <span class="mi">12</span><span class="p">,</span>
    <span class="na">green</span><span class="p">:</span> <span class="mi">13</span><span class="p">,</span>
    <span class="na">blue</span><span class="p">:</span> <span class="mi">14</span><span class="p">,</span>
  <span class="p">};</span>

  <span class="kd">let</span> <span class="nx">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kd">let</span> <span class="nx">gameId</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">line</span> <span class="k">of</span> <span class="nx">lines</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">passes</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

    <span class="kd">const</span> <span class="nx">sets</span> <span class="o">=</span> <span class="nx">line</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/Game </span><span class="se">[\d]</span><span class="sr">*:</span><span class="se">\s</span><span class="sr">/g</span><span class="p">,</span> <span class="dl">""</span><span class="p">).</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2">;</span><span class="dl">"</span><span class="p">);</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="kd">set</span> <span class="k">of</span> <span class="nx">sets</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">colorWithCount</span> <span class="o">=</span> <span class="kd">set</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2">,</span><span class="dl">"</span><span class="p">);</span>
      <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">colorCount</span> <span class="k">of</span> <span class="nx">colorWithCount</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">color</span><span class="p">]</span> <span class="o">=</span> <span class="nx">colorCount</span><span class="p">.</span><span class="nx">trim</span><span class="p">().</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">count</span> <span class="o">&gt;</span> <span class="nx">maxDict</span><span class="p">[</span><span class="nx">color</span><span class="p">])</span> <span class="p">{</span>
          <span class="nx">passes</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
        <span class="p">}</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">passes</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">gameId</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="nx">gameId</span><span class="o">++</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nx">log</span><span class="p">(</span><span class="nx">sum</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">part2</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">line</span> <span class="k">of</span> <span class="nx">lines</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">sets</span> <span class="o">=</span> <span class="nx">line</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/Game </span><span class="se">[\d]</span><span class="sr">*:</span><span class="se">\s</span><span class="sr">/g</span><span class="p">,</span> <span class="dl">""</span><span class="p">).</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2">;</span><span class="dl">"</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">gameMaxMap</span> <span class="o">=</span> <span class="p">{};</span>

    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="kd">set</span> <span class="k">of</span> <span class="nx">sets</span><span class="p">)</span> <span class="p">{</span>

      <span class="kd">const</span> <span class="nx">colorWithCount</span> <span class="o">=</span> <span class="kd">set</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2">,</span><span class="dl">"</span><span class="p">);</span>
      <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">colorCount</span> <span class="k">of</span> <span class="nx">colorWithCount</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">color</span><span class="p">]</span> <span class="o">=</span> <span class="nx">colorCount</span><span class="p">.</span><span class="nx">trim</span><span class="p">().</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">);</span>

        <span class="kd">const</span> <span class="nx">countAsNum</span> <span class="o">=</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">count</span><span class="p">);</span>

        <span class="kd">const</span> <span class="nx">currentMax</span> <span class="o">=</span> <span class="nx">gameMaxMap</span><span class="p">[</span><span class="nx">color</span><span class="p">]</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">countAsNum</span> <span class="o">&gt;</span> <span class="nx">currentMax</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">gameMaxMap</span><span class="p">[</span><span class="nx">color</span><span class="p">]</span> <span class="o">=</span> <span class="nx">countAsNum</span><span class="p">;</span>
        <span class="p">}</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">red</span> <span class="o">=</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">gameMaxMap</span><span class="p">[</span><span class="dl">"</span><span class="s2">red</span><span class="dl">"</span><span class="p">])</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">green</span> <span class="o">=</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">gameMaxMap</span><span class="p">[</span><span class="dl">"</span><span class="s2">green</span><span class="dl">"</span><span class="p">])</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">blue</span> <span class="o">=</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">gameMaxMap</span><span class="p">[</span><span class="dl">"</span><span class="s2">blue</span><span class="dl">"</span><span class="p">])</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">power</span> <span class="o">=</span> <span class="nx">red</span> <span class="o">*</span> <span class="nx">green</span> <span class="o">*</span> <span class="nx">blue</span><span class="p">;</span>

    <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">power</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nx">log</span><span class="p">(</span><span class="nx">sum</span><span class="p">);</span>
<span class="p">}</span>

<span class="nx">part1</span><span class="p">();</span>
<span class="nx">part2</span><span class="p">();</span>
</code></pre></div></div>]]></content><author><name>adam</name></author><category term="dev" /><category term="js" /><summary type="html"><![CDATA[Solution for Advent of Code (AoC) 2023 Day 2 - Cube Conundrum - Solution in JavaScript (JS)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devtails.xyz/assets/img/advent-of-code-2023-day-2-cube-conundrum-in-javascript.png" /><media:content medium="image" url="https://devtails.xyz/assets/img/advent-of-code-2023-day-2-cube-conundrum-in-javascript.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Advent of Code (AoC) 2023 Day 1 - Trebuchet?! - Solution in JavaScript</title><link href="https://devtails.xyz/adam/2023-advent-of-code-day-one" rel="alternate" type="text/html" title="Advent of Code (AoC) 2023 Day 1 - Trebuchet?! - Solution in JavaScript" /><published>2023-12-01T00:00:00+00:00</published><updated>2023-12-01T00:00:00+00:00</updated><id>https://devtails.xyz/adam/aoc</id><content type="html" xml:base="https://devtails.xyz/adam/2023-advent-of-code-day-one"><![CDATA[<p>Other solutions:
<a href="/adam/2023-advent-of-code-day-two">Day 2</a>
<a href="/adam/2023-advent-of-code-day-3">Day 3</a></p>

<p>Solution for Advent of Code (AoC) 2023 <a href="https://adventofcode.com/2023/day/1">Day 1: Trebuchet?!</a> in JavaScript:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">fs</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">fs</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">log</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">console</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">lines</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">./day1.txt</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span><span class="na">encoding</span><span class="p">:</span> <span class="dl">"</span><span class="s2">utf-8</span><span class="dl">"</span><span class="p">}).</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="se">\n</span><span class="dl">"</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">isDigit</span><span class="p">(</span><span class="nx">char</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="sr">/^</span><span class="se">\d</span><span class="sr">$/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">char</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">part1</span><span class="p">()</span> <span class="p">{</span>  
  <span class="kd">let</span> <span class="nx">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">line</span> <span class="k">of</span> <span class="nx">lines</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">firstNum</span><span class="p">,</span> <span class="nx">lastNum</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">c</span> <span class="k">of</span> <span class="nx">line</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">isDigit</span><span class="p">(</span><span class="nx">c</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">firstNum</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">firstNum</span> <span class="o">=</span> <span class="nx">c</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="nx">lastNum</span> <span class="o">=</span> <span class="nx">c</span><span class="p">;</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">twoDigit</span> <span class="o">=</span> <span class="s2">`</span><span class="p">${</span><span class="nx">firstNum</span><span class="p">}${</span><span class="nx">lastNum</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span>
    <span class="nx">sum</span> <span class="o">+=</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">twoDigit</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nx">log</span><span class="p">(</span><span class="nx">sum</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">part2</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">for</span><span class="p">(</span><span class="kd">const</span> <span class="nx">line</span> <span class="k">of</span> <span class="nx">lines</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">digits</span> <span class="o">=</span> <span class="p">[];</span>

    <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">line</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">c</span> <span class="o">=</span> <span class="nx">line</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>

      <span class="k">if</span> <span class="p">(</span><span class="nx">isDigit</span><span class="p">(</span><span class="nx">c</span><span class="p">))</span> <span class="p">{</span>
        <span class="nx">digits</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">c</span><span class="p">);</span>
      <span class="p">}</span>

      <span class="kd">const</span> <span class="nx">textDigits</span> <span class="o">=</span> <span class="p">[</span><span class="dl">'</span><span class="s1">one</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">two</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">three</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">four</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">five</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">six</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">seven</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">eight</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">nine</span><span class="dl">'</span><span class="p">];</span>
      <span class="kd">const</span> <span class="nx">lineSubstring</span> <span class="o">=</span> <span class="nx">line</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span>
      <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">d</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">d</span> <span class="o">&lt;</span> <span class="nx">textDigits</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">d</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">textDigit</span> <span class="o">=</span> <span class="nx">textDigits</span><span class="p">[</span><span class="nx">d</span><span class="p">];</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">lineSubstring</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">textDigit</span><span class="p">))</span> <span class="p">{</span>
          <span class="nx">digits</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">d</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span>
        <span class="p">}</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">lastIndex</span> <span class="o">=</span> <span class="nx">digits</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">twoDigits</span> <span class="o">=</span> <span class="s2">`</span><span class="p">${</span><span class="nx">digits</span><span class="p">[</span><span class="mi">0</span><span class="p">]}${</span><span class="nx">digits</span><span class="p">[</span><span class="nx">lastIndex</span><span class="p">]}</span><span class="s2">`</span><span class="p">;</span>

    <span class="nx">sum</span> <span class="o">+=</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">twoDigits</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nx">log</span><span class="p">(</span><span class="nx">sum</span><span class="p">);</span>
<span class="p">}</span>

<span class="nx">part1</span><span class="p">();</span>
<span class="nx">part2</span><span class="p">();</span>
</code></pre></div></div>]]></content><author><name>adam</name></author><category term="dev" /><category term="js" /><summary type="html"><![CDATA[Solution for Advent of Code (AoC) Day 1 in JavaScript (JS)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devtails.xyz/assets/img/advent-of-code-2023-day-1-trebuchet-in-javascript.png" /><media:content medium="image" url="https://devtails.xyz/assets/img/advent-of-code-2023-day-1-trebuchet-in-javascript.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Tutorial: How to Write a Wav File in JavaScript</title><link href="https://devtails.xyz/@adam/how-to-write-a-wav-file-in-javascript" rel="alternate" type="text/html" title="Tutorial: How to Write a Wav File in JavaScript" /><published>2023-11-29T00:00:00+00:00</published><updated>2023-11-29T00:00:00+00:00</updated><id>https://devtails.xyz/@adam/how-to-write-a-wav-file-in-js</id><content type="html" xml:base="https://devtails.xyz/@adam/how-to-write-a-wav-file-in-javascript"><![CDATA[<h2 id="intro">Intro</h2>

<p>This is part of a <a href="https://kaizen.place/music-tails/4-chords-630150ea1b3ccbc86a636770">project</a> I have started to see if I can create a full song using just code.  I started out <a href="/@adam/how-to-write-a-wav-file-in-c">writing in C</a>, but for creating more interactive demos I decided to port over to JavaScript.  I had never heard of the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView">DataView</a> class in JavaScript.  This will ultimately be a lot easier to work with and easier for others to grok as well.</p>

<h2 id="wav-file-format">Wav File Format</h2>

<p>Head <a href="https://docs.fileformat.com/audio/wav/">here</a> for a breakdown of the WAV File Format</p>

<h2 id="demo">Demo</h2>

<audio controls="" id="audioPlayer"></audio>

<script>
const sampleRate = 8000;
const durationSeconds = 10;
const numChannels = 1;
const bytesPerSample = 2 * numChannels;
const bytesPerSecond = sampleRate * bytesPerSample;
const dataLength = bytesPerSecond * durationSeconds;
const headerLength = 44;
const fileLength = dataLength + headerLength;
const bufferData = new Uint8Array(fileLength);
const dataView = new DataView(bufferData.buffer);
const writer = createWriter(dataView);

// HEADER
writer.string("RIFF");
// File Size
writer.uint32(fileLength);
writer.string("WAVE");

writer.string("fmt ");
// Chunk Size
writer.uint32(16);
// Format Tag
writer.uint16(1);
// Number Channels
writer.uint16(numChannels);
// Sample Rate
writer.uint32(sampleRate);
// Bytes Per Second
writer.uint32(bytesPerSecond);
// Bytes Per Sample
writer.uint16(bytesPerSample);
// Bits Per Sample
writer.uint16(bytesPerSample * 8);
writer.string("data");

writer.uint32(dataLength);

for (let i = 0; i < dataLength / 2; i++) {
  const t = i / sampleRate;
  const frequency = 256;
  const volume = 0.6;
  const val = Math.sin(2 * Math.PI * 256 * t) * volume;
  writer.pcm16s(val);
}

const blob = new Blob([dataView.buffer], { type: 'application/octet-stream' });

audioPlayer.src = URL.createObjectURL(blob);

function createWriter(dataView) {
  let pos = 0;

  return {
    string(val) {
      for (let i = 0; i < val.length; i++) {
        dataView.setUint8(pos++, val.charCodeAt(i));
      }
    },
    uint16(val) {
      dataView.setUint16(pos, val, true);
      pos += 2;
    },
    uint32(val) {
      dataView.setUint32(pos, val, true);
      pos += 4;
    },
    pcm16s: function(value) {
      value = Math.round(value * 32768);
      value = Math.max(-32768, Math.min(value, 32767));
      dataView.setInt16(pos, value, true);
      pos += 2;
    },
  }
}
</script>

<h2 id="source">Source</h2>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">sampleRate</span> <span class="o">=</span> <span class="mi">8000</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">durationSeconds</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">numChannels</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">bytesPerSample</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="nx">numChannels</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">bytesPerSecond</span> <span class="o">=</span> <span class="nx">sampleRate</span> <span class="o">*</span> <span class="nx">bytesPerSample</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">dataLength</span> <span class="o">=</span> <span class="nx">bytesPerSecond</span> <span class="o">*</span> <span class="nx">durationSeconds</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">headerLength</span> <span class="o">=</span> <span class="mi">44</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">fileLength</span> <span class="o">=</span> <span class="nx">dataLength</span> <span class="o">+</span> <span class="nx">headerLength</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">bufferData</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="nx">fileLength</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">dataView</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">DataView</span><span class="p">(</span><span class="nx">bufferData</span><span class="p">.</span><span class="nx">buffer</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">writer</span> <span class="o">=</span> <span class="nx">createWriter</span><span class="p">(</span><span class="nx">dataView</span><span class="p">);</span>

<span class="c1">// HEADER</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">string</span><span class="p">(</span><span class="dl">"</span><span class="s2">RIFF</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// File Size</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">uint32</span><span class="p">(</span><span class="nx">fileLength</span><span class="p">);</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">string</span><span class="p">(</span><span class="dl">"</span><span class="s2">WAVE</span><span class="dl">"</span><span class="p">);</span>

<span class="nx">writer</span><span class="p">.</span><span class="nx">string</span><span class="p">(</span><span class="dl">"</span><span class="s2">fmt </span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// Chunk Size</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">uint32</span><span class="p">(</span><span class="mi">16</span><span class="p">);</span>
<span class="c1">// Format Tag</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">uint16</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="c1">// Number Channels</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">uint16</span><span class="p">(</span><span class="nx">numChannels</span><span class="p">);</span>
<span class="c1">// Sample Rate</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">uint32</span><span class="p">(</span><span class="nx">sampleRate</span><span class="p">);</span>
<span class="c1">// Bytes Per Second</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">uint32</span><span class="p">(</span><span class="nx">bytesPerSecond</span><span class="p">);</span>
<span class="c1">// Bytes Per Sample</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">uint16</span><span class="p">(</span><span class="nx">bytesPerSample</span><span class="p">);</span>
<span class="c1">// Bits Per Sample</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">uint16</span><span class="p">(</span><span class="nx">bytesPerSample</span> <span class="o">*</span> <span class="mi">8</span><span class="p">);</span>
<span class="nx">writer</span><span class="p">.</span><span class="nx">string</span><span class="p">(</span><span class="dl">"</span><span class="s2">data</span><span class="dl">"</span><span class="p">);</span>

<span class="nx">writer</span><span class="p">.</span><span class="nx">uint32</span><span class="p">(</span><span class="nx">dataLength</span><span class="p">);</span>

<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">dataLength</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">t</span> <span class="o">=</span> <span class="nx">i</span> <span class="o">/</span> <span class="nx">sampleRate</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">frequency</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">volume</span> <span class="o">=</span> <span class="mf">0.6</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">val</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="mi">256</span> <span class="o">*</span> <span class="nx">t</span><span class="p">)</span> <span class="o">*</span> <span class="nx">volume</span><span class="p">;</span>
  <span class="nx">writer</span><span class="p">.</span><span class="nx">pcm16s</span><span class="p">(</span><span class="nx">val</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">blob</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Blob</span><span class="p">([</span><span class="nx">dataView</span><span class="p">.</span><span class="nx">buffer</span><span class="p">],</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/octet-stream</span><span class="dl">'</span> <span class="p">});</span>

<span class="nx">audioPlayer</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">URL</span><span class="p">.</span><span class="nx">createObjectURL</span><span class="p">(</span><span class="nx">blob</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">createWriter</span><span class="p">(</span><span class="nx">dataView</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">pos</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">return</span> <span class="p">{</span>
    <span class="nx">string</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">val</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">dataView</span><span class="p">.</span><span class="nx">setUint8</span><span class="p">(</span><span class="nx">pos</span><span class="o">++</span><span class="p">,</span> <span class="nx">val</span><span class="p">.</span><span class="nx">charCodeAt</span><span class="p">(</span><span class="nx">i</span><span class="p">));</span>
      <span class="p">}</span>
    <span class="p">},</span>
    <span class="nx">uint16</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">dataView</span><span class="p">.</span><span class="nx">setUint16</span><span class="p">(</span><span class="nx">pos</span><span class="p">,</span> <span class="nx">val</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
      <span class="nx">pos</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
    <span class="p">},</span>
    <span class="nx">uint32</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">dataView</span><span class="p">.</span><span class="nx">setUint32</span><span class="p">(</span><span class="nx">pos</span><span class="p">,</span> <span class="nx">val</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
      <span class="nx">pos</span> <span class="o">+=</span> <span class="mi">4</span><span class="p">;</span>
    <span class="p">},</span>
    <span class="na">pcm16s</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">value</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="nx">value</span> <span class="o">*</span> <span class="mi">32768</span><span class="p">);</span>
      <span class="nx">value</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="o">-</span><span class="mi">32768</span><span class="p">,</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="mi">32767</span><span class="p">));</span>
      <span class="nx">dataView</span><span class="p">.</span><span class="nx">setInt16</span><span class="p">(</span><span class="nx">pos</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
      <span class="nx">pos</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
    <span class="p">},</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>adam</name></author><category term="dev" /><category term="js" /><category term="music" /><summary type="html"><![CDATA[Step by step example to create a wav file using JavaScript]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devtails.xyz/assets/img/how-to-write-a-wav-file-in-javascript.png" /><media:content medium="image" url="https://devtails.xyz/assets/img/how-to-write-a-wav-file-in-javascript.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Tutorial: How to Write a Wav File in C</title><link href="https://devtails.xyz/@adam/how-to-write-a-wav-file-in-c" rel="alternate" type="text/html" title="Tutorial: How to Write a Wav File in C" /><published>2023-11-27T00:00:00+00:00</published><updated>2023-11-27T00:00:00+00:00</updated><id>https://devtails.xyz/@adam/how-to-write-a-wav-file-in-c</id><content type="html" xml:base="https://devtails.xyz/@adam/how-to-write-a-wav-file-in-c"><![CDATA[<iframe width="100%" height="400px" src="https://www.youtube.com/embed/8nOi-0kBv2Y?si=ue-nRqbxMUK5Vrpw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>

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

<p>This is part of a <a href="https://kaizen.place/music-tails/4-chords-630150ea1b3ccbc86a636770">project</a> I have started to see if I can create a full song using just code.</p>

<h2 id="wav-file-format">Wav File Format</h2>

<p>Head <a href="https://docs.fileformat.com/audio/wav/">here</a> for a breakdown of the WAV File Format</p>

<h2 id="create-wav-struct">Create Wav Struct</h2>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">wav_header</span>
<span class="p">{</span>
  <span class="kt">char</span> <span class="n">riff</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>           <span class="cm">/* "RIFF"                                  */</span>
  <span class="kt">int32_t</span> <span class="n">flength</span><span class="p">;</span>        <span class="cm">/* file length in bytes                    */</span>
  <span class="kt">char</span> <span class="n">wave</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>           <span class="cm">/* "WAVE"                                  */</span>
  <span class="kt">char</span> <span class="n">fmt</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>            <span class="cm">/* "fmt "                                  */</span>
  <span class="kt">int32_t</span> <span class="n">chunk_size</span><span class="p">;</span>     <span class="cm">/* size of FMT chunk in bytes (usually 16) */</span>
  <span class="kt">int16_t</span> <span class="n">format_tag</span><span class="p">;</span>     <span class="cm">/* 1=PCM, 257=Mu-Law, 258=A-Law, 259=ADPCM */</span>
  <span class="kt">int16_t</span> <span class="n">num_chans</span><span class="p">;</span>      <span class="cm">/* 1=mono, 2=stereo                        */</span>
  <span class="kt">int32_t</span> <span class="n">srate</span><span class="p">;</span>          <span class="cm">/* Sampling rate in samples per second     */</span>
  <span class="kt">int32_t</span> <span class="n">bytes_per_sec</span><span class="p">;</span>  <span class="cm">/* bytes per second = srate*bytes_per_samp */</span>
  <span class="kt">int16_t</span> <span class="n">bytes_per_samp</span><span class="p">;</span> <span class="cm">/* 2=16-bit mono, 4=16-bit stereo          */</span>
  <span class="kt">int16_t</span> <span class="n">bits_per_samp</span><span class="p">;</span>  <span class="cm">/* Number of bits per sample               */</span>
  <span class="kt">char</span> <span class="n">data</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>           <span class="cm">/* "data"                                  */</span>
  <span class="kt">int32_t</span> <span class="n">dlength</span><span class="p">;</span>        <span class="cm">/* data length in bytes (filelength - 44)  */</span>
<span class="p">};</span>
</code></pre></div></div>

<h2 id="populate-wav-struct">Populate Wav Struct</h2>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// main.c</span>
<span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span>
<span class="k">struct</span> <span class="n">wav_header</span> <span class="n">wavh</span><span class="p">;</span>

<span class="k">const</span> <span class="kt">int</span> <span class="n">sample_rate</span> <span class="o">=</span> <span class="mi">8000</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">duration_seconds</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">buffer_size</span> <span class="o">=</span> <span class="n">sample_rate</span> <span class="o">*</span> <span class="n">duration_seconds</span><span class="p">;</span>
<span class="kt">short</span> <span class="kt">int</span> <span class="n">buffer</span><span class="p">[</span><span class="n">buffer_size</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">wavh</span><span class="p">.</span><span class="n">riff</span><span class="p">,</span> <span class="s">"RIFF"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">wavh</span><span class="p">.</span><span class="n">wave</span><span class="p">,</span> <span class="s">"WAVE"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">wavh</span><span class="p">.</span><span class="n">fmt</span><span class="p">,</span> <span class="s">"fmt "</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">wavh</span><span class="p">.</span><span class="n">data</span><span class="p">,</span> <span class="s">"data"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>

  <span class="n">wavh</span><span class="p">.</span><span class="n">chunk_size</span> <span class="o">=</span> <span class="mi">16</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">format_tag</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">num_chans</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">srate</span> <span class="o">=</span> <span class="n">sample_rate</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">bits_per_samp</span> <span class="o">=</span> <span class="mi">16</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">bytes_per_sec</span> <span class="o">=</span> <span class="n">wavh</span><span class="p">.</span><span class="n">srate</span> <span class="o">*</span> <span class="n">wavh</span><span class="p">.</span><span class="n">bits_per_samp</span> <span class="o">/</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">wavh</span><span class="p">.</span><span class="n">num_chans</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">bytes_per_samp</span> <span class="o">=</span> <span class="n">wavh</span><span class="p">.</span><span class="n">bits_per_samp</span> <span class="o">/</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">wavh</span><span class="p">.</span><span class="n">num_chans</span><span class="p">;</span>

  <span class="n">wavh</span><span class="p">.</span><span class="n">dlength</span> <span class="o">=</span> <span class="n">buffer_size</span> <span class="o">*</span> <span class="n">wavh</span><span class="p">.</span><span class="n">bytes_per_samp</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">flength</span> <span class="o">=</span> <span class="n">wavh</span><span class="p">.</span><span class="n">dlength</span> <span class="o">+</span> <span class="n">header_length</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="writing-wav-file-to-disk">Writing Wav File to Disk</h2>

<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">
</span>
<span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"test.wav"</span><span class="p">,</span> <span class="s">"w"</span><span class="p">);</span>
<span class="n">fwrite</span><span class="p">(</span><span class="o">&amp;</span><span class="n">wavh</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">header_length</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span>
<span class="n">fwrite</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="playing-a-c-note">Playing a C Note</h2>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;math.h&gt;</span><span class="cp">
</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">buffer_size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">buffer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">short</span> <span class="kt">int</span><span class="p">)((</span><span class="n">cos</span><span class="p">((</span><span class="mi">2</span> <span class="o">*</span> <span class="n">M_PI</span> <span class="o">*</span> <span class="n">MIDDLE_C</span> <span class="o">*</span> <span class="n">i</span><span class="p">)</span> <span class="o">/</span> <span class="n">sample_rate</span><span class="p">)</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="full-source-code">Full Source Code</h2>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// main.c</span>
<span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;math.h&gt;</span><span class="cp">
</span>
<span class="k">struct</span> <span class="n">wav_header</span>
<span class="p">{</span>
  <span class="kt">char</span> <span class="n">riff</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>           <span class="cm">/* "RIFF"                                  */</span>
  <span class="kt">int32_t</span> <span class="n">flength</span><span class="p">;</span>        <span class="cm">/* file length in bytes                    */</span>
  <span class="kt">char</span> <span class="n">wave</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>           <span class="cm">/* "WAVE"                                  */</span>
  <span class="kt">char</span> <span class="n">fmt</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>            <span class="cm">/* "fmt "                                  */</span>
  <span class="kt">int32_t</span> <span class="n">chunk_size</span><span class="p">;</span>     <span class="cm">/* size of FMT chunk in bytes (usually 16) */</span>
  <span class="kt">int16_t</span> <span class="n">format_tag</span><span class="p">;</span>     <span class="cm">/* 1=PCM, 257=Mu-Law, 258=A-Law, 259=ADPCM */</span>
  <span class="kt">int16_t</span> <span class="n">num_chans</span><span class="p">;</span>      <span class="cm">/* 1=mono, 2=stereo                        */</span>
  <span class="kt">int32_t</span> <span class="n">srate</span><span class="p">;</span>          <span class="cm">/* Sampling rate in samples per second     */</span>
  <span class="kt">int32_t</span> <span class="n">bytes_per_sec</span><span class="p">;</span>  <span class="cm">/* bytes per second = srate*bytes_per_samp */</span>
  <span class="kt">int16_t</span> <span class="n">bytes_per_samp</span><span class="p">;</span> <span class="cm">/* 2=16-bit mono, 4=16-bit stereo          */</span>
  <span class="kt">int16_t</span> <span class="n">bits_per_samp</span><span class="p">;</span>  <span class="cm">/* Number of bits per sample               */</span>
  <span class="kt">char</span> <span class="n">data</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>           <span class="cm">/* "data"                                  */</span>
  <span class="kt">int32_t</span> <span class="n">dlength</span><span class="p">;</span>        <span class="cm">/* data length in bytes (filelength - 44)  */</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">wav_header</span> <span class="n">wavh</span><span class="p">;</span>

<span class="k">const</span> <span class="kt">float</span> <span class="n">MIDDLE_C</span> <span class="o">=</span> <span class="mi">256</span><span class="p">.</span><span class="mo">00</span><span class="p">;</span>

<span class="k">const</span> <span class="kt">int</span> <span class="n">sample_rate</span> <span class="o">=</span> <span class="mi">8000</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">duration_seconds</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">buffer_size</span> <span class="o">=</span> <span class="n">sample_rate</span> <span class="o">*</span> <span class="n">duration_seconds</span><span class="p">;</span>
<span class="kt">short</span> <span class="kt">int</span> <span class="n">buffer</span><span class="p">[</span><span class="n">buffer_size</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>

<span class="k">const</span> <span class="kt">int</span> <span class="n">header_length</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">wav_header</span><span class="p">);</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">wavh</span><span class="p">.</span><span class="n">riff</span><span class="p">,</span> <span class="s">"RIFF"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">wavh</span><span class="p">.</span><span class="n">wave</span><span class="p">,</span> <span class="s">"WAVE"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">wavh</span><span class="p">.</span><span class="n">fmt</span><span class="p">,</span> <span class="s">"fmt "</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">wavh</span><span class="p">.</span><span class="n">data</span><span class="p">,</span> <span class="s">"data"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>

  <span class="n">wavh</span><span class="p">.</span><span class="n">chunk_size</span> <span class="o">=</span> <span class="mi">16</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">format_tag</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">num_chans</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">srate</span> <span class="o">=</span> <span class="n">sample_rate</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">bits_per_samp</span> <span class="o">=</span> <span class="mi">16</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">bytes_per_sec</span> <span class="o">=</span> <span class="n">wavh</span><span class="p">.</span><span class="n">srate</span> <span class="o">*</span> <span class="n">wavh</span><span class="p">.</span><span class="n">bits_per_samp</span> <span class="o">/</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">wavh</span><span class="p">.</span><span class="n">num_chans</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">bytes_per_samp</span> <span class="o">=</span> <span class="n">wavh</span><span class="p">.</span><span class="n">bits_per_samp</span> <span class="o">/</span> <span class="mi">8</span> <span class="o">*</span> <span class="n">wavh</span><span class="p">.</span><span class="n">num_chans</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">buffer_size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">buffer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">short</span> <span class="kt">int</span><span class="p">)((</span><span class="n">cos</span><span class="p">((</span><span class="mi">2</span> <span class="o">*</span> <span class="n">M_PI</span> <span class="o">*</span> <span class="n">MIDDLE_C</span> <span class="o">*</span> <span class="n">i</span><span class="p">)</span> <span class="o">/</span> <span class="n">sample_rate</span><span class="p">)</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">));</span>
  <span class="p">}</span>

  <span class="n">wavh</span><span class="p">.</span><span class="n">dlength</span> <span class="o">=</span> <span class="n">buffer_size</span> <span class="o">*</span> <span class="n">wavh</span><span class="p">.</span><span class="n">bytes_per_samp</span><span class="p">;</span>
  <span class="n">wavh</span><span class="p">.</span><span class="n">flength</span> <span class="o">=</span> <span class="n">wavh</span><span class="p">.</span><span class="n">dlength</span> <span class="o">+</span> <span class="n">header_length</span><span class="p">;</span>

  <span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"test.wav"</span><span class="p">,</span> <span class="s">"w"</span><span class="p">);</span>
  <span class="n">fwrite</span><span class="p">(</span><span class="o">&amp;</span><span class="n">wavh</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">header_length</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span>
  <span class="n">fwrite</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">buffer_size</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>adam</name></author><category term="dev" /><category term="music" /><summary type="html"><![CDATA[Step by step instructions to create a wav file and write it to disk using C code]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devtails.xyz/assets/img/how-to-write-a-wav-file-in-c.png" /><media:content medium="image" url="https://devtails.xyz/assets/img/how-to-write-a-wav-file-in-c.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>