Pogo Player

download Pogo Player

If you can't read please download the document

description

PogoPlug + sound card converted to music player

Transcript of Pogo Player

PogoPlayerA few years ago I purchased a SqueezeBox Boom to use as an alarm clock. The old alarm clock had the usual features. It had an AM/FM tuner, a tape deck, and dual alarmand a snooze button. It was your typical nightstand alarm clock. Switching to the Boom was wonderful. The sound is excellent (for a an alarm clock), it has an unlimited number of custom alarms, it streams music over WiFi from my nearly 10,000 track music collection, and it has a snooze button. On the other hand, it's not the most user-friendly device in the world... not by a long shot. Even after sleeping beside it for years my wife still cannot figure out how to set the alarm. But as you will later learn, I am not afraid to dig into the technical side things, so I haven't minded setting the alarm for herand she can use her iPhone in a pinch. So we are happy with our SqueezeBox.

Things got even better when I found an iOS app to control the SqueezeBoxnow it is possible to queue up playlists and adjust the volume with my iTouch. I've always dreamed of having speakers throughout the house all attached to the media library on the main computer. Things were starting to look promising; I thought it was about time to add another SqueezeBox to the network. Much to my dismay I learned that Logitech had discontinued the SqueezeBox line. A quick check on Amazon and eBay revealed sky-high prices on used SBTouch and SBReceiver. I momentarily entertained the idea of selling the Boom. That was when I found the Logitech SqueezeBox replacement for under $30 (http://vortexbox.org/content/149-Logitech-SqueezeBox-replacement-for-under-30).

The family room needs a pair of speakers, both for listening to music and for watching movies. The current setup involves carting an old set of computer speakers over to the TV for movies. Better than the built-in speakers on the TV for sure, but it's ugly, does not sound great, and sorely lacks in the convenience department. A cable can be used with the same old computer speakers to play music from the iTouch, but again, very inconvenient and ugly. Who wants to have an iTouch tethered to a set of old computer speakers in the corner?

I made a plan:

Build a PogoPlug-based SqueezeBox

Buy a pair of high quality speakers

The primary objective was to have a not-too-shabby-sounding music streaming system. I figured I'd improvise from there, possibly buying more hardware if necessary, to pipe in the TV. A bit more research turned up the components (approximate prices):

PogoPlug E02 $20

Behringer UCA-202 USB DAC $30

USB WiFi dongle $10

4GB USB drive had one laying around

Mackie HR624 powered studio monitors a lot more than the rest...

As you can see, the sound card alone cost $30, so it didn't quite live up to the under $30 claim, but apart from the speakers it is still far cheaper than buying a Squeezebox, even at the price before they were discontinued. After a few days waiting for things to arrive, packaging materials strewn about the office, and way too many late nights researching PogoPlug boot loaders, Linux WiFi drivers, ALSA configurations, and kernel memory leaks we have high quality music in the family room at last.

Now for the rest of the story, some of the gory technical details of what it took to build this thing. If you don't want to build your own Linux-based music system then you probably will want to stop here.

Building the PogoPlayer

Debian Wheezy on the PogoPlug

WiFi

ALSA, UCA-202, squeezelite

ShairPort

Source switch and volume controller app for mobile device (or any web browser)

This is not a complete how-to guide. Rather, it points to resources I used and issues that I ran into along the way. I hope others will find it to be a useful reference for building a Linux-based music appliance.Debian Wheezy on the PogoPlugInitially I installed Andrew's VAMP image from the SqueezeBox for under $30 tutorial. Many thanks to Andrew for writing that tutorial. Without it, I doubt I would have started this project. But the VAMP does not support my WiFi dongle (at least not out of the box). I also didn't like the find-my-VAMP web service, which periodically dialed home to myvortextbox.com with the IP and MAC addresses of my PogoPlug. In hindsight I realize that the WiFi driver probably could have been installed in the VAMP image, but my initial research led me to believe that I needed a 3.x Linux kernel. Many hours on the Debian forums over at doozan.com finally got me up and running on Debian Wheezy with a 3.2.0 kernel.

Enable SSH on the PogoPlug and flash the uBoot. WARNING be sure to have your PogoPlug connected to a stable power source while flashing the uBoot. If it loses power during that process it could become your next door stop (bricked). The forums at doozan.com, especially davygravy's post on uBoot, should have all the information you need in this department.

My PogoPlug would not boot with a 3.x kernel when I set the machid uBoot environment variable. I spent hours trying to figure this out. With netconsole enabled I could watch the device start up and load the kernel and initramfs images, but then it would just hang. There were no boot logs in /var/log, so I had no idea what was causing it to hang. It was only through trial and error that I finally arrived at the solution, which was to remove machid from the uBoot environment.

fw_setenv machid # clear machidfw_setenv arcNumber 3542 # set arcNumber

I also set the arcNumber to 3542, the appropriate value for my E02 PogoPlug, which is supposed to make the light on the front change colors depending on the boot status (with appropriate scripts). It does not work though. I believe it would require a recompiled kernel to fix it, but it doesn't matter enough to me to go to that bother.WiFiGetting USB WiFi working was the next hurdle. I bought the Airlink Fully compatible Wireless N 150 Ultra Mini-USB Adapter (AWLL5099), which has the Realtek 8188CUS chipset. I would not recommend this WiFi dongle for others. The drivers were hard to setup and had a memory leak (more on that later). I ended up downloading and installing the driver from the RealTek website. Follow the installation instructions in the zip file. One disconcerting thing I noted on the download page is that the supported kernel versions were Linux Kernel 2.6.18~2.6.38 and Kernel 3.0.8 (it might have worked on the VAMP image after-all); as noted above I'm running a 3.2.0 kernel.

Some friends have since recommended Atheros chipsets as being more Linux-friendly. I hope you have better luck than I did, but I'm not complaining too loudly since the one I have does work in the end.

At first I could not connect to my WPA2-encrypted hotspot. I installed WPASupplicant and found the following configurations to work:

/etc/wpa_supplicant/wpa_supplicant.conf

ctrl_interface=/var/run/wpa_supplicantctrl_interface_group=0ap_scan=1

network={ssid="YOUR-WiFi-SSID"key_mgmt=WPA-PSKpsk=HEX-ENCODED-PASSPHRASEpriority=5}

/etc/network/interfaces

auto loiface lo inet loopback

auto eth0allow-hotplug eth0iface eth0 inet dhcp

auto wlan0allow-hotplug wlan0iface wlan0 inet staticwpa-conf /etc/wpa_supplicant/wpa_supplicant.confaddress 192.168.1.13netmask 255.255.255.0network 192.168.1.0broadcast 192.168.1.255gateway 192.168.1.1

As you can see I have configured a static IP address, which is fine for this application. For some reason DHCP would not work. My preference was to have my router's DHCP server assign the IP address rather than to have it hard-coded in the PogoPlug, but obviously it didn't work out that way.

I'm not sure if this next thing is a Linux configuration issue or an issue with my specific WiFi adaptor, but it does not seem to work when the wired NIC is connected and then disconnected. I did not take the time to debug this completely since I am primarily interested in using this device with WiFi only. I was happy and moved on once I realized that I could connect to SSH over WiFi if I booted the device with the wired NIC disconnected. It is important to wait long enough for WiFi and SSH to come up. I can ping the PogoPlug about a minute after applying power, but cannot connect to SSH for more than two minutes. This is not a big deal since it hardly ever needs to be rebooted, although a faster boot would be nice.

The final WiFi issue was not solved until much later when I had everything else up and running. I noticed that the PogoPlug would steadily run out of RAM over the course of a few days. At first I had no idea what it was. The usual suspects were not guilty: top showed no processes consuming huge amounts of memory, there were no tmpfs file systems growing without bound, but there was constantly less and less free memory. After about three or four days the OS would run out of RAM and freeze up. The only remedy at that point was to pull the power and reboot. I started digging into /proc/meminfo and slabtop since the symptoms were pointing at a kernel memory leak. I observed that SUnreclaim in /proc/meminfo was steadily increasing. As I understand it, this is unreclaimable memory consumed by the kernel. slabtop showed many objects named size-512 consuming lots of memory after a day or two. I had also noticed a lot of messages like this in dmesg:

[190125.169528] delay: estimated 134, actual 1[190127.151059] rtw_set_ps_mode(): Enter 802.11 power save mode...[190127.157037] delay: estimated 90, actual 310[190127.157073] rtl8192c_set_FwPwrMode_cmd(): Mode = 1, SmartPS = 2[190127.163118] delay: estimated 90, actual 1[190127.168515] delay: estimated 134, actual 1[190129.151055] rtw_set_ps_mode(): Busy Traffic , Leave 802.11 power save..[190129.157815] delay: estimated 46, actual 310[190129.158029] rtl8192c_set_FwPwrMode_cmd(): Mode = 0, SmartPS = 0[190129.167397] delay: estimated 222, actual 1[190131.150923] rtw_set_ps_mode(): Enter 802.11 power save mode...

Knowing that the WiFi driver did not explicitly support my kernel version, on a whim I decided to see if I could disable the power save mode. Maybe that was causing the memory leak? It was certainly generating a lot of dmesg churn, and it was happening constantly regardless of whether the machine was in use or sitting idle, which matched the pattern of decreasing free memory. A quick Google turned up RTL8192cu WiFi disable power save on forum.doozan.com again (that site has been a lifeline for this project). So I added a file:

/etc/modprobe.d/8192cu.conf

options 8192cu rtw_power_mgnt=0

Reboot. A lot less dmesg output and no more memory leak. Excellent!ALSA, UCA-202, squeezelite, ShairPortNext we move to audio. Linux sound is rather poorly documented, and therefore rather hard to configure if you don't know what you're doing. The initial goal was to play audio from squeezelite (a software Squeezebox client), and also play audio from the TV (line-in on the sound card).First Try: dmix and alsaloopMy first attempt was to use dmix to mix audio from the squeezelite service with the line-in on the UCA-202 sound card. Getting the squeezelite signal into dmix was relatively easy once I figured out the general structure of the ALSA configuration file. However, getting the line-in channel of the sound card to be passed through to the line-out channel was more challenging. I ended up using a program called alsaloop to send audio from an ALSA line-in device to another line-out device.

Sample rates were a problem though. For some reason the UCA-202 seems to prefer sampling the line-in at 48kHz (I didn't look into this much; maybe I did something wrong). Therefore, 44.1kHz audio, which is common for CD audio, will not play when alsaloop is running. This is just one example of conflicting sample rates. In this type of situation, if a particular audio stream was playing, the other process (alsaloop or squeezelite) would consume 100% CPU when started, and the audio coming out of the speakers would become very choppy or stop abruptly. Through more digging I learned that I needed a resampling plugin. I tried libsamplerate, but it did not seem to work. I don't know what was wrong with it, but the primary symptom was very high CPU usage and choppy or stopped audiosimilar to no resampling at all. Then I found the speexrate plugin, which worked very well. It used varying amounts of CPU depending on the quality setting, but most importantly it worked, which was progress. To use the speexrate plugin add one of the following lines to your asound.conf:

defaults.pcm.rate_converter "speexrate_best"defaults.pcm.rate_converter "speexrate_medium"defaults.pcm.rate_converter "speexrate"

Conceptually I realized that the mixing was unnecessary, but this setup had the advantage that any sound source (the TV connected to line-in or squeezelite) could be turned on and it's audio signal immediately started to play through the speakers. The disadvantage is that dmix requires that all incoming audio signals be resampled to a single common sample rate.Better solution: snd-aloop kernel module with alsaloopThe single sample rate requirement of dmix bothered me because I wanted to play audio at its native sample rate to avoid loss of quality due to resampling. Conceptually, I wanted a software source switch similar to the source switch you might find on an AV amplifier/tuner unit. At first I looked for a program to set on top of ALSA to do this. Ideally, I would like to control it from my iTouch...

After a few false starts, a bit of digging, and some inferring about how the ALSA config worked, I was able to achieve what I wanted: no resampling. This involved configuring the snd-aloop kernel module, which provides ALSA loopback devices. Each input source is routed into a loopback device. A config file is better than words do describe how this is done:

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# Hardware devices

# USB soundcard line output (playback)

pcm.line_out {type hwcard CODEC # UCA-202 usb soud card}

# USB soundcard line input (capture)

pcm.line_in {type hwcard CODEC # UCA-202 usb soud card}

# Channel bindings?

bindings {0 0 # from 0 => to 01 1 # from 1 => to 1}

# Hardware control device

ctl.mixer0 {type hwcard CODEC # UCA-202 usb soud card}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# Loopback capture devices (to be routed to line_out with alsaloop)

pcm.squeeze_loop {type hwcard Loopback # snd-aloop devicedevice 1subdevice 1}

pcm.airpogo_loop {type hwcard Loopback # snd-aloop devicedevice 1subdevice 2}

pcm.default_loop {type hwcard Loopback # snd-aloop devicedevice 1subdevice 0}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# Application plug devices# flow: app > PLUG > PLUG_loop > alsaloop > line_out## The Loopback devices allow us to use alsaloop as a source-switch, routing a# single audio source to line_out. Each app can connect and stream to its plug# device even when its audio is not being routed to line_out. This is done to# avoid dmix with its fixed sample rate and extra latency. With this setup,# sample rates are chosen by the source application and are unchanged all the# way to the sound card.

pcm.squeeze {type plugslave {# no softvol because SqueezeLite has a volume controlpcm "hw:Loopback,0,1"format S16_LE}}

pcm.airpogo {type plugslave {pcm {# softvol because level from shairport is too hightype softvolslave.pcm "hw:Loopback,0,2"control {name "AirPogo"card 0}}format S16_LE}}

# Plug to line_out with volume control. Enables a line-level device (with no# volume control of its own) to be routed from line_in to line_out without# annoying the neighbors.# flow: line_in > alsaloop > line > line_out

pcm.line {type plugslave.pcm {type softvolslave.pcm "line_out"control {name "Line in"card 0}}}

# Default device (is it needed?)

pcm.!default {type plugslave {pcm {type softvolslave.pcm "hw:Loopback,0,0"control {name "Default"card 0}}format S16_LE}}

A few notes about this setup:

The loopback devices allow the connected source (squeezelite, shairport, or line-in) to operate normally without being connected to the line-out device. Without them, a playing input source would cause high CPU load and general system instability if it was not connected to the line-out device. I'm assuming this is because ALSA did not know what to do with the audio stream? The loopback devices seem to know how to discard the input signal if there is nothing connected to the output end of the loop.

I put a softvol on line-in because my TV only has line-level outputs. This is annoying because I can't use the TV remote to control the volume. I had to come up with another solution (more on that later).

Likewise, a softvol on airpogo because ShairPort (AirPlay) volume could not be adjusted precisely at lower volume levels without it. It may be a bug in shairport? This is a hack, and definitely not ideal. It's sort of a trim control that makes it possible to adjust the volume on the client over a subset of the full volume range. Setting the level of the softvol control to approximately 50% allows AirPlay clients to adjust the volume at comfortable to quiet listening levels, but it doesn't give full range control to the client. This is acceptable since I'm not normally trying to blast the walls down.

If you've followed closely you'll notice that we're missing the connection between the loopback devices and the line out. Enter alsaloop. It is used like a cable to route output from a loopback device the line_out device. Because alsaloop is a process that can be started and stopped, it provides exactly what we need to implement the input source switch. And each source can play at its native sample rate since there is only ever one source routed through the line-out device. The line-out device automatically assumes the sample rate of the input source. Caveat: this works as long as the samplerate is supported by the sound card. The UCA-202 does not support sample rates above 48KHz, so I'd need to do some resampling for 96KHz audio, but I don't have any audio encoded at that sample rate.

I had some sound quality issues with squeezelite when I first got this setup. The symptoms were crackling and popping in the audio output, which seemed strange since it was using the proper sample rate all the way through ALSA. The issue was fixed by increasing the buffer time parameter from 20ms (the default) to 100ms. This is done by passing -a 100 when invoking squeezelite.ShairPort AirPlay receiverIn addition to the squeezelite Squeezebox client, I installed ShairPort [link] to allow audio streaming directly from any AirPlay capable device such as an iPhone, iPod Touch, or Mac OS X computer. The installation is a bit cumbersome and requires a lot of extra software to be installed including a compiler, etc. But it's super convenient and easy to use once setup. At first it had very poor sound quality, but I was able to fix this by setting period_time=100000 in /etc/libao.conf. It seems to work best when buffer_size and period_size are equal in /proc/asound/card1/pcm0p/sub2/hw_params (ShairPort must be playing audio to verify those numbers).Webctl the source switchFinally, the input source switch allows a user to choose which audio source should be routed to the line-out device. For this I wrote a little web app, which can be controlled by any computer with a web browser on my LAN. It's written with jQuery-mobile, so it works nicely on the iTouch.

SCREENSHOT HERE

The source for this app is available on GitHub. TODO link to webctl source, be sure to include custom init scripts. This is the final piece needed to make the iTouch into a universal remote: it can switch between music sources, play audio with iTunes/AirPlay, control Squeezelite with iPeng or the Logitech Squeezebox app for iOS, and adjust the TV volume.Resetting ALSA configurationIt was necessary to reset the ALSA configuration numerous times throughout the process of setting up and debugging the PogoPlayer. This is rather more tricky than it should be. Here is the command sequence to do that:

/etc/init.d/alsa-utils stopchmod -x /etc/init.d/alsa-utilsrm /var/lib/alsa/asound.staterebootchmod +x /etc/init.d/alsa-utils/etc/init.d/alsa-utils start

Changes to the ALSA configuration sometimes do not work as expected if you forget to do this procedure. The most common reason I had to do this was when I wanted to remove a device such as a softvol control.Known issues and annoyancesThe source switch is nice because it allows a single input source to play at a time. However it's inconvenient to open up a web browser to make the switch. My 4th generation iTouch is a little slow to launch a browser these days, so it takes longer than I'd like to do this. Luckily it's not something I do often, so it's mostly just an annoyance. A physical switch would be nice as well, but probably hard to implement. Auto-switching when an audio signal is detected could be nice if implemented correctly. Maybe I'll try to do that sometime.Various operations such as changing sample rate, input source, or turning off squeezelite cause a click in the outputsounds like the waveform got chopped. It's not terribly loud, but it doesn't sound nice. I'm not sure what would need to change to fix this issue. Maybe it's something in the way I have ALSA configured?Cannot listen to sample rates above 48KHz (or any rate not supported by the UCA-202). A different sound card that supports more/higher sample rates would be one way to fix this issue. Another solution would be to bring resampling back into ALSA, but that is likely going to be tricky (it should not resample when the rate is supported by the sound card); probably not an ideal solution.Latency is an issue when there's video involved. It is low enough that it's barely noticeable when watching TV, but it's not perfect. I looked into using JACK, but the PogoPlug does not have hardware floating point support, so that doesn't seem to be an option. Eliminating dmix definitely helped. I'd be interested in tips on how to reduce latency even more. Setting the various latency options for squeezelite and/or alsaloop below 50ms, or even 100ms in some cases, results in choppy sound and high CPU load. Maybe other adjustments are needed as well to bring latency down?Very high latency with AirPlay streams. ShairPort seems to have pretty nasty latency. Maybe I don't have it configured correctly? Maybe this is inherent in the AirPlay protocol? This is not an issue when listening to music in iTunes. But watching a video is agonizing. It would be nice if this could be improved, but it's not a high priority for me at this time.