Adventures in Podcasting (with apologies to Shakespeare)

Bid the players make haste

I wanted to write this article to describe my attempts to create an “interesting” piece of technology using available frameworks. It all started when I recently decided to get myself an mp3 player. No run of the mill iPod for me. I had some very specific requirements. So, I created a features-benefits matrix to help me make a decision. Here it is:

Features

Benefits

USB 2.0 connectivity
  • Simple fast transfers
  • no additional software needed
  • can plug into any computer
  • function as a storage device for other files if necessary
Small (size of a pack of gum would be nice)
  • Easy portability
  • Store it in my pockets with all my other junk (wallet, phone, keys, etc.)
LCD Screen
  • So I can see what song is playing (this is why the iPod Shuffle is out)
Microphone
  • So I can record reminders to myself
At least 1 GB storage capacity
  • I don’t want to max out my mp3 player with Dark Side of the Moon!
Internal rechargeable battery
  • Who wants to keep running to the store for AA or AAA batteries?
FM Radio
  • Not a deal breaker, but would be nice
Equalizer/Presets
  • It’s really no fun listening to classical, jazz and heavy metal when they all sound the same!

Sound like a tall order? Well, maybe just a year or so ago this would have been as fanciful as sighting the Loch Ness monster on a UFO. However, I found that I had quite a few options to choose from. I reviewed a number of mp3 players from Samsung, Sony, Apple, Creative and iRiver, and then finally settled on the Cowon iAudio U2 player, which had not only a great set of features, but fantastic reviews online as well.

The Earl of Salisbury craveth supply…

Now that I had selected an mp3 player, I had to figure out what kind of content I wanted on it. While music was an obvious choice, there were also other sources of content that were available. I found http://www.americanrhetoric.com/ which featured numerous speeches in mp3 format that were interesting and sometimes amusing (cf: Nixon’s “Checkers” speech). Another site was the UBUWeb “365 days” project [http://www.ubu.com/outsiders/365/] that presented a collection of quirky and rare recordings. Finally, I discovered Podcasting.
Podcasting is a way to have mp3 content automatically downloaded to your computer using RSS. Podcasting clients such as iPodder can even sync this content with your mp3 device. (Note: this is a short introduction. More information can be found online, or in the Resources section).

Podcasting essentially consists of two steps:
1. A publish step, in which the creator (or publisher) of the content makes it available on a server.
2. A subscribe step, in which a subscriber uses a podcasting client to download the published material.

An overview of this process is shown in the diagram below:

Podcasting
Figure 1: Podcasting Overview

…which but to-day by feeding is allay’d

Podcasting provides mp3 fans with a whole new way of accessing content. Content providers such as bloggers, Internet radio stations and online news providers can create automatic feeds of their content for consumption by subscribers. The advantage of this format is that content is updated automatically by the client and can be downloaded to the mp3 device according to a specified schedule.

Discovering podcasting added a new dimension to my thinking about mp3 and its applications. I began to consider ways in which podcasting could be used to deliver various types of content to users. One of the ideas that occurred to me was to create a feed of incoming email in mp3 format. This would provide a way for you to take your email with you on your mp3 device and listen to it whenever you wanted. I started to think about the technology that would be required to make this idea work. Specifically, the following problems would need to be solved (the second column lists the technologies I identified to solve each problem):

Intercept and process email messages at the email server Apache James
Convert each email message to speech James mailet architecture + FreeTTS
Save the converted speech file to mp3 format Lame mp3 encoding engine
Generate the rss feed for the mp3 file of your email FreePod (use its libraries. FreePod is designed for existing RSS)
Publish both mp3 and rss files to a server Podifier (optional); or ftp/file copy
Access the feed from a client iPodder

Due to space limitations, I will not cover these technologies in depth. However, the Resources section points to online locations for more information.

Pretty too! What say you, James Soundpost?

Although each of the technologies listed above is important in addressing a piece of the puzzle, it is James that lies at the center of the processing logic. James provides pluggable components called mailets that are very similar to servlets in their architecture. In that famliar paradigm, mailets implement init(), destroy() and service() methods, and are subclassed from a base class called GenericMailet.
The service() method takes a parameter of type Mail that allows you to perform processing actions on a Mail item. James can be configured to load a set of mailets on startup. The James installation also comes with a number of mailets that allow an administrator to configure actions such as redirecting undeliverable mail, or adding a footer to an email message.
In order to generate an mp3 from an email message, we can write a mailet that will intercept each email received at the server, extract the text from it, and then use the FreeTTS (text-to-speech) libraries to synthesize the associated speech. This allows James to create sound posts (to borrow from Shakespeare) of email messages. The utility libraries packaged with FreePod contain the logic required to invoke the Lame mp3 encoder that is needed to actually generate the mp3 files. These mp3 files can then be stored on the server for use by the feed generator. Look at Listing 1 for the code for the service() method of such a mailet.

An overview of the approach (with the key API method calls) is shown below:

Email to MP3

Figure 2: Inside the code

I know this man well: he hath been since an ape-bearer; then a process-server

The final piece of the puzzle is to have a process serve the rss. For this, I visualized a servlet that would accept any requests for files ending in .rss. The servlet would incorporate any security requirements (for example, authenticating against LDAP tp gain access to the user’s mailbox) and then scan the appropriate spool directory for mp3 files and generate the podcast rss on the fly. Again FeedPod’s libraries can be used to access any functionality that has already been written.
iPodder could be configured to retrieve the feed from a pre-specified URL. Each user on the system could have a unique URL, or access the same URL after passing authentication parameters. When iPodder sends the request to the server, the servlet will process the request and return the content.

Give not this rotten orange to your friend

There are still additional issues to be resolved, namely:

  • The mailet code as written may not be thread-safe owing to the dependencies on other libraries. This needs to be tested and fixed if necessary (for example, James may process a second email while the first email is still being written to file as mp3).
  • Need to understand James’ classloading architecture so that the dependencies can be located in the correct libraries. Currently, external libraries can be placed in james/lib, james/bin/lib or james/apps/james/SAR-INF/lib. Not sure how (or in what order) James loads classes from each of these locations. Also, James is an Avalon application, which means that Avalon acts as the container. This has some implications here as well.
  • James has a mailet passthrough option that allows multiple mailets to handle the same email. This flag may need to be set.
  • Would like to remove dependencies on FeedPod and code directly against the underlying libraries
  • Need to create proper procedures to access the RSS feeds in a secure manner (so that each user accesses his or her own podcast)
  • Solve any integration issues between James and Tomcat (should the mp3 files be written to a web location, or should the RSS generator read from a mail spool directory?)
  • Handle multiple content types (ex: text/plain, text/html, etc.). Can probably use java.net.ContentHandler for this. Feedpod also has a Html2Text class that may be useful here.
  • The mp3 folder needs to be refreshed after a certain period (daily? weekly?) or the podcast needs to pick up only the last n files to avoid overwhelming clients and servers.
  • Need to invent a naming scheme for the generated mp3 files so that they are unique. Right now, it’s coded to use:
    sender@host.com_subject_date as the format.
  • FreeTTS can be improved with better voices and better voice modulation through some customization (ex: MBROLA). This would result in the generation of better sounding MP3’s.
  • etc.

If you are interested in working on this project, or in examining the source code in further detail, please let me know via email.

Great floods have flown from simple sources

Pease use the following for reference while exploring the technologies behind the solution discussed in this article:

Also see

Listing 1

public void service(Mail incoming) {
MailImpl email = (MailImpl) incoming;
try {
log(”in mp3 Mailet’s service method”); // Example to show how to log status messages

String outputFolder = “C:\\MP3″; // hard-coded for now; but can use input parameters
File temp = new File(outputFolder);
temp.mkdirs(); // create the directory if it doesn’t exist

// Extract the message portion of the email
MimeMessage emailMessage = email.getMessage();

// Extract identifying information
String fromAddress =
((InternetAddress)emailMessage.getFrom()[0]).getAddress();

// Generate the filename for the audio file
String basicFileName = (new StringBuilder()
.append(outputFolder)
.append(File.separator)
.append(fromAddress)
.append(”_”)
.append(emailMessage.getSubject())
.append(”_”)
.append(DateFormat.getDateInstance().format(emailMessage.getSentDate())).toString());

String mp3FileName = (new StringBuilder()
.append(basicFileName)
.append(”.mp3″)).toString();

String wavFileName = (new StringBuilder()
.append(basicFileName)
.append(”.wav”)).toString();

// Compose the IDv3 tag for the mp3 file
String title = emailMessage.getSubject();
if (title == null)
title = “No Title Available”;

String author = ((MailAddress) email.getSender()).getUser().toString();
if (author == null) {
author = “Ishi Mail2MP3″;
}
String year = (new StringBuilder()).append(”").append(
(new GregorianCalendar()).get(1)).toString();

String comment = “Ishi Mail2MP3″;
String album = “Email’s Greatest Hits”;

// Get the message body
String emailString = emailMessage.getContent().toString();
if (emailString == null) {
emailString = “There was no content in this email from ”
+ email.getSender().toString();
}

File mp3File = new File(mp3FileName);

try {
// 1. Create a wav file using the TTS engine
speakToFile(emailString, wavFileName);

// 2. Encode the wav file into MP3
MP3Encoder.writeFile(wavFileName);

// 3. Add the IDv3 information
MP3Encoder.writeID3(new File(mp3FileName), title, author, album, comment, year);

} catch (Exception e) {
e.printStackTrace();

}
} catch (Exception e) {
e.printStackTrace();
}
}

Learn more about our vertical solutions