#!/usr/bin/php
<?php
// Quick alpha-quality code
// Uses command line tools to decode an HD Radio stream via RTL-SDR, then encode it for Icecast Streaming
//
// Tools used: PHP (duh), RTLSDR, nrsc5, ffmpeg, ezstream, Icecast
$debug = 0;
$ppm = 19;
$bitrate = 256;
$metafile = "/home/zefie/.config/.hdradio_meta";
$signal = 0;
$oldtitle = "";
$oldartist = "";
$buffdat = "";
$totalrcv = 0;
$totalsnd = 0;
$rate = 1024;
// Program direction (aka if program is gonna send data to a pipe (write), use "w")
$desc = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$cwd = '/home/zefie';
if (@$argv[1] && @$argv[2] !== null) {
if (@$argv[3] !== null) {
$gain = intval($argv[3] * 10);
} else {
$gain = 480;
}
$freq = intval($argv[1] * 1000000);
$prog = intval($argv[2]);
} else {
echo "Usage: ".$_SERVER['PHP_SELF']." <freq> <program> [gain]\n";
echo "Example: ".$_SERVER['PHP_SELF']." 99.5 0 48.0\n";
exit(1);
}
$hdradio = proc_open('nrsc5 -l 1 -g '.$gain.' -p '.$ppm.' -o - -f adts '.$freq.' '.$prog, $desc, $radio_pipes, $cwd);
echo "Searching for HD Radio Signal on ".floatval($freq / 1000000)."MHz...\n";
if (is_resource($hdradio)) {
$hdradio_status = proc_get_status($hdradio);
if ($debug) echo "Decoder PID: ".$hdradio_status['pid']."\n";
stream_set_blocking($radio_pipes[1], 0);
stream_set_blocking($radio_pipes[2], 0);
while(true) {
if ($signal) {
if (is_resource(@$encoder) && is_resource(@$streamer)) {
$decdata = fread($radio_pipes[1],$rate);
if (strlen($decdata) > 0) {
fwrite($encoder_pipes[0],$decdata);
}
$strdata = fread($encoder_pipes[1],(($bitrate / 8) * 1024) + 128); // try to keep up, eh?
if (strlen($strdata) > 0) {
fwrite($streamer_pipes[0],$strdata);
}
$totalrcv = $totalrcv + strlen($decdata);
$totalsnd = $totalsnd + strlen($strdata);
} else {
$encoder = proc_open('ffmpeg -loglevel panic -i /dev/stdin -c:a vorbis -b:a '.$bitrate.'k -ar 44100 -f ogg -strict -2 -', $desc, $encoder_pipes, $cwd);
if (is_resource($encoder)) {
$encoder_status = proc_get_status($encoder);
if ($debug) echo "Encoder PID: ".$encoder_status['pid']."\n";
stream_set_blocking($encoder_pipes[0], 0);
stream_set_blocking($encoder_pipes[1], 0);
}
$streamer = proc_open('ezstream -n -c /home/zefie/.config/ezstream_hdradio.cfg', $desc, $streamer_pipes, $cwd);
if (is_resource($streamer)) {
$streamer_status = proc_get_status($streamer);
if ($debug) echo "Streamer PID: ".$streamer_status['pid']."\n";
stream_set_blocking($streamer_pipes[0], 0);
}
}
}
$radiodata = fread($radio_pipes[2],1024);
while (substr($radiodata,strlen($radiodata)-1,1) != PHP_EOL) {
$radiodata .= fread($radio_pipes[2],1);
}
if ($debug) echo $radiodata;
$radiodata = preg_split("/\n/",$radiodata);
foreach ($radiodata as $r) {
if (strpos($r,"rtlsdr_open error") > 0) {
clearLine();
echo $r."\n";
exit(1);
}
if (strpos($r,"Synchronized") > 0) {
$signal = 1;
clearLine();
echo "Found HDRadio Signal\n";
}
if (strpos($r,"lost sync") > 0) {
$signal = 0;
clearLine();
echo "Lost HDRadio Signal\n";
}
if (strpos($r,": MER:") > 0) {
$a = (strpos($r,": MER:") + 7);
$b = strlen($r) - $a;
$siglvl = substr($r,$a,$b);
}
if (strpos($r,": Title:") > 0) {
$a = (strpos($r,": Title:") + 9);
$b = strlen($r) - $a;
$title = substr($r,$a,$b);
}
if (strpos($r,": Artist:") > 0) {
$a = (strpos($r,": Artist:") + 10);
$b = strlen($r) - $a;
$artist = substr($r,$a,$b);
}
}
if ((@$artist != $oldartist) && (@$title != $oldtitle)) {
$oldartist = $artist;
$oldtitle = $title;
clearLine();
echo "Song: ".$artist." - ".$title."\n";
$fp = fopen($metafile,"w");
fwrite($fp,$artist."\n".$title);
fclose($fp);
posix_kill($streamer_status['pid'],12); // SIGUSR2
}
if ($signal) {
if (@$siglvl) {
echo "Signal: ".$siglvl." | ";
}
echo "FM Recv'd: ".friendlyBytes($totalrcv)." | INet Sent: ".friendlyBytes($totalsnd);
echo " \r";
} else {
sleep(1);
}
}
}
function getScreenWidth() {
preg_match_all("/rows.([0-9]+);.columns.([0-9]+);/", strtolower(exec('stty -a |grep columns')), $output);
if(sizeof($output) == 3) {
return $output[2][0];
}
}
function clearLine() {
for ($i=0; $i<(getScreenWidth()-1); $i++) {
echo " ";
}
echo "\r";
}
function friendlyBytes($size) {
if ($size > 1073741824) {
return round(($size / 1073741824),2)."GB";
}
if ($size > 1048576) {
return round(($size / 1048576),2)."MB";
}
if ($size > 1024) {
return round(($size / 1024),2)."KB";
}
return $size."B";
}
Comments