Teaching your Asterisk phone system to control your music
Posted by Admin • Saturday, January 17. 2009 • Category: AsteriskEver since I built my Asterisk-based VOIP phone system I've been finding more and more interesting ways to put it to work. I mean, it's a business phone server, but it's got plenty of resources as it sits around waiting for a phone call. So I figured... why not be able to pick up the nearest phone, dial an extension, and be able to stop/start/skip my whole-house music? How about doing this from anywhere? OK this may seem odd to you, but you'd be surprised how often it saves you from having to get up and go find the remote or having to wake up a computer . OK you're not convinced that this is very useful. How about pausing music for a phone call? Automatically?
For those who don't know, Asterisk is a open source (and free) software PBX system. You know, the thing that IP phones connect to (you know, that little box you got from Vonage - it connects to something - well in my house, it connects to my server). It provides call routing and management, voicemail, IVR (menus), etc. In short, it's awesome. I mean, yeah, it's a little unintuitive at first, but it's not that complicated once you loosen up your old fixed programmatic thinking a little
So, here is the setup.
Asterisk controlling MPD
I have:
- Asterisk Server (just an old PC with a NIC)
- MPD server (happens to be the same PC, but doesn't have to be) with a sound card plugged into receivers in various parts of the house
- Some IP phones all around the house. Some are analog phones on ATA's like with Vonage, some are real IP phones. Now you don't need these if you just want to call into your system from your cell phone... but you may want to password-protect the extension
MPD is what I've consistently found to be the best software for playing music if you actually want to be able to control it from more than one place - it's a server process, so it is cleanly separated from the control UI by XML-RPC, and a variety of clients can control it. It does not need X running and starts with /etc/init.d. I'm not sure if it runs on Windows, but I'm sure it's available on every flavor of Linux. Besides, do you really want to entrust something as important as music to your windows machine? In my case, I'll am using mpc (command-line client) to control it. mpc does not need to be on the same machine (XML-RPC, remember?). I manage playlists and stuff using phpmp, but for this exercise I won't be doing that.
So if you have the music working, you should be able to run mpc start and mpc stop from just about any machine on your network. Let's move on to integrating it into Asterisk. Note that if you've got Asterisk@Home (or what do they call it now, TrixBox?) then you'll have to translate my rules into mouse clicks. I prefer to edit files myself
So here is the code from extensions.conf to make it all work. This of course assumes that mpc is installed on this machine, and the asterisk user can invoke it. Adjust the value of MPD_HOST below to the hostname of the machine running MPD. This recipe also makes it very easy to control several MPD boxes - just add 701 and point it at server2.
; This belongs in your internal extensions: exten => 700,1,Set(MPD_HOST=mymusicserverhostname) exten => 700,n,Goto(music,s,1) [macro-mpccontrol] ; ARG1 is MPD Hostname ; ARG2 is MPC Command ; ARG3 is MPC Command Description (for festival) exten => s,1,NoOp(Running mpc ${ARG3}) exten => s,n,Background(custom/music-${ARG3}) exten => s,n,TrySystem(MPD_HOST=${ARG1} mpc ${ARG2}) ; If you are having trouble, uncomment this and look in your /var/log/ to see if it's trying to work at all ;exten => s,n,TrySystem(logger MPD_HOST=${ARG1} mpc ${ARG2}) exten => s,n,Background(custom/music-done) [music] ; Requires ${MPD_HOST} to be set exten => s,1,Background(custom/music-greeting-${MPD_HOST}) exten => s,n,Set(TIMEOUT(response)=30) ; Set Response Timeout exten => s,n(musicrestart),Background(custom/music-input) exten => s,n,WaitExten exten => 1,1,Macro(mpccontrol,${MPD_HOST},prev,prev) exten => 1,n,Goto(s,musicrestart) exten => 2,1,Macro(mpccontrol,${MPD_HOST},play,play) exten => 2,n,Goto(s,musicrestart) exten => 3,1,Macro(mpccontrol,${MPD_HOST},next,next) exten => 3,n,Goto(s,musicrestart) exten => 4,1,Macro(mpccontrol,${MPD_HOST},volume -10,down) exten => 4,n,Goto(s,musicrestart) exten => 5,1,Macro(mpccontrol,${MPD_HOST},stop,stop) exten => 5,n,Goto(s,musicrestart) exten => 6,1,Macro(mpccontrol,${MPD_HOST},volume +10,up) exten => 6,n,Goto(s,musicrestart) exten => 0,1,Background(custom/music-control-off) exten => 0,2,Goto(internalextensions,s,1) ; //change this later to vars for where to go exten => ,1,Background(custom/music-instructions) exten => ,n,Goto(s,musicrestart) exten => i,1,Background(custom/music-unknown-command) exten => i,n,Goto(s,musicrestart) exten => t,1,Background(custom/music-control-timeout)
As you can see, I recorded some custom sounds to give me prompts, and you can do that too pretty easily as well, though they are of course optional. The last parameters to mpcontrol macro is the suffix of the sound file it should play to confirm what it's done. Alternatively, you can change the Background to Festival(${ARG3}) in the macro if you don't want to record these and you have Festival.
Here are the filenames of the custom sounds:
music-control-off.gsm music-control-timeout.gsm music-done.gsm music-down.gsm music-greeting-musicbox.gsm music-greeting-gremlin.gsm music-greeting-potato.gsm music-input.gsm music-instructions.gsm music-next.gsm music-play.gsm music-prev.gsm music-stop.gsm music-unknown-command.gsm music-up.gsm
It's pretty simple otherwise.
AEL2 Syntax
Since I upgraded to Asterisk 1.6 I've gradually reworked most of my dialplan using the AEL language. The code is more concise and certainly more readable. Here is the same thing in AEL:
context mpd { s => { Background(custom/music-greeting-${MPD_HOST}); Set(TIMEOUT(response)=30); musicrestart: Background(custom/music-input); WaitExten(); goto musicrestart; }; 1 => &mpdcontrol(${MPD_HOST},prev,prev); 2 => &mpdcontrol(${MPD_HOST},play,play); 3 => &mpdcontrol(${MPD_HOST},next,next); 4 => &mpdcontrol(${MPD_HOST},volume -10,down); 5 => &mpdcontrol(${MPD_HOST},stop,stop); 6 => &mpdcontrol(${MPD_HOST},volume +10,up); //disconnect 0 => Background(custom/music-control-off); }; macro mpdcontrol(host, command, label) { NoOp(Running mpc ${label}); Background(custom/music-${label}); TrySystem(MPD_HOST=${host} mpc ${command}); Background(custom/music-done); goto mpd,s,musicrestart; return; };
Entering the context: (Passing MPD_HOST allows me to control MPD on multiple machines)
701 => { Set(MPD_HOST=musicbox); //the hostname of the machine goto mpd,s,1; };
Pausing Music for a phone call
I don't want music paused during the call as much as I want it paused when the call comes in so that I can hear it. No, not hear it ringing... Hear the caller id. Huh???
OK yeah so I have Festival (text-to-speech) announce "Incoming call from So-And-So" when a call comes in, which gives the inhabitants some extra time to go find the phone, while the caller navigates the IVR menus and enters an extension.
But if music is playing, I won't hear this at all. So I wrote a little shell script which I invoke like this:
exten => s,n,TrySystem(/usr/local/bin/speak-local-ensure-audible.sh ${ARG1} &)
({$ARG1} has the string I want to announce at this time}
Actually, this script does the speaking and the pausing all-in-one for simplicity:
#!/bin/sh if mpc status | grep "\[playing\]" ; then playing=true mpc pause fi curl http://SOME-BOX/localtools/speech.php -s --retry 0 --connect-timeout 1 --max-time 1 --form-string "string=$*" sleep 15 if [ "$playing" == "true" ] ; then mpc play fi
Long story on why I use HTTP to access the speech engine, but the important thing is what this does:
- Pauses if playing
- Speaks what needs to be spoken
- Gives it 15 seconds
- Resumes playback if it was playing
Clearly not a foolproof concept, but it's worked fine for a year so far.
0 Comments
Add Comment