zefie icon

Cheap HDRadio to Icecast thing w/ Metadata (using RTLSDR)

zefie | PRO | 08/21/17 05:33:23 AM UTC | 0 ⭐ | 907 👁️ | Never ⏰ | []
PHP |

4.9 KB

|

None

|

0 👍

/

0 👎

#!/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