<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>opus &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/opus/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Thu, 24 Apr 2025 08:02:26 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>opus &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[Podcasts on Floppy Disk]]></title>
		<link>https://shkspr.mobi/blog/2020/09/podcasts-on-floppy-disk/</link>
					<comments>https://shkspr.mobi/blog/2020/09/podcasts-on-floppy-disk/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 05 Sep 2020 11:12:41 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[floppy]]></category>
		<category><![CDATA[opus]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=36490</guid>

					<description><![CDATA[An old 3.5 inch floppy disk holds 1.44 MB of data. According to my calculations, that&#039;s 1,424 KB blocks. For a total of 1,458,176 Bytes. Once formatted as FAT, you end up with 1,457,664 Bytes of storage.  But how much audio can a floppy hold?  (Here I mean wave based audio of human speech. It&#039;s trivial to fit more in using MIDI or speech synthesis.)  I&#039;m going to use &#34;A Podcast Of Unnecessary…]]></description>
										<content:encoded><![CDATA[<p>An old 3.5 inch floppy disk holds <code>1.44</code> MB of data. According to my calculations, that's <code>1,424</code> KB blocks. For a total of <code>1,458,176</code> Bytes. Once formatted as FAT, you end up with <code>1,457,664</code> Bytes of storage.  But how much <em>audio</em> can a floppy hold?</p>

<p>(Here I mean wave based audio of human speech. It's trivial to fit more in using MIDI or speech synthesis.)</p>

<p>I'm going to use "<a href="https://festivalofthespokennerd.com/podcast/">A Podcast Of Unnecessary Detail</a>" to experiment with, as this blogpost also has too much detail.  The podcast is a 39MB MP3, running just over half-an-hour.</p>

<p>Here's the first 40 seconds of the original MP3 file, so you can hear the music, and male and female voices.</p>

<audio controls="" style="width: 100%;">
  <source src="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim.mp3" type="audio/mp3">
</audio>

<p>That's about 800KB. A floppy can hold about a minute and a half of that quality audio.</p>

<h2 id="squash-it-down"><a href="https://shkspr.mobi/blog/2020/09/podcasts-on-floppy-disk/#squash-it-down">Squash it down</a></h2>

<p>A floppy disk holds about <code>11,000</code> Kilobits.  So, to hold 1,800 seconds (30 minutes) of audio, we need to encode audio at about 6kbps (Kilo<em>bits</em> per second)</p>

<p>By coincidence, the Opus audio format supports <a href="https://opus-codec.org/docs/opus-tools/opusenc.html#Encoding_options">encoding speech at around 6Kbps</a>.</p>

<p>Here's the same sample of the podcast bounced down to mono and encoded at 6Kbps.  The voices are pretty clear, but the music is extremely mushy. (The following files are in .opus format. They should play fine on <a href="https://caniuse.com/#search=opus">Android and most desktop browsers</a>).</p>

<audio controls="" style="width: 100%;">
  <source src="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim-6.opus" type="audio/ogg">
</audio>

<p>Encoded using <code>opusenc in.wav --downmix-mono --bitrate 6 out.opus</code></p>

<p>But that's still a little too big. The 33 minute podcast weighs in at <code>1,581,781</code> Bytes. Too large for a floppy.</p>

<p>Using the <code>--framesize</code> option, we can set the <a href="https://wiki.xiph.org/index.php?title=Opus_Recommended_Settings&amp;mobileaction=toggle_view_desktop#Framesize_Tweaking">Framesize</a> to 60 milliseconds. Not great for streaming, but we don't care about that, and it makes the files much smaller.</p>

<p>But <code>opusenc</code> has a few more tricks up its sleeve! Using <code>--cvbr</code> we can force the encoder never to go above a bitrate limit.</p>

<p>So, using <code>opusenc in.wav --downmix-mono --bitrate 6 --cvbr --framesize 60 out.opus</code> we can save 33 minutes, 8 seconds, in <code>1,422,676</code> Bytes. Enough space left over on a floppy disk for an image.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2020/09/Podcast-on-a-floppy-disk.jpg" alt="Podcast on a floppy disk." width="1024" height="576" class="alignleft size-full wp-image-36599">

<h2 id="but-wait-theres-more"><a href="https://shkspr.mobi/blog/2020/09/podcasts-on-floppy-disk/#but-wait-theres-more">But wait! There's more!</a></h2>

<p>Surprisingly, the <code>opusenc</code> documentation is not quite telling us the whole story! You can pass any number <em>lower</em> than 6 to <code>opusenc</code> and it will try its best.</p>

<p>In practice, the lowest bitrate it will generate for speech is about 4Kbps.  Here's the same sample:</p>

<audio controls="" style="width: 100%;">
  <source src="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim-4.opus" type="audio/ogg">
</audio>

<p><code>opusenc in.wav --downmix-mono --bitrate 4 out-4.opus</code></p>

<p>What's the <em>lowest</em> we can go? How low before we lose all meaning?</p>

<p>The absolutely lowest encoding which produced any sound at all was 1.2Kbps. I warn you, this sounds awful!</p>

<audio controls="" style="width: 100%;">
  <source src="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim-1.2-hard.opus" type="audio/ogg">
</audio>

<p><code>opusenc in.wav --downmix-mono --bitrate 1.2 --hard-cbr out-1.2-hard.opus</code></p>

<p>This uses the <a href="https://opus-codec.org/docs/opus-tools/opusenc.html#Encoding_options"><code>--hard-cbr</code></a> option which forces the encoder to a specific constant bitrate.</p>

<p>About the lowest you can go and still have things even vaguely intelligible was 2Kbps. Again, this sounds horrible, but it is just about possible to understand most of the speech. Even if they do sound like Daleks with low batteries!</p>

<audio controls="" style="width: 100%;">
  <source src="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim-2-hard.opus" type="audio/ogg">
</audio>

<p><code>opusenc in.wav --downmix-mono --bitrate 2 --hardcbr out-2-hard.opus</code></p>

<p>If you were prepared to have you audio that shitty, you can just about squeeze a full hour of speech onto an old floppy disk.</p>

<p>So, guess what tomorrow's blog post is going to be...?</p>

<h2 id="enjoyed-this-post"><a href="https://shkspr.mobi/blog/2020/09/podcasts-on-floppy-disk/#enjoyed-this-post">Enjoyed this post?</a></h2>

<p>If you like the silly things I do, you can say thanks by:</p>

<ul>
<li><a href="https://ko-fi.com/edent">Buying me a Ko-Fi</a></li>
<li><a href="https://github.com/sponsors/edent">Sponsoring me on GitHub</a></li>
<li><a href="https://www.amazon.co.uk/hz/wishlist/ls/13GFCFR2B2IX4?type=wishlist&amp;linkCode=sl2&amp;tag=shksprblogwish-21">Getting me a present from my Wishlist</a></li>
</ul>

<p>Or, just leave a supportive comment.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=36490&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2020/09/podcasts-on-floppy-disk/feed/</wfw:commentRss>
			<slash:comments>13</slash:comments>
		
		<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim.mp3" length="800764" type="audio/mpeg" />
<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim-6.opus" length="32045" type="audio/opus" />
<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim-4.opus" length="27084" type="audio/opus" />
<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim-1.2-hard.opus" length="9952" type="audio/opus" />
<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2020/09/trim-2-hard.opus" length="13954" type="audio/opus" />

			</item>
		<item>
		<title><![CDATA[Removing default metadata from .opus files]]></title>
		<link>https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/</link>
					<comments>https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 24 Apr 2020 11:03:00 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[metadata]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[opus]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=34763</guid>

					<description><![CDATA[I&#039;m trying to create some ridiculously tiny audio files. The sort where every single byte matters.  I&#039;ve encoded a small sample. But the opusenc tool automatically adds metadata - even if you don&#039;t specify any.  Using the amazing Mutagen Python library I was able to completely strip out all the metadata!  import mutagen mutagen.File(&#34;example.opus&#34;).delete()   It edits the file immediately - so be …]]></description>
										<content:encoded><![CDATA[<p>I'm trying to create some ridiculously tiny audio files. The sort where every single byte matters.</p>

<p>I've encoded a small sample. But the <code>opusenc</code> tool automatically adds metadata - even if you don't specify any.</p>

<p>Using the amazing <a href="https://mutagen.readthedocs.io/en/latest/">Mutagen Python library</a> I was able to completely strip out <em>all</em> the metadata!</p>

<pre><code class="language-python">import mutagen
mutagen.File("example.opus").delete()
</code></pre>

<p>It edits the file <strong>immediately</strong> - so be careful!</p>

<p>But what is it actually doing? I wanted to understand a bit more - so let's go hex diving!</p>

<h2 id="what-the-user-sees"><a href="https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/#what-the-user-sees">What the user sees</a></h2>

<p>Running <code>opusinfo example.opus</code> gives:</p>

<pre><code class="language-_">New logical stream (#1, serial: 03fe3cc9): type opus
Encoded with libopus 1.3.1, libopusenc 0.2.1
User comments section follows...
    ENCODER=opusenc from opus-tools 0.2
    ENCODER_OPTIONS=--bitrate 6 --comp 10 --framesize 60 --padding 0
Opus stream 1:
    ...
Logical stream 1 ended
</code></pre>

<p>There are two "mandatory" comments. The ENCODER and the ENCODER_OPTIONS.
I can't find a way to stop those being generated by <code>opusenc</code>.</p>

<p>The <a href="https://opus-codec.org/docs/opusfile_api-0.7/structOpusTags.html">Opus File API</a> gives some idea about the binary structure of the file.</p>

<p>But the real magic happens in the <a href="https://tools.ietf.org/html/rfc7845.html#section-5.2">Opus Forumat Specification RFC</a>. It details the header format in 32 bit clumps.</p>

<pre><code class="language-_">      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |      'O'      |      'p'      |      'u'      |      's'      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |      'T'      |      'a'      |      'g'      |      's'      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                     Vendor String Length                      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                                               |
     :                        Vendor String...                       :
     |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                   User Comment List Length                    |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                 User Comment #0 String Length                 |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                                               |
     :                   User Comment #0 String...                   :
     |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                 User Comment #1 String Length                 |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     :                                                               :

</code></pre>

<p>Let's take a look at our file in binary, jumping straight to the comment section.</p>

<pre><code class="language-_">0000004b: 4f70 7573  Opus
0000004f: 5461 6773  Tags
</code></pre>

<p>Starts as expected. Next is the Vendor String Length</p>

<pre><code class="language-_">00000053: 1f00 0000  ....
</code></pre>

<p>0x1f is 31 bytes. This is a 32 bit, unsigned, <a href="https://en.wikipedia.org/wiki/Endianness">little endian number</a>. Hence it is written as <code>1f00</code> which becomes <code>00001f</code>.</p>

<pre><code class="language-_">00000057: 6c69 626f  libo
0000005b: 7075 7320  pus 
0000005f: 312e 332e  1.3.
00000063: 312c 206c  1, l
00000067: 6962 6f70  ibop
0000006b: 7573 656e  usen
0000006f: 6320 302e  c 0.
00000073: 322e 31    2.1
</code></pre>

<p>According to the spec, no terminating null octet is necessary. So the next bytes are the User Comment List Length.  Continuing on from the previous line:</p>

<pre><code class="language-_">00000073:        02     .
00000077: 0000 00    ...
</code></pre>

<p>There are two comments (again, 32 bit little endian).</p>

<blockquote><p>This field indicates the number of user-supplied comments.  It MAY indicate there are zero user-supplied comments, in which case there are no additional fields in the packet.</p></blockquote>

<p>This means we <em>can</em> have an empty comment section! This is what you get by default:</p>

<pre><code class="language-_">00000077:        23  ...#
0000007b: 0000 00    ...
</code></pre>

<p>First string length is 0x23 = 35 bytes long. Again, little endian.</p>

<pre><code class="language-_">0000007e: 454e 434f  ENCO
00000082: 4445 523d  DER=
00000086: 6f70 7573  opus
0000008a: 656e 6320  enc 
0000008e: 6672 6f6d  from2
00000092: 206f 7075   opu
00000096: 732d 746f  s-to
0000009a: 6f6c 7320  ols 
0000009e: 302e 3240  0.2@
</code></pre>

<p>After exactly 35 bytes, we get our next little endian number 0x40 = 64.</p>

<pre><code class="language-_">000000a1: 4000 0000  @...
000000a5: 454e 434f  ENCO
000000a9: 4445 525f  DER_
000000ad: 4f50 5449  OPTI
000000b1: 4f4e 533d  ONS=
000000b5: 2d2d 6269  --bi
000000b9: 7472 6174  trat
000000bd: 6520 3620  e 6 
000000c1: 2d2d 636f  --co
000000c5: 6d70 2031  mp 1
000000c9: 3020 2d2d  0 --
000000cd: 6672 616d  fram
000000d1: 6573 697a  esiz
000000d5: 6520 3630  e 60
000000d9: 202d 2d70   --p
000000dd: 6164 6469  addi
000000e1: 6e67 2030  ng 0
</code></pre>

<p>And that's the end of the comment section!</p>

<h2 id="manually-editing-the-file"><a href="https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/#manually-editing-the-file">Manually editing the file</a></h2>

<p>I started by setting the User Comment List Length to zero, and removing all the subsequent comment data. That didn't work. <code>opusinfo</code> gave the following errors:</p>

<pre><code class="language-_">WARNING: Hole in data (28 bytes) found at approximate offset 1492 bytes. Corrupted Ogg.
WARNING: Hole in data (51 bytes) found at approximate offset 1492 bytes. Corrupted Ogg.
WARNING: sequence number gap in stream 1. Got page 2 when expecting page 1. Indicates missing data.
WARNING: discontinuity in stream (1)
</code></pre>

<p><a href="https://tools.ietf.org/html/rfc7845.html#section-3">Back to the documentation</a>!</p>

<blockquote><p>An Ogg Opus stream is organized as follows (see Figure 1 for an example).</p></blockquote>

<pre><code class="language-_">        Page 0         Pages 1 ... n        Pages (n+1) ...
     +------------+ +---+ +---+ ... +---+ +-----------+ +---------+ +--
     |            | |   | |   |     |   | |           | |         | |
     |+----------+| |+-----------------+| |+-------------------+ +-----
     |||ID Header|| ||  Comment Header || ||Audio Data Packet 1| | ...
     |+----------+| |+-----------------+| |+-------------------+ +-----
     |            | |   | |   |     |   | |           | |         | |
     +------------+ +---+ +---+ ... +---+ +-----------+ +---------+ +--
     ^      ^                           ^
     |      |                           |
     |      |                           Mandatory Page Break
     |      |
     |      ID header is contained on a single page
     |
     'Beginning Of Stream'

    Figure 1: Example Packet Organization for a Logical Ogg Opus Stream
</code></pre>

<blockquote><p>There are two mandatory header packets.  The first packet in the logical Ogg bitstream MUST contain the identification (ID) header, which uniquely identifies a stream as Opus audio.  The format of this header is defined in Section 5.1.  It is placed alone (without any other packet data) on the first page of the logical Ogg bitstream and completes on that page.  This page has its 'beginning of stream' flag set.</p>

<p>The second packet in the logical Ogg bitstream MUST contain the comment header, which contains user-supplied metadata.  The format of this header is defined in Section 5.2.  It MAY span multiple pages, beginning on the second page of the logical stream.  However many pages it spans, the comment header packet MUST finish the page on which it completes.</p></blockquote>

<p>I tried saying there was one comment, with a length of zero and a null comment. That didn't work either.</p>

<p>I think this is because <em>before</em> the start of the comment header there is something describing how long the packet will be.</p>

<h2 id="headers"><a href="https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/#headers">Headers</a></h2>

<p>Here are the headers from the original file, and the one stripped by Mutagen.</p>

<h3 id="original-header"><a href="https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/#original-header">Original Header</a></h3>

<pre><code class="language-_">00000000: 4f67 6753 0002 0000  OggS....
00000008: 0000 0000 0000 c93c  .......&lt;
00000010: fe03 0000 0000 f90e  ........
00000018: f775 0113 4f70 7573  .u..Opus
00000020: 4865 6164 0101 3801  Head..8.
00000028: 80bb 0000 0000 004f  .......O
00000030: 6767 5300 0000 0000  ggS.....
00000038: 0000 0000 00c9 3cfe  ......&lt;.
00000040: 0301 0000 0035 dfaf  .....5..
00000048: 0601 9a4f 7075 7354  ...OpusT
00000050: 6167 731f 0000 006c  ags....l
00000058: 6962 6f70 7573 2031  ibopus 1
</code></pre>

<h3 id="stripped-header"><a href="https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/#stripped-header">Stripped Header</a></h3>

<pre><code class="language-_">00000000: 4f67 6753 0002 0000  OggS....
00000008: 0000 0000 0000 c93c  .......&lt;
00000010: fe03 0000 0000 f90e  ........
00000018: f775 0113 4f70 7573  .u..Opus
00000020: 4865 6164 0101 3801  Head..8.
00000028: 80bb 0000 0000 004f  .......O
00000030: 6767 5300 0000 0000  ggS.....
00000038: 0000 0000 00c9 3cfe  ......&lt;.
00000040: 0301 0000 00ae 941c  ........
00000048: 4e01 2f4f 7075 7354  N./OpusT
00000050: 6167 731f 0000 006c  ags....l
00000058: 6962 6f70 7573 2031  ibopus 1
</code></pre>

<h3 id="the-difference"><a href="https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/#the-difference">The Difference</a></h3>

<pre><code class="language-_">Original                                  Stripped
00000040: 0301 0000 0035 dfaf  .....5.. | 00000040: 0301 0000 00ae 941c  ........
00000048: 0601 9a4f 7075 7354  ...OpusT | 00000048: 4e01 2f4f 7075 7354  N./OpusT
</code></pre>

<p>So, something is happening in bytes 45 - 50. But what?</p>

<blockquote><p>A page is a header of 26 bytes, followed by the length of the data, followed by the data. The constructor is givin a file-like object pointing to the start of an Ogg page. After the constructor is finished it is pointing to the start of the next page</p>

<p><a href="https://github.com/quodlibet/mutagen/blob/master/mutagen/ogg.py#L35">Mutagen Source Code</a></p></blockquote>

<p>Unfortunately, my brain freezes up when I see things like</p>

<pre><code class="language-python">header = struct.unpack('&lt;4sBBqIIiB', header_data)
</code></pre>

<p>But the code does point to the <a href="https://wiki.xiph.org/Ogg#Ogg_page_format">Ogg page format specification</a>.</p>

<blockquote><p>The LSb (least significant bit) comes first in the Bytes. Fields with more than one byte length are encoded LSB (least significant byte) first.</p></blockquote>

<pre><code class="language-_">  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | capture_pattern: Magic number for page start "OggS"           | 0-3
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | version       | header_type   | granule_position              | 4-7
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                                                               | 8-11
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                               | bitstream_serial_number       | 12-15
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                               | page_sequence_number          | 16-19
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                               | CRC_checksum                  | 20-23
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                               | page_segments | segment_table | 24-27
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | ...                                                           | 28-
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
</code></pre>

<p>So, it is the CRC Checksum which is different.  The <a href="https://xiph.org/vorbis/doc/framing.html">Vorbis framing documentation</a> has a brief description of how the CRC is calculated - but the full documentation 404s.</p>

<h2 id="conclusion"><a href="https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/#conclusion">Conclusion</a></h2>

<p>Hand editing binary files is for mugs.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=34763&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2020/04/removing-default-metadata-from-opus-files/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Bouncing all my music down to Opus]]></title>
		<link>https://shkspr.mobi/blog/2020/01/bouncing-all-my-music-down-to-opus/</link>
					<comments>https://shkspr.mobi/blog/2020/01/bouncing-all-my-music-down-to-opus/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 26 Jan 2020 18:00:06 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[mp3]]></category>
		<category><![CDATA[opus]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=33686</guid>

					<description><![CDATA[As much as technology marches forward, there are two truths I need to accept.   File transfer speeds are always going to be slower that I can be bothered to wait My ears aren&#039;t going to get any better at hearing   For years, I ripped all of my music as FLAC. I collected ridiculously high-resolution audio files. I devoured disk drive space for surround sound soundtracks.  &#34;One day,&#34; I thought,…]]></description>
										<content:encoded><![CDATA[<p>As much as technology marches forward, there are two truths I need to accept.</p>

<ol>
<li>File transfer speeds are always going to be slower that I can be bothered to wait</li>
<li>My ears aren't going to get any better at hearing</li>
</ol>

<p>For years, I ripped all of my music as FLAC. I collected ridiculously high-resolution audio files. I devoured disk drive space for surround sound soundtracks.</p>

<p>"One day," I thought, "I'll have an amazing audio system to play these back on."</p>

<p>The reality is that I spend most of my time listening to music on <a href="https://shkspr.mobi/blog/2020/01/usb-c-powered-bluetooth-headphones-the-life-q10-from-anker/">£40 bluetooth headphones</a>.  I have a nice 5.1 surround sound system - but it isn't exactly THX certified. And, thanks to the construction of British houses, I can't turn it up to 11 without my neighbours complaining.</p>

<p>Yesterday, as I was waiting for a couple of GB of new music to fly through the aether to my phone, I was struck by a realisation...</p>

<p>I am not an archivist.</p>

<p>I don't need to preserve all my commercially-bought music in the highest resolution possible. It isn't my job to faithfully preserve every ultrasonic decibel. And I am <em>never</em> going to own a set of speakers which will super-charge my old ears.</p>

<p>It's OK to bounce my music down to a <em>more convenient</em> file format.</p>

<h2 id="enter-opus"><a href="https://shkspr.mobi/blog/2020/01/bouncing-all-my-music-down-to-opus/#enter-opus">Enter Opus</a></h2>

<p>I've written before <a href="https://shkspr.mobi/blog/tag/opus/">about the Opus file format</a>. It's the modern and open successor to MP3. It isn't lossless - but I've compared the quality, and I can't hear a damned difference.</p>

<p>Turns out, <a href="https://wiki.hydrogenaud.io/index.php?title=Hydrogenaudio_Listening_Tests">everyone agrees with me</a>.  Even at <a href="https://listening-test.coresv.net/results.htm">extremely low bitrates</a> it is superior to every other format.</p>

<p>Opus plays back natively on Android, it supports all the normal music metadata / IDv3 tags, and <a href="https://people.xiph.org/~xiphmont/demo/opus/demo3.shtml">works perfectly with surround sound</a>. The codec and tools are Open Source and Linux friendly.</p>

<p>And, best of all, it's small!  Even when I encode at the maximum possible bitrate (I'm not a <em>total</em> savage!) an hour of 5.1 audio is about 20% of the size of FLAC.</p>

<p>I know I could buy a bigger disk. But while home storage is relatively cheap, mobile storage is still expensive. Yes, WiFi 6 will make everything better - but I don't <em>need</em> to fling gigabytes through the air to my tin ears.</p>

<p>So, from now on, everything is getting run through:
<code>opusenc --bitrate 1536 in.flac out.opus</code></p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=33686&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2020/01/bouncing-all-my-music-down-to-opus/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Extracting DVD-Audio on Linux, the modern(ish) way]]></title>
		<link>https://shkspr.mobi/blog/2019/01/extracting-dvd-audio-on-linux-the-modernish-way/</link>
					<comments>https://shkspr.mobi/blog/2019/01/extracting-dvd-audio-on-linux-the-modernish-way/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 24 Jan 2019 17:16:04 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[dvd]]></category>
		<category><![CDATA[dvda]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[opus]]></category>
		<category><![CDATA[ripping]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=31270</guid>

					<description><![CDATA[DVD-Audio (henceforce DVDA) is an unloved and mostly forgotten audio format. Nevertheless, there&#039;s a large back-catalogue of music which is still trapped on ancient discs encoded in the proprietary MLP format.  A few years ago I wrote about how to extract the audio using the obsolete Windows program DVD-Audio Explorer.  I wanted to be able to run the extraction via the command line, which means…]]></description>
										<content:encoded><![CDATA[<p>DVD-Audio (henceforce DVDA) is an unloved and mostly forgotten audio format. Nevertheless, there's a large back-catalogue of music which is still trapped on ancient discs encoded in the proprietary MLP format.</p>

<p>A few years ago <a href="https://shkspr.mobi/blog/2014/08/dealing-with-quadrophonic-dvd-a-files-in-linux/">I wrote about how to extract the audio using the obsolete Windows program DVD-Audio Explorer</a>.  I wanted to be able to run the extraction via the command line, which means trying to find a native Linux app.  I tried <a href="http://audiotools.sourceforge.net/">Python AudioTools</a> but I got lost in an endless maze of incompatible dependencies.</p>

<p>So I went with <a href="https://github.com/tuffy/libdvd-audio">Brian "tuffy" Langenberger's <code>libDVD-Audio</code></a>.</p>

<p>To install, simply run:</p>

<pre><code class="language-_">sudo make install
</code></pre>

<p>That will give you two new programs.  To get info about your DVDA, run:</p>

<pre><code class="language-_">dvda-debug-info -A /path/to/your/AUDIO_TS
</code></pre>

<p>That will pump out details about each track like so:</p>

<pre><code class="language-_">Title  Track  Length  PTS Length  First Sector  Last Sector
    1      1    3:30    13450000             0        86547
    1      2    4:11    12500000         73144       122600
    1      3    2:11    16010000        370601       233337
</code></pre>

<h2 id="extract"><a href="https://shkspr.mobi/blog/2019/01/extracting-dvd-audio-on-linux-the-modernish-way/#extract">Extract</a></h2>

<p>To extract the tracks, run:</p>

<pre><code class="language-_">dvda2wav -A /path/to/your/AUDIO_TS
</code></pre>

<p>That will spit out the files in WAV format.</p>

<h2 id="encode"><a href="https://shkspr.mobi/blog/2019/01/extracting-dvd-audio-on-linux-the-modernish-way/#encode">Encode</a></h2>

<p>WAV is pretty large - about 20MB per minute per channel.  Converting to FLAC (the Free Lossless Audio Codec) gets you down to about 10MB.  I just go straight for the modern <a href="https://opus-codec.org/">Opus Codec</a> which does excellent quality surround sound at low file sizes.</p>

<pre><code class="language-_">opusenc --bitrate 4096 track-01-01.wav 1.opus
</code></pre>

<p>That's about 2MB/minute/channel and I promise that you won't hear the difference.</p>

<h2 id="metadata"><a href="https://shkspr.mobi/blog/2019/01/extracting-dvd-audio-on-linux-the-modernish-way/#metadata">Metadata</a></h2>

<p>If you want to add metadata to a track, it's done like this:</p>

<pre><code class="language-_">opusenc --bitrate 4096 in.wav out.opus --title "Yesterday" --artist "The Beatles" --tracknumber "02"
</code></pre>

<p>Older versions of Opusenc, oddly, don't have a native way to express track numbers, so you'll need to do it manually using <code>--comment "tracknumber=02"</code></p>

<p><ins datetime="2019-07-30T08:08:18+00:00">Newer versions can use <code>--tracknumber</code> to add track numbers.</ins></p>

<h3 id="automating"><a href="https://shkspr.mobi/blog/2019/01/extracting-dvd-audio-on-linux-the-modernish-way/#automating">Automating</a></h3>

<p>You can make it slightly easier to add the metadata if you give the files predictable names.  For example: <code>01-Yesterday-The Beatles.wav</code></p>

<p>Here's a scrappy bash script:</p>

<pre><code class="language-_">#!/bin/bash
for FILE in *.wav
do
    FILENAME="${FILE%.*}"

    TRACK=$(echo  $FILENAME | cut -d'-' -f 1)
    TITLE=$(echo  $FILENAME | cut -d'-' -f 2)
    ARTIST=$(echo $FILENAME | cut -d'-' -f 3)

    OUTPUT="[$TRACK] $ARTIST - $TITLE.opus"

    opusenc --bitrate 4096 "$FILE" "$OUTPUT" --title "$TITLE" --artist "$ARTIST" --tracknumber "$TRACK"
done
</code></pre>

<p>I hope future me finds these notes useful!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=31270&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2019/01/extracting-dvd-audio-on-linux-the-modernish-way/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Convert Surround Sound WAV albums to individual opus files]]></title>
		<link>https://shkspr.mobi/blog/2018/03/convert-surround-sound-wav-albums-to-individual-opus-files/</link>
					<comments>https://shkspr.mobi/blog/2018/03/convert-surround-sound-wav-albums-to-individual-opus-files/#respond</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 18 Mar 2018 18:38:16 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[flac]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[opus]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=29185</guid>

					<description><![CDATA[As ever, notes to myself.  This is a method to take a .wav and .cue and transform it into individual files. In this case, .opus.  Transform to .flac  FLAC is a good intermediary file format, especially for surround sound files.  avconv -i file.wav out.flac  Transform to .opus  An optional step if you want smaller files. Maximum quality for 6 channel audio.  opusenc --bitrate 4096 out.flac…]]></description>
										<content:encoded><![CDATA[<p>As ever, notes to myself.  This is a method to take a <code>.wav</code> and <code>.cue</code> and transform it into individual files. In this case, <code>.opus</code>.</p>

<h3 id="transform-to-flac"><a href="https://shkspr.mobi/blog/2018/03/convert-surround-sound-wav-albums-to-individual-opus-files/#transform-to-flac">Transform to <code>.flac</code></a></h3>

<p>FLAC is a good intermediary file format, especially for surround sound files.</p>

<p><code>avconv -i file.wav out.flac</code></p>

<h3 id="transform-to-opus"><a href="https://shkspr.mobi/blog/2018/03/convert-surround-sound-wav-albums-to-individual-opus-files/#transform-to-opus">Transform to <code>.opus</code></a></h3>

<p>An optional step if you want smaller files.
Maximum quality for 6 channel audio.</p>

<p><code>opusenc --bitrate 4096 out.flac out.opus</code></p>

<h3 id="create-an-mkv"><a href="https://shkspr.mobi/blog/2018/03/convert-surround-sound-wav-albums-to-individual-opus-files/#create-an-mkv">Create an <code>.mkv</code></a></h3>

<p>Add to an MKV with all the chapter information.</p>

<p><code>mkvmerge -o test.mkv --chapters file.cue out.opus</code></p>

<h3 id="split-to-individual-files"><a href="https://shkspr.mobi/blog/2018/03/convert-surround-sound-wav-albums-to-individual-opus-files/#split-to-individual-files">Split to individual files</a></h3>

<p>Individual MKVs</p>

<p><code>mkvmerge test.mkv --split chapters:all -o track.mkv</code></p>

<h3 id="extract-the-audio"><a href="https://shkspr.mobi/blog/2018/03/convert-surround-sound-wav-albums-to-individual-opus-files/#extract-the-audio">Extract the Audio</a></h3>

<p>One at a time:</p>

<p><code>mkvextract tracks "track-001.mkv" 0:"individual.opus"</code></p>

<h2 id="all-in-one-bash-script"><a href="https://shkspr.mobi/blog/2018/03/convert-surround-sound-wav-albums-to-individual-opus-files/#all-in-one-bash-script">All-In-One  bash script</a></h2>

<pre><code class="language-bash">#!/bin/bash
json=$(ffprobe -i "file.mkv" -print_format json -show_chapters -loglevel error)
count=$(echo $json | jq ".chapters | length" )

mkvmerge test.mkv -D -S --split chapters:all -o "%02d.mkv"

COUNTER=1
while [ $COUNTER -le $count ]; do

  printf -v zerotrack "%02d" $COUNTER

  json=$(ffprobe -i "$zerotrack.mkv" -print_format json -show_chapters -loglevel error)
  title=$(echo $json | jq ".chapters[0].tags.title" -r)
  filename="[$zerotrack] $title"

  mkvextract tracks "$zerotrack.mkv" 0:"$filename.opus"
  let COUNTER=COUNTER+1 
done
</code></pre>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=29185&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2018/03/convert-surround-sound-wav-albums-to-individual-opus-files/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
