Shallow Thoughts : tags : audio

Akkana's Musings on Open Source Computing and Technology, Science, and Nature.

Thu, 18 Apr 2024

Using Sox Play to Help Learn Guitar

I mentioned last month that I'm learning guitar. It's been going well and I'm having fun. But I've gotten to the point where I sometimes get chords confused: a song is listed as using E major and I play D major instead.

Also, it's important to practice transitions between chords, which is easy when you only know three chords; but with eight or so, I had stopped practicing transitions in general and was only practicing the ones that occur in songs I like to play.

I found myself wishing I had something like flash cards for guitar chords.

Someone must have already written that, right? But I couldn't find anything promising with a web search. And besides, it's more fun to write programs than to flail at unhelpful search engines, and you always end up learning something new.

Read more ...

Tags: , , , ,
[ 20:02 Apr 18, 2024    More linux | permalink to this entry | ]

Thu, 07 May 2020

PulseAudio from the Command Line

Controlling PulseAudio from the Command Line #tags linux,audio,pulseaudio,ubuntu,cmdline

Controlling PulseAudio via pavucontrol is all very nice, but it's time consuming and fiddly: you have to do a lot of clicking in a lot of tabs any time you want to change anything.

So I've been learning how to control PulseAudio from the command line, so I can make aliases to switch between speakers quickly, or set audio defaults at login time.

That was going to be a blog post, but I think this is going to be an evolving document for quite some time, so instead, I just made it a page on the Linux section of my website: Controlling PulseAudio from the Command Line.

I also wrote a Python script, pulsehelper.py, that uses some of these commands to provide clearer output and easier switching. It even uses color and bold fonts if you have the termcolor module installed. Like the document, this script is likely to be evolving for quite some time.

Happy listening and recording!

Tags: , , ,
[ 12:21 May 07, 2020    More linux | permalink to this entry | ]

Mon, 04 May 2020

PulseAudio via GUI: Pavucontrol

(Note: this is not an alphabet post. You may have noticed I'm a little stuck on I. I hope to get un-stuck soon; but first, here are a pair of articles on configuring audio on Linux.)

I'm a very late adopter for PulseAudio. In the past, on my minimal Debian machines, nearly any sound problem could be made better by apt-get remove pulseaudio. But pulse seems like it's working better since those days, and a lot of applications (like Firefox) require it, so it's time to learn how to use it. Especially in these days of COVID-19 and video conferencing, when I'll need to be using the microphone and speakers a lot more. (I'd never actually had a reason to use the microphone on my last laptop.)

Beginner tutorials always start with something like "Go into System Preferences and click on Audio", leaving out anyone who doesn't use the standard desktop. The standard GUI PulseAudio controller is pavucontrol. It has four tabs.

[Configuration tab in pavucontrol]

Read more ...

Tags: , , ,
[ 18:04 May 04, 2020    More linux | permalink to this entry | ]

Sun, 24 Nov 2019

Adjusting PulseAudio Volume from the Command-Line (or a Window Manager)

I have a new laptop, a birthday present to myself last month. For once, rather than buying a cut-rate netbook, I decided to treat myself to a fancy Lenovo Carbon X1 with an up-to-date processor and lots of RAM.

Since I have way more resources than I'm used to, I decided I'd try installing a full Ubuntu and not trying to pare it down to a super lightweight system. I'm still running the lightweight, fast, highly configurable Openbox window manager instead of a full Gnome desktop: Openbox does just what I tell it and no more, and doesn't surprise me with random redesigns. But I did let Ubuntu install some system utilities I've always avoided in the past, like NetworkManager and PulseAudio. I decided I'd give them a chance, see if they've gotten better since I last checked.

They have, though they're still a bit of a hassle to deal with. NetworkManager can be controlled through nmcli, which is poorly documented but works okay if you google long enough to find the proper incancations. PulseAudio gave me a bit more trouble.

The standard GUI for controlling PulseAudio is pavucontrol. It showed two audio devices: "USB PnP Audio Device Analog Stereo" and "Built-in Audio Analog Stereo". Turns out the USB PnP option is a sound card built into the USB hub, a Totu tt-hb003a 11-in-1 USB-C hub that lets me connect to a charger, external monitor, SD and micro-SD slots, and extra USB ports without juggling a lot of extra cables.

Pulse assumes -- probably reasonably, though it's wrong in this case -- that if I have a USB audio device connected, I probably want to use it in preference to the laptop's built-in audio. That would make sense if I had external speakers plugged in, but I left all my computer speakers behind when I moved. I should probably order some speakers. But meanwhile, I needed to persuade PulseAudio to ignore the hub and use the laptop's built-in sound system.

Mute/Unmute via the Keyboard

The Lenovo, like most laptops, has a dedicated key for muting, Fn-F1. It even has a little light on it to show whether it's muted. In Openbox, pressing Fn-F1 actually muted the sound, and even turned on the light. This is probably because I'd previously set key="XF86AudioMute" to run amixer set Master toggle in .config/openbox/rc.xml, which worked on my Pulse-free pared-down Debian netbook. The problem is that pressing iFn-F1 again didn't bring the sound back. Instead, it was unmuting the USB hub's audio. Clicking "Set as fallback" on the built-in audio in pavucontrol made no difference.

It turns out that it is virtually impossible to persuade PulseAudio to use "Built-in Audio" when a "USB PnP Audio Device" is available. I finally found the secret: in pavucontrol's Configuration tab, set Profile for the PnP USB device to Off. Now only the built-in device shows up in the other tabs.

But that amixer command still wasn't unmuting properly, so the next step was to find a command that would actually unmute. Someone on #linux suggested pactl set-sink-mute @DEFAULT_SINK@ toggle and that worked great from the command line. But when I tried to bind it in Openbox to the XF86AudioMute key, it did nothing. I still don't understand why not; I wasted a lot of time comparing my shell environment to openbox's environment and never found the difference.

Back to web searching, I found an askbuntu thread suggesting some Openbox stanzas. In particular, it apparently works better to use alsamixer rather than pactl. This finally worked for toggling mute:

    <keybind key="XF86AudioMute">
        <action name="Execute">
            <command>amixer -q -D pulse sset Master toggle</command>
        </action>
    </keybind>

Volume Controls via Function Keys

Partial success! Unfortunately, the volume control commands in that same askbuntu post, amixer -q -D pulse sset Master 3%+ unmute, did nothing. I had already noticed that in pavucontrol, the volume controls didn't work either. In fact, if I started some music playing and then called up alsamixer, channels like Master and Speaker didn't do anything; the only channel that affected volume was ALSA PCM. After some fiddling, I discovered that I had to change Master to PCM and remove the -D pulse:

    <keybind key="XF86AudioRaiseVolume">
      <action name="Execute">
            <command>amixer sset PCM 4%+ unmute</command>
        </action>
    </keybind>
    <keybind key="XF86AudioLowerVolume">
        <action name="Execute">
            <command>amixer sset PCM 4%- unmute</command>
        </action>
    </keybind>

I'm sure I'll eventually need to fiddle some more. For one thing, if I ever want to use audio during a talk (as I did briefly at my Stonehenge talk earlier this year) I'll need to figure out how to enable a temporary HDMI sound sink quickly without needing to fiddle with pavucontrol. But for now, I'm happy to have the basic laptop volume and mute keys working.

Tags: , , ,
[ 15:43 Nov 24, 2019    More linux | permalink to this entry | ]

Fri, 20 Jul 2018

Pulseaudio: the more things change, the more they stay the same

Such a classic Linux story.

For a video I'll be showing during tonight's planetarium presentation (Sextants, Stars, and Satellites: Celestial Navigation Through the Ages, for anyone in the Los Alamos area), I wanted to get HDMI audio working from my laptop, running Debian Stretch. I'd done that once before on this laptop (HDMI Presentation Setup Part I and Part II) so I had some instructions to follow; but while aplay -l showed the HDMI audio device, aplay -D plughw:0,3 didn't play anything and alsamixer and alsamixergui only showed two devices, not the long list of devices I was used to seeing.

Web searches related to Linux HDMI audio all pointed to pulseaudio, which I don't use, and I was having trouble finding anything for plain ALSA without pulse. In the old days, removing pulseaudio used to be the cure for practically every Linux audio problem. But I thought to myself, It's been a couple years since I actually tried pulse, and people have told me it's better now. And it would be a relief to have pulseaudio working so things like Firefox would Just Work. Maybe I should try installing it and see what happens.

So I ran an aptitude search pulseaudio to find the package name I'd need to install. Imagine my surprise when it turned out that it was already installed!

So I did some more web searching to find out how to talk to pulse and figure out how to enable HDMI, or un-mute it, or whatever it was I needed. But to no avail: everything I found was stuff like "In the Ubuntu audio panel, do this". The few pages I found that listed commands to run didn't help -- the commands all gave errors.

Running short on time, I reverted to the old days: aptitude purge pulseaudio. Rebooted to make sure the audio system was reset, ran alsamixergui and sure enough, there were all my normal devices, including the IEC958 device for HDMI, which was indeed muted. I unmuted it, tried the video again -- and music blasted from my TV's speakers.

I'm sure there are machines where pulseaudio works. There are even a few people who have audio setups complicated enough to need something like pulseaudio. But in 2018, just as in 2006, aptitude purge pulseaudio is the easiest solution to a Linux sound problem.

Tags: , , , ,
[ 14:17 Jul 20, 2018    More linux | permalink to this entry | ]

Thu, 28 Sep 2017

Audio Output from a Raspberry Pi Zero

Someone at our makerspace found a fun Halloween project we could do at Coder Dojo: a motion sensing pumpkin that laughs evilly when anyone comes near. Great! I've worked with both PIR sensors and ping rangefinders, and it sounded like a fun project to mentor. I did suggest, however, that these days a Raspberry Pi Zero W is cheaper than an Arduino, and playing sounds on it ought to be easier since you have frameworks like ALSA and pygame to work with.

The key phrase is "ought to be easier". There's a catch: the Pi Zero and Zero W don't have an audio output jack like their larger cousins. It's possible to get analog audio output from two GPIO pins (use the term "PWM output" for web searches), but there's a lot of noise. Larger Pis have a built-in low-pass filter to screen out the noise, but on a Pi Zero you have to add a low-pass filter. Of course, you can buy HATs for Pi Zeros that add a sound card, but if you're not super picky about audio quality, you can make your own low-pass filter out of two resistors and two capacitors per channel (multiply by two if you want both the left and right channels).

There are lots of tutorials scattered around the web about how to add audio to a Pi Zero, but I found a lot of them confusing; e.g. Adafruit's tutorial on Pi Zero sound has three different ways to edit the system files, and doesn't specify things like the values of the resistors and capacitors in the circuit diagram (hint: it's clearer if you download the Fritzing file, run Fritzing and click on each resistor). There's a clearer diagram in Sudomod Forums: PWM Audio Guide, but I didn't find that until after I'd made my own, so here's mine.

Parts list:

And here's how to wire it: [Adding audio to the Raspberry Pi Zero]
(Fritzing file, pi-zero-audio.fzz.)

This wiring assumes you're using pins 13 and 18 for the left and right channels. You'll need to configure your Pi to use those pins. Add this to /boot/config.txt:

dtoverlay=pwm-2chan,pin=18,func=2,pin2=13,func2=4

Testing

Once you build your circuit up, you need to test it. Plug in your speaker or headphones, then make sure you can play anything at all:

aplay /usr/share/sounds/alsa/Front_Center.wav

If you need to adjust the volume, run alsamixer and use the up and down arrow keys to adjust volume. You'll have to press up or down several times before the bargraph actually shows a change, so don't despair if your first press does nothing.

That should play in both channels. Next you'll probably be curious whether stereo is actually working. Curiously, none of the tutorials address how to test this. If you ls /usr/share/sounds/alsa/ you'll see names like Front_Left.wav, which might lead you to believe that aplay /usr/share/sounds/alsa/Front_Left.wav might play only on the left. Not so: it's a recording of a voice saying "Front left" in both channels. Very confusing!

Of course, you can copy a music file to your Pi, play it (omxplayer is a nice commandline player that's installed by default and handles MP3) and see if it's in stereo. But the best way I found to test audio channels is this:

speaker-test -t wav -c 2

That will play those ALSA voices in the correct channel, alternating between left and right. (MythTV has a good Overview of how to use speaker-test.

Not loud enough?

I found the volume plenty loud via earbuds, but if you're targeting something like a Halloween pumpkin, you might need more volume. The easy way is to use an amplified speaker (if you don't mind putting your nice amplified speaker amidst the yucky pumpkin guts), but you can also build a simple amplifier. Here's one that looks good, but I haven't built one yet: One Transistor Audio for Pi Zero W

Of course, if you want better sound quality, there are various places that sell HATs with a sound chip and line or headphone out.

Tags: , , , ,
[ 15:49 Sep 28, 2017    More hardware | permalink to this entry | ]

Fri, 30 Oct 2015

HDMI presentation setup on Linux, Part II: problems and tips

In Part I of HDMI Presentation Setup on Linux, I covered the basics of getting video and audio working over HDMI. Now I want to cover some finer-grained details: some problems I had, and ways to make it easier to enable HDMI when you need it.

Testing follies, delays, and screen blinking/flashing woes

While I was initially trying to get this working, I was using my own short sound clip (one of the alerts I use for IRC) and it wasn't working. Then I tried the test I showed in part I, $ aplay -D plughw:0,3 /usr/share/sounds/alsa/Front_Center.wav and that worked fine. Tried my sound clip again -- nothing. I noticed that my clip was mono and 8-bit while the ALSA sample was stereo and 16-bit, and I wasted a lot of time in web searches on why HDMI would play one and not the other.

Eventually I figured out that the reason my short clip wasn't playing was that there's a delay when switching on HDMI sound, and the first second two two of any audio may be skipped. I found lots of complaints about people missing the first few seconds of sound over HDMI, so this problem is quite common, and I haven't found a solution.

So if you're giving a talk where you need to play short clips -- for instance, a talk on bird calls -- be aware of this. I'm probably going to make a clip of a few seconds of silence, so I can play silence before every short clip to make sure I'm fully switched over to HDMI before the clip starts: aplay -D plughw:0,3 silence.wav osprey.wav

Another problem, probably related, when first starting an audio file: the screen blinks brieftly off then on again, then blinks again a little while after the clip ends. ("Flicker" turns out to be a better term to use when web searching, though I just see a single blink, not continued flickering). It's possible this is something about my home TV, and I will have to try it with another monitor somewhere to see if it's universal. It sounds like kernel bug 51421: Enabling HDMI sound makes HDMI video flicker, but that bug was marked resolved in 2012 and I'm seeing this in 2015 on Debian Jessie.

Making HDMI the sound default

What a pain, to have to remember to add -D plughw:0,3 every time you play a sound. And what do you do for other programs that don't have that argument?

Fortunately, you can make HDMI your default sound output. Create a file in your home directory called .asoundrc with this in it (you may be able to edit this down -- I didn't try) and then all audio will go to HDMI:

pcm.dmixer {
  type dmix
  ipc_key 1024
  ipc_key_add_uid false
  ipc_perm 0660
  slave {
    pcm "hw:0,3"
    rate 48000
    channels 2
    period_time 0
    period_size 1024
    buffer_time 0
    buffer_size 4096
  }
}
 
pcm. !default {
  type plug
  slave.pcm "dmixer"
}

Great! But what about after you disconnect? Audio will still be going to HDMI ... in other words, nowhere. So rename that file:

$ mv .asoundrc asoundrc-hdmi
Then when you connect to HDMI, you can copy it back:
$ cp asoundrc-hdmi .asoundrc 

What a pain, you say again! This should happen automatically!

That's possible, but tricky: you have to set up udev rules and scripts. See this Arch Linux discussion on HDMI audio output switching automatically for the gory details. I haven't bothered, since this is something I'll do only rarely, when I want to give one of those multimedia presentations I sometimes contemplate but never actually give. So for me, it's not worth fighting with udev when, by the time I actually need HDMI audio, the udev syntax probably will have changed again.

Aliases to make switching easy

But when I finally do break down and design a multimedia presentation, I'm not going to be wanting to do all this fiddling in the presentation room right before the talk. I want to set up aliases to make it easy.

There are two things that need to be done in that case: make HDMI output the default, and make sure it's unmuted.

Muting can be done automatically with amixer. First run amixer with no arguments to find out the channel name (it gives a lot of output, but look through the "Simple mixer control" lines, or speed that up with amixer | grep control.

Once you know the channel name (IEC958 on my laptop), you can run: amixer sset IEC958 unmute The rest of the alias is just shell hackery to create a file called .asoundrc with the right stuff in it, and saving .asoundrc before overwriting it. My alias in .zshrc is set up so that I can say hdmisound on or hdmisound off (with no arguments, it assumes on), and it looks like this:

# Send all audio output to HDMI.
# Usage: hdmisound [on|off], default is on.
hdmisound() {
    if [[ $1 == 'off' ]]; then
        if [[ -f ~/.asoundrc ]]; then
            mv ~/.asoundrc ~/.asoundrc.hdmi
        fi
        amixer sset IEC958 mmute
    else
        if [[ -f ~/.asoundrc ]]; then
            mv ~/.asoundrc ~/.asoundrc.nohdmi
        fi
        cat >> ~/.asoundrc <<EOF
pcm.dmixer {
  type dmix
  ipc_key 1024
  ipc_key_add_uid false
  ipc_perm 0660
  slave {
    pcm "hw:0,3"
    rate 48000
    channels 2
    period_time 0
    period_size 1024
    buffer_time 0
    buffer_size 4096
  }
}
 
pcm. !default {
  type plug
  slave.pcm "dmixer"
}
EOF
        amixer sset IEC958 unmute
    fi
}

Of course, I could put all that .asoundrc content into a file and just copy/rename it each time. But then I have another file I need to make sure is in place on every laptop; I decided I'd rather make the alias self-contained in my .zshrc.

Tags: , ,
[ 11:57 Oct 30, 2015    More linux/laptop | permalink to this entry | ]

Tue, 27 Oct 2015

HDMI presentation setup on Linux, video and audio: Part I

For about a decade now I've been happily connecting to projectors to give talks. xrandr --output VGA1 --mode 1024x768 switches on the laptop's external VGA port, and xrandr --auto turns it off again after I'm disconnected. No fuss.

But increasingly, local venues are eschewing video projectors and instead using big-screen TVs, some of which offer only an HDMI port, no VGA. I thought I'd better figure out how to present a talk over HDMI now, so I'll be ready when I need to know.

Fortunately, my newest laptop does have an HDMI port. But in case it ever goes on the fritz and I have to use an older laptop, I discovered you can buy VGA to HDMI adaptors rather cheaply (about $10) on ebay. I bought one of those, tested it on my TV at home and it at least worked there. Be careful when shopping: you want to make sure you're getting something that takes VGA in and outputs HDMI, rather than the reverse. Ebay descriptions aren't always 100% clear on that, but if you check the gender of the connector in the photo and make sure it's right to plug into the socket on your laptop, you should be all right.

Once you're plugged in (whether via an adaptor, or native HDMI built into your laptop), connecting is easy, just like connecting with VGA:

xrandr --output HDMI1 --mode 1024x768

Of course, you can modify the resolution as you see fit. I plan to continue to design my presentations for a 1024x768 resolution for the forseeable future. Since my laptop is 1366x1024, I can use the remaining 342-pixel-wide swath for my speaker notes and leave them invisible to the audience.

But for GIMP presentations, I'll probably want to use the full width of my laptop screen. --mode 1366x768 didn't work -- that resolution wasn't available -- but running xrandr with no arguments got me a list of available resolutions, which included 1360x768. That worked fine and is what I'll use for GIMP talks and other live demos where I want more screen space.

Sound over HDMI

My Toastmasters club had a tech session where a few of us tried out the new monitor in our meeting room to make sure we could use it. One person was playing a video with sound. I've never used sound in a talk, but I've always wanted to find an excuse to try it. Alas, it didn't "just work" -- xrandr's video settings have nothing to do with ALSA's audio settings. So I had to wait until I got home so I could do web searches and piece together the answer.

First, run aplay -l , which should show something like this:

$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Intel [HDA Intel], device 0: STAC92xx Analog [STAC92xx Analog]
  Subdevices: 0/1
  Subdevice #0: subdevice #0
card 0: Intel [HDA Intel], device 3: HDMI 0 [HDMI 0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

Find the device number for the HDMI device, which I've highlighted here: in this case, it's 3 (which seems to be common on Intel chipsets).

Now you can run a test:

$ aplay -D plughw:0,3 /usr/share/sounds/alsa/Front_Center.wav
Playing WAVE '/usr/share/sounds/alsa/Front_Center.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Mono
If you don't hear anything, don't worry: the HDMI channel is probably muted if you've never used it before. Run either alsamixer or alsamixergui.

[alsamixergui with HDMI muted] [alsamixer] Now find the channel representing your HDMI connection. (HDMI must be plugged in for this to work.) In alsamixer, it's called S/PDIF; in alsamixergui, it's called IEC958. If you look up either of those terms, Wikipedia S/PDIF will tell you that S/PDIF is the Sony/Philips Digital Interconnect Format, a data protocol and a set of physical specifications. Those physical specifications appear to have nothing to do with video, and use connectors that are nothing like HDMI. So it doesn't make much sense. Just remember that if you see IEC958 or S/PDIF in ALSA, that's probably your HDMI channel.

In the alsamixergui screenshot, IEC958 is muted: you can tell because the little speaker icon at the top of the column is bright white. If it were unmuted, the speaker icon would be grey like most of the others. Yes, this seems backward. It's Linux audio: get used to obscure user interfaces.

In the alsamixer screenshot, the mutes are at the bottom of each column, and MM indicates a channel is muted (like the Beep channel in the screenshot). S/PDIF is not muted here, though it appears to be at zero volume. (The 00 doesn't tell you it's at zero volume; 00 means it's not muted. What did I say about Linux audio?) ALSA apparently doesn't let you adjust the volume of HDMI output: presumably they expect that your HDMI monitor will have its own volume control. If your S/PDIF is muted, you can use your right-arrow key to arrow over to the S/PDIF channel, then type m to toggle muting. You can exit alsamixer with Ctrl-C (Q and Ctrl-Q don't work).

Now try that aplay -D command again and see if it works. With any luck, it will (loudly).

A couple of other tests you might want to try:
speaker-test -t sine -f 440 -c 2 -s 1 -D hw:0,3
plays a sine wave. speaker-test -c 2 -r 48000 -D hw:0,3
runs a general speaker test sequence.

In Part II of Linux HDMI Presentations, I'll cover some problems I had, and how to write an alias to make it easy to turn HDMI audio on and off.

Tags: , ,
[ 14:36 Oct 27, 2015    More linux/laptop | permalink to this entry | ]

Tue, 02 Dec 2014

Ripping a whole CD on Linux

I recently discovered that my ancient stereo turntable didn't survive our move. So all those LPs I brought along, intending to rip to mp3 when I had more time, will never see bits.

So I need to buy new versions of some of that old music. In particular, I'd lately been wanting to listen to my old Flanders and Swann albums. Flanders and Swann were a terrific comedy music duo (think Tom Lehrer only less scientifically oriented) from the 1960s.

So I ordered a CD of The Complete Flanders & Swann, which contains all three of the albums I inherited from my parents. Woohoo! I ran a little script I have that rips a whole CD to a directory of separate MP3 songs, and I was all set.

Until I listened to it. It turns out that when the LP album was turned into a CD, they put the track breaks in the wrong place. These albums are recordings of live performances. Each song has a spoken intro, giving a little context for the song that follows. On the CD, each track starts with a song, and ends with the spoken intro for the next song. That's no problem if you always listen to whole albums in order. But I like to play individual tracks, or listen to music on random play. So this wasn't going to work at all.

I tried using audacity to copy the intro from the end of one track and paste it onto the beginning of another. That worked, but it was tedious and fiddly. A little research showed me a much better way.

First: Rip the whole CD

First I needed to rip the whole CD as one gigantic track. My script had been running cdparanoia tracknumber filename.wav. But it took some study of the cdparanoia manual before I finally found the way to rip a whole CD to one track: you can specify a range of tracks, starting at 0 and omitting the end track.

cdparanoia 0- outfile.wav

Use Audacity to split and save the tracks

Now what's the best way to split a recording into separate tracks? Fortunately the Audacity manual has a nice page on that very subject: Splitting a recording into separate tracks.

Mostly, the issue is setting labels -- with Tracks->Add Label at Selection or Tracks->Add Label at Playback Position. Use Ctrl-1 to zoom as much as you need to see where the short pauses are. Then listen to the audio, pausing or clicking and setting labels appropriately.

It's a bit fiddly. For instance, if you pause your listening to set a label, you might want to save the audacity project so you don't lose the label positions you've set so far. But you can't save unless you Stop the playback; and that loses the current playback position which you may not yet have set a label for. Even if you have set a label for it, you'll need to click to set the selection to the label you just made if you want to continue playing from where you left off. It all seems a little silly and unintuitive ... but after a few tries you'll find a routine that works for you.

When all your labels are set, then File->Export Multiple.... You will have to go through a bunch of dialogs involving metadata for each track; just hit return, since audacity ignores any metadata you type in and won't actually write it to the MP3 file. I have no idea why it always prompts for metadata then doesn't use it, but you can use a program like id3tool later to add proper metadata to the tracks.

So, no, the tools aren't perfect. On the other hand, I now have a nice set of Flanders and Swann tracks, and can listen to Misalliance, Ill Wind and The GNU Song complete with their proper introductions.

Tags: ,
[ 13:35 Dec 02, 2014    More linux | permalink to this entry | ]

Wed, 17 Oct 2012

Asynchronous sound playing in Python

A little while back I wrote about my Python xchat script to play sound alerts.

But one thing that's been annoying me about it -- it was a problem with the old perl alert script too -- is the repeated sounds. If lots of twitter updates come in on the Bitlbee channel, or if someone pastes numerous lines into a channel, I hear POPPOPPOPPOPPOPPOP or repetitions of whatever the alert sound is for that type of message. It's annoying to me, but even more so to anyone else in the same room.

It would be so much nicer if I could have it play just one repetition of any given alert, even if there are eight lines all coming in at the same time. So I decided to write a Python class to handle that.

My existing code used subprocesses to call the basic ALSA sound player, /usr/bin/aplay -q. Initially I used
if not os.fork() : os.execl(APLAY, APLAY, "-q", alertfile)
but I later switched to the cleaner
subprocess.call([APLAY, '-q', alertfile])
But of course, it would be better to do it all from Python without requiring an external process like aplay. So I looked into that first.

Sadly, it turns out Python audio support is a mess. The built-in libraries are fairly limited in functionality and formats, and the external libraries that handle sound are mostly unmaintained, unless you want to pull in a larger library like pygame. After a little web searching I decided that maybe an aplay subprocess wasn't so bad after all.

Okay, so how should I handle the subprocesses? I decided the best way was to keep track of what sound was currently playing. If another alert fires for the same sound while that sound is already playing, just ignore it. If an alert comes in for a different sound, then wait() for the current sound to finish, then start the new sound.

That's all quite easy with Python's subprocess module. subprocess.Popen() returns a Popen object that tracks a process ID and can check whether that process has finished or not. If self.curpath is the path to the sound currently playing and self.current is the Popen object for whatever aplay process is currently running, then:

    if self.current :
        if self.current.poll() == None :
            # Current process hasn't finished yet. Is this the same sound?
            if path == self.curpath :
                # A repeat of the currently playing sound.
                # Don't play it more than once.
                return
            else :
                # Trying to play a different sound.
                # Wait on the current sound then play the new one.
                self.wait()

    self.curpath = path
    self.current = subprocess.Popen([ "/usr/bin/aplay", '-q', path ] )

Finally, it's a good idea when exiting the program to check whether any aplay process is running, and wait() for it. Otherwise, you might end up with a zombie aplay process.

    def __del__(self) :
        self.wait()

I don't know if xchat actually closes down Python objects gracefully, so I don't know whether the __del__ destructor will actually be called. But at least I tried. It's possible that a context manager might be more reliable.

The full scripts are on github at pyplay.py for the basic SoundPlayer class, and chatsounds.py for the xchat script that includes SoundPlayer.

Tags: , ,
[ 13:07 Oct 17, 2012    More programming | permalink to this entry | ]

Mon, 18 Apr 2011

A simple Python mixer (to solve a problem with sound in Natty)

I had to buy a new hard drive recently, and figured as long as I had a new install ahead of me, why not try the latest Ubuntu 11.04 beta, "Natty Narwhal"?

One of the things I noticed right away was that sound was really LOUD! -- and my usual volume keys weren't working to change that.

I have a simple setup under openbox: Meta-F7 and Meta-F8 call a shell script called "louder" and "softer" (two links to the same script), and depending on how it's invoked, the script calls aumix -v +4 or aumix -v -4.

Great, except it turns out aumix doesn't work -- at all -- under Natty (bug 684416). Rumor has it that Natty has dropped all support for OSS sound, though I don't know if that's actually true -- the bug has been sitting for four months without anyone commenting on it. (Ubuntu never seems terribly concerned about having programs in their repositories that completely fail to do anything; sadly, programs can persist that way for years.)

The command-line replacement for aumix seems to be amixer, but its documentation is sketchy at best. After a bit of experimentation, I found if I set the Master volume to 100% using alsamixergui, I could call amixer set PCM 4- or 4-. But I couldn't use amixer set Master 4+ -- sometimes it would work but most of the time it wouldn't.

That all seemed a bit too flaky for me -- surely there must be a better way? Some magic Python library? Sure enough, there's python-alsaaudio, and learning how to use it took a lot less time than I'd already wasted trying random amixer commands to see what worked. Here's the program:

#!/usr/bin/env python
# Set the volume louder or softer, depending on program name.

import alsaaudio, sys, os

increment = 4

# First find a mixer. Use the first one.
try :
    mixer = alsaaudio.Mixer('Master', 0)
except alsaaudio.ALSAAudioError :
    sys.stderr.write("No such mixer\n")
    sys.exit(1)

cur = mixer.getvolume()[0]
if os.path.basename(sys.argv[0]).startswith("louder") :
    mixer.setvolume(cur + increment, alsaaudio.MIXER_CHANNEL_ALL)
else :
    mixer.setvolume(cur - increment, alsaaudio.MIXER_CHANNEL_ALL)
print "Volume from", cur, "to", mixer.getvolume()[0]

Tags: , , ,
[ 21:13 Apr 18, 2011    More programming | permalink to this entry | ]

Sun, 13 Jun 2010

Enabling system beeps on Ubuntu Lucid

Update: though the rest of this article is still useful in explaining how to un-blacklist the pcspkr module, unfortunately that module works very erratically. Sometimes you'll get a beep, sometimes not. So this article may be a good start but it still doesn't explain why Ubuntu's kernels have such a flaky pcspkr module.

For years I've used Ubuntu with my own kernels rather than the kernels Ubuntu provides. I have several reasons: home-built kernels boot a lot faster (when I say a lot, I mean like saving 30 seconds off a one-minute boot) and offer more control over options. But a minor reason is that Ubuntu kernels generally don't support the system beep, so for example there's no way to tell in vim when you get out of insert mode. (In the past I've sometimes used the excellent fancy beeper module to play sounds, but I don't always want that.)

On Ubuntu's latest "Lucid Lynx", I'm using their kernel (so far). The Ubuntu kernel team has made huge improvements in boot time and number of modules loaded, so it's much more efficient than past kernels. But it did leave me without a beeper.

modprobe pcspkr failed to do anything except print the enigmatic:

WARNING: All config files need .conf: /etc/modprobe.d/00local, it will be ignored in a future release.
modprobe -v pcspkr (verbose) was no help -- it printed install /bin/true which didn't make anything clearer.

To get my beep back, I had to do two things:

First, edit /etc/modprobe.d/blacklist.conf and comment out the line blacklisting pcspeakr. It looks like this:

# ugly and loud noise, getting on everyone's nerves; this should be done by a
# nice pulseaudio bing (Ubuntu: #77010)
blacklist pcspkr
(They don't seem to be concerned about anyone who doesn't run Pulse, or about the various other bugs involved -- there's quite a laundry list in bug 486154.)

Secomd. pcspkr was blacklisted a second time in a different way, in that file so confusingly alluded to by the warning. /etc/modprobe.d/00local was apparently left over from a previous version of Ubuntu, and never removed by any upgrade script, and consisted of this:

install pcspkr /bin/true

Aha! So that's why modprobe -v pcspkr printed install /bin/true -- because that's all it was doing instead of loading the module like I'd asked.

So rm /etc/modprobe.d/00local was the second step, and once I'd done that, modprobe pcspkr loaded the module and gave me my system beep.

Tags: , ,
[ 14:02 Jun 13, 2010    More linux/kernel | permalink to this entry | ]

Sun, 11 Oct 2009

Silencing speaker hum

Ah, silence is golden!

For my birthday last month, Dave gave me a nice pair of Bose powered speakers to replace the crappy broken set I'd been using. Your basic computer speakers, except I actually use them primarily for a little portable radio I listen to while hacking.

Only one problem: they had a major hum as soon as I turned them on. Even when I turned on the radio, I could hear the hum in the background. It got better if I turned the speakers way down and the radio up -- it wasn't coming from the radio.

After about a month it was starting to irritate me. I mentioned it on #linuxchix to see if anyone had any insights.

Maria and Wolf did, and narrowed it down pretty quickly to some sort of ground problem. The speakers need to get a real ground from somewhere. They don't get it through their AC power plug (a two-prong wall wart). They also don't get it from the radio, which is plugged in to AC via its own 2-prong wall wart, so it doesn't have a ground either.

How could I test this? Wolf suggested an alligator clip going from one of the RCA plugs on the back of the speaker to my computer's case. But it turned out there was an easier way. These speakers have dual inputs: a second set of RCA plugs so I can have another cable going to an MP3 player, radio or whatever, without needing to unplug from the radio first. I ran a spare cable from these second RCA plugs to the sound card output of my spare computer -- bingo! The hum entirely went away.

I suppose most people buy this type of speaker for use with computers, so it isn't a problem. But I was surprised that they'd adapt so poorly to a portable device like a radio or MP3 player. Is that so uncommon?

Tags: ,
[ 21:49 Oct 11, 2009    More tech | permalink to this entry | ]

Wed, 17 Jun 2009

Random beeps

A couple of year ago I figured out how to make custom system beep sounds on Linux, like MacOS has done forever. But then I changed machines and somehow never got around to setting it up on any other machine.

But the Intel dual-Atom board doesn't seem to support a system beep -- there's no obvious place on the motherboard to plug in the connector going to the case speaker. How odd!

With the alternative being no beep at all, I dusted off my old blog post and went to see if Fancy Beeper Daemon kernel module still existed. Happily, it does, and it's up-to-date for current kernels, so all I had to do was download the latest and build it. Easy! Then I added "beep" to the list of automatically loaded modules in /etc/modules, blacklisted the pcspkr module using the /etc/modprobe.d/00local technique, and I was all set.

Except for the really important question: what sound to choose? I did a little web searching for free sounds and downloaded some samples to try out. Then I added a few bird calls from my Stokes Field Guide to Western Bird Songs CD, editing them in audacity to make them shorter and more appropriate for system beeps.

But I still couldn't decide on just one ... and why should I? I've really been enjoying my random wallpaper: every time I log in, I get a different desktop background. It's fun to see a new picture every day. Why not do the same for my system beep?

That's no problem, using the same randomline script I use for wallpaper. I just put this in my .xinitrc:

$HOME/bin/mybeepd `find $HOME/Music/beeps -name "*.wav" | randomline` &
and now I get a different beep sound each day.

Yesterday it was a loon. Today it's a cow mooing.

Tags: , ,
[ 21:11 Jun 17, 2009    More linux | permalink to this entry | ]

Wed, 03 Jun 2009

Bullfrogs in stereo

bullfrog The Walden West pond is hopping -- literally! This afternoon around 3pm the pond's resident bullfrogs, who normally just float quietly in the scum on the surface, would suddenly hop out of the water for no obvious reason, then settle back down a few feet away. One pair was apparently mating like that, the larger frog hopping onto the back of the smaller frog, then immediately off again. And the pond was full of sound, sometimes with two or more frogs booming at once. Bullfrogs in stereo!

I didn't have the SLR along, but some of the frogs were close enough (and calm enough not to submerge when we got near them) that I was able to get a few decent shots.

But I really wanted to capture that sound. So I put the camera in video mode and shot a series of videos hoping to catch some of the music ... and did. They sound like this: bullfrog (mp3, 24kb).

Despite the title of this entry, the recording doesn't have any interesting stereo effects; the only microphone was the one built in to my Canon A540. It did okay, though! You'll just have to use your imagination to place two frogs as you listen, one 20 feet to the left and the other 15 feet to the right.

How to extract the audio from a camera video

(Non open source people can quit reading here.)

Extracting the audio was a little tricky. I found lots of pages ostensibly telling me how to do it with mencoder, but none of them seemed to work. This did:

mplayer -vc null -af volume=15 -vo null -ao pcm -benchmark mvi_8992.avi

I added that -af volume=15 argument to make the sound louder, since it was a bit quiet as it came from the camera.

That produced a file named audiodump.wav, which I turned into an mp3 like this:

lame audiodump.wav bullfrogs.mp3

Tags: , , ,
[ 21:42 Jun 03, 2009    More nature | permalink to this entry | ]

Fri, 07 Dec 2007

Bug fixes? Why would we bother to ship bug fixes?

(A culture of regressions, part 2)

I've been running on Ubuntu's latest, "Gutsy gibbon", for maybe a month now. Like any release, it has its problems that I've needed to work around. Like many distros, these problems won't be fixed before the next release. But unlike other distros, it's not just lack of developer time; it turns out Ubuntu's developers point to an official policy as a reason not to fix bugs.

Take the case of the aumix bug. Aumix just plain doesn't work in gutsy. It prints, "aumix: SOUND_MIXER_READ_DEVMASK" and exits.

This turns out to be some error in the way it was compiled. If you apt-get the official ubuntu sources, build the package and install it yourself, it works fine. So somehow they got a glitch during the process of building it, and produced a bad binary.

(Minor digression -- does that make this a GPL violation? Shipping sources that don't match the distributed binary? No telling what sources were used to produce the binary in Gutsy. Not that anyone would actually want the sources for the broken aumix, of course.)

It's an easy fix, right? Just rebuild the binary from the source in the repository, and push it to the servers.

Apparently not. A few days ago, Henrik Nilsen Omma wrote in the bug:

This bug was nominated for Gutsy but does currently not qualify for a 7.10 stable release update (SRU) and the nomination is therefore declined. According the the SRU policy, the fix should already be deployed and tested in the current development version before an update to the stable releases will be considered. [ ... ] See: https://wiki.ubuntu.com/StableReleaseUpdates.

Of course, I clicked on the link to receive enlightenment. Ubuntu's Stable Release page explains

Users of the official release, in contrast, expect a high degree of stability. They use their Ubuntu system for their day-to-day work, and problems they experience with it can be extremely disruptive. Many of them are less experienced with Ubuntu and with Linux, and expect a reliable system which does not require their intervention.
by way of explaining the official Ubuntu policy on updates:
Stable release updates will, in general, only be issued in order to fix high-impact bugs. Examples of such bugs include:
  • Bugs which may, under realistic circumstances, directly cause a security vulnerability
  • Bugs which represent severe regressions from the previous release of Ubuntu
  • Bugs which may, under realistic circumstances, directly cause a loss of user data

Clearly aumix isn't a security vulnerability or a loss of user data. But I could make a good argument that a package that doesn't work ... ever ... for anyone ... constitutes a severe regression from the previous version of that package.

Ubuntu apparently thinks that users get used to packages not working, and grow to like it. I guess that if you actually fixed packages that you broke, that would be disruptive to users of the stable release.

I'm trying to picture these Ubuntu target users, who embrace regressions and get upset when something that doesn't work at all gets fixed so that it works as it did in past releases. I can't say I've ever actually met a user like that myself. But evidently the Ubuntu Updates Team knows better.

Update: I just have to pass along Dave's comment: "When an organization gets to the point where it spends more energy on institutional processes for justifying not fixing something than on just fixing it -- it's over."

Update: Carla Schroder has also written about this.

Tags: , ,
[ 11:21 Dec 07, 2007    More linux | permalink to this entry | ]

Sat, 08 Sep 2007

Custom beep sounds

It's always amazed me that Linux doesn't let you customize the system beep. You can call xset b volume pitch duration to make it beep higher or lower, or you can turn it off or switch to "visual bell"; but there's nothing like the way most other OSes let you customize it to any sound you want. (Some desktops let you customize the desktop alerts, but that doesn't do anything about the beeping you get from vim, or emacs, or bash, or a hundred other programs that run in terminals.)

Today someone pointed me toward a Gentoo wiki page that led me to Fancy Beeper Daemon. This is a small kernel module that adds a device, /dev/beep, which outputs a byte every time there's a beep. You can write a very simple daemon (several samples in Python are included with the module) which reads /dev/beep and plays the sound of your choice.

I downloaded beep-2.6.15+.tar.gz, but it needed one tweak to build it under 2.6.23-rc3: I needed to add

#include <linux/sched.h>
among the includes at the beginning of beep.c. After that, it compiled and installed just fine. Since it's a kernel module, it works in consoles as well as under X.

/dev/beep is created with owner and group root, and readable/ writable only by owner and group, so to test it I had to chmod 666 /dev/beep to run the daemon. I fixed that by making a file in /etc/udev/rules.d called 41-beep.rules, containing:

KERNEL=="beep", GROUP="audio"

Finally, based on the nice samples that came with the module, I wrote my own very simple Python daemon, playbeepd, that uses aplay.

Lots of fun! I don't know how much I'll use it, but it's good to have the option of custom beep sounds.

Tags: , ,
[ 21:47 Sep 08, 2007    More linux | permalink to this entry | ]

Thu, 28 Jun 2007

A Quartet of Workarounds

I upgraded my laptop's Ubuntu partition from Edgy to Feisty. Debian Etch works well, but it's just too old and I can't build software like GIMP that insists on depending on cutting-edge libraries.

But Feisty is cutting edge in other ways, so it's been a week of workarounds, in two areas: Firefox and the kernel. I'll start with Firefox.

Firefox crashes playing flash

First, the way Ubuntu's Firefox crashes when running Flash. I run flashblock, fortunately, so I've been able to browse the web just fine as long as I don't click on a flashblock button. But I like being able to view the occasional youtube video, and flash 7 has worked fine for me on every distro except Ubuntu. I first saw the problem on Edgy, and upgrading to Feisty didn't cure the problem.

But it wasn't their Firefox build; my own "kitfox" firefox build crashed as well. And it wasn't their flash installation; I've never had any luck with either their adobe flash installer nor their opensource libswfdec, so I'm running the same old flash 7 plug-in that I've used all along for other distros.

To find out what was really happening, I ran Firefox from the commandline, then went to a flash page. It turned out it was triggering an X error:

The error was: 'BadMatch (invalid parameter attributes)'.
(Details: serial 104 error_code 8 request_code 145 minor_code 3)

That gave me something to search for. It turns out there's a longstanding Ubuntu bug, 14911 filed on this issue, with several workarounds. Setting the environment variable XLIB_SKIP_ARGB_VISUALS to 1 fixed the problem, but, reading farther in the bug, I saw that the real problem was that Ubuntu's installer had, for some strange reason, configured my X to use 16 bit color instead of 24. Apparently this is pretty common, and due to some bug involving X's and Mozilla's or Flash's handling of transparency, this causes flash to crash Mozilla.

So the solution is very simple. Edit /etc/X11/xorg.conf, look for the DefaultDepth line, and if it's 16, that's your problem. Change it to 24, restart X and see if flash works. It worked for me!

Eliminating Firefox's saved session pester dialog

While I was fiddling with Firefox, Dave started swearing. "Why does Firefox always make me go through this dialog about restoring the last session? Is there a way to turn that off?"

Sure enough, there's no exposed preference for this, so I poked around about:config, searched for browser and found browser.sessionstore.resume_from_crash. Doubleclick that line to change it to false and you'll have no more pesky dialog.

For more options related to session store, check the Mozillazine Session Restore page.

Kernel: runaway kacpid

Alas, having upgraded to Feisty expressly so that I could build cutting-edge programs like GIMP, I discovered that I couldn't build anything at all. Anything that uses heavy CPU for more than a minute or two triggers a kernel daemon, kacpid, to grab most of the CPU for itself. Being part of the kernel (even though it has a process ID), kacpi is unkillable, and prevents the machine from shutting down, so once this happens the only solution is to pull the power plug.

This has actually been a longstanding Ubuntu problem (bug 75174) but it used to be that disabling acpid would stop kacpid from running away, and with feisty, that no longer helps. The bug is also kernel.org bug 8274.

The Ubuntu bug suggested that disabling cpufreq solved it for one person. Apparently the only way to do that is to build a new kernel. There followed a long session of attempted kernel building. It was tricky because of course I couldn't build on the target machine (inability to build being the problem I was trying to solve), and even if I built on my desktop machine, a large rsync of the modules directory would trigger a runaway kacpi. In the end, building a standalone kernel with no modules was the only option.

But turning off cpufreq didn't help, nor did any of the other obvious acpi options. The only option which stops kacpid is to disable acpi altogether, and use apm. I'm sorry to lose hibernate, and temperature monitoring, but that appears to be my only option with modern kernels. Sigh.

Kernel: Hangs for 2 minutes at boot time initializing sound card

While Dave and I were madly trying to find a set of config options to build a 2.6.21 that would boot on a Vaio (he was helping out with his SR33 laptop, starting from a different set of config options) we both hit, at about the same time, an odd bug: partway through boot, the kernel would initialize the USB memory stick reader:

sd 0:0:0:0: Attached scsi removable disk sda
sd 0:0:0:0: Attached scsi generic sg0 type 0
and then it would hang, for a long time. Two minutes, as it turned out. And the messages after that were pretty random: sometimes related to the sound card, sometimes to the network, sometimes ... GConf?! (What on earth is GConf doing in a kernel boot sequence?) We tried disabling various options to try to pin down the culprit: what was causing that two minute hang?

We eventually narrowed the blame to the sound card (which is a Yamaha, using the ymfpci driver). And that was enough information for google to find this Linux Kernel Mailing List thread. Apparently the sound maintainer decided, for some reason, to make the ymfpci driver depend on an external firmware file ... and then didn't include the firmware file, nor is it included in the alsa-firmware package he references in that message. Lovely. I'm still a little puzzled about the timeout: the post does not explain why, if a firmware file isn't found on the disk, waiting for two minutes is likely to make one magically appear.

Apparently it will be fixed in 2.6.22, which isn't much help for anyone who's trying to run a kernel on any of the 2.6.21.* series in the meantime. (Isn't it a serious enough regression to fix in 2.6.21.*?) And he didn't suggest a workaround, except that alsa-firmware package which doesn't actually contain the firmware for that card. Looks like it's left to the user to make things work.

So here's what to do: it turns out that if you take a 2.6.21 kernel, and substitute the whole sound/pci/ymfpci directory from a 2.6.20 kernel source tree, it builds and boots just fine. And I'm off and running with a standalone apm kernel with no acpi; sound works, and I can finally build GIMP again.

So it's been quite a week of workarounds. You know, I used to argue with all those annoying "Linux is not ready for the desktop" people. But sometimes I feel like Linux usability is moving in the wrong direction. I try to imagine explaining to my mac-using friends why they should have to edit /etc/X11/xorg.conf because their distro set up a configuration that makes Firefox crash, or why they need to build a new kernel because the distributed ones all crash or hang ... I love Linux and don't regret using it, but I seem to need workarounds like this more often now than I did a few years ago. Sometimes it really does seem like the open source world is moving backward, not forward.

Tags: , , , , ,
[ 23:24 Jun 28, 2007    More linux | permalink to this entry | ]

Wed, 01 Jun 2005

How to save ALSA sound volume

During Debian upgrades over the last few months, apparently my system's alsa and aumix scripts had a little private battle for control of the mixer, and alsa won. The visible symptom was that my volume was always at 0 when I started up.

I tried re-enabling the aumix script in /etc/init.d, which had previously controlled my default volume, but it just said "Saved ALSA mixer settings detected; aumix will not touch mixer."

The solution, in the end, was to remove /var/lib/alsa/asound.state, set the volume, and run alsactl store. Someone suggested that I use chattr -i to make the asound.state file inviolable; it isn't on an ext2/3 filesystem, so that isn't a solution for me, but if my volume goes wonky again at least I know where to look.

Tags: , ,
[ 10:40 Jun 01, 2005    More linux | permalink to this entry | ]

Mon, 05 Jul 2004

midi/Timidity working!

Cool -- moray on #debian-women worked with me a little on midi, and I finally have timidity working!

The trick is that there's a tarball I downloaded a while ago (which means I no longer have the url, but I think it was linked from the main timidity site) which extracts into a timidity.cfg and an "instruments" directory (plus a couple of readmes that don't explain anything, which is why I hadn't unpacked this before). Turns out that if you put that .cf into /etc/timidity.cfg, then add a line that says

dir /usr/local/share/timidity
(or wherever you unpack the tarball, as long as it contains "instruments") then voila, timidity starts working.

That means not only that I can play .midi files (big whoop), but, more important, I can use all those programs like denemo that come in the education-music package, plus all the java music API stuff like the CA music code (for some reason, all these things output only midi!)

So I played with denemo a little (found out how to enter a chord) in brief spurts, between long interludes in the house because it's TOO HOT out here in the office.

Tags: ,
[ 20:00 Jul 05, 2004    More linux | permalink to this entry | ]