Instagramophone

Instagram takes your photos and makes them look bad, because vintage is trendy. Instagramophone imagines what would happen if phone calls worked the same way. My final project for Redial, Instagramophone* is a service that lets a user choose from five different vintage audio filters to apply to their voice. You can try it out for yourself by calling (360) 215-1975 (edit: I took down the server that ran this, so the number no longer works).

Callers are asked to choose one of five different filters for their voice, all inspired by vintage sound media. The choices are a wax cylinder, an LP record, an FM radio, a cassette tape and scrambled porn. After recording their message, the effect is applied and played back. Callers then have the option of uploading their sound to Soundcloud.

The application was built in Asterisk, with SoX to do the audio processing and Ruby to upload to Soundcloud. When a user records their message, Asterisk sends the recorded file in to a shell script that does the necessary slicing and dicing to create my desired effect. The cassette tape filter, for example, looks like this:

1
2
3
4
5
6
7
8
#split the source recording and apply bends up and down
sox $1 /projects/instagramophone/temp/$tempConversionDir/bend.wav trim 0 1 bend .25,300,.25  .25,-300,.25 : newfile : restart

#recombine the split files
sox /projects/instagramophone/temp/$tempConversionDir/*.wav -c1 /projects/instagramophone/temp/$tempConversionDir/mixdown.wav

#add cassette tape sound effects to beginning and end
sox -c1 /projects/instagramophone/static/wav/cassette.wav /projects/instagramophone/temp/$tempConversionDir/mixdown.wav -c1 /projects/instagramophone/static/wav/cassetteend.wav -r 8000 -c1 /projects/instagramophone/messages/altered/$sourcefile

The scrambled porn filter is a little more complicated. After watching this clip of scrambled pay-per-view (SFW), I wanted to make my filter cut the audio in and out rapidly. I did that by splitting the recorded file in to many tiny chunks and alternating the volume up and down:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#split the file in to 0.1 second chunks
sox $1 /projects/instagramophone/temp/$tempConversionDir/trim.wav trim 0 0.1 : newfile : restart

declare -i counter=0

#loop through all the chunks
for i in /projects/instagramophone/temp/$tempConversionDir/*.wav; do
counter=$counter+1
filename=${i##*/}

#alternate high and low volume
if [[ $counter%2 -eq 0 ]]; then
sox -v2.0 $i /projects/instagramophone/temp/$tempConversionDir/$tempEffectDir/$filename
else
sox -v0.1 $i /projects/instagramophone/temp/$tempConversionDir/$tempEffectDir/$filename
fi
done

#recombine and add scrambled porn background
sox /projects/instagramophone/temp/$tempConversionDir/$tempEffectDir/*.wav /projects/instagramophone/temp/$tempConversionDir/mixdown.wav
sox -m -v1.5 /projects/instagramophone/temp/$tempConversionDir/mixdown.wav -v0.8 /projects/instagramophone/static/wav/scrambledporn.wav -r 8000 -c1 /projects/instagramophone/messages/altered/$sourcefile

Please call it up and leave a message!

*I searched the name and found that a similar project already exists with the name Instagramophone (link, GitHub), so I hope they don’t mind me using it as well. Both our projects use SoX to apply effects to the voice, but that’s about as much as they have in common.

Redial: AGI and Ruby

This week, Redial had me finally becoming familiar with Ruby. We used AGI (Asterisk Gateway Interface) to pull info from the web and have it do something over the phone. My application takes two zipcodes entered on the keypad and sends them to a web service that calculates the distance between them. That distance is then read back to the caller.

Super quick and dirty, no error handling or input validation. Here’s the extensions.conf – it takes the two zip codes and sends it to Ruby:

1
2
3
4
5
[zip]
exten => s,1,Answer()
same => n,Read(zipA,"beep",5)
same => n,Read(zipB,"beep",5)
same => n,AGI(zip.rb,${zipA},${zipB})

And here’s zip.rb – it takes the zip codes, sends them to this zip code distance API, gets the XML response and tells AGI to read it back to the caller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/ruby

require 'rubygems'
require 'ruby-agi'
require 'net/http'
require 'rexml/document'

agi = AGI.new

zipA = ARGV[0]
zipB = ARGV[1]

url = "http://zipcodedistanceapi.cymi.org/rest/distance.xml/" + zipA + "/" + zipB + "/mile"
response = Net::HTTP.get_response(URI.parse(url)).body
doc = REXML::Document.new(response)
miles = REXML::Functions.number(doc.get_text('response/distance')).floor

agi.say_number(miles)