<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Get Info: #script</title>
    <description>Posts tagged “script” — Blog of independent game and app developer Matt Sephton. Featuring vintage Macintosh, game development, digital artwork, Japanese esoterica, video game reviews, hacks and tips, and much more.</description>
    <link>https://blog.gingerbeardman.com/tag/script/</link>
    <atom:link href="https://blog.gingerbeardman.com/tag/script/index.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 12 Jul 2026 20:54:08 +0000</pubDate>
    <lastBuildDate>Sun, 12 Jul 2026 20:54:08 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>

    
      
        <item>
          <title>Controlling local web servers using xbar</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;I’ve released a native macOS app that does all this script can do &lt;em&gt;and much more!&lt;/em&gt;&lt;br /&gt;Read all about it: &lt;a href=&quot;https://www.gingerbeardman.com/apps/localmost/&quot;&gt;gingerbeardman.com/apps/localmost/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes I want to run local web servers for projects I’m working on. Usually more than one at a time, or at least over a short space of time.&lt;/p&gt;

&lt;p&gt;So I thought it would be cool to have a controller for those local servers in my menu bar. Sounded like the perfect job for a little scripting and &lt;a href=&quot;https://github.com/matryer/xbar&quot;&gt;xbar&lt;/a&gt;, which is a great way to prove a menubar app idea quickly.&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;The plugin allows you to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;toggle servers on and off&lt;/li&gt;
  &lt;li&gt;open in browser&lt;/li&gt;
  &lt;li&gt;view ports&lt;/li&gt;
  &lt;li&gt;view paths&lt;/li&gt;
  &lt;li&gt;view log sizes&lt;/li&gt;
  &lt;li&gt;clear logs&lt;/li&gt;
  &lt;li&gt;edit config&lt;/li&gt;
  &lt;li&gt;and more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xbar-localhost-dark.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;example-config&quot;&gt;Example config&lt;/h2&gt;

&lt;p&gt;In the config file we set the starting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SERVER_PORT&lt;/code&gt;, followed by one or more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SERVER_DIR&lt;/code&gt; for as many projects as you might want servers. We can temporarily comment out those server lines to prevent projects from appearing in the menu.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# .xbar_httpd_config&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SERVER_PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;8000
&lt;span class=&quot;c&quot;&gt;#SERVER_DIR=~/Projects/starchasers/&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SERVER_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/Projects/serenity/
&lt;span class=&quot;nv&quot;&gt;SERVER_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/Projects/point-cloud/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;source-code&quot;&gt;Source code&lt;/h2&gt;

&lt;p&gt;Python source code is available in the following gist:&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/a81df96cd0b4c7a397b04711cafeb287&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/a81df96cd0b4c7a397b04711cafeb287.js&quot;&gt;&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 12 Jan 2026 20:57:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/01/12/xbar-local-web-server-controller/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/01/12/xbar-local-web-server-controller/</guid>
        </item>
      
    
      
        <item>
          <title>Automating the cleaning of macOS-specific files on Eject</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;I’ve released a native macOS app that does all this script can do &lt;em&gt;and much more!&lt;/em&gt;&lt;br /&gt;Read all about it: &lt;a href=&quot;https://www.gingerbeardman.com/apps/driveaway/&quot;&gt;gingerbeardman.com/apps/driveaway/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dot underscore &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;._&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.DS_Store&lt;/code&gt; files are macOS-specific metadata cruft generated for foreign filesystems (like FAT32 or exFAT) that are not usually needed for disks that are mainly used on other platforms. Digital cameras, music players, e-book readers, and handheld gaming devices can get confused when they encounter these odd files during file system parsing and directory listing. The problem is compounded if the devices naïvely process files by looking only at the file extension as they will then see the dot underscore version of a file as a duplicate and try to preview/play/open it.&lt;/p&gt;

&lt;p&gt;For years I’ve used an app called &lt;a href=&quot;https://web.archive.org/web/20250208072547/https://macpaw.com/cleanmymac&quot;&gt;CleanMyDrive&lt;/a&gt; to remove such files, but &lt;a href=&quot;https://macpaw.com/news/cleanmydrive-no-longer-developed&quot;&gt;it was discontinued in October 2023&lt;/a&gt;. I continued to use it until it recently stopped working completely …so I needed to find an alternative solution. There are some apps on the Mac App Store that look like they’ll do the trick, but I don’t really want to spend the time buying and trialling multiple apps to find one that fits my usage habits. I can make one!&lt;/p&gt;

&lt;p&gt;I already use an app called &lt;a href=&quot;https://xbarapp.com&quot;&gt;xbar&lt;/a&gt; for keeping track of my GitHub issues, itch.io sales, network/server status, and more. So I decided to flex my shell script muscles and put together an xbar script to do it.&lt;/p&gt;

&lt;p&gt;The script adds a menu bar item that allows you to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Eject (click)&lt;/li&gt;
  &lt;li&gt;Unmount (option-click)&lt;/li&gt;
  &lt;li&gt;Eject All (without cleaning, useful when you want to disconnect all drives from your computer)&lt;/li&gt;
  &lt;li&gt;All with a handy notification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it! Straight to the point, no frills, functional software.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xbar-volumes.png&quot; alt=&quot;IMG&quot; title=&quot; &quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/610f22180117ad20465d7c529cc5faa0&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/610f22180117ad20465d7c529cc5faa0.js&quot;&gt;&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 08 Feb 2025 20:45:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/02/08/automating-the-cleaning-of-macos-specific-files-on-eject/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/02/08/automating-the-cleaning-of-macos-specific-files-on-eject/</guid>
        </item>
      
    
      
        <item>
          <title>Automatically classifying the content of sound files using ML</title>
          <description>&lt;p&gt;Following on from yesterday’s &lt;a href=&quot;/2023/08/12/extracting-sounds-from-macromedia-director-files/&quot;&gt;extraction of old sound effects&lt;/a&gt;, I quickly realised I needed an easier way to search them as they came out of Director as unlabelled, numbered files. I can use QuickLook or a media player to quickly audition them, but how could I easily find the sample that contains the sound of running water or a horse trotting?&lt;/p&gt;

&lt;p&gt;I wondered if there was a way of using Machine Learning (ML) to automatically categorise sounds. It seemed like something that should be possible, especially given the recent explosion in “AI” (really: ML) tools. I quickly found Google’s AudioSet, which sounded like the perfect dataset:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;AudioSet consists of an expanding ontology of 632 audio event classes and a collection of 2,084,320 human-labeled 10-second sound clips drawn from YouTube videos. The ontology is specified as a hierarchical graph of event categories, covering a wide range of human and animal sounds, musical instruments and genres, and common everyday environmental sounds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the data set is only one half of the solution. You need to use the dataset to create a model and then run that model against your own data to get the required results. Thankfully, I found YAMNet:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;YAMNet is a deep net that predicts ~521 audio event classes from the AudioSet-YouTube corpus it was trained on.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I guess YAMNet is tracking behind AudioSet in terms of total categories, but it is good enough for me. Here is a &lt;a href=&quot;https://github.com/tensorflow/models/blob/master/research/audioset/yamnet/yamnet_class_map.csv&quot;&gt;list of all the classes&lt;/a&gt; of sounds it can recognise.&lt;/p&gt;

&lt;h2 id=&quot;lets-go&quot;&gt;Let’s go&lt;/h2&gt;

&lt;p&gt;I used the script described in &lt;a href=&quot;https://www.tensorflow.org/hub/tutorials/yamnet&quot;&gt;this tutorial&lt;/a&gt; as a starting point. I’m not a regular python user but using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; to &lt;a href=&quot;https://www.tensorflow.org/install&quot;&gt;install tensorflow&lt;/a&gt;, along with any other missing imports, and after that it …just worked.&lt;/p&gt;

&lt;h2 id=&quot;getting-your-files-in-order&quot;&gt;Getting your files in order&lt;/h2&gt;

&lt;p&gt;According to the documentation all sound files need to be at a sample rate of 16000Hz. After getting some calssification results of “Silence”, I realised they also need to be 16-bit resolution. So I ran a quick &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sox&lt;/code&gt; command to create compliant copies of all my sounds. I’ll delete these when I’m done. Notice how I decided to trim sounds to a maximum length of 3 seconds. This helps speed things up and most sounds can still be recognised with such a short starting section.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-iname&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*.wav&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exec&lt;/span&gt; sox &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; 16000 &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 16 &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;_16k.wav trim 0 00:03 &lt;span class=&quot;se&quot;&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;optimisation&quot;&gt;Optimisation&lt;/h2&gt;

&lt;p&gt;Running the classifier works at about real-time, a few seconds per sound, but I noticed that it was leaving a lot of my CPU unused. This struck me as a prime candidate for parallelisation, which is pretty easy on the command line. I used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parallel&lt;/code&gt; command to scale up the classification to use all 10-cores of the M1 Pro CPU in my 2021 MacBook Pro.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-iname&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*.wav&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exec&lt;/span&gt; parallel python3 classify.py &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; ::: &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\+&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As I type my computer is making short order of the task, whilst remaining perfectly responsive, if a little warm. Final speed for me is one sound every ~0.85 seconds.&lt;/p&gt;

&lt;h2 id=&quot;python-script&quot;&gt;Python Script&lt;/h2&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/9e9bde623673ed2f50aeb15e97aae4a3&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/9e9bde623673ed2f50aeb15e97aae4a3.js&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;creating-my-sfx-library&quot;&gt;Creating my SFX Library&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://getsoundly.com&quot;&gt;Soundly&lt;/a&gt; is a sort of iTunes for sound effects. It’s an app that enables easy, automatic organisation of files, quick searching of metadata, painless playback/auditioning, non-destructive edits, and simple exporting of the final sounds. The free version allows a local library of 10,000 files which is more than enough for my usage. I’m not affiliated with them in any way, but they offer a free version and a &lt;a href=&quot;https://getsoundly.com/news/soundly-promo-code-free/&quot;&gt;1-month free trial of their paid version&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As you add your local folder of files it allows you to import a (semicolon-separated) .csv file containing additional metadata. It’s here that I point it to the file that was generated by the classifier. The categories are imported as the description of the sound, and are able to be searched. Perfect!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 13 Aug 2023 17:01:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/08/13/automatically-classifying-the-content-of-sound-files-using-ml/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/08/13/automatically-classifying-the-content-of-sound-files-using-ml/</guid>
        </item>
      
    
      
        <item>
          <title>Extracting sounds from Macromedia Director files</title>
          <description>&lt;p&gt;For my latest “quick” &lt;a href=&quot;https://play.date&quot;&gt;Playdate&lt;/a&gt; project—a remaster of a ~1997 web game by &lt;a href=&quot;https://lostmediawiki.com/Thoru_Yamamoto_works_(partially_found_interactive_media;_1990s)&quot;&gt;Thoru Yamamoto&lt;/a&gt;—I decided to add sound effects. In order to keep it as authentic as possible I decided to use only sound effects created by Thoru Yamamoto that were used in his other productions.&lt;/p&gt;

&lt;p&gt;The largest collection of sounds I could think of were those included in his Macromedia Director web experiments which include everything from short animations, through games and interactive toys, to abstract slideshows. The problem is, these Director files are tricky to deal with some 25 years later.&lt;/p&gt;

&lt;h2 id=&quot;halt-and-catch-fire&quot;&gt;Halt and Catch Fire&lt;/h2&gt;

&lt;p&gt;A bit of reading and some help from &lt;a href=&quot;https://www.mistys-internet.website&quot;&gt;Misty De Méo&lt;/a&gt; led me to &lt;a href=&quot;https://github.com/ProjectorRays/ProjectorRays&quot;&gt;ProjectorRays&lt;/a&gt; which can convert a protected .dcr file into an editable .dir file, and also allows saving of all the individual chunks that comprise each file. Think of it as one chunk for each piece of graphics, sound, and so on.&lt;/p&gt;

&lt;p&gt;Once you’ve converted the .dcr to .dir you can open the file in, say, &lt;a href=&quot;https://vinizinho.net/projects/shockwave-rip&quot;&gt;Macromedia Director 2004 and use CastRipperTool&lt;/a&gt; to export sounds and graphics and more. But it’s a very manual process and the whole setup is prone to crashing. Crucially, it won’t open some older Director movies so this wasn’t a good enough solution for me.&lt;/p&gt;

&lt;h2 id=&quot;chunks&quot;&gt;Chunks&lt;/h2&gt;

&lt;p&gt;I looked to the chunks that had been dumped by ProjectorRays and some quick experimentation showed that the ‘&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snd &lt;/code&gt;’ chunks contained raw PCM audio date with a bespoke header. Loading these into something like ocenaudio wave editor was proof enough, but I would need to figure out some of the header to see if I could get the correct sample rate of each file.&lt;/p&gt;

&lt;p&gt;With a bit of help bouncing ideas off &lt;a href=&quot;https://hikari.noyu.me&quot;&gt;hikari_no_yume&lt;/a&gt; in a &lt;a href=&quot;https://github.com/hikari-no-yume/dream-sparer/issues/1&quot;&gt;bug report&lt;/a&gt; on one of their tools I figured out the location of the sample rate. Interestingly, the values were unexpected and not the usual values 11025, 22050, etc. It turns out that classic Apple Macintosh used some slightly &lt;a href=&quot;https://whitefiles.org/dta/pgs/c08.htm&quot;&gt;different sample rate values&lt;/a&gt;: 11127, 22254, etc.&lt;/p&gt;

&lt;p&gt;Using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xxd&lt;/code&gt; tool I dumped the relevant section of the headers of 1163 sounds from 105 .dcr files. The breakdown in sample rates found was as follows:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Rate (Hz)&lt;/th&gt;
      &lt;th&gt;Hex&lt;/th&gt;
      &lt;th&gt;Quantity Found&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;7,418&lt;/td&gt;
      &lt;td&gt;0x1cfa&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;11,127&lt;/td&gt;
      &lt;td&gt;0x2b77&lt;/td&gt;
      &lt;td&gt;616&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;22,050&lt;/td&gt;
      &lt;td&gt;0x5622&lt;/td&gt;
      &lt;td&gt;31&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;22,254&lt;/td&gt;
      &lt;td&gt;0x56ee&lt;/td&gt;
      &lt;td&gt;515&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The lack of any unexpected sample rate values was further proof that this was the real deal.&lt;/p&gt;

&lt;h2 id=&quot;shell-script&quot;&gt;Shell script&lt;/h2&gt;

&lt;p&gt;Armed with this information I decided to write a short shell script that would do the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;extract the sample rate from the ‘&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snd &lt;/code&gt;’ chunk&lt;/li&gt;
  &lt;li&gt;create a trimmed raw pcm file excluding the 78-byte header&lt;/li&gt;
  &lt;li&gt;use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sox&lt;/code&gt; to add a new WAV header using the correct sample rate&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This script allows me to batch process all the audio and that’s much more to my liking: it captures everything compared to CastRipperTool and it’s a lot quicker.&lt;/p&gt;

&lt;p&gt;So, dump your chunks using ProjectorRays and then call my script like this:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-iname&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*.bin&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exec&lt;/span&gt; ./bin2wav.sh &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/1e6170d2652352bf30623b2a6c8d12fd&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/1e6170d2652352bf30623b2a6c8d12fd.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;I’m assuming 8-bit, mono, unsigned PCM data in the ‘&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snd &lt;/code&gt;’ chunks and have not found anything else in them.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sox.sourceforge.net/sox.html&quot;&gt;SoX&lt;/a&gt; (Sound eXchange, the Swiss Army knife of audio manipulation) requires the .raw extension for raw PCM audio data, it will refuse to process the files without it.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 12 Aug 2023 19:42:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/08/12/extracting-sounds-from-macromedia-director-files/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/08/12/extracting-sounds-from-macromedia-director-files/</guid>
        </item>
      
    
      
        <item>
          <title>Post-processing Animated GIFs</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;This article was originally posted on &lt;a href=&quot;https://www.lexaloffle.com/bbs/?tid=3614&quot;&gt;pico8 BBS in 2016&lt;/a&gt;, and on &lt;a href=&quot;https://devforum.play.date/t/post-processing-animated-gifs/1074&quot;&gt;Playdate devforum in 2020&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was interested to see how easy/difficult it is to edit the animated GIF recordings. My goal was to trim some frames from the beginning and end to make a more succinct video.&lt;/p&gt;

&lt;p&gt;After much trial, error and experimentation here are my findings using Mac OS X.&lt;/p&gt;

&lt;p&gt;The game shown in the GIFs is &lt;a href=&quot;https://www.lexaloffle.com/bbs/?tid=3547&quot;&gt;Worm Nom Nom by ilkkke &amp;amp; kometbomb&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;Animated GIFs can be:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;viewed frame-by-frame using system Preview.app&lt;/li&gt;
  &lt;li&gt;manipulated using “gifsicle”, “imagemagick”, “graphicsmagick” command line tools&lt;/li&gt;
  &lt;li&gt;converted using “ffmpeg” command line tool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notes&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;useful tools need to be installed, you may wish to use the “brew” command line tool or similar.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;viewing&quot;&gt;Viewing&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Open the GIF in Preview.app and it will show you all frames.&lt;/li&gt;
  &lt;li&gt;Preview calls the first frame 1 (one), but other tools usually call it 0 (zero).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;original/source animated GIF:
&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/post-processing-animated-gifs-01.gif&quot; alt=&quot;GIF&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;un-optimising&quot;&gt;Un-Optimising&lt;/h2&gt;

&lt;p&gt;Warning: If you have previously optimised your GIF to reduce filesize, &lt;a href=&quot;https://devforum.play.date/t/optimising-gifs-from-mb-to-kb/788&quot;&gt;as in this other thread&lt;/a&gt;, and you want to edit the GIF further then be sure to first use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gifsicle -U&lt;/code&gt; to unoptimise it, otherwise the editing commands won’t work as expected.&lt;/p&gt;

&lt;h2 id=&quot;trimming&quot;&gt;Trimming&lt;/h2&gt;

&lt;p&gt;required: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gifsicle&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;then this is how you trim&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gifsicle anim.gif &lt;span class=&quot;s2&quot;&gt;&quot;#212-238&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; trimmed.gif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;note: this makes a copy of the GIF and keep frames 213 to 239 (gifsicle uses zero based frame count)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/post-processing-animated-gifs-02.gif&quot; alt=&quot;GIF&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;resizing&quot;&gt;Resizing&lt;/h2&gt;

&lt;p&gt;if you want to double size of the image:&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gifsicle &lt;span class=&quot;nt&quot;&gt;--scale&lt;/span&gt; 2 trimmed.gif &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; resized.gif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/post-processing-animated-gifs-03.gif&quot; alt=&quot;GIF&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;captioning&quot;&gt;Captioning&lt;/h2&gt;

&lt;p&gt;if you want to add an overlay to caption the animation&lt;/p&gt;

&lt;p&gt;required: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imagemagick&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;graphicsmagick&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;then use this bash script:&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;caption&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;
: &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Usage: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; anim.gif overlay.gif [output.gif]&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
: &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Usage: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; anim.gif overlay.gif [output.gif]&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;fnsource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;basename&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$source&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; .gif&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;fncaption&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;basename&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$caption&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; .gif&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fnsource&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fncaption&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.gif&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

gifsicle &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$source&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.gif.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;composite &lt;span class=&quot;nv&quot;&gt;$caption&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done
&lt;/span&gt;gifsicle &lt;span class=&quot;nt&quot;&gt;--loopcount&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.gif.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.gif.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;how to run the command&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./caption.sh anim.gif overlay.gif &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;output.gif]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;note: if you do not specify an output name, it will be named using original filenames, eg. anim-overlay.gif&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/posts/post-processing-animated-gifs-04.gif&quot;&gt;overlay.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/post-processing-animated-gifs-05.gif&quot; alt=&quot;GIF&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;converting-to-video-for-youtube&quot;&gt;Converting to Video for YouTube&lt;/h2&gt;

&lt;p&gt;required: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;to convert the GIF to MP4:&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; trimmed.gif &lt;span class=&quot;nt&quot;&gt;-movflags&lt;/span&gt; faststart &lt;span class=&quot;nt&quot;&gt;-pix_fmt&lt;/span&gt; yuv420p video.mp4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;video uploaded to YouTube:&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 1/1;&quot; videoid=&quot;Ydeg4bNHn08&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 16 Jun 2016 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2016/06/16/post-processing-animated-gifs/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2016/06/16/post-processing-animated-gifs/</guid>
        </item>
      
    

  </channel>
</rss>
