Thursday, April 22, 2010

Twitter Audio Update

I decided a while ago to take down the radio station. I wasn't really getting any listeners, so I decided that it would be one less thing to try to keep up. If you are interested in continuing the project, and would like detailed scripts to do it, message me on twitter @POTUSCamacho

Saturday, April 10, 2010

Linksys NSLU2 Utilizing Bluetooth Audio

I was posting on Twitter and got a request from @m1n1m3 to cover this topic. The requirements are a Linksys NSLU2, a linux compatible usb bluetooth adapter (I use IOGEAR GBU211 or GBU421), a bluetooth headset (any should do), and a usb thumb drive (4GB or higher). This is for mono audio on a bluetooth headset. This does not cover a stereo configuration, though it should be possible.


To get started, follow the directions at https://wiki.ubuntu.com/ARM/NSLU2 to get your base Ubuntu system installed on the NSLU2. I have used other alternative firmwares in the past, but not for this particular project. The Debian install should work, but I cannot verify it. Note that it will take a while to complete the install. Also, if your NSLU2 is of a certain manufacture date, I strongly suggest that you follow the overclocking hack here: http://www.nslu2-linux.org/wiki/HowTo/OverClockTheSlug I have only run the NSLU2 at 266Mhz.


Once you have installed Ubuntu, log in and update your packages:

sudo -i apt-get update

Then install the following packages:

sudo -i apt-get install linux-sound-base bluetooth bluez bluez-alsa bluez-btsco bluez-compat bluez-utils python python-bluez vlc

Note: I do alot of other development stuff on my unit. I may have left off something, and if so let me know if you try it and it doesn't work.


Now that the needed packages are installed, an .asoundrc needs to be created in your user home directory. An example:

pcm.bluetooth {
type bluetooth
device 00:00:00:00:00:00
}

Edit the device section with the MAC address of your bluetooth headset. To find this, issue a reset of the bluetooth adapter by issuing the command:

hciconfig hci0 reset

Then place your headset in pairing mode and issue the following command:

sdptool browse

Now copy the following python script into a file named pair.py:

#!/usr/bin/python

import gobject

import sys
import dbus
import dbus.service
import dbus.mainloop.glib

class Rejected(dbus.DBusException):
_dbus_error_name = "org.bluez.Error.Rejected"

class Agent(dbus.service.Object):
exit_on_release = True

def set_exit_on_release(self, exit_on_release):
self.exit_on_release = exit_on_release

@dbus.service.method("org.bluez.Agent",
in_signature="", out_signature="")
def Release(self):
print "Release"
if self.exit_on_release:
mainloop.quit()

@dbus.service.method("org.bluez.Agent",
in_signature="os", out_signature="")
def Authorize(self, device, uuid):
print "Authorize (%s, %s)" % (device, uuid)
authorize = raw_input("Authorize connection (yes/no): ")
if (authorize == "yes"):
return
raise Rejected("Connection rejected by user")

@dbus.service.method("org.bluez.Agent",
in_signature="o", out_signature="s")
def RequestPinCode(self, device):
print "RequestPinCode (%s)" % (device)
return raw_input("Enter PIN Code: ")

@dbus.service.method("org.bluez.Agent",
in_signature="o", out_signature="u")
def RequestPasskey(self, device):
print "RequestPasskey (%s)" % (device)
passkey = raw_input("Enter passkey: ")
return dbus.UInt32(passkey)

@dbus.service.method("org.bluez.Agent",
in_signature="ou", out_signature="")
def DisplayPasskey(self, device, passkey):
print "DisplayPasskey (%s, %d)" % (device, passkey)

@dbus.service.method("org.bluez.Agent",
in_signature="ou", out_signature="")
def RequestConfirmation(self, device, passkey):
print "RequestConfirmation (%s, %d)" % (device, passkey)
confirm = raw_input("Confirm passkey (yes/no): ")
if (confirm == "yes"):
return
raise Rejected("Passkey doesn't match")

@dbus.service.method("org.bluez.Agent",
in_signature="s", out_signature="")
def ConfirmModeChange(self, mode):
print "ConfirmModeChange (%s)" % (mode)

@dbus.service.method("org.bluez.Agent",
in_signature="", out_signature="")
def Cancel(self):
print "Cancel"

def create_device_reply(device):
print "New device (%s)" % (device)
mainloop.quit()

def create_device_error(error):
print "Creating device failed: %s" % (error)
mainloop.quit()

if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

bus = dbus.SystemBus()
manager = dbus.Interface(bus.get_object("org.bluez", "/"),
"org.bluez.Manager")

if len(sys.argv) > 1:
path = manager.FindAdapter(sys.argv[1])
else:
path = manager.DefaultAdapter()

adapter = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Adapter")

path = "/test/agent"
agent = Agent(bus, path)

mainloop = gobject.MainLoop()

if len(sys.argv) > 2:
if len(sys.argv) > 3:
device = adapter.FindDevice(sys.argv[2])
adapter.RemoveDevice(device)

agent.set_exit_on_release(False)
adapter.CreatePairedDevice(sys.argv[2], path, "DisplayYesNo",
reply_handler=create_device_reply,
error_handler=create_device_error)
else:
adapter.RegisterAgent(path, "DisplayYesNo")
print "Agent registered"

mainloop.run()

#adapter.UnregisterAgent(path)
#print "Agent unregistered"

Now place your headset into pairing mode again and issue the follow command:

sudo -i python pair.py hci0 00:00:00:00:00:00

Once again replacing the address above with your headsets MAC.


The last step involves editing the vlcrc. To do this, issue the command:

pico .vlc/vlcrc

Scroll to the end and edit the entry that says alsadev=(default soundcard) to:

alsadev=bluetooth

If it does not exist then create it.

Now you should be ready to listen to audio. To listen to the audio from NASA TV's online video stream, issue the command:

cvlc --aout-rate 8000 --novideo http://www.nasa.gov/55644main_NASATV_Windows.asx

If everything is working you should hear audio. Keep in mind that the sample rate needs to be 8000Hz since that is the sample rate the headset can handle.


To pair another headset, or to re-pair the current, the /var/lib/bluetooth/(your MAC)/linkkeys file must be deleted. It will regenerate once you have paired the devices. Also remember that the .asoundrc file must be edited to match a different headset as the MAC will be different.

Other things I have done in the way of bluetooth that have worked has been using a TCP/IP piconet using pand. Since I have been unable so far to get a Belkin 802.11 usb wireless adater to work, it is useful as a wireless connection. I have connected 2 GBU211 adapters to a Targus ACH7405US travel usb hub, with one connecting to a computer via pand and the other providing bluetooth audio. I tried for 3, but the power draw was too much. I have tried to use the NSLU2 as a bluetooth to cellular bridge via pand, ip forwarding, iptables, rfcomm, and wvdial, but the version of wvdial for NSLU2 Ubuntu (and I believe Debian) crashes when connecting. Linphonc will work if you use wav files as soundcards. I have been trying to get a bluetooth headset to work (which I have no issues with on x86), but I have been unable to make calls. I have yet to try a bluttooth hands free speaker phone, but I would imagine that it would work the same.

Information on bluez-audio can be found here: http://wiki.bluez.org/wiki/HOWTO/AudioDevices I can't remember where exactly I found the pairing script. Let me know where to find it and I will post a link. ( Update: It's called simple-agent and I found it here: http://git.kernel.org/?p=bluetooth/bluez.git;a=blob_plain;f=test/simple-agent;hb=HEAD ) If you would like more information on this project or others contact me via http://twitter.com/POTUSCamacho

Friday, December 11, 2009

Audio based interfaces for Twitter Part 3

At this point, I have the ability to listen to my /home feed on a rotation of my choosing, as well as @public_timeline, and have made an online radio station to explain/share the experience. The very night that I implemented the bluetooth audio feed of my /home feed, I said to myself: "How great would it be if I could actually tweet via voice?". I already knew what the state of speech to text offerings for Linux which was there really isn't much out there from what I could tell. Then on November 10th, I saw the following post on http://www.hackaday.com: http://hackaday.com/2009/11/10/voice-controlled-led-sign It took me about a minute to think of an easy way to do the same thing except send the message to Twitter.


Thanks to the generosity of someone I chat with on Twitter, I was able to get a Google Voice account. I set up the Google Voice account and sent it to my cell phone. I tested it by leaving a voicemail. Sure enough it had emailed a copy of the transcription to Gmail. Next I set up a mail forward to a free mailbox on my ISP. I then configured Getmail to check and remove any messages from my isp. I then set up a Bash script to output the file to a generic name, run sed to edit out everything that is not text, create a variable exectweet="`cat tweet`" , and insert the variable into the status portion of the curl post, delete any messages in the inbox (rm ~/inbox/*), sleep for two minutes, and then check for messages and repeat. The reason for sleeping two minutes is to allow Google Voice enough time to transcribe the message and Gmail to forward the message to my ISP mail. It worked. Sort of.

There was no flaw in my aggregation or editing in the script. This part worked as expected. The let down came with the accuracy of Google Voice. It isn't. You can view two days worth of weird status updates at my test account http://twitter.com/mcteststein. After those last two messages came through clean, I connected the script to my POTUSCamacho account and threw caution to the wind. I tweeted blind every once in a while. I did not check the accuracy of the messages. When I got home and checked them, they ranged from close but not quite to way off base. I have since "mothballed" any further research into using this as a way to tweet for the time being. Some things are too good to be true sometimes.

I would like to quickly cover some auto status update work I have done on Twitter that runs the status posts of http://twitter.com/timelineradio. To do this I need to tangent for just a second to two simpler projects I have on Twitter. They are http://twitter.com/cl0ckbot and http://twitter.com/baitbot Cl0ckbot was just a simple bot to see if I could throw one together quickly. I actually keep it running because it serves as nice time hash marks for checking Twitter via the web. I decided to make baitbot to see how much spam I could catch with a simple bot. Baitbot repeats a set of ten different programmed responses on rotation for forty minutes of every hour (This is done to stay below the API limit of 1000 tweets a day which works out to be 41.6 possible every hour). The responses are absurd comments with a popular word or two sprinkled in and a warning message not to follow it. What is interesting about the bot is it repeats the exact same message (sort of) more than once a day (which Twitter will allow you to do). The trick to this is in this line of a status variable in the baitbot script: `date +%S%M` For those that don't know, I used that to sprinkle in the seconds and the minutes of the current hour allowing for a changing two digit number, that does not repeat in a day, in every status message. It makes it "different" as far as Twitter is concerned. I adapted this technique to make an automated status script to advertise the radio station on twitter. The script runs every five minutes of every hour. I did not want it to post constantly, just enough to make it hit in a search for the @public_timeline.

I am going to wrap up the series in saying that I will continue to improve upon my scripts and the overall quality of the radio station portion of the project. If you have comments and suggestions pleased find me on Twitter. If I have interested you, I suggest taking a look at the Twitter API. It really is so easy a cave man could do it.

Stay tuned for more projects and musings.

Wednesday, December 9, 2009

Audio based interfaces for Twitter Part 2

After I came up with the idea of creating a radio station from an audio stream of http://twitter.com/public_timeline, I started working on a solution to make it work. First thing I need was a free hosting service. I didn't really expect to find anything, but lo and behold I found two immediately. After some consideration, I decided to go with http://www.listen2myradio.com. They support Shoutcast streaming and their site is ad supported with an option for a premium account.

Now with a host in mind and a format, I now had to figure out how to make my scripts work with a Shoutcast source client. I went ahead and used the Nullsoft sc_trans binary since I was familiar with it. My first thought of playing the audio from my server and have sc_trans pick it up via /dev/dsp failed miserably. The sc_trans binary is older than dirt at this point, and on Ubuntu Server 8.04.2 sc_trans would lock up when /dev/sdp was set as the source. Then I got another idea. I decided to add a step in the script that would convert the wav to mp3 using LAME. This added a marginal processing time of of approximatly 2 secs on average per RSS pulled. The total processing time now was up to about four to six seconds. Throwing Shoutcast into the mix did change things though. With the bluetooth system, once a wav file had reached the end, it would disconnect audio from the headset and then repeat the cycle. The Shoutcast source streamer is going to stream the audio based on a playlist. In the playlist configuration, it is not designed for a live feed and I can't capture /dev/dsp. Since this is Linux we are talking about it ended up not really being a problem. The aggregation and audio converstion script will ouput an mp3 with a single filename. sc_trans can be playing that file and if overwritten, the new audio immediatly begins playing. It's a hack for sure, but it doesn't sound bad. The aggregation script had to be changed to run every minute and sleep since there would be nothing to make it pause in the course of the script.

Once everything was in place I tested it. It worked as well as expected. I let it run for a while to see how long it would stay up. The answer was about a day or two. Reasons varied from sc_trans having a segmentation fault to full system lockup. Occasionally sc_trans would be fine. The aggregation and conversion script would be running, but the stream from the net would be silent. One reboot everyday or two would be a short term solution till a better system was devised, but that was a problem. I have a virtual machine on that server that hosts some services for an expiremental IPV6 overlay network. Some of the services in that virutal machine have to be brought up manually. This made doing this a chore. My answer was to setup a speprate VM for the radio station broadcasting software. Now all that is done in a Ubuntu 9.04 server running in VirtualBox. Cron was rebooting the VirtualBox server 4 times a day until a few days ago when I set it for 12 am and 12 pm. It takes approxiamly 2 minutes from reboot to audio. Occasionally I am getting unknown Shoutcast server disconnects, but after reading logs I think it is my stream host shutting down it's end due to an error caused at the host. The station's twitter page is here: http://twitter.com/timelineradio The bit.ly link will take you to the audio streams page.

In "Audio based interfaces for Twitter Part 3" I will cover the mechanisms for advertising the service so that it is picked up in the public timeline and my attempt to use Google Voice transcription as a speech to text conversion tool.



Monday, December 7, 2009

Audio based interfaces for Twitter Part 1

Back in July I decided to join Twitter. My main motivation for joining is that I hate sms messaging and most of my friends prefer to communicate that way. I figured it would be a happy medium for communication. Since then I have found that Twitter is useful for all kinds of things.

For those that don't know, Twitter's API is well documented and can be interfaced with extremly easily. Most feeds on Twitter can be pulled in RSS format, and that gave me an idea. I decided to make an audio interface for my http://twitter.com/home feed. To accomplish this I made a Bash script that does the following: Curl requests my http://twitter.com/statuses/friends_timeline/(status #).rss and saves it to a file. Next I use sed to strip any unnecessary data from the file such as HTML or XML code etc. Once the file is edited, I use a text to speech program called espeak to convert the text to a wav file. Lastly the wav file is played by mplayer and ouput to a bluetooth headset via the alsa bluetooth option.

The script was set up to sleep for fifteen minutes, convert the data, and then run again. It worked as I had planned. After about a week I had another idea. I realized that http://twitter.com/public_timeline RSS feed could be converted in the same fashion. So I did. This time I had to modify some things.

I copied the origianl script and modified it. The first modification I made was to have this script run for fifteen minutes then sleep. This basically filled the silent gap the Home script left. The second was something that I made shortly after listening to the audio. Things like " as pure text show up as decimal notated Unicode, so anything that is not readable text gets filtered. Eventually I made the same edit to my original Home audio generating script. The only other edit to this new script was the credentials asking for the data (made a test account) and the url to the RSS data which is: http://twitter.com/statuses/public_timeline.rss , and lastly changed the filenames that espeak, sed, and curl output.

Now pleased with my bizarre creation I began telling people about it. With this I ran into a problem. It was really hard to explain it to someone face to face, but it was even harder explaining it in 140 characters. After a couple months I got another idea. I was going to broadcast the audio via an online radio station.


In "Audio based interfaces for Twitter Part 2", I will talk about the the things needed to make the radio station work, and some of the on going difficulties I still have. If you want to go ahead and preview the audio then click here If the station is down for some reason, try again latter as I am still working out some issues.