Archive for October, 2007

Record your favorite radio shows and generate a podcast with the episodes.

Tuesday, October 9th, 2007

Podcast as shown by iTunes 7Ok, this is something that i had in mind since some time ago but i didn’t had the time to actually do it. But now i finished it.
The situation is like this, there is a radio show that i love, but the problem is that this radio show starts at 12 AM and finishes at 2 AM, and usually, I’m doing any other thing at that time in the night.
So, i decided to create some simple script to record the audio in one of my servers (by using the Radio station internet broadcast).

For all this to work, you will need the following things:

  1. PHP 5.1.6 or newer
  2. MPlayer
  3. the GPAC utilitles
  4. the Nero Digital AAC Encoder

The idea for the bash script to record the audio stream is based on a nice tutorial by Phil Salkie, but honestly, i think i improved it a lot, specially because it uses a FIFO as temporary storage, and because it compresses the audio to a more recent audio format like HE-AAC.

Basically, the following bash script will record any audio stream of your choice and will convert it to a HE-AAC file inside an m4a container compatible with any audio player out there AND iTunes / iPod of course ;)

After the audio file is done, the second script will generate a podcast feed (RSS) compatible with iTunes or any podcast client out there, like for example Juice.

Here is the script to capture the radio show and convert it to a M4A file (AAC Plus inside an MP4 container).

#!/bin/bash
TERM="linux"
# RANDOM GUID GENERATION
SUM=`date`
SUM2=`echo $SUM | md5sum | cut -c 1-8`

# INITIALIZATION VARIABLES
DATE=`date +%F`  # Save the date as YYYY-MM-DD
FILE=/home/plusmedia/public_html/media/$DATE---$SUM2.mp4  # WHERE TO SAVE THE STREAM ?
STREAM=mms://200.43.193.143/radio10  # The URL of the stream to save.
FIFO=/tmp/stream.wav  # We create a FIFO as intermediate storage for the encoding.
ARTWORK=/home/plusmedia/public_html/lvst.jpg  # Path to some artwork file for iTunes, can be JPG or PNG.
DURATION=122m # How many minutes should we record ?

# MP4 TAGS
AUTHOR="Some Author"
ALBUM="Some Radio Show"
TITLE="Some Radio Program - $DATE" 

# We use mplayer to capture the stream to a wav file being stored into a FIFO.
mkfifo $FIFO
/usr/bin/mplayer $STREAM -af resample=44100:0:2,volume=0:1,pan=1:0.5:0.5,comp -ao pcm:file="$FIFO" -vo null -vc dummy < /dev/null 2>&1 >/dev/null & pA=$!

# Encoding the fifo to AAC with the Nero Digital Encoder for Linux.
/usr/bin/neroAacEnc -br 24000 -he -if $FIFO -of $FILE & pB=$!

sleep $DURATION  # wait for the show to be over

kill $pA # end the mplayer process
sleep 2s # IMPORTANT! wait a while for the Nero encoder to finish and close the opened files.

echo "CREATING M4A AND APPLYING TAGS..."
/usr/bin/MP4Box -add "$FILE#audio" -itags cover="$ARTWORK":artist="$AUTHOR":comment="$DATE":album_artist="$AUTHOR" -lang english -new "${FILE%.*}.m4a"
sleep 1s
echo "Generating Podcast Item..."
# We have a new episode recorded, so it is time to call the podcast generation script to update the RSS feed.
/home/plusmedia/podcast

echo "Deleting FIFO and temporary files..."
rm $FIFO -f
rm $FILE -f

echo "done!"

And this is the PHP script to generate the “iTunes Compliant” podcast feed:

#!/usr/bin/php
setIndent = true;
    $xmlObject->openMemory();
    $xmlObject->startDocument('1.0','UTF-8');
    $xmlObject->startElement ('rss'); //
    $xmlObject->writeAttribute( 'xmlns:itunes', 'http://www.itunes.com/dtds/podcast-1.0.dtd');
    $xmlObject->writeAttribute( 'version', '2.0');
	$xmlObject->startElement ('channel');
	$xmlObject->writeElement ('title', $title);
	$xmlObject->writeElement ('link', $link);
	$xmlObject->writeElement ('language', $language);
	$xmlObject->writeElement ('copyright', $copyright);
	$xmlObject->writeElement ('itunes:subtitle', $itunesSubtitle);
	$xmlObject->writeElement ('itunes:author', $itunesAuthor);
	$xmlObject->writeElement ('itunes:summary', $itunesSummary);
	$xmlObject->writeElement ('itunes:explicit', $itunesExplicit);
	$xmlObject->writeElement ('description', $description);
		$xmlObject->startElement ('itunes:owner');
		$xmlObject->writeElement ('itunes:name', $ownerName);
		$xmlObject->writeElement ('itunes:email', $ownerEmail);
		$xmlObject->endElement();
	$xmlObject->startElement ('itunes:image');
		$xmlObject->writeAttribute ('href', $baseURL.$image);
	$xmlObject->endElement();
		$xmlObject->startElement ('itunes:category');
		$xmlObject->writeAttribute ('text', $mainCategory);
			$xmlObject->startElement ('itunes:category');
			$xmlObject->writeAttribute ('text', $childCategory);
			$xmlObject->endElement();
		$xmlObject->endElement();
// 	ITEMS
	$files = scandir($mediaDIR);
	$sortedFiles = array();
	foreach ($files as $filePath) {
		$fileItem = array( 	'modified'  => filemtime($mediaDIR.$filePath),
							'path'		=> $filePath
		            	 );
		array_push ($sortedFiles, $fileItem);
	}
	usort($sortedFiles, "cmp");
	foreach ($sortedFiles as $value) {
		$extension = substr($value['path'], strrpos($value['path'], "."));
		if ($extension == $fileExtension) {
			$nameParts = explode("---", parseFileName($value['path']));
			$xmlObject->startElement ('item');
			$dateComponent = strtotime($nameParts[0]);
			$xmlObject->writeElement ('title', $itemTitle.strftime('%A, %B %d, %Y', $dateComponent));
			$xmlObject->writeElement ('itunes:author', $itunesAuthor);
			$xmlObject->writeElement ('itunes:subtitle', $itunesSubtitle);
			$xmlObject->writeElement ('itunes:image', $baseURL.$image);
			$xmlObject->writeElement ('pubDate', date('r', $dateComponent));
			$xmlObject->startElement ('enclosure');
				$xmlObject->writeAttribute( 'url', $baseURL.$mediaURL.$value['path']);
				$xmlObject->writeAttribute( 'length', filesize($mediaDIR.$value['path']));
				$xmlObject->writeAttribute( 'type', 'audio/x-m4a');
			$xmlObject->endElement(); //
			$xmlObject->writeElement ('guid', $baseURL.$mediaURL.$value['path']);
			$xmlObject->endElement(); //
		}
	}
	$xmlObject->endElement(); //
    $xmlObject->endElement(); // 

// 	Write XML data to file...
    writeXmlFile($xmlObject->outputMemory(true), $XML);    

function writeXmlFile($contents, $file) {
	$fh = fopen($file, 'w') or die("can't open file");
	fwrite($fh, $contents);
}

function parseFileName($fileName) {
	$input = explode(".",$fileName);
	$noExt = $input[0];
	return $noExt;
}

// Custom array ordering function for usort
// orders by REVERSE order (Biggest timestamp goes FIRST).
function cmp($a, $b)
{
    return strcmp($b["modified"], $a["modified"]);
}
?>

The PHP script will scan a given directory for files with the extension “m4a” (or any other extension configured in the $fileExtension variable), it will order the files by their created date (in reverse order) and will generate a file called “podcast.xml” that you can access with iTunes on mac or PC, or you could use any other podcast client like Juice.

You should for example call the bash script “recorder” and call the php script “podcast”, then chmod +x both files to make them executables, and then just run the bash script by typing:

./recorder

Obviously, the idea behind all this is that you should create a crontab entry to run the process automatically, in my case, it runs everyday at 12:00 AM and i did set the duration variable to 121 minutes (2 hours and a bit).
All the rest should be smooth and automatic if you configured correctly the initialization variables in both scripts.

“Here’s to the crazy ones”. Remake of the “Think Different” soundtrack.

Thursday, October 4th, 2007

Einstein thinks different…It was some days ago that i had some free time and i decided to re-create the glorious soundtrack from the Apple’s “Think Different” TV commercial.
I don’t know who is the original author of this, so i cant really credit him / her, but whoever you are, i really love this track, in fact, i love it that much, that i decided to make it again.
This is not an “exact” replica of the original of course, because i play it with my own style, but if you know the original, you will find that this one is pretty close to it.

You can listen or download the song by going HERE.

For the techies out there:

The Piano comes from the fabulous Steinberg’s “The Grand”.
The Strings are from the Miroslav Vitous – String Ensembles AKAI library.
All this has been sequenced and mixed in my beloved Cubase sequencer and the reverb used is Waves IR-1.

More changes to Illacrimo for WordPress

Thursday, October 4th, 2007

As you can see, i did several other changes to the Illacrimo theme for WordPress.
Between several minor fixes, you will notice that there is more space on the post column now, and I removed the original column for the post’s date too. Instead, the date is located now next to the post title.
I also replaced several images with CSS equivalents, so the site loads a little faster now and uses less bandwidth.
You can download the new version by clicking here.

If you have any questions or suggestions, you can leave a comment on this post.

Serverside video resizing script for ffmpeg or Mencoder

Monday, October 1st, 2007

Some days ago, while working on a project that involves the re-encoding of a lot of videos coming from several sources, aspect ratios, resolutions, etc, i found myself in the situation where i needed to “standarize” somehow all the videos to a prefixed size in order to place them on a fixed space in a web page.
While most linux tools like ffmpeg or mencoder include native functions to scale or resize the video, they don’t care about the aspect ratios or about the fact that most video encoders expect mod16 values for the height and width values.
So my situation was like this:

I needed to make all the videos 480 pixels high, and scale the width proportionally.

That being said, i came up with this PHP script that i call from inside a bash script:

#!/usr/bin/php
<?php
$cmdWidth = 'midentify '.$argv[1];
$finalHeight = $argv[2];
exec($cmdWidth, $output);

foreach ($output as $value) {
    if (strstr($value, "ID_VIDEO_WIDTH")) {
		
		$width = parseResult($value);
    }
    if (strstr($value, "ID_VIDEO_HEIGHT")) {
    	$height = parseResult($value);
    }
}
$frameSize = $width / $height;
$finalWidth = $finalHeight * $frameSize;
echo getMod16(round($finalWidth)) . "x" . getMod16(round($finalHeight)) . "\n";
function parseResult($line) {
	$v1 = explode("=",$line);
	$v20 = $v1[1];
	
	//var_dump($v2);
	return $v20;
}
function getMod16($number){
	while (fmod($number, 16) != 0) {
		$number ++;
	}
	return $number;
}
?>

This script assumes that you have the “midentify” utility (wich comes with mplayer) installed in your path, and expects 2 arguments, being argument 1 the movie you want to resize and argument 2 the actual height you want to reach.
That being said, suppose you have a movie that is 848 pixels width and 480 pixels high and you want to re-scale it so it fits inside a 400 pixels high space, you would call the script like this:

./resizer mymovie.avi 400

The script will output this:

720x400

You can use later this value as an input parameter for ffmpeg for example.

Before somebody asks, the reason because i used PHP instead of BASH, is simply because bash does not supports floats.