The Fugue

Counterpoint by Hans Fugal

Irssi on a Laptop

Posted by Hans Fugal Thu, 06 Nov 2008 15:42:00 GMT

I love irssi, but it has laptop issues. It's really unintelligent about network disconnects and waking up from sleep. It usually either takes forever to time out and try to reconnect, or tries to reconnect immediately upon wake before the wireless connection is established and then takes forever to time out and try to reconnect. So I tried it in screen on my server, and that works fine, but it lacks some usability features. Plus, it lacks some niceties that I've come to need on OS X from chat clients. I need my chat client to use growl to tell me when someone talks to me (directly to me, in the case of IRC). Then I can read the message quickly and decide whether I need to stop what I'm doing right then to respond, or if it can wait a few seconds while I complete my thought, or if it can wait a few minutes while I complete my thought. Plus, with the right theme, growl notifications can be easy to tune out as well (but your mind still registers that something happened and wants your attention, when you can give it). Second, it's really nice to have IRC in a different application, so it can be hidden without affecting the rest of the Terminal.app windows. I use the terminal too heavily for productive work for IRC to insert itself on a terminal tab or in a terminal window. But, I could work with this by starting irssi in its own window and getting used to minimizing IRC. Not a deal-breaker.

I've been using Colloquy, and before that X-Chat Aqua, and while both meet the essential needs listed above (smart reconnect after sleep and growl), the both have the same problem: they're not irssi (plus they don't get much attention from the developers, and X-Chat Aqua is all but officially abandoned). Colloquy in particular crashes more frequently than a demolition derby car. So I find myself yearning for irssi. There are a couple wrap-irssi-in-cocoa apps, but they are also abandoned and incomplete.

In one last stab of hope, has anyone discovered a way for irssi to be intelligent about reconnecting? This is just as applicable in linux as in osx, and I know a lot of you linux users have laptops and use irssi—do you all just punt and use it in screen on a server? Has anyone found/written an irssi plugin for growl? (I assume someone probably has, or that it would be easy to whip up, but I haven't bothered to look because I haven't found the solution to the first problem yet.) Or, has anyone found an IRC client that actually comes close to being as awesome as irssi, reconnects intelligently, uses growl, and doesn't crash frequently?

1 comment |

IMMS

Posted by Hans Fugal Fri, 19 Sep 2008 22:24:00 GMT

So Apple added this Genius thing to iTunes recently. Not being the type to get excited about new iPod styles, it looks like the most interesting thing they could come up with this year. I gave it a try. I am not impressed.

I think it's because I've been spoiled. 5 years ago I was using what I still consider to be the peak of intelligent listening software, IMMS. Genius isn't half as cool as IMMS was then, and while IMMS hasn't made any quantum leaps in coolness, quite a few rough edges have been rounded off in the meantime.

I've been living in a sort of IMMS drought the past couple of years, since I switched to using a laptop primarily. Namely, an Apple laptop. This Genius release spurred me on to rectify that situation. If the best Apple could do was generate a 25-song playlist based on statistics gathered from other people the hopes of someone else hacking up an iTunes plugin to do IMMS or something like it dwindled to obscurity.

The bane of IMMS is, ironically, its most compelling feature. IMMS is cool because you don't have to do anything. It pays attention to your listening habits, and analyzes the audio, and makes intelligent decisions for you when you turn on random. 4 years ago I would show up to work and be in a Depeche Mode mood, so I'd manually queue up a Depeche Mode song or two and the whole day I'd be treated to complementary music. If the occasional happy song slipped through, I just skipped it and IMMS took the hint. Don't underestimate the amazing wow factor of a computer apparently reading your mind.

But this focus on simple non-obtrusive UI has been its biggest technical struggle. Media players are now a dime a dozen, and few of them have the plugin and UI sophistication to support IMMS' modus operandi. IMMS was developed originally as a plugin for XMMS and even then ugly workaround hacks were required. Then someone wrote a queue control patch for XMMS, and if you patched your XMMS you were in heaven. Oh, did I mention that still almost no other media players even have queue functionality, let alone let the plugins control the queue? Then when you consider the set of media players usable on OS X the situation gets laughable.

Somewhere in the middle MPD came along. It fit my situation well because the speakers over on the desktop were a lot nicer than the ones in my laptop. But queues it has not and nobody seems to care. Von bravely came up with an IMMS hack for MPD, but it was too hacky for me—too much like the old XMMS days before the queue control patch (incidentally, queue control is part of XMMS proper now as of version 1.2.11).

So I suffered along with manual or truly random music listening. Until now.

Recently I looked into this again for the desktop, and I was delighted to discover that one of the many XMMS descendants has finally solved the XMMS bitrot without throwing the baby out with the bathwater. Audacious is as cool as XMMS ever was and as modern as your favorite modern player (unless you measure modern by klunky iTunes-like screen-wasting music browsers). What's more, the imms plugin for it is right there in the Ubuntu repository. Just apt-get install imms-audacious and enable the plugin and you're off and running. So I set it up and… didn't use it. As in, we rarely listen to music on the desktop because nobody really sits there for very long. So finally earlier this week I hammered out a simple remote control using Audacious' dbus interface. That's another post, once I knock off a few other TODO points.

Feeling on a roll and feeling left out when at school, I decided to get an IMMS solution on my laptop, running OS X Leopard (10.5.4). I'll spare you the agonizing play-by-play and give you the shortest path to success: install Audacious and then IMMS. Actually the really shortest path is to install XMMS and then IMMS, because XMMS is in MacPorts. But it's the old version of XMMS without queue control, and doesn't have CoreAudio support (you have to use the JACK output plugin) so I don't recommend that.

To install Audacious, install its dependencies (mostly using MacPorts), then build it and its plugins. Installing its dependencies is the hardest part because it's difficult to locate libmcs and libmowgli (they're not where the README says they are, and Google is less than helpful). I just ended up stealing the *.orig.tar.gz files from the Ubuntu packages (apt-get source -d libmcs1 libmowgli). There is one patch you need for the plugins.

 src/CoreAudio/audio.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

Index: audacious-plugins-1.5.1/src/CoreAudio/audio.c
===================================================================
--- audacious-plugins-1.5.1.orig/src/CoreAudio/audio.c  2008-09-19 12:08:01.000000000 -0600
+++ audacious-plugins-1.5.1/src/CoreAudio/audio.c   2008-09-19 12:10:28.000000000 -0600
@@ -326,7 +326,12 @@ gint osx_get_output_time(void)
 {
        gint retval;

-        retval = output_time_offset + ((output_total * sample_size * 1000) / output.bps);
+        if (output.bps == 0)
+        {
+            printf("Avoiding divide by zero in osx_get_output_time()\n");
+            retval = 0;
+        } else
+            retval = output_time_offset + ((output_total * sample_size * 1000) / output.bps);
        retval = (int)((float)retval / user_pitch);

        //printf("osx_get_output_time(): time is %d\n",retval);

Next you need to install IMMS. This is a bit more involved, but should be straightforward with these patches. I'll put them here and talk about each in turn.

First, a missing include for mkdir()

 immsd/immsd.cc |    1 +
 1 file changed, 1 insertion(+)

Index: imms-3.1.0-rc4/immsd/immsd.cc
===================================================================
--- imms-3.1.0-rc4.orig/immsd/immsd.cc  2008-03-02 18:54:06.000000000 -0700
+++ imms-3.1.0-rc4/immsd/immsd.cc   2008-09-19 08:05:58.000000000 -0600
@@ -2,6 +2,7 @@
 #include <errno.h>
 #include <signal.h>
 #include <unistd.h>
+#include <sys/stat.h>

 #include <iostream>
 #include <sstream>

Then, a workaround due to OS X not having an initstate_r() (which I incidentally couldn't find in the current Linux manpages on Ubuntu or Debian either). This patch may not apply cleanly by itself, you may need to apply your cognitive reasoning.

configure.ac         |    3 +++
immsconf.h           |    3 +++
immsconf.h.in        |    3 +++
immscore/immsutil.cc |    9 +++++++++
4 files changed, 18 insertions(+)

Index: imms-3.1.0-rc4/immscore/immsutil.cc
===================================================================
--- imms-3.1.0-rc4.orig/immscore/immsutil.cc    2008-03-02 18:54:06.000000000 -0700
+++ imms-3.1.0-rc4/immscore/immsutil.cc 2008-09-19 08:13:29.000000000 -0600
@@ -27,6 +27,7 @@ int imms_random(int max)
{
    int rand_num;
    static bool initialized = false;
+#ifndef INITSTATE_BUG
    static struct random_data rand_data;
    static char rand_state[256];
    if (!initialized)
@@ -36,6 +37,14 @@ int imms_random(int max)
        initialized = true;
    }
    random_r(&rand_data, &rand_num);
+#else
+    if (!initialized)
+    {
+        srandom(time(0));
+        initialized = true;
+    }
+    rand_num = random();
+#endif
    double cof = rand_num / (RAND_MAX + 1.0);
    return (int)(max * cof);
}
Index: imms-3.1.0-rc4/configure.ac
===================================================================
--- imms-3.1.0-rc4.orig/configure.ac    2008-03-02 18:54:06.000000000 -0700
+++ imms-3.1.0-rc4/configure.ac 2008-09-19 08:17:58.000000000 -0600
@@ -68,6 +68,9 @@ else
    AC_MSG_RESULT([yes])
fi

+AC_DEFINE(INITSTATE_BUG,, [initstate_r is buggy])
+
+
AC_CHECK_LIB(z, compress,, [with_zlib=no])
AC_CHECK_HEADERS(zlib.h,, [with_zlib=no])
if test "$with_zlib" = "no"; then
Index: imms-3.1.0-rc4/immsconf.h
===================================================================
--- imms-3.1.0-rc4.orig/immsconf.h  2008-09-19 08:05:31.000000000 -0600
+++ imms-3.1.0-rc4/immsconf.h   2008-09-19 08:18:23.000000000 -0600
@@ -121,6 +121,9 @@
/* Define to 1 if you have the <zlib.h> header file. */
#define HAVE_ZLIB_H 1

+/* initstate_r is buggy */
+#define INITSTATE_BUG /**/
+
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "mag@luminal.org"

Index: imms-3.1.0-rc4/immsconf.h.in
===================================================================
--- imms-3.1.0-rc4.orig/immsconf.h.in   2008-09-19 07:48:52.000000000 -0600
+++ imms-3.1.0-rc4/immsconf.h.in    2008-09-19 08:16:32.000000000 -0600
@@ -120,6 +120,9 @@
/* Define to 1 if you have the <zlib.h> header file. */
#undef HAVE_ZLIB_H

+/* initstate_r is buggy */
+#undef INITSTATE_BUG
+
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT

This patch is just so libpcre can be found

build/Makefile |    1 +
1 file changed, 1 insertion(+)

Index: imms-3.1.0-rc4/build/Makefile
===================================================================
--- imms-3.1.0-rc4.orig/build/Makefile  2008-03-02 18:54:06.000000000 -0700
+++ imms-3.1.0-rc4/build/Makefile   2008-09-19 12:25:05.000000000 -0600
@@ -18,6 +18,7 @@ libimmscore.a: $(call objects,../immscor
libmodel.a: $(call objects,../model) svm-similarity-data.o
        $(AR) $(ARFLAGS) $@ $(filter %.o,$^)

+immstool-LIBS=`pcre-config --libs`
immstool: immstool.o libmodel.a libimmscore.a 
training_data: training_data.o libmodel.a libimmscore.a 
train_model: train_model.o libmodel.a libimmscore.a

Linking shared libraries on OS X is so much different from on Linux that there is almost always a need to do a patch something like this.

rules.mk |    5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

Index: imms-3.1.0-rc4/rules.mk
===================================================================
--- imms-3.1.0-rc4.orig/rules.mk    2008-09-19 09:04:13.000000000 -0600
+++ imms-3.1.0-rc4/rules.mk 2008-09-19 12:25:50.000000000 -0600
@@ -14,9 +14,8 @@ link = $(CXX) $(filter-out %.a,$1) $(fil
%.o: %.c; $(call compile, $(CC), $<, $@, $($*-CFLAGS) $(CFLAGS) $($*-CPPFLAGS) $(CPPFLAGS))
%: %.o; $(call link, $^ $($*-OBJ) $(LIBS), $@, $($*-LIBS) $(LDFLAGS))
%.so:
-   $(CXX) $^ $($*-OBJ) $($*-LIBS) $(LIBS) \
-       $(LDFLAGS) \
-            -shared -Wl,-z,defs,-soname,$@ -o $@
+   gcc -flat_namespace -undefined suppress -o $@ -bundle $^ $($*-OBJ) $($*-LIBS) $(LIBS) \
+       $(LDFLAGS) -o $@

%-data.o: %
        $(OBJCOPY) -I binary -O $(OBJCOPYTARGET) -B $(OBJCOPYARCH) --rename-section .data=.rodata,alloc,load,readonly,data,contents $< $@

This final patch fixes IMMS to use the proper interface for audacious (seems like this would have to be done anywhere?)

clients/audacious/audaciousinterface.c |  177 +++++++++++++++++++++++++++++++++
clients/audacious/rules.mk             |    2 
2 files changed, 178 insertions(+), 1 deletion(-)

Index: imms-3.1.0-rc4/clients/audacious/audaciousinterface.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ imms-3.1.0-rc4/clients/audacious/audaciousinterface.c   2008-09-19 15:30:21.000000000 -0600
@@ -0,0 +1,177 @@
+#include <gtk/gtk.h>
+
+#ifdef BMP
+#include <bmp/configdb.h>
+#include <bmp/util.h>
+#include <bmp/plugin.h>
+#elif AUDACIOUS
+#include <audacious/configdb.h>
+#include <audacious/util.h>
+#include <audacious/plugin.h>
+#endif
+#include "immsconf.h"
+#include "cplugin.h"
+
+
+int use_xidle = 1;
+int poll_tag = 0;
+
+GtkWidget *configure_win = NULL, *about_win = NULL, *xidle_button = NULL;
+
+gint poll_func(gpointer unused)
+{
+    imms_poll();
+    return TRUE;
+}
+
+void read_config(void)
+{
+    ConfigDb *cfgfile;
+
+    if ((cfgfile = cfg_db_open()) != NULL)
+    {
+        cfg_db_get_int(cfgfile, "imms", "xidle", &use_xidle);
+        cfg_db_close(cfgfile);
+    }
+}
+
+void init(void)
+{
+    imms_init();
+    read_config();
+    imms_setup(use_xidle);
+    poll_tag = gtk_timeout_add(200, poll_func, NULL);
+}
+
+void cleanup(void)
+{
+    imms_cleanup();
+
+    if (poll_tag)
+        gtk_timeout_remove(poll_tag);
+
+    poll_tag = 0;
+}
+
+void configure_ok_cb(gpointer data)
+{
+    ConfigDb *cfgfile = cfg_db_open();
+
+    use_xidle = !!GTK_TOGGLE_BUTTON(xidle_button)->active;
+
+    cfg_db_set_int(cfgfile, "imms", "xidle", use_xidle);
+    cfg_db_close(cfgfile);
+
+    imms_setup(use_xidle);
+    gtk_widget_destroy(configure_win);
+}  
+
+#define ADD_CONFIG_CHECKBOX(pref, title, label, descr)                          \
+    pref##_frame = gtk_frame_new(title);                                        \
+    gtk_box_pack_start(GTK_BOX(configure_vbox), pref##_frame, FALSE, FALSE, 0); \
+    pref##_vbox = gtk_vbox_new(FALSE, 10);                                      \
+    gtk_container_set_border_width(GTK_CONTAINER(pref##_vbox), 5);              \
+    gtk_container_add(GTK_CONTAINER(pref##_frame), pref##_vbox);                \
+                                                                                \
+    pref##_desc = gtk_label_new(label);                                         \
+                                                                                \
+    gtk_label_set_line_wrap(GTK_LABEL(pref##_desc), TRUE);                      \
+    gtk_label_set_justify(GTK_LABEL(pref##_desc), GTK_JUSTIFY_LEFT);            \
+    gtk_misc_set_alignment(GTK_MISC(pref##_desc), 0, 0.5);                      \
+    gtk_box_pack_start(GTK_BOX(pref##_vbox), pref##_desc, FALSE, FALSE, 0);     \
+    gtk_widget_show(pref##_desc);                                               \
+                                                                                \
+    pref##_hbox = gtk_hbox_new(FALSE, 5);                                       \
+    gtk_box_pack_start(GTK_BOX(pref##_vbox), pref##_hbox, FALSE, FALSE, 0);     \
+                                                                                \
+    pref##_button = gtk_check_button_new_with_label(descr);                     \
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pref##_button), use_##pref); \
+    gtk_box_pack_start(GTK_BOX(pref##_hbox), pref##_button, FALSE, FALSE, 0);   \
+                                                                                \
+    gtk_widget_show(pref##_frame);                                              \
+    gtk_widget_show(pref##_vbox);                                               \
+    gtk_widget_show(pref##_button);                                             \
+    gtk_widget_show(pref##_hbox);
+
+void configure(void)
+{
+    GtkWidget *configure_vbox;
+    GtkWidget *xidle_hbox, *xidle_vbox, *xidle_frame, *xidle_desc; 
+    GtkWidget *configure_bbox, *configure_ok, *configure_cancel;
+
+    if (configure_win)
+        return;
+
+    read_config();
+
+    configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_signal_connect(GTK_OBJECT(configure_win), "destroy",
+            GTK_SIGNAL_FUNC(gtk_widget_destroyed), &configure_win);
+    gtk_window_set_title(GTK_WINDOW(configure_win), "IMMS Configuration");
+
+    gtk_container_set_border_width(GTK_CONTAINER(configure_win), 10);
+
+    configure_vbox = gtk_vbox_new(FALSE, 10);
+    gtk_container_add(GTK_CONTAINER(configure_win), configure_vbox);
+
+    ADD_CONFIG_CHECKBOX(xidle, "Idleness", 
+#ifdef BMP
+            "Disable this option if you use BEEP on a dedicated machine",
+#elif AUDACIOUS
+            "Disable this option if you use Audacious on a dedicated machine",
+#endif
+            "Use X idleness statistics");
+
+    /* Buttons */
+    configure_bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(configure_bbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(configure_bbox), 5);
+    gtk_box_pack_start(GTK_BOX(configure_vbox), configure_bbox, FALSE, FALSE, 0);
+
+    configure_ok = gtk_button_new_with_label("Ok");
+    gtk_signal_connect(GTK_OBJECT(configure_ok), "clicked",
+            GTK_SIGNAL_FUNC(configure_ok_cb), NULL);
+    GTK_WIDGET_SET_FLAGS(configure_ok, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(configure_bbox), configure_ok, TRUE, TRUE, 0);
+    gtk_widget_show(configure_ok);
+    gtk_widget_grab_default(configure_ok);
+
+    configure_cancel = gtk_button_new_with_label("Cancel");
+    gtk_signal_connect_object(GTK_OBJECT(configure_cancel), "clicked",
+            GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(configure_win));
+    GTK_WIDGET_SET_FLAGS(configure_cancel, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(configure_bbox), configure_cancel, TRUE, TRUE, 0);
+    gtk_widget_show(configure_cancel);
+    gtk_widget_show(configure_bbox);
+    gtk_widget_show(configure_vbox);
+    gtk_widget_show(configure_win);
+}
+
+void about(void)
+{
+    if (about_win)
+        return;
+
+    about_win =
+#ifdef AUDACIOUS
+        audacious_info_dialog(
+#else
+        xmms_show_message(
+#endif
+            "About IMMS",
+            PACKAGE_STRING "\n\n"
+            "Intelligent Multimedia Management System" "\n\n"
+            "IMMS is an intelligent playlist plug-in for BPM" "\n"
+            "that tracks your listening patterns" "\n"
+            "and dynamically adapts to your taste." "\n\n"
+            "It is incredibly unobtrusive and easy to use" "\n"
+            "as it requires no direct user interaction." "\n\n"
+            "For more information please visit" "\n"
+            "http://www.luminal.org/wiki/index.php/IMMS" "\n\n"
+            "Written by" "\n"
+            "Michael \"mag\" Grigoriev <mag@luminal.org>",
+            "Dismiss", FALSE, NULL, NULL);
+
+    gtk_signal_connect(GTK_OBJECT(about_win), "destroy",
+            GTK_SIGNAL_FUNC(gtk_widget_destroyed), &about_win);
+}
Index: imms-3.1.0-rc4/clients/audacious/rules.mk
===================================================================
--- imms-3.1.0-rc4.orig/clients/audacious/rules.mk  2008-03-02 18:54:06.000000000 -0700
+++ imms-3.1.0-rc4/clients/audacious/rules.mk   2008-09-19 15:28:17.000000000 -0600
@@ -7,7 +7,7 @@ libaudaciousimms-LIBS = $(AUDACIOUSLDFLA
audaciousinterface-CPPFLAGS=$(AUDACIOUSCPPFLAGS)
audplugin-CPPFLAGS=$(AUDACIOUSCPPFLAGS)

-audaciousinterface.o: bmpinterface.c
+audaciousinterface.o: audaciousinterface.c
        $(call compile, $(CC), $<, $@, $($*-CFLAGS) $(CFLAGS) $($*-CPPFLAGS) $(CPPFLAGS))

AUDACIOUSDESTDIR=""

Phew. And that's not all. When you build IMMS you need to have OBJDUMP=gobjdump if you're using the default binutils variant from MacPorts, and this patch:

 rules.mk   |    2 +-
 vars.mk    |    6 +++---
 vars.mk.in |    1 +
 3 files changed, 5 insertions(+), 4 deletions(-)

Index: imms-3.1.0-rc4/rules.mk
===================================================================
--- imms-3.1.0-rc4.orig/rules.mk        2008-09-19 08:49:43.000000000 -0600
+++ imms-3.1.0-rc4/rules.mk     2008-09-19 16:17:33.000000000 -0600
@@ -19,7 +19,7 @@ link = $(CXX) $(filter-out %.a,$1) $(fil
             -shared -Wl,-z,defs,-soname,$@ -o $@

 %-data.o: %
-       objcopy -I binary -O $(OBJCOPYTARGET) -B $(OBJCOPYARCH) --rename-section .data=.rodata,alloc,load,readonly,data,contents $< $@
+       $(OBJCOPY) -I binary -O $(OBJCOPYTARGET) -B $(OBJCOPYARCH) --rename-section .data=.rodata,alloc,load,readonly,data,contents $< $@

 # macros that expand to the object files in the given directories
 objects=$(sort $(notdir $(foreach type,c cc,$(call objects_$(type),$1))))
Index: imms-3.1.0-rc4/vars.mk
===================================================================
--- imms-3.1.0-rc4.orig/vars.mk 2008-09-19 09:03:05.000000000 -0600
+++ imms-3.1.0-rc4/vars.mk      2008-09-19 15:07:44.000000000 -0600
@@ -5,8 +5,8 @@ INSTALL = /opt/local/bin/ginstall -c
 prefix = /usr
 PREFIX = $(prefix)
 OBJCOPY = gobjcopy
-OBJCOPYTARGET = 
-OBJCOPYARCH = 
+OBJCOPYTARGET = mach-o-le
+OBJCOPYARCH = i386
 exec_prefix = ${prefix}
 bindir = ${exec_prefix}/bin
 datadir = ${prefix}/share
@@ -15,7 +15,7 @@ VPATH = ../immscore:../analyzer:../model
 ARFLAGS = rs

 SHELL = bash
-PLUGINS = libxmmsimms.so
+PLUGINS = libxmmsimms.so libaudaciousimms.so
 OPTIONAL = immsremote analyzer

 GLIB2CPPFLAGS=`pkg-config glib-2.0 --cflags`
Index: imms-3.1.0-rc4/vars.mk.in
===================================================================
--- imms-3.1.0-rc4.orig/vars.mk.in      2008-03-02 18:54:06.000000000 -0700
+++ imms-3.1.0-rc4/vars.mk.in   2008-09-19 16:17:24.000000000 -0600
@@ -4,6 +4,7 @@ VERSION = @PACKAGE_VERSION@
 INSTALL = @INSTALL@
 prefix = @prefix@
 PREFIX = $(prefix)
+OBJCOPY = @OBJCOPY@
 OBJCOPYTARGET = @OBJCOPYTARGET@
 OBJCOPYARCH = @OBJCOPYARCH@
 exec_prefix = @exec_prefix@

Finally, make install doesn't finish the job.

cp build/libaudaciousimms.so /usr/local/lib/General/imms.impl

Well I think that's all the information you need, though it may not go smoothly. Hopefully we can get this all worked into IMMS proper and the 3.1.0 release will just work. If you use linux give Audacious+IMMS a try—it's easy and painless. If you think Audacious is for sissies, learn about the queue and the jump feature and try out IMMS for a week or two before you pass final judgement.

Oh, two final notes: Installing Torch can be a real pain and Audacious keyboard shortcuts don't work well with the gtk2 +quartz variant in MacPorts, so you want to stick with X11 gtk2. Oh, and though Audacious has a Last.fm plugin I haven't yet been able to figure out how to get it to stay enabled.

no comments |

k20

Posted by Hans Fugal Thu, 10 Jul 2008 01:40:53 GMT

I finished the promised K-20 meter. I imaginatively called it k20, and you can find it at http://hans.fugal.net/src/k20. Here's a screenshot:

k20 screenshot

From left to right, read average (VU), peak (instantaneous with 26 dB / 3 sec falloff), maximum peak, and overs.

This is pure unadulterated printf() abuse. No ncurses. Not that I have anything against ncurses, just that I'm lazy. Of course you need an ANSI capable terminal, but I'm sure you can find one lying around.

no comments |

opg ftw

Posted by Hans Fugal Wed, 09 Jul 2008 15:30:10 GMT

Few things about programming (in most languages) are less enjoyable than writing option parsing code. On the other hand, few things are more irritating to users than no -h and no options where options are needed (or underdeveloped option parsers). In few languages is it more painful to do option parsing than it is in C.

So I did what any sane lunatic would do. I wrote an option parser generator. I think it's quite nice. This input:

usage: foo [options] other stuff 
-f --foo          bool     Short name, long name, type, help text.
-b --bar=name     char*    This has a required string argument.
-z --baz=decibels int?     Optional integer argument
-q --quux=MACH    float    char*, int, and float are the recognized types

Any line not starting with a dash is copied into the help message verbatim.

becomes this output (a header and source file):

/* This file is automatically generated by opg */
#ifndef _OPG_H
#define _OPG_H

struct options {
    int   f; /* foo */
    char* b; /* bar */
    int   z; /* baz */
    float q; /* quux */
};

/* Print usage and exit(1) */
void usage(void);

/* Parse options, populate opts, adjust argc/argv */
void parse_options(int *argc, char * const *argv, struct options *opts);

#endif


/* This file is automatically generated by opg */
#include "opts.h"

...

void usage(void)
{
    puts("usage: foo [options] other stuff");
    puts("  -f  --foo             Short name, long name, type, help text.");
    puts("  -b  --bar=name        This has a required string argument.");
    puts("  -z  --baz[=decibels]  Optional integer argument");
    puts("  -q  --quux=MACH       char*, int, and float are the recognized types");
    puts("");
    puts("Any line not starting with a dash is copied to the help message verbatim.");

    exit(1);
}

void parse_options(int *argc, char * const *argv, struct options *opts)
{
    ...
}

http://hans.fugal.net/src/opg. Enjoy.

no comments |

JACK on the MacBook

Posted by Hans Fugal Wed, 04 Jun 2008 14:05:00 GMT

I spent the better part of two days fine tuning my linux audio setup on my MacBook, so maybe I can save anothe MacBook user some time with this post.

The sound card in this thing is an Intel HDA Controller, driven by the kernel module snd-hda-intel. Intel HDA cards (usually onboard cards) are looked down upon and generally derided, and I can testify with good reason. Like all sorry excuses for an audio card, it has only one subdevice which means only one application can use the card at a time. (If you want to know if your audio card is cheap, this is a good indicator—just look in /proc/asound/card0/pcm0p/info for subdevices_count)

Luckily in these modern times, the default ALSA device does software mixing (dmix), so even on a cheap card you can usually hear more than one application just fine. No, no, you do not need PulseAudio for this. In fact, PulseAudio steals the audio card in its default configuration (at least on Ubuntu 8.04). So if PulseAudio is running, applications that aren't PulseAudio aware (or ESD aware) will simply not be able to make sound. There are other misbehaved kids on the block, but they're fairly rare. The difference is that a well-behaved application will grab the default ALSA device, instead of the first audio card in the system explicitly, hw:0. PulseAudio in fact advises the use of this trick, to set PulseAudio as the default ALSA device, which I suppose explains why PulseAudio grabs hw:0 by default. Unfortunately Ubuntu is only halfhearted here—it enables PulseAudio but does not set up the default ALSA device to point to it. So in Ubuntu you need to either set up the default ALSA device with an ~/.asoundrc that looks like this

pcm.!default {
    type pulse
}
ctl.!default {
    type pulse
}

or you need to configure PulseAudio to use the default device instead of hw:0. If you are going to be using JACK too (and you want to hear other applications outside the JACK pipeline when JACK is running), I recommend the latter, though if you're twisted enough you might try JACK as a PulseAudio client.

JACK also by default grabs hw:0, because JACK is all about low latency and high performance and going through dmix adds a layer of overhead. If you're using JACK, you may be enough of a snob that you're ok with leaving those non-JACK applications out in the cold while JACK is running. In fact you may not want to hear Pidgin sounds (for example) at all while you're doing audio work. Semisnobs like myself, though, might want a compromise. Setting up my studio just the way I want is enough of a pain, I really don't want to quit all my JACK applications just so I can listen to Last.fm or watch sb_email.

Now at this point I would be remiss if I didn't mention the very cool JACK plugin for ALSA. It allows you to make well-behaved ALSA applications (the ones that use the default device or allow you to configure which device is used) go through JACK. I modified my .asoundrc in a manner slightly different from the example given:

pcm.jack {
        type plug
        slave {
                pcm {
                        type jack
                        playback_ports {
                                0 alsa_pcm:playback_1
                                1 alsa_pcm:playback_2
                        }
                        capture_ports {
                                0 alsa_pcm:capture_1
                                1 alsa_pcm:capture_2
                        }
                }
                rate 48000
        }
}

Then if you want to make the JACK plugin the default, you add

pcm.!default {
    type plug
    slave.pcm "jack"
}

I tried configuring PulseAudio to use the JACK plugin, but it would crash on startup. Last.fm's client also had issues—it will play fine for one song and then crash jackd when the second song starts. So unfortunately it doesn't look like the JACK plugin for ALSA is quite ready for prime time, but you can certainly use it from time to time in applications that let you choose the ALSA device.

Unfortunately, the JACK plugin isn't found in Ubuntu's libasound2-plugins package where it belongs. It's an easy remedy, however, just install libjack-dev and fakeroot, then build the package from source (you don't even have to patch it):

apt-get install libjack-dev fakeroot
apt-get build-dep libasound2-plugins
fakeroot apt-get source -b libasound2-plugins
sudo dpkg -i libasound2-plugins*.deb

Getting Ubuntu to not annoy you constantly about "upgrading" that package is another story.

Ok, so now to the meat of this post. JACK does not work well on this sound card with its default settings. It either has an insane number of xruns, or it sounds terrible. For quite some time I chased the red herring of the position_fix parameter to the snd-hda-intel module, and I can report with confidence that on this hardware you don't want to change it from the default (0, which is auto). However, if you are only concerned with JACK, you will want to change it to position_fix=3, which gives rock-solid JACK with the default settings on hw:0. However, although JACK or other direct-to-hw:0 applications sound fine, dmix sounds crackly using position_fix=3. So it's probably not a good all-around solution if you're interested in more than just JACK.

The first order of business in good JACK performance (on any system) is to enable realtime. Edit /etc/security/limits.conf and add something like this:

@audio - memlock unlimited
@audio - nice -10
@audio - rtprio 99

Now (after logging out and back in) you should be able to pass the -R option to jackd and get realtime.

If you do jackd -R -d alsa (unless you use position_fix=3) you will get lots of xruns. The best I have been able to do is jackd -R -d alsa -p 512 -n 4, as it seems that the trick is getting at least 3 periods (and to do that with hw:0 you have to reduce the period size). This works well but qjackctl reports lots of xruns still. Actually, they're mysterious messages like this

delay of 5152.000 usecs exceeds estimated spare time of 4071.000; restart ...

which don't actually cause an audio blip (but you will get an occasional real xrun). I still need to try the realtime kernel (linux-image-rt package) to see if that might help here. In my early tests (mostly playing with position_fix) the realtime kernel was actually doing worse than the generic kernel, but that was before I learned the number of periods should be at least 3, so I need to test again.

If you run jackd -R -d alsa -d default you will theoretically be able to use JACK and other applications at the same time via dmix/dsnoop. JACK will complain

You appear to be using the ALSA software "plug" layer, probably a result of using the "default" ALSA device. This is less efficient than it could be. Consider using a hardware device instead rather than using the plug layer. Usually the name of the hardware device that corresponds to the first soun

[sic] but pay it no heed, we're doing this on purpose, and actually are able to get better performance than the hw:0 route (with position_fix=0). That command will not actually work, though. It will crash within a minute even without any clients. Again the fix seems to be the number of periods, but this time we can avoid the excess delay by leaving the period size at 1024 (at the cost of some latency, of course). So, jackd -R -d alsa -d default -n 4. This is rock solid. It went all night without a single xrun. (But it wasn't doing much, though Ardour, Aeolus, and Hexter were "running". I was able to play around with them for a half hour or so with no xruns before I went to bed.) However, sometime down the road it will miss a deadline and it will crash. This crashing seems to be specific to using dmix, usually you'll just get an xrun. The workaround is to use softmode with the -s switch. Now you can run JACK 24/7 with excellent performance and without locking other applications out of the soundcard.

So in summary, if you don't care about dmix but only JACK (or any other application using hw:0, which can be all of them if you change your .asoundrc, but only one at a time), set position_fix=3 for snd-hda-intel e.g. in a file in /etc/modprobe.d/ with a line like this: options snd-hda-intel position_fix=3, and do update-initramfs -uk all. If you want a more balanced setup, where you can have JACK running and other well-behaved ALSA applications can use the sound card, leave the module parameters alone and set up realtime and use the following command to start JACK (or equivalent settings in QJackCtl):

/usr/bin/jackd -R -dalsa -ddefault -r48000 -p1024 -n4 -s

If you want to use PulseAudio in this situation, configure it to use the default ALSA device instead of hw:0.

If you like PulseAudio and JACK both, the ideal situation would be PulseAudio using JACK as a backend, JACK using hw:0 with position_fix=3, and the PulseAudio plugin as the default ALSA device. Unfortunately this is just a theoretical ideal, and doesn't work (yet) because of bugs.

And finally, if you have no or limited use for JACK, but want to use PulseAudio, just change your .asoundrc as above to make PulseAudio the default ALSA device, so that all applications, ESD aware or not, use PulseAudio.

Oh, and I almost forgot to mention the mixer. There's Master, PCM, Front, Surround, Center, LFE, Side, and various toggles. AFAICT the Front controls the internal speakers, and Surround controls the headphone volume. JACK on hw:0 has 8 system ports. The first two correspond to the front speakers and the second two to the headphone jack. When you run JACK on default, it's simply stereo output, and goes to the speakers or the headphones if they're plugged in.

Finally, I regret to report that JACK on default will crash on resume (on hw:0 it won't, at least with position_fix=3).

2 comments |

Linux on the MacBook

Posted by Hans Fugal Mon, 02 Jun 2008 15:44:55 GMT

So now that I'm done with comps, it's time to start doing real research. In my case, that means playing with audio and MIDI.

Specifically, I'm going to be generating preliminary data by recording chromatic scales and musical works on Aeolus, an absolutely fantastic pipe organ synthesizer. I did get it ported to OS X (feel free to contact me about that, or just wait until the next release, he is integrating my patches), and Ardour works in OS X as well. But realizing that I wanted to record the same things with many different registrations (stop choices), I needed a MIDI sequencer, like Rosegarden. Frankly, there just doesn't seem to be anything even close available on OS X that doesn't cost an arm and a leg (at least from a student's perspective). Combined with the fact that the OS X driver for my Radium 61 seems to be buggy, I decided I need to go Linux.

OS X is supposedly the darling of multimedia types, but in my experience there is nothing like the wealth of interesting software available, for free, on the Linux Audio scene. And when it comes to audio stability and low-latency performance, there is nothing like a well-tuned Linux box. In short, I can't imagine doing serious audio work in anything but Linux.

So there are many guides out there for installing Linux on a MacBook, and I won't try to duplicate that information here. What I would like to do is detail what I had to do, and which choices I made.

The first choice is that of partitioning. In the end I decided to share the internal hard drive, giving 10G to linux. It looked like the easiest way to do that was with Boot Camp, although it appears possible without it. But Boot Camp Assistant just would not resize my OS X volume. It would either complain about files that couldn't be moved, or running out of disk space. I thought it might be running programs, so I shut them down. I thought it might be swap space so I rebooted. I thought it might work if I did it from the installation DVD. I thought maybe it needed more free space to do the shuffle. None of these fixed it. So I googled around and found that it might be possible for a file to be locked in position. So I needed to figure out what files were entrenched at the end of my HDD and see if I couldn't do something about that. I came across a not-free defragmenter, iDefrag and fired up the demo. It processed the disk and eventually showed me a map of the sectors of the drive, and mousing over them I was able to see the files using those sectors. Near the end of the drive there were a lot of red sectors that all belonged to Google Desktop. I assumed red sectors probably meant stuff that couldn't be moved, but I couldn't be bothered to look it up. Google Desktop seemed like a logical lead, though. So I uninstalled it and gave the repartitioning a try, and it worked like a charm. Incidentally I like Google Desktop, as also Quicksilver and Spotlight (I use all three, depending on what I want to do), so I'll probably reinstall it. Oh, and I didn't defragment with iDefrag since the demo only defragments drives smaller than 100M. But just try finding that information on their website or in the README.

I then installed rEFIt, which although not necessary is a nice way to bootload a dual-boot system.

I rebooted and chose to boot from CD in rEFIt. Odd, I just get a blank screen and no activity. I know this Ubuntu CD works on this very laptop because I had already tried it. So I rebooted and held down alt, which gave me the Apple boot chooser, and I chose the CD, and it worked fine.

I installed Ubuntu in the usual way. I manually partitioned, formatting the partition Boot Camp made for Windows as ext3 and not bothering with swap (I made a swapfile after installation). I told it to install the bootloader (GRUB) on the partition instead of the disk (the partition is sda3).

When I rebooted, I did the gptsync thing using the rEFIt "Partitioning Tool", which synchronized the legacy MBR with the newfangled GPT. Then I tried to boot into Linux, and again rEFIt gave me a blank screen. I booted into OS X and googled it, finally stumbling across something that said that happened once or twice and then stopped happening. So I rebooted and sure enough, it worked the second time. That's odd, and not the end of booting troubles. Sometimes when booting Linux you get a kernel panic talking about APIC. I remembered this from early experiments last year, so I didn't panic. You just try try again until it works.

I will adorn this blog with a flurry of posts on the rest of my adventures soon, but for now suffice it to say I had Linux installed, and it has come a long way. Ubuntu 8.04 had wireless, multi-finger trackpad tapping (though I prefer two fingers to be the right button not the middle button), basic sound, video acceleration, and even suspend working out of the box. It's beginning to look like I could not only do my research in Linux, but maybe even make it the primary OS on my laptop the rest of the time too.

2 comments |

Stuff StuffIt!

Posted by Hans Fugal Wed, 14 May 2008 22:12:01 GMT

I wish StuffIt would just die, already. I'm sick of running into .sit and .hqx files. Thankfully these are quite rare, and usually an indication that the project is ancient and unmaintained. If you ever find you do need to open one, though, head on over to The Unarchiver. Do not under any circumstances even visit the StuffIt website. Only you can prevent StuffIt fires!

I'm sick of Firefox asking me if I want to open archive files (zip, tar, etc.) with "StuffIt Expander (Default)". Earth to Firefox! StuffIt Expander hasn't been included with the OS for several versions now. It is not and never will be installed on my computer. Incidentally, it works just fine—opening files with the appropriate application. In other words, Firefox is a big fat liar. But we knew that.

I applaud Apple for finally dropping StuffIt a few years back. There has never, ever been a good reason (other than "I'm stuck in the 1990s OS 9 mac scene") to use StuffIt on OS X. Please join us in the 21st century. Use zip, tar+gzip, or tar+bzip2. Even better (for OS X recipients) use a .dmg. For the love of all that is green, do not do .dmg.sit! You won't get any more compression over a compressed disk image, and you will only make rational people foam at the mouth.

Die. Die. DIE.

1 comment |

QCad on Leopard

Posted by Hans Fugal Wed, 26 Mar 2008 15:01:42 GMT

I finally got around to building QCad on OS X Leopard. There are two main hurdles: getting Qt3 to build and getting QCad to build.

At first I tried building Qt3 with macports, but building QCad was a royal pain with the X11 version of Qt3 on OS X, for whatever reason. So I tried to install the qt3-mac MacPorts package, but that failed. So I was on my own building Qt3.

This patch will allow Qt3 to build on Leopard, by following the instructions in the INSTALL file. Here's the diffstat:

 config.tests/mac/mac_version.test |    2 +-
 src/kernel/qcursor_mac.cpp        |    4 ++++
 src/kernel/qt_mac.h               |    2 +-
 src/tools/qglobal.h               |    5 ++++-
 4 files changed, 10 insertions(+), 3 deletions(-)

I put it in /Developer/qt3, and I wrote a script to source on demand rather than setting QTDIR and friends in my .profile or .bashrc, since I more often want Qt4 than Qt3. I configure with -static, so applications like QCad are built with Qt3 statically, which just makes things work better.

QCad needs a patch as well:

Index: qcad-2.0.5.0-1-community.src/mkspecs/defs.pro
===================================================================
--- qcad-2.0.5.0-1-community.src.orig/mkspecs/defs.pro  2008-03-26 08:46:25.000000000 -0600
+++ qcad-2.0.5.0-1-community.src/mkspecs/defs.pro       2008-03-26 08:46:48.000000000 -0600
@@ -1,6 +1,6 @@
 # $Id: defs.pro 606 2004-12-25 03:08:40Z andrew $
-QMAKE_CXXFLAGS_DEBUG += -pedantic
-QMAKE_CXXFLAGS += -pedantic
+#QMAKE_CXXFLAGS_DEBUG += -pedantic
+#QMAKE_CXXFLAGS += -pedantic

 win32 {
   QMAKE_CFLAGS_THREAD -= -mthreads
Index: qcad-2.0.5.0-1-community.src/scripts/build_qcad.sh
===================================================================
--- qcad-2.0.5.0-1-community.src.orig/scripts/build_qcad.sh     2008-03-26 08:46:06.000000000 -0600
+++ qcad-2.0.5.0-1-community.src/scripts/build_qcad.sh  2008-03-26 08:46:49.000000000 -0600
@@ -30,7 +30,7 @@ then
     export MAKE=gmake
     echo "Platform is Solaris"
     platform=solaris
-elif [ "x$OSTYPE" == "xdarwin8.0" ]
+elif [ "x$OSTYPE" == "xdarwin8.0" -o "x$OSTYPE" == "xdarwin9.0" ]
 then
     export MAKE=make
     echo "Platform is Mac OS X"

Then do

cd scripts
./build_qcad.sh notrans

It will complain about not finding qm/*.qm, but that's a nonfatal error. QCad.app will be in the qcad directory, ready for your use.

I built this on an Intel MacBook running Leopard. If you think that matches your setup, you're free to download my QCad.app and avoid building both Qt3 and QCad.

4 comments |

MacPorts QTDIR

Posted by Hans Fugal Wed, 26 Mar 2008 03:38:30 GMT

If I had a nickel for every time I had to scour the web to figure out the right setting for QTDIR on {Debian,Ubuntu,OS X}…

On OS X, if you installed the qt3 package, then the proper setting is QTDIR=/opt/local/lib/qt3. I wrote this little script to source before commencing building a Qt3 project:

#QTDIR=/usr/local/Trolltech/qt-mac-free-3.3.7
#QTDIR=/Developer/qt
QTDIR=/opt/local/lib/qt3
PATH=$QTDIR/bin:$PATH
DYLD_LIBRARY_PATH=$QTDIR/lib:$DYLD_LIBRARY_PATH

export QTDIR PATH DYLD_LIBRARY_PATH

no comments |

CoreMIDI

Posted by Hans Fugal Sun, 23 Mar 2008 12:50:12 GMT

I'm porting Aeolus to OS X. In the process I'm learning how CoreMIDI works. Naturally you get to hear my opinion on the matter.

CoreMIDI seems like a decent framework, actually. It is callback-based, which is good. It has a pretty reasonable design; physical devices and virtual devices alike communicate with eachother in the same way. They each have endpoints—source endpoints and destination endpoints.

It all looks well and good on the surface, but there's some problems. The first problem is arguably a feature. You can create input/output ports and connect sources/destinations to those ports from within your application. This allows you to make a cute or complicated dialog box where the user can select the MIDI (virtual) device(s) she wants to use. Sounds reasonable right? And so it is, and I wouldn't argue against this ability.

The badness comes in when you consider that every application has to duplicate this functionality. It would be much better to have an external patchbay for connecting applications together. This would be more powerful and flexible and free up application developers to not worry about it. They just have to create the endpoints and then they're done.

Alas, OS X proper has no such patchbay. "Yes it does, silly. It's called Audio MIDI Setup" you say. That's the most infuriating thing—Audio MIDI Setup lets you route between devices in just the sort of way I'm talking about, but it only works for physical devices. Someone needs to be shot.

Luckily, some guy named Pete wrote a MIDI Patchbay. It's serviceable, if quirky and ugly. He also wrote a simple software synthesizer called SimpleSynth (also quirky and ugly) that does what something in OS X (e.g. QuickTime Player) should already be doing: accept MIDI input and use the QuickTime music synthesizer to render it. Kudos to Pete for filling in the gaps, and I'm sorry for calling your children ugly.

While I'm complaining about patchbays, I'm still dumbfounded that JACK doesn't seem to have a command-line application for patching things together. I'm thinking something akin to aconnect for ALSA MIDI, though of course for JACK it would be for audio and MIDI both. qjackctl is absolutely marvelous, and I wouldn't use anything else given the choice, but sometimes you don't have qjackctl handy and it might be quite difficult indeed to get it. This was the case for me the other day. I had the latest greatest JACK installed from source, but qjackctl (which I finally managed to figure out how to build using the QtMac binary, whose qmake refuses to output a real Makefile but instead an XCode project) was choking on it. So I had to downgrade to Jack OS X and rebuild qjackctl (it's still an immense improvement over JackPilot). This is depressing because the newer version of JACK is much more friendly to the CLI user on OS X. The version in Jack OS X 0.76 still requires some ugly workarounds (which JackPilot helps you to do). The latest version of JACK (0.109.2) Just Works™ when you type jackd -R -d coreaudio. So I'm still starting JACK with JackPilot, which I then summarily quit in favor of qjackctl.

2 comments |

Older posts: 1 2 3 ... 6