<?xml version="1.0" encoding="UTF-8"?>
<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>No, I am better than that!</title>
	<atom:link href="http://rickosborne.org/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://rickosborne.org/blog</link>
	<description>Striving to subdue the mediocrity.</description>
	<lastBuildDate>Sun, 21 Aug 2011 23:27:16 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Still fighting with audiobooks</title>
		<link>http://rickosborne.org/blog/2011/08/still-fighting-with-audiobooks/</link>
		<comments>http://rickosborne.org/blog/2011/08/still-fighting-with-audiobooks/#comments</comments>
		<pubDate>Sun, 21 Aug 2011 23:27:16 +0000</pubDate>
		<dc:creator>Rick Osborne</dc:creator>
				<category><![CDATA[Books]]></category>
		<guid isPermaLink="false">http://rickosborne.org/blog/?p=1326</guid>
		<description><![CDATA[I&#8217;m fighting with audiobooks again. I&#8217;ve mostly got my process down, but there continue to be new levels of dumb to deal with. As it stands, the process goes something like: Buy an audiobook. Generally, this is on CD because I trust Audible about as much as I trust Ubisoft, and for the exact same [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m fighting with audiobooks again.</p>
<p>I&#8217;ve mostly got my process down, but there continue to be new levels of dumb to deal with.  As it stands, the process goes something like:</p>
<ol>
<li>Buy an audiobook.  Generally, this is on CD because I trust Audible about <a href="http://arst.ch/qm1">as much as I trust Ubisoft</a>, and for the exact same reason.</li>
<li>Rip the discs to high-quality MP3.  On OSX I use <a href="http://sbooth.org/Max/">Max</a>, and on Windows I built a custom version of <a href="http://cdexos.sf.net">CDex</a> to support disc numbers.</li>
<li>Queue up all of the individual tracks and play the first few seconds of each, looking for chapter breaks.  For the breaks, title the track something smart like <q>4: The Leaky Cauldron</q>.</li>
<li>Use the <a href="https://github.com/rickosborne/rickosborne/tree/master/audiobookify">audiobookify Perl script</a> I wrote to bundle tracks into chapters and convert from MP3 to M4B format so that I get nice chapter markers, cover art, etc.  (And because pretty much every device plays M4B at this point.)</li>
</ol>
<p>It&#8217;s a real PITA most of the time, but manageable.  Sometimes, if I&#8217;m lucky, the CDDB titles for the tracks will be something like <q>Chapter 4c</q> so I don&#8217;t have to manually scan and tag.</p>
<p>But sometimes it&#8217;s just profoundly dumb.</p>
<p>Some audiobook producers (Blackstone, for one) think it&#8217;s amusing to disregard the actual spoken breaks in the book and instead use some fixed amount, such as tracks every 3 minutes.  Some producers find it necessary to use all 99 available tracks on a CD.  Some include intro and outro music on each disc.  Some include a minute or two of the end of the previous disc as the start of the next disc (and then don&#8217;t put the overlap into its own track).  Some audiobooks are only available as giant disc-length MP3 files with no breaks at all.</p>
<p style="margin-left: 5em; font-style: italic;">Why am I so hung up on chapter breaks?  I spend most of my time listening to audiobooks while running.  Trying to manage iPod volume and sport accoutrements while running involves a whole lot of fumbling.  Occasionally, I&#8217;ll accidentally hit the track forward or back buttons.  This is compounded by the length of audiobooks, which are often packaged into 5&frac12; hour chunks, owing to a longstanding iPod firmware issue that does hinky things with longer audiobooks.  Without proper breaks, scanning through a 5&frac12; hour file is a nontrivial task.</p>
<p>When I am confronted with such lunacy, the easiest solution I&#8217;ve found is:</p>
<ol>
<li>Join together all of the track files into one giant file.</li>
<li>Use <a href="http://mp3splt.sourceforge.net/">mp3splt</a> to find likely chapter breaks based on silence.  But since I can&#8217;t under-split, as it would offend my delicate sensibilities to have 18 of 20 chapters marked, I have to over-split.  And mp3splt is a bit of a hammer, so you have to <em>really</em> over-split.  I find that a 20:1 ratio is what generally works: for every 1 chapter in the audiobook, split the book into 20 parts.</li>
<li>Pick up on step 3 above.</li>
</ol>
<p style="margin-left: 5em; font-style: italic;">I occasionally run into audiobooks that are just a bit too long to fit neatly into 5&frac12; hour chunks.  In such a case, <a href="http://sox.sourceforge.net/">SoX</a> has functionality to let you change tempo without changing pitch, which works well on audiobooks.  I&#8217;ve found that speeding up most audiobooks up to 20% is almost negligible for impacting comprehension.  This is also useful for the narrators that are just a bit too slow for my liking.</p>
<p>I&#8217;ve been looking into speech recognition software to help me out.  You would think that it would be trivial: obtain an electronic text of the book, give the audio and the text to some magical program that parses the speech and associates timecodes with the text, and then do a phenome search for words like <q>chapter</q>, <q>book</q>, and <q>part</q>.</p>
<p>Unfortunately, it looks like that magical software doesn&#8217;t exist yet.  The closest thing might be <a href="http://labs.google.com/gaudi">Google Audio Indexing</a>, the technology that powers Google Voice.  But as near as I can tell, it&#8217;s not for public use yet.  Something might be built atop <a href="http://cmusphinx.sourceforge.net/sphinx4/">CMU Sphinx</a>, but it would take a significant effort.</p>
<p>Oh, and on a side note: M4B files will upload to Google Music (with some limitations on file size), but are automagically converted to MP3 format on their servers, thus removing any chapter breaks.  Amazon&#8217;s Cloud Player does not yet support M4B files.</p>
<p>Side note the second: a 5-hour M4B file of decent quality weighs in at around 100MB.  Even really long audiobooks, Neal Stephenson&#8217;s <em>Anathem</em> for example, don&#8217;t generally run more than 30-35 hours.  You could fit even that entire book as a series of 7 M4B files on a single CD.  Up that to a 1GB flash drive and get ridiculous audio quality instead of just decent.  And again, pretty much every device supports M4B files these days.  Or, you know, you could go on selling <a href="http://www.audioeditions.com/products/Anathem-Neal-Stephenson-281473.aspx">a 2lb stack of 28 CDs</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://rickosborne.org/blog/2011/08/still-fighting-with-audiobooks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bind SERVFAIL on slave zone</title>
		<link>http://rickosborne.org/blog/2011/08/bind-servfail-on-slave-zone/</link>
		<comments>http://rickosborne.org/blog/2011/08/bind-servfail-on-slave-zone/#comments</comments>
		<pubDate>Wed, 10 Aug 2011 05:53:11 +0000</pubDate>
		<dc:creator>Rick Osborne</dc:creator>
				<category><![CDATA[Web]]></category>
		<guid isPermaLink="false">http://rickosborne.org/blog/?p=1322</guid>
		<description><![CDATA[This is for my own future reference. I had a problem earlier tonight where the slave DNS server my domains wouldn&#8217;t return anything but SERVFAIL for any domain. The server was up and running and the config files hadn&#8217;t changed, but it just wouldn&#8217;t give any useful answers. The log (in /var/log/named/bind.log) only gave messages [...]]]></description>
			<content:encoded><![CDATA[<p>This is for my own future reference.</p>
<p>I had a problem earlier tonight where the slave DNS server my domains wouldn&#8217;t return anything but SERVFAIL for any domain.  The server was up and running and the config files hadn&#8217;t changed, but it just wouldn&#8217;t give any useful answers.</p>
<p>The log (in <kbd>/var/log/named/bind.log</kbd>) only gave messages like:</p>
<pre>general: warning: zone rickosborne.org/IN: expired</pre>
<p>I tried using <kbd>rndc</kbd> on the slave to retransfer the zones, upped the debug trace logging, and even eventually stopped and restarted Bind.  No change.</p>
<p>On a whim, I restarted the master Bind.  This produced a more useful error:</p>
<pre>security: error: client a.b.c.d#nnn: request has invalid signature: TSIG rickosborne: tsig
 verify failure (BADTIME)</pre>
<p>That <kbd>BADTIME</kbd> message comes up when the clocks on the master and slave get more than 5 minutes out of sync.  The master server&#8217;s clock was off by ~15 minutes.</p>
<p>The virtual server, running on Xen, ignored any attempts to set the clock.  I tried plain old <kbd>date</kbd> and <kbd>ntpdate</kbd>, and both appeared to work, but were actually silently ignored.  There&#8217;s a magical incantation to fix that:</p>
<pre>echo 1 &gt; /proc/sys/xen/independent_wallclock</pre>
<p>So, yeah.  That was a fun 2 hours of my life to track down and fix.  I hope it helps someone else.</p>
]]></content:encoded>
			<wfw:commentRss>http://rickosborne.org/blog/2011/08/bind-servfail-on-slave-zone/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Because no one in Web Dev uses interfaces</title>
		<link>http://rickosborne.org/blog/2011/03/because-no-one-in-web-dev-uses-interfaces/</link>
		<comments>http://rickosborne.org/blog/2011/03/because-no-one-in-web-dev-uses-interfaces/#comments</comments>
		<pubDate>Fri, 25 Mar 2011 00:26:09 +0000</pubDate>
		<dc:creator>Rick Osborne</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[adb]]></category>
		<category><![CDATA[couchdb]]></category>
		<category><![CDATA[mongodb]]></category>
		<guid isPermaLink="false">http://rickosborne.org/blog/?p=1305</guid>
		<description><![CDATA[One of my long-standing assertions is that perfect OOP is amusing and all, but it tends to stumble a bit when it comes to web development. Interfaces are my go-to example for this. How often, in everyday real-world web development, are you suddenly going to need to swap out one Model class for another? &#8220;Well, [...]]]></description>
			<content:encoded><![CDATA[<p>One of my long-standing assertions is that perfect OOP is amusing and all, but it tends to stumble a bit when it comes to web development.  Interfaces are my go-to example for this.  How often, in everyday real-world web development, are you suddenly going to need to swap out one Model class for another?  &ldquo;Well, it&#8217;s Tuesday, so we need to load the Oracle layer instead of the Access layer.&rdquo;</p>
<p>To prove myself wrong, I built interfaces into this term&#8217;s <em>Advanced Database Structures</em> final exam practical.  The course has changed a bit since I started teaching it almost 2 years ago (holy crap!).  The core objectives are the same, but the underlying technology has been migrated to CouchDB and MongoDB.</p>
<p>From the exam instructions:</p>
<blockquote><p>You will be refactoring an existing application to work against a MongoDB database and a CouchDB database.  The codebase has been set up so that the application works as-is against a MySQL database and you can easily switch back and forth between each of the databases without having to modify anything other than the two Model classes you are responsible for.</p></blockquote>
<p class="noindent" style="float:right;margin:0 0 1.5em 1.5em"><a href="/blog/wp-content/uploads/2011/03/fsail-home.png"><img src="/blog/wp-content/uploads/2011/03/fsail-home-300x195.png" title="fsa.il link shortening service" width="300" height="195" class="aligncenter" /></a></p>
<p>The application I give them is a really simple proof-of-concept link shortening service called &ldquo;<kbd>fsa.il</kbd>&rdquo; and works the same way any other does: you input a long URL and your name, and you get a short URL back.  You can also come back later and track click-through and referring URL statistics.</p>
<p>Along the top of the page are tabs for three persistence layer choices: MySQL, MongoDB, and CouchDB.  The code they are given to start has a service that keeps track of your preferred data layer in your Session and handles all of the object creation for you.  The students, who built the MySQL version of the app for last month&#8217;s final exam, are given a working MySQL class, an interface for the persistence layer, and two stubbed-out skeleton classes for the other two layers.  Oh, and data dumps for quick import into all three layers.</p>
<p class="noindent" style="float:right; margin: 0 0 1em 1em;"><a href="/blog/wp-content/uploads/2011/03/php-impl.png"><img src="/blog/wp-content/uploads/2011/03/php-impl-300x149.png" alt="" title="Exam: Build out classes to talk to CouchDB and MongoDB" width="300" height="149" class="aligncenter size-medium wp-image-1314" /></a></p>
<p>That&#8217;s it.  That&#8217;s the exam.  The students just need to make the app work against the other two databases.  The interface definition is only five functions, and ends up yielding ~150-200 lines of code they need to bang out over 3&frac12; hours.</p>
<p>I think it&#8217;s a fun exam, but more importantly it&#8217;s a great piece for each student&#8217;s portfolio.  How much of a rock star would you look like on an interview if you could show a piece of code that worked against both relational and non-relational persistence layers without a truckload of third-party code?</p>
<p>It&#8217;s interesting writing code that will work against all three layers at the same time.  Naming conventions play a big part.  It&#8217;s easy in SQL to write a query that aliases column names to something friendlier to code:</p>
<pre class="sql">SELECT
    id AS linkID,
    long_url AS longURL,
    creator_name AS fullName,
    created_date AS createdDate
FROM link_links
WHERE (id = :linkId)</pre>
<p>And while you <em>could</em> use Map functions in CouchDB and MongoDB to alias your document field names, that seems like a heckuva waste.  Instead, you have to think about it a little bit harder beforehand.  If you don&#8217;t, you end up doing translation of array key names at the Model layer, which again seems sub-optimal.</p>
<p class="noindent" style="float:right; margin: 0 0 1em 1em;"><a href="/blog/wp-content/uploads/2011/03/mongo-console.png"><img src="/blog/wp-content/uploads/2011/03/mongo-console-300x174.png" alt="" title="The MongoDB Console" width="300" height="174" class="aligncenter size-medium wp-image-1310" /></a></p>
<p>The seemingly-minor differences between the implementations of Map/Reduce in CouchDB and MongoDB also some into play.  For example, when using the <kbd>group()</kbd> method in MongoDB you get an array back instead of a cursor, which means you can&#8217;t chain the <kbd>sort()</kbd> or <kbd>limit()</kbd> methods and have to do those in your Model.</p>
<p class="noindent" style="float:right;margin:0 0 1.5em 1.5em;"><a href="/blog/wp-content/uploads/2011/03/couchdb-futon.png"><img src="/blog/wp-content/uploads/2011/03/couchdb-futon-300x169.png" alt="" title="Editing a document in CouchDB Futon" width="300" height="169" class="aligncenter size-medium wp-image-1309" /></a></p>
<p>Sorting in CouchDB can also be tricky.  CouchDB assumes that you want to sort by your key and doesn&#8217;t give you any way to sort by value, making Top 10 lists a real pain to do.  You&#8217;re back to sorting in your Model.</p>
<p>String manipulation functions are another friction point.  SQL has a bevy of them, such as <kbd>LEFT()</kbd> and even the dreaded <kbd>LIKE</kbd>.  No such luck in either MongoDB or CouchDB.  You have to fake them in JavaScript.  Compare:</p>
<pre class="sql">WHERE (LEFT(:link, link_len) = link_start)</pre>
<p>Versus:</p>
<pre class="javascript">function(obj, prev) {
    if("http://".substr(0, obj.linkLen) === obj.linkStart)
        prev.count++;
}</pre>
<p>While that&#8217;s doable, and arguably potentially more powerful, it certainly isn&#8217;t what I would call <em>elegant</em>.  Similarly, CouchDB&#8217;s <kbd>startkey</kbd> and <kbd>endkey</kbd> parameters are nice and speedy, but there are times when I&#8217;d kill for a <kbd>keycontains</kbd> parameter.</p>
<p>More to the point, I think the course is teaching great skills.  Not only do students get to play with a variety of databases<sup>1</sup>, but they also get to see how easy it is to decouple layers of an application.  It&#8217;s nice that they get to muck around in Map/Reduce and REST APIs and what not, but the architectural and problem-solving lessons will take them so much further.</p>
<p>Oh, and just to head anyone off at the pass, the exam is done in PHP because working with MongoDB in PHP is stupid easy, while in ColdFusion it&#8217;s not for the faint of heart<sup>2</sup>.  The underlying Java library is usable, but requires a whole lot of syntactic icing.  That is, a metric ton of <kbd>javaCast</kbd> calls and type paranoia.  While that might be great for the students to encounter on a take-home assignment, it&#8217;s not something they need to be tripping over on a timed exam.  And yes, there are some great MongoDB libraries available for ColdFusion, but the point of the exam is to have the students show that they can do it themselves.</p>
<hr/>
<ol>
<li>
<p>If I had another 3 hours on the exam I would totally make the students add in layers for Sqlite and XML.</p>
</li>
<li>
<p>JSON-related quirks aside, using CouchDB requires an equivalent effort level from both PHP and ColdFusion.  Yay REST API.</p>
</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://rickosborne.org/blog/2011/03/because-no-one-in-web-dev-uses-interfaces/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

