<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Lukáš Lalinský</title>
	
	<link>http://oxygene.sk/lukas</link>
	<description>Random notes and stuff</description>
	<lastBuildDate>Sun, 25 Jul 2010 18:33:13 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/LukasLalinsky" /><feedburner:info uri="lukaslalinsky" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Introducing Chromaprint</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/E84U45hlWXc/</link>
		<comments>http://oxygene.sk/lukas/2010/07/introducing-chromaprint/#comments</comments>
		<pubDate>Sat, 24 Jul 2010 22:36:57 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[Announce]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[chromaprint]]></category>
		<category><![CDATA[fingerprinting]]></category>
		<category><![CDATA[MusicBrainz]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=529</guid>
		<description><![CDATA[After several months of reading research papers, learning and weekend coding, I&#8217;m very happy to make the half-finished code of my audio fingerprinting library public. :) I&#8217;m doing this mostly for selfish reasons, because it will force me to stop thinking in &#8220;hacker mode&#8221; and hopefully properly finish it, and I also hope to get [...]]]></description>
			<content:encoded><![CDATA[<p>After several months of reading research papers, learning and weekend coding, I&#8217;m very happy to make the half-finished code of my audio fingerprinting library public. :) I&#8217;m doing this mostly for selfish reasons, because it will force me to stop thinking in &#8220;hacker mode&#8221; and hopefully properly finish it, and I also hope to get some help and feedback from other people. There is nothing for regular users yet though, just for developers or people not afraid of the command line.</p>
<p>It all started in February this year, when when I got my Google Alerts mail for &#8220;musicbrainz picard&#8221; which included a link to <a href="http://portal.acm.org/citation.cfm?id=1399913&#038;CFID=98145194&#038;CFTOKEN=53175875">this paper</a> (&#8220;<em>Waveprint: Efficient wavelet-based audio fingerprinting</em>&#8221; (2007) by Shumeet Baluja and Michele Covell). I&#8217;ve never paid much attention to how audio identification systems are actually implemented, but I found it interesting that a paper published by Google researchers cited <a href="http://musicbrainz.org/doc/PicardTagger">Picard</a>, especially because Picard doesn&#8217;t implement any fingerprinting algorithm, just uses <a href="http://code.google.com/p/musicip-libofa/">libofa</a>. Anyway, I&#8217;ve read the paper and realized that maybe it&#8217;s not that hard to implement such a system. No tough DSP stuff or scary mathematics (of course there is some DSP stuff and mathematics, but mostly basics). The system described in the paper seemed quite straight-forward to implement, so I become curious and decided to give it a try. Later on I realized that it&#8217;s perhaps not the best system and that even the authors published new papers describing different approaches. That, combined with the fact that I was still officially an university student and had free access to all the papers from most organizations like ACM or IEEE, caused that I started reading more and more papers on the topic, learning about the history, how the systems evolved, and so on.</p>
<p>Many ideas were based on a paper by Yan Ke, Derek Hoiem, and Rahul Sukthankar called &#8220;<a href="http://www.cs.cmu.edu/~yke/musicretrieval/"><em>Computer Vision for Music Identification</em></a>&#8221; (2005). In fact, even the <a href="http://blog.last.fm/2010/07/09/fingerprint-api-and-app-updated">Last.fm fingerprinter</a> uses the code published by the authors of this paper. This is where I learned that audio identification is more about machine learning that it is about DSP. Many useful methods for extracting interesting features from audio streams are well-known and the problem is more about how to apply and index them the best way. The basic idea here is to treat audio as a spectral image and index the content of the image. I&#8217;ll explain this in more detail and how Chromaprint uses this in a following post.</p>
<p>Another important paper for me was &#8220;<em><a href="http://dx.doi.org/10.1109/TIFS.2009.2034452">Pairwise Boosted Audio Fingerprint</a></em>&#8221; (2009) by Dalwon Jang, Chang D. Yoo, Sunil Lee, Sungwoong Kim and Ton Kalker (Ton Kalker is a co-author of a historically important paper &#8220;<a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.16.2893"><em>Robust Audio Hashing for Content Identification</em></a>&#8221; (2001) published by Philips Research), which combined previous experiments of the authors with audio identification based on spectral centroid features and the indexing approach similar to the one suggested by Y. Ke, D. Hoiem and R. Sukthankar. For a long time this was the best solution I had and since it was actually not very hard to implement, the most time I spent on tweaking the configuration to get the best results.</p>
<p>The last major change came after I learned about &#8220;chroma&#8221; features by reading the &#8220;<em><a href="http://dx.doi.org/10.1109/TASL.2007.911552">Efficient Index-Based Audio Matching</a></em>&#8221; (2008) by Frank Kurth and Meinard Müller. I&#8217;ve read more papers about chroma features later, but this was the first and also the most important one for me and some ideas about processing the feature vectors from it are implemented in Chromaprint. Chroma features are typically used for music identification, as opposed to audio file identification, but I tried to use them with the approach I already had implemented and it nicely improved the quality of the fingerprinting function and actually reduced complexity which allowed me to use much larger training data sets.</p>
<p>Anyway, this is more or less how I got to this point. As I mentioned, I&#8217;ll try to describe in more detail how Chromaprint works and where are the exact ideas from in an another post later. The code is not finished yet, but the core ideas are already implemented and tested. The work that has to be done is mostly about cleaning the code, tweaking the configuration, running the learning algorithm on a better training data set (as I used only random selections of my music collection so far) and building some API that can be used by external applications.</p>
<p>The code is written in C++, but I plan the public API to be in plain C. Except for a <a href="http://en.wikipedia.org/wiki/Fast_Fourier_transform">FFT</a> library (either <a href="http://www.fftw.org/">FFTW3</a> or <a href="http://www.ffmpeg.org/">FFmpeg</a>), it has no external dependencies. It&#8217;s released under the LGPL 2.1 license, so there should be no problem integrating it into a commercial application, assuming FFmpeg is used for FFT calculations (using FFTW3 would require the binary to be GPL compatible). The project is hosted on <a href="https://launchpad.net/chromaprint">Launchpad</a> using Bazaar for development. I&#8217;m sorry I didn&#8217;t include the complete development history there, but it&#8217;s just full of junk commits, so you will not miss much. :)</p>
<p>What I&#8217;d really like is to start actually working also on the fingerprint lookup service, for which I need as many fingerprints as possible. I have a proof of concept written in Java, using PostgreSQL and the <a href="http://www.postgresql.org/docs/8.4/static/intarray.html">intarray</a> extension, which allows me to search the fingerprints using <a href="http://www.postgresql.org/docs/8.4/interactive/gin.html">GIN indexes</a>. This works fine on a database with tens of thousands of fingerprints, but I&#8217;m not sure if it will scale to much higher numbers. If you would like to help and you are running Debian/Ubuntu Linux, please run these commands and <a href="mailto:lalinsky@gmail.com">email me</a> the compressed <code>fpcollect.log</code> file:</p>
<pre>
sudo apt-get install bzr cmake libfftw3-dev libavcodec-dev libavformat-dev libtag1-dev libboost-dev
bzr branch lp:chromaprint
cd chromaprint
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TOOLS=ON .
make
./tools/fpcollect /path/to/your/music/library >fpcollect.log
</pre>
<p>I&#8217;m sure the recipe should be easy enough to modify for other Linux distributions. I&#8217;ll try to build a Windows binary in the next days.</p>
<p><em><strong>UPDATE:</strong> I&#8217;ve compiled a <a href="http://dl.dropbox.com/u/5215054/fpcollect-r8.zip">Windows version of fpcollect</a>. If you would like to help, please download it, change the <code>fpcollect.bat</code> file to point to your collection and run it. It should should produce a file called <code>fpcollect.log</code> like the Linux example above.</em></p>
<p>So that&#8217;s all for now. My plans for near future are to clean up the library and build some simple GUI application for collecting fingerprints. Once this is done, I can ask non-programmers to help me build a test database of fingerprints (including MusicBrainz IDs) and work primarily on the server component. There are some things that could be improved on the client library from functional point of view, but I think it&#8217;s good enough for now, so the server part seems more important at the moment.</p>
<p>Btw, I always considered &#8220;Chromaprint&#8221; to be a temporary name for the client library, not meant to be its final name. If you can think of a better name, ideally something that could be used also for the service/server, please let me know!</p>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/E84U45hlWXc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/07/introducing-chromaprint/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/07/introducing-chromaprint/</feedburner:origLink></item>
		<item>
		<title>Submit tags from Quod Libet to MusicBrainz</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/Tn_RhnznDaU/</link>
		<comments>http://oxygene.sk/lukas/2010/07/submit-tags-from-quod-libet-to-musicbrainz/#comments</comments>
		<pubDate>Sun, 18 Jul 2010 13:58:15 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[MusicBrainz]]></category>
		<category><![CDATA[Tools]]></category>
		<category><![CDATA[quod libet]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=514</guid>
		<description><![CDATA[I wanted to write something like this for a long time, but for some reason never did it. MusicBrainz has support for folksonomy tagging since 2007, but the coverage of track tags is still not very good. I try to keep some tags in the &#8220;genre&#8221; tag in audio files, but even with one-time import [...]]]></description>
			<content:encoded><![CDATA[<p>I wanted to write something like this for a long time, but for some reason never did it. MusicBrainz has support for folksonomy tagging since 2007, but the coverage of track tags is still not very good. I try to keep some tags in the &#8220;genre&#8221; tag in audio files, but even with one-time import tool, I&#8217;m sure I&#8217;d not remember to run this on new files. So the idea here is to submit these tags to MusicBrainz as I listen to the files in my music player (<a href="http://code.google.com/p/quodlibet/">Quod Libet</a>). It&#8217;s inspired by a Quod Libet plugin called <a href="http://code.google.com/p/thisfred-quodlibet-plugins/">LastFMTagger</a>, which does something similar, but for Last.fm. I had some free time today, so I wrote a <a href="http://dl.dropbox.com/u/5215054/mbtagsubmit.py">plugin</a> that does one-way synchronization of tags from Quod Libet to MusicBrainz. You can install the plugin using the following commands:</p>
<pre>
mkdir -p ~/.quodlibet/plugins/events/
cd ~/.quodlibet/plugins/events/
wget http://dl.dropbox.com/u/5215054/mbtagsubmit.py
</pre>
<p>After you enable it and let it know your MusicBrainz username and password (Music → Plugins), it will watch the songs you listen and if any of them has a &#8220;musicbrainz_trackid&#8221; and at least one &#8220;genre&#8221; tag, it will use the MusicBrainz web service to submit them. The submission normally happens only every half an hour, but if you change many files in a short time, it will submit them in batches of 20 tracks (the maximum number allowed by the web service) every 2 minutes.</p>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/Tn_RhnznDaU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/07/submit-tags-from-quod-libet-to-musicbrainz/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/07/submit-tags-from-quod-libet-to-musicbrainz/</feedburner:origLink></item>
		<item>
		<title>Oracle…</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/2mN_ShbKXQ4/</link>
		<comments>http://oxygene.sk/lukas/2010/06/oracle/#comments</comments>
		<pubDate>Wed, 16 Jun 2010 18:37:40 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[fail]]></category>
		<category><![CDATA[oracle]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=495</guid>
		<description><![CDATA[Working with Oracle is always an adventure. The error messages are usually not very helpful, so you have to guess a lot. What I&#8217;ve seen today is an extreme though. Oracle allows you to create a table with a column named &#8220;TIMESTAMP&#8221; if you quote it:

CREATE TABLE "SOME_TABLE" (
    ...
   [...]]]></description>
			<content:encoded><![CDATA[<p>Working with Oracle is always an adventure. The error messages are usually not very helpful, so you have to guess a lot. What I&#8217;ve seen today is an extreme though. Oracle allows you to create a table with a column named &#8220;TIMESTAMP&#8221; if you quote it:</p>
<pre>
CREATE TABLE "SOME_TABLE" (
    ...
    "TIMESTAMP" TIMESTAMP WITH TIME ZONE
);
</pre>
<p>Oracle is rather picky on identifier names, but since it accepted &#8220;TIMESTAMP&#8221;, I was assuming everything is fine. Later I needed to create a trigger for this table and that&#8217;s where the fun starts.</p>
<pre>
CREATE OR REPLACE TRIGGER "SOME_TABLE_TR"
BEFORE INSERT ON "SOME_TABLE"
FOR EACH ROW
BEGIN
    ...
END;
</pre>
<p>This was failing for some reason though. The only thing I got was this &#8220;nice&#8221; error message, pointing to the table name in the <code>CREATE TRIGGER</code> statement:</p>
<pre>
ORA-06552: PL/SQL: Compilation unit analysis terminated
ORA-06553: PLS-320: the declaration of the type of this expression is incomplete or malformed
</pre>
<p>What type? Do I have a typo somewhere? Did the table somehow get corrupted? You can&#8217;t imaging how long did it take for me to figure out that it doesn&#8217;t like the column name, which was not mentioned anywhere in the PL/SQL block. I would have no problem if it told me that I can&#8217;t use the name. There are too many restrictions on identifiers anyway. What I don&#8217;t understand is why does it allow me to create something that&#8217;s going to break other core functionality.</p>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/2mN_ShbKXQ4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/06/oracle/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/06/oracle/</feedburner:origLink></item>
		<item>
		<title>Tomboy’s “Start Here” note</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/z9Se0fLtn9k/</link>
		<comments>http://oxygene.sk/lukas/2010/05/tomboys-start-here-note/#comments</comments>
		<pubDate>Sun, 02 May 2010 19:13:27 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[Tools]]></category>
		<category><![CDATA[dropbox]]></category>
		<category><![CDATA[gnome]]></category>
		<category><![CDATA[snippet]]></category>
		<category><![CDATA[tomboy]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=486</guid>
		<description><![CDATA[I use Dropbox to synchronize my Tomboy notes. This works very well, but there is a problem when setting it up on a new computer. Tomboy has a special &#8220;Start Here&#8221; note, which is used mainly for organizing other notes. When I tell it to synchronize notes from the Dropbox directory and overwrite the existing [...]]]></description>
			<content:encoded><![CDATA[<p>I use <a href="http://www.dropbox.com/">Dropbox</a> to synchronize my <a href="http://projects.gnome.org/tomboy/">Tomboy</a> notes. This works very well, but there is a problem when setting it up on a new computer. Tomboy has a special &#8220;Start Here&#8221; note, which is used mainly for organizing other notes. When I tell it to synchronize notes from the Dropbox directory and overwrite the existing default notes, it will do so, but it doesn&#8217;t change it&#8217;s &#8220;Start Here&#8221; note pointer. As as result, Tomboy is not aware that my new Start Here note is the one it should use. As far as I know, there is no way fix this using Tomboy itself. It can be done only using GConf. Here is an example how to do it from the command line:</p>
<pre>$ grep 'Start Here' -R ~/.local/share/tomboy/ -l
/home/lukas/.local/share/tomboy/Backup/4a47410a-4976-4cb6-8ddc-fd744710dba7.note
/home/lukas/.local/share/tomboy/<b>7d41fff6-6cae-44bc-87b9-6486c809e7ee</b>.note
$ gconftool-2 --set /apps/tomboy/start_note --type string 'note://tomboy/7d41fff6-6cae-44bc-87b9-6486c809e7ee'
</pre>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/z9Se0fLtn9k" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/05/tomboys-start-here-note/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/05/tomboys-start-here-note/</feedburner:origLink></item>
		<item>
		<title>TagLib 1.6.3</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/CTt79E0IlK8/</link>
		<comments>http://oxygene.sk/lukas/2010/04/taglib-1-6-3/#comments</comments>
		<pubDate>Sat, 24 Apr 2010 14:41:25 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[Announce]]></category>
		<category><![CDATA[TagLib]]></category>
		<category><![CDATA[release]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=478</guid>
		<description><![CDATA[TagLib 1.6.3 was released this Monday, but somehow I forgot to post an update here. There isn&#8217;t many changes, the main reason for the release were configuration issues with 1.6.2. The 1.6.3 tarball can be downloaded here or here.
Changes log:

Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
Fixed upgrading of ID3v2.3 genre frame with ID3v1 code [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://article.gmane.org/gmane.comp.kde.devel.taglib/1493">TagLib 1.6.3 was released</a> this Monday, but somehow I forgot to post an update here. There isn&#8217;t many changes, the main reason for the release were configuration issues with 1.6.2. The 1.6.3 tarball can be downloaded <a href="http://developer.kde.org/~wheeler/files/src/taglib-1.6.3.tar.gz">here</a> or <a href="http://launchpad.net/taglib/trunk/1.6.3/+download/taglib-1.6.3.tar.gz">here</a>.</p>
<p>Changes log:</p>
<ul>
<li>Fixed definitions of the <code>TAGLIB_WITH_MP4</code> and <code>TAGLIB_WITH_ASF</code> macros.</li>
<li>Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).</li>
<li>New method <code>int String::toInt(bool *ok)</code> which can return whether the conversion to a number was successfull.</li>
<li>Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly compressed frames). (<a href="https://bugs.kde.org/show_bug.cgi?id=231075">Bug #231075</a>)</li>
</ul>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/CTt79E0IlK8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/04/taglib-1-6-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/04/taglib-1-6-3/</feedburner:origLink></item>
		<item>
		<title>TagLib 1.6.2</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/C35Si5dIxIY/</link>
		<comments>http://oxygene.sk/lukas/2010/04/taglib-1-6-2/#comments</comments>
		<pubDate>Fri, 09 Apr 2010 18:00:34 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[Announce]]></category>
		<category><![CDATA[TagLib]]></category>
		<category><![CDATA[release]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=472</guid>
		<description><![CDATA[TagLib 1.6.2 has been released. It&#8217;s another bug-fix release in the 1.6 series. There is no new functionality, only a few bugs and compatibility issues have been fixed. Please see the change log for details. You can download the source code tarball here (or here). As with previous releases, there are no Windows binaries, but [...]]]></description>
			<content:encoded><![CDATA[<p>TagLib 1.6.2 has been released. It&#8217;s another bug-fix release in the 1.6 series. There is no new functionality, only a few bugs and compatibility issues have been fixed. Please see the change log for details. You can download the source code tarball <a href="http://developer.kde.org/~wheeler/files/src/taglib-1.6.2.tar.gz">here</a> (or <a href="http://launchpad.net/taglib/trunk/1.6.2/+download/taglib-1.6.2.tar.gz">here</a>). As with previous releases, there are no Windows binaries, but you can easily build your own with CMake.</p>
<p>Change log:</p>
<ul>
<li>Read Vorbis Comments from the first FLAC metadata block, if there are multipe ones. (<a href="https://bugs.kde.org/show_bug.cgi?id=211089">Bug #211089</a>)</li>
<li>Fixed a memory leak in FileRef&#8217;s OGA format detection.</li>
<li>Fixed compilation with the Sun Studio compiler. (<a href="https://bugs.kde.org/show_bug.cgi?id=215225">Bug #215225</a>)</li>
<li>Handle WM/TrackNumber attributes with DWORD content in WMA files. (<a href="https://bugs.kde.org/show_bug.cgi?id=218526">Bug #218526</a>)</li>
<li>More strict check if something is a valid MP4 file. (<a href="https://bugs.kde.org/show_bug.cgi?id=216819">Bug #216819</a>)</li
<li>Correctly save MP4 int-pair atoms with flags set to 0.</li>
<li>Fixed compilation of the test runner on Windows.</li>
<li>Store ASF attributes larger than 64k in the metadata library object.</li>
<li>Ignore trailing non-data atoms when parsing MP4 covr atoms.</li>
<li>Don&#8217;t upgrade ID3v2.2 frame TDA to TDRC. (<a href="https://bugs.kde.org/show_bug.cgi?id=228968">Bug #228968</a>)</li>
</ul>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/C35Si5dIxIY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/04/taglib-1-6-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/04/taglib-1-6-2/</feedburner:origLink></item>
		<item>
		<title>MusicBrainz server code history</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/Jo65EaT_YNY/</link>
		<comments>http://oxygene.sk/lukas/2010/04/musicbrainz-server-code-history/#comments</comments>
		<pubDate>Sun, 04 Apr 2010 18:14:44 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[MusicBrainz]]></category>
		<category><![CDATA[gource]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=460</guid>
		<description><![CDATA[After seeing Gource videos for Perl and Twisted today, I wanted to make a similar video for the MusicBrainz server code. Well, and for some of my personal projects, but those turned out to be boring :). Anyway, here is the result (YouTube link):

]]></description>
			<content:encoded><![CDATA[<p>After seeing <a href="http://code.google.com/p/gource/">Gource</a> videos for <a href="http://www.youtube.com/watch?v=jl9HVEJl_-w">Perl</a> and <a href="http://vimeo.com/10661539">Twisted</a> today, I wanted to make a similar video for the MusicBrainz server code. Well, and for some of my personal projects, but those turned out to be boring :). Anyway, here is the result (<a href="http://www.youtube.com/watch?v=EGe2MyaLNzE">YouTube link</a>):</p>
<p style="text-align: center;"><object width="640" height="505"><param name="movie" value="http://www.youtube.com/v/EGe2MyaLNzE&#038;hl=cs_CZ&#038;fs=1&#038;rel=0&#038;hd=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/EGe2MyaLNzE&#038;hl=cs_CZ&#038;fs=1&#038;rel=0&#038;hd=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="505"></embed></object></p>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/Jo65EaT_YNY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/04/musicbrainz-server-code-history/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/04/musicbrainz-server-code-history/</feedburner:origLink></item>
		<item>
		<title>“Punchcard” graphs for Bazaar</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/z9MhPQqxM6A/</link>
		<comments>http://oxygene.sk/lukas/2010/03/punchcard-graphs-for-bazaar/#comments</comments>
		<pubDate>Sat, 13 Mar 2010 14:23:26 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[Tools]]></category>
		<category><![CDATA[bzr]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[MusicBrainz]]></category>
		<category><![CDATA[picard]]></category>
		<category><![CDATA[qbzr]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=421</guid>
		<description><![CDATA[GitHub has a nice feature called &#8220;Punchcard&#8221;. It&#8217;s a graph that represents numbers of commits by day and hour (example). You can easily see wherether a project was hacked over weekends or nights, if it&#8217;s done by full-time employees, etc. There are two problems though:

It ignores timezones. All timestamps are treated as if they were [...]]]></description>
			<content:encoded><![CDATA[<p>GitHub has a nice feature called &#8220;Punchcard&#8221;. It&#8217;s a graph that represents numbers of commits by day and hour (<a href="http://github.com/lalinsky/musicbrainz-server/graphs/punch_card">example</a>). You can easily see wherether a project was hacked over weekends or nights, if it&#8217;s done by full-time employees, etc. There are two problems though:</p>
<ol>
<li>It ignores timezones. All timestamps are treated as if they were somewhere in the US. For projects where committers live across the world (most open source projects) it&#8217;s therefore almost useless.</li>
<li>It only works for GitHub projects, obviously.</li>
</ol>
<p>I&#8217;ve put together a little Bazaar plugin that can generate similar graphs for Bazaar branches. You can get the plugin <a href="http://bzr.oxygene.sk/bzr-plugins/punchcard">here</a>. It requires PyQt for image drawing, but many Bazaar users should already have PyQt installed. Once you install the plugin, you can generate a &#8220;Punchcard&#8221; by running a command like this in a branch directory:</p>
<pre>bzr punchcard project-punchcard.png
</pre>
<p>For example, for <a href="http://wiki.bazaar.canonical.com/QBzr">QBzr</a> you will get this graph:<br />
<a href="http://oxygene.sk/lukas/blog/wp-content/uploads/punchcard-qbzr-2010-03-13.png"><img title="punchcard-qbzr-2010-03-13"  class="aligncenter size-full wp-image-443" src="http://oxygene.sk/lukas/blog/wp-content/uploads/punchcard-qbzr-2010-03-13.png" alt="" width="600" /></a></p>
<p>You can see that the project is done by people who obviously have too much free time. There is activity pretty much any time. I guess only on weekend nights, people are tired and enjoy some sleep. :)</p>
<p>On the other hand, <a href="http://musicbrainz.org/doc/PicardTagger">Picard</a> was hacked by people with good sleep patterns over evenings and weekends:<br />
<a href="http://oxygene.sk/lukas/blog/wp-content/uploads/punchcard-picard-2010-03-13.png"><img src="http://oxygene.sk/lukas/blog/wp-content/uploads/punchcard-picard-2010-03-13.png" alt="" title="punchcard-picard-2010-03-13" width="600" class="aligncenter size-full wp-image-443" /></a></p>
<p><a href="http://musicbrainz.org/">MusicBrainz Server</a> is in a similar situation, minus the good sleep patterns:</p>
<p><a href="http://oxygene.sk/lukas/blog/wp-content/uploads/punchcard-mbserver-2010-03-13.png"><img src="http://oxygene.sk/lukas/blog/wp-content/uploads/punchcard-mbserver-2010-03-13.png" alt="" title="punchcard-mbserver-2010-03-13" width="600" class="aligncenter size-full wp-image-448" /></a></p>
<p>The last example is <a href="http://bazaar.canonical.com/en/">Bazaar</a>. It&#8217;s mostly written by full-time employees and it shows:<br />
<a href="http://oxygene.sk/lukas/blog/wp-content/uploads/punchcard-bzr-2010-03-13.png"><img src="http://oxygene.sk/lukas/blog/wp-content/uploads/punchcard-bzr-2010-03-13.png" alt="" title="punchcard-bzr-2010-03-13" width="600" class="aligncenter size-full wp-image-454" /></a></p>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/z9MhPQqxM6A" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/03/punchcard-graphs-for-bazaar/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/03/punchcard-graphs-for-bazaar/</feedburner:origLink></item>
		<item>
		<title>Fun with timestamps</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/j17nwTQYLwM/</link>
		<comments>http://oxygene.sk/lukas/2010/03/fun-with-timestamps/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 19:32:56 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[fail]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[stackoverflow]]></category>
		<category><![CDATA[timezone]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=342</guid>
		<description><![CDATA[Some programming languages really encourage using UNIX timestamps for working with dates. PHP is a good example of such a language. Functions like date, strtotime, strftime are used all the time. Most people don&#8217;t realize that timestamps in general can&#8217;t really be used for calculations though. The problem is that most countries use daylight saving [...]]]></description>
			<content:encoded><![CDATA[<p>Some programming languages really encourage using <a href="http://en.wikipedia.org/wiki/Unix_time">UNIX timestamps</a> for working with dates. PHP is a good example of such a language. Functions like <a href="http://php.net/manual/en/function.date.php"><code>date</code></a>, <a href="http://php.net/manual/en/function.strtotime.php"><code>strtotime</code></a>, <a href="http://php.net/manual/en/function.strftime.php"><code>strftime</code></a> are used all the time. Most people don&#8217;t realize that timestamps in general can&#8217;t really be used for calculations though. The problem is that most countries use <a href="http://en.wikipedia.org/wiki/Daylight_saving_time">daylight saving time</a>, which means that two times a year the local timezone changes. This nicely breaks the assumption that every day has 24 hours. It doesn&#8217;t. Sometimes it has 23 or 25 hours.</p>
<p>This <a href="http://stackoverflow.com/questions/2361222/php-date-function-finding-the-previous-week/2361335#2361335">StackOverflow answer</a> is a nice example of the problem:</p>
<pre>
$mondayStr = "last monday";
if (date('N') !== '1') {  // it's not Monday today
    $mondayStr .= " last week";
}

$monday = strtotime($mondayStr);
echo date('r', $monday);    // Mon, 22 Feb 2010 00:00:00 +1000

$sunday = $monday + 86400 * 7 - 1;
echo date('r', $sunday);    // Sun, 28 Feb 2010 23:59:59 +1000
</pre>
<p>The code seems logical. Get the timestamp of the last Monday, add 60 * 60 * 24 * 7 &#8211; 1 seconds and you have the end of Sunday. Works fine most of the time. Although, if the <code>$monday</code> happens to be <em>Mon, 22 Mar 2010 00:00:00</em>, the date that was supposed to be <em>Sun, 28 Mar 2010  23:59:59</em> will actually be <em>Mon, 29 Mar 2010 00:59:59</em>. Why? Because 28 March 2010 has only 23 hours.</p>
<p>Never use timestamps to do calendar calculations. It&#8217;s hard to get it right. If you really have to, at least use GMT timestamps.</p>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/j17nwTQYLwM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/03/fun-with-timestamps/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/03/fun-with-timestamps/</feedburner:origLink></item>
		<item>
		<title>Database Modeller 0.3</title>
		<link>http://feedproxy.google.com/~r/LukasLalinsky/~3/iD99I43iGc8/</link>
		<comments>http://oxygene.sk/lukas/2010/01/database-modeller-0-3/#comments</comments>
		<pubDate>Sun, 24 Jan 2010 08:41:06 +0000</pubDate>
		<dc:creator>Lukáš Lalinský</dc:creator>
				<category><![CDATA[Announce]]></category>
		<category><![CDATA[Database Modeller]]></category>
		<category><![CDATA[dbmodel]]></category>
		<category><![CDATA[release]]></category>

		<guid isPermaLink="false">http://oxygene.sk/lukas/?p=320</guid>
		<description><![CDATA[Database Modeller 0.3 has been released. There isn&#8217;t as many changes as I wanted, but the main changes were unreleased for almost half a year. You can download the source code and Windows installer on the project page.
Changes since version 0.2:

PDF export
Support for printing
Opening of files passed through the command line arguments (Bug #401595)
The recently [...]]]></description>
			<content:encoded><![CDATA[<p>Database Modeller 0.3 has been released. There isn&#8217;t as many changes as I wanted, but the main changes were unreleased for almost half a year. You can download the source code and Windows installer on the <a href="http://oxygene.sk/lukas/projects/dbmodel/">project page</a>.</p>
<p>Changes since version 0.2:</p>
<ul>
<li>PDF export</li>
<li>Support for printing</li>
<li>Opening of files passed through the command line arguments (<a href="https://bugs.launchpad.net/dbmodel/+bug/">Bug #401595</a>)</li>
<li>The recently used files list now always uses absolute paths</li>
<li>Implemented auto-scrolling in the diagram view (<a href="https://bugs.launchpad.net/dbmodel/+bug/420324">Bug #420324</a>)</li>
<li>The &#8220;Close&#8221; menu item will now only close the document, not the window (<a href="https://bugs.launchpad.net/dbmodel/+bug/420322">Bug #420322</a>)</li>
<li>Use the selected file format in the export dialog, instead of relying only on the extension (<a href="https://bugs.launchpad.net/dbmodel/+bug/396056">Bug #396056</a>)</li>
</ul>
<img src="http://feeds.feedburner.com/~r/LukasLalinsky/~4/iD99I43iGc8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://oxygene.sk/lukas/2010/01/database-modeller-0-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://oxygene.sk/lukas/2010/01/database-modeller-0-3/</feedburner:origLink></item>
	</channel>
</rss>
