Location: PHPKode > projects > StreamOnTheFly > node/code/classes/sotf_AudioCheck.class.php
<?php

/**
* This is a class for checking the requested formats, and for converting them
* So this is a bad class name, should be AudioTools
*
* @author	Tamas Kezdi SZTAKI DSD <hide@address.com>
* @package	StreamOnTheFly
* @version	0.1
*/
class sotf_AudioCheck
{
	/**
	* Array of the arrays describes which requestments can be satisfied.
	*
	* The description of the requestment can be found at the same index where the requestment was
	* found in the $config['audioFormats'] array. The first element of these arrays is a boolean indicates
	* whether the conditions are matched. If it is true the second elements of these arrays show
	* where is the matching file in the $list member variable. If the first element is false, the
	* second element can be an index shows from which file can be generated the requested file,
	* or it can be false if there was not so file. For example we need a 128 and a 24 kbps mp3
	* but we have only the 128. The first element will be (true,0) so we have the file at index 0,
	* the second element will be (false,0) so we haven't the 24 kbps file but it can be generated
	* from the 128 kbps file that has the index 0. If we needed a 192 kbps mp3, we got (false,false),
	* so we haven't so file, and we cannot generate it from 128, because 128 is less than 192.
	* @attribute 	array	$reqs
	* @see	{@link $config['audioFormats']}
	*/
	var $reqs = array();

	/**
	* FileList object.
	*
	* @attribute 	object	$list
	* @see	{@link FileList}
	*/
	var $list;

  var $console = false;

	/**
	* Sets up the object
	*
	* @constructor sotf_AudioCheck
	* @param	object	$list	FileList object contains list of files to be checked
	* @use	$config['audioFormats'], $config['bitrateTolerance']
	*/
	function sotf_AudioCheck($list)
	{
		global $config;
		global $config;

		$this->list = & $list;
		for($i=0;$i<count($config['audioFormats']);$i++)			// walk thru the requested formats
		{
			$found = false;								// indicates whether the current audio format has been found
			for($j=0;$j<count($this->list->list);$j++)	// walk thru the the files we have
			{
				// $this->list->list[$j] means the current AudioFile object
				if ($this->list->list[$j]->type != "audio")
					continue;							// This is not an audio, get another one
				if ($config['audioFormats'][$i]['format'] != $this->list->list[$j]->format)
					continue;							// This is not the one we need, get another one
				//if ($config['audioFormats'][$i]['bitrate'] != $this->list->list[$j]->bitrate)
				//	continue;							// This is not the one we need, get another one
				if (abs($this->list->list[$j]->average_bitrate - $config['audioFormats'][$i]['bitrate']) > $config['bitrateTolerance'])
					continue;							// This is not the one we need, get another one
				else
				{
					$this->list->list[$j]->bitrate = $config['audioFormats'][$i]['bitrate'];
					$this->list->list[$j]->average_bitrate = $config['audioFormats'][$i]['bitrate'];
				}
				if ($config['audioFormats'][$i]['channels'] != $this->list->list[$j]->channels)
					continue;							// This is not the one we need, get another one
				if ($config['audioFormats'][$i]['samplerate'] != $this->list->list[$j]->samplerate)
					continue;							// This is not the one we need, get another one
				$found = $j;							// All conditions matched, that's what we need, store its position
				break;									// don't need to search for another, leave the loop
			}
			if ($found !== false)
			{
				$this->reqs[] = array(true,$found);		// store the position of the matched file
				continue;								// get the next requested format
			}
			$found = false;								// indicates whether a better quality audio has been found
			for($j=0;$j<count($this->list->list);$j++)	// walk thru the the files again to get a better quality audio
			{
				if ($this->list->list[$j]->type != "audio")
					continue;							// This is not an audio, get another one
				if ($config['audioFormats'][$i]['bitrate'] > $this->list->list[$j]->average_bitrate)
					continue;							// This is not the one we need, get another one
				if ($config['audioFormats'][$i]['channels'] > $this->list->list[$j]->channels)
					continue;							// This is not the one we need, get another one
				if ($config['audioFormats'][$i]['samplerate'] > $this->list->list[$j]->samplerate)
					continue;							// This is not the one we need, get another one
				$found = $j;							// Found a better one
				// easier to encode an mp3 from an mp3
				if (($config['audioFormats'][$i]['format'] != 'mp3') || (($config['audioFormats'][$i]['format'] == 'mp3') && ($this->list->list[$j]->format == 'mp3')))
					break;								// don't need to search for another, leave the loop
			}
			if ($found !== false)
			{
				$this->reqs[] = array(false,$found);	// store the position of the better quality file
				continue;								// get the next requested format
			}
			$this->reqs[] = array(false,false);			// There was nothing we could have use
		}
	} // end func sotf_AudioCheck

	/**
	* Gets the request index for a sotf_AudioFile
	*
	* @param	object	$audiofile	sotf_AudioFile object to be checked
	* @return	mixed	If the audio file satisfies any requestment returns an integer which is an index of the $config['audioFormats'] global variable. If the file satisfy any requestment returns boolean false
	* @use	$config['audioFormats'], $config['bitrateTolerance']
	*/
	function getRequestIndex($audiofile)
	{
		global $config;
		for($i=0;$i<count($config['audioFormats']);$i++)			// walk thru the requested formats
		{
			if ($audiofile->type != "audio")
				continue;								// This is not an audio, get another one
			if ($config['audioFormats'][$i]['format'] != $audiofile->format)
				continue;								// This is not the one we need, get another one
			//if ($config['audioFormats'][$i]['bitrate'] != $audiofile->bitrate)
			//	continue;								// This is not the one we need, get another one
			if (abs($audiofile->average_bitrate - $config['audioFormats'][$i]['bitrate']) > $config['bitrateTolerance'])
				continue;							// This is not the one we need, get another one
			if ($config['audioFormats'][$i]['channels'] != $audiofile->channels)
				continue;								// This is not the one we need, get another one
			if ($config['audioFormats'][$i]['samplerate'] != $audiofile->samplerate)
				continue;								// This is not the one we need, get another one
			return $j;									// All conditions matched, that's what we need, return the index
		}
		return false;
	} // end func getRequestIndex

	/**
	* Gets best quality audio file
	*
	* @return	mixed	If found return the index of the file, else returns boolean false
	*/
	function getBest()
	{
		$bitrate = 0;								// set minimum bitrate
		$index = false;								// initialize file index
		for($i=0;$i<count($this->list->list);$i++)	// walk thru files
			if ($this->list->list[$i]->type == 'audio')
				if ($this->list->list[$i]->average_bitrate > $bitrate)
				{
					$bitrate = $this->list->list[$i]->average_bitrate;
					$index = $i;
				}
		return $index;
	}

	/**
	* Gets best quality MP3 file
	*
	* @return	mixed	If found return the index of the file, else returns boolean false
	*/
	function getBestMP3()
	{
		$bitrate = 0;								// set minimum bitrate
		$index = false;								// initialize file index
		for($i=0;$i<count($this->list->list);$i++)	// walk thru files
			if ($this->list->list[$i]->type == 'audio')
				if ($this->list->list[$i]->format == 'mp3')
					if ($this->list->list[$i]->average_bitrate > $bitrate)
					{
						$bitrate = $this->list->list[$i]->average_bitrate;
						$index = $i;
					}
		return $index;
	}

	/**
	* Encode format to a filename.
	*
	* @param	integer	$index	Format index of the jingle in the $config['audioFormats'] global variable
	* @return	string	Encoded format. Example: 24kbps_1chn_22050Hz.mp3
	* @use	$config['audioFormats']
	*/
	function getFormatFilename($index)
	{
		global $config;

		return $config['audioFormats'][$index]['bitrate'] . 'kbps_' . $config['audioFormats'][$index]['channels'] . 'chn_' . $config['audioFormats'][$index]['samplerate'] . 'Hz.' . $config['audioFormats'][$index]['format'];
	} // end func getFormatFilename

  ////////////////////////////////////////////////////////
  //
  //  AUDIO CONVERSION
  //
  ///////////////////////////////////////////////////////


    function getTempWavName()
      {
        global $config;
        
        $tempname = tempnam($config['tmpDir'],"__");
        unlink($tempname);
        return $tempname. ".wav";
      }

    function progressBar($cmd,$regexp)
      {
        global $config;

        $line = "";
        $out = 0;
        $left = $config['progressBarLength'];

        debug('execute',$cmd);
        $fp = popen($cmd . ' 2>&1', 'r');
        while(!feof($fp))
          {
            $data = fread($fp,1);
            if ((ord($data) == 13) || (ord($data) == 10))
              {
                if (preg_match($regexp,$line,$match))
                  {
                    $curr = (integer) (((integer) $match[1]) * $config['progressBarLength'] / 100);
                    for ($i=$out;$i<$curr;$i++)
                      {
                        echo $config['progressBarChar'];
                        $out++;
                        $left--;
                      }
                  }
                $line = "";
              }
            else
              $line .= $data;
            flush();
          }
        pclose($fp);
        while($left)
          {
            echo $config['progressBarChar'];
            $left--;
            $out++;
          }
        flush();
      }

    function exec($cmd) {
      debug('execute',$cmd);
      exec($cmd);
    }

    function encodeWithLame($cmd)
      {
        global $config, $page;

        if($this->console) {
          echo "<p>" . $page->getlocalized('encoding_mp3') . "<br />\n";
          flush();
          
          $this->progressBar($cmd,$config['lameencRegexp']);
          echo "</p>\n";
          flush();
        } else {
          $this->exec($cmd);
        }
      }
        
    function decodeWithLame($cmd)
      {
        global $config, $page;
        
        if($this->console) {
          echo "<p>" . $page->getlocalized('decoding_mp3') . "<br />\n";
          flush();
          debug('execute',$cmd);
          $result = exec($cmd);
          debug('result',$result);
          for ($i=0;$i<$config['progressBarLength'];$i++)
            echo $config['progressBarChar'];
          echo "</p>\n";
          flush();
        } else {
          $this->exec($cmd);
        }
      }
    
    function encodeWithOgg($cmd)
      {
        global $config, $page;
        
        if($this->console) {
          echo "<p>" . $page->getlocalized('encoding_ogg') . "<br />\n";
          flush();
          $this->progressBar($cmd,$config['oggencRegexp']);
          echo "</p>\n";
          flush();
        } else {
          $this->exec($cmd);
        }
      }
    
    function decodeWithOgg($cmd)
      {
        global $config, $page;
        
        if($this->console) {
          echo "<p>" . $page->getlocalized('decoding_ogg') . "<br />\n";
          flush();
          debug('execute',$cmd);
          exec($cmd);
          for ($i=0;$i<$config['progressBarLength'];$i++)
            echo $config['progressBarChar'];
          echo "</p>\n";
          flush();
        } else {
          $this->exec($cmd);
        }
      }
    
    function convertWithSox($cmd)
      {
        global $config, $page;
        
        if($this->console) {
          echo "<p>" . $page->getlocalized('convert_mono') . "<br />\n";
          flush();
          debug('execute',$cmd);
          exec($cmd);
          for ($i=0;$i<$config['progressBarLength'];$i++)
            echo $config['progressBarChar'];
          echo "</p>\n";
          flush();
        } else {
          $this->exec($cmd);
        }
      }
    
    function checkFile($file) {
      if(!is_readable($file)) {
        raiseError("conversion_failed");
      }
    }

    function rmFile($file) {
      unlink($file) or logError("Could not delete file: $file");
    }

    /** returns array of names of new audio files */
    function convertAll($id) {
      global $config;
      for($i=0; $i<count($config['audioFormats']); $i++) {
        $file = $this->convert($id, $i);
        if($file)
          $files[] = $file;
      }
      return $files;
    }

    /** returns name of new audio file */
    function convert($id, $index) {
      global $config, $page;

      debug('conversion started', $this->getFormatFilename($index));

      if ($this->reqs[$index][0] === true) {
        debug("We have this format already", $index);
        return;
      }

      if ($this->reqs[$index][1] !== false) {
        // We have a better quality audio
        $sourceindex = $this->reqs[$index][1];
      } else {
        // We don't have a better quality audio, get the best
        $sourceindex = $this->getBest();
      }

      if ($sourceindex === false) {
        logError("Could not find a base format to convert format $index");
        return;
      }

      $audioFiles = & $this->list;
      $source = $audioFiles->list[$sourceindex]->getPath();
      debug("source", $source);

      $target = $config['tmpDir'] . '/' . $id . '_' . time() . '_' . $this->getFormatFilename($index);

      $bitrate = $config['audioFormats'][$index]["bitrate"];
      $samplerate = $config['audioFormats'][$index]["samplerate"];
      if ($config['audioFormats'][$index]["channels"] == 1)
        $mode = "mono";
      else
        $mode = "joint";

      if($this->console)
        echo "<p>" . $page->getlocalized('conversion_started') . ' ' . $this->getFormatFilename($index) . "</p>\n";

      if (($config['audioFormats'][$index]['format'] == 'mp3') && ($audioFiles->list[$sourceindex]->format == 'mp3'))
        {
          if (($config['audioFormats'][$index]['channels'] == 2) && ($audioFiles->list[$sourceindex]->channels == 1))
            {
              $tempname1 = $this->getTempWavName();
              $this->decodeWithLame($config['lame'] . " --decode \"$source\" \"$tempname1\"");
              $this->checkFile($tempname1);
              $tempname2 = $this->getTempWavName();
              $this->convertWithSox($config['sox'] . " \"$tempname1\" -c2 \"$tempname2\"");
              $this->checkFile($tempname2);
              $this->rmFile($tempname1);
              $this->encodeWithLame($config['lame'] . " --disptime 1 --cbr -b $bitrate -m $mode --resample $samplerate \"$tempname2\" \"$target\"");
              $this->checkFile($target);
              $this->rmFile($tempname2);
            }
          else
            {
              $this->encodeWithLame($config['lame'] . " --disptime 1 --cbr --mp3input -b $bitrate -m $mode --resample $samplerate \"$source\" \"$target\"");
            }
        }
      elseif (($config['audioFormats'][$index]['format'] == 'ogg') && ($audioFiles->list[$sourceindex]->format == 'mp3'))
        {
          $tempname1 = $this->getTempWavName();
          $this->decodeWithLame($config['lame'] . " --decode \"$source\" \"$tempname1\"");
          $this->checkFile($tempname1);
          if (($config['audioFormats'][$index]['channels'] == 2) && ($audioFiles->list[$sourceindex]->channels == 1))
            {
              $tempname2 = $this->getTempWavName();
              $this->convertWithSox($config['sox'] . " \"$tempname1\" -c2 \"$tempname2\"");
              $this->checkFile($tempname2);
              $this->rmFile($tempname1);
              $this->encodeWithOgg($config['oggenc'] . " -b $bitrate -m $bitrate -M $bitrate --resample $samplerate -o \"$target\" \"$tempname2\"");
              $this->checkFile($target);
              $this->rmFile($tempname2);
            }
          else
            {
              if (($config['audioFormats'][$index]['channels'] == 1) && ($audioFiles->list[$sourceindex]->channels == 2))
                $addparam = "--downmix";
              $this->encodeWithOgg($config['oggenc'] . " -b $bitrate -m $bitrate -M $bitrate --resample $samplerate $addparam -o \"$target\" \"$tempname1\"");
              $this->checkFile($target);
              $this->rmFile($tempname1);
            }
        }
      elseif (($config['audioFormats'][$index]['format'] == 'mp3') && ($audioFiles->list[$sourceindex]->format == 'ogg'))
        {
          $tempname1 = $this->getTempWavName();
          $this->decodeWithOgg($config['oggdec'] . " -o \"$tempname1\" \"$source\"");
          $this->checkFile($tempname1);
          if (($config['audioFormats'][$index]['channels'] == 2) && ($audioFiles->list[$sourceindex]->channels == 1))
            {
              $tempname2 = $this->getTempWavName();
              $this->convertWithSox($config['sox'] . " \"$tempname1\" -c2 \"$tempname2\"");
              $this->checkFile($tempname2);
              $this->rmFile($tempname1);
              $this->encodeWithLame($config['lame'] . " --disptime 1 --cbr -b $bitrate -m $mode --resample $samplerate \"$tempname2\" \"$target\"");
              $this->checkFile($target);
              $this->rmFile($tempname2);
            }
          else
            {
              $this->encodeWithLame($config['lame'] . " --disptime 1 --cbr -b $bitrate -m $mode --resample $samplerate \"$tempname1\" \"$target\"");
              $this->checkFile($target);
              $this->rmFile($tempname1);
            }
        }
      elseif (($config['audioFormats'][$index]['format'] == 'ogg') && ($audioFiles->list[$sourceindex]->format == 'ogg'))
        {
          $tempname1 = $this->getTempWavName();
          $this->decodeWithOgg($config['oggdec'] . " -o \"$tempname1\" \"$source\"");
          $this->checkFile($tempname1);
          if (($config['audioFormats'][$index]['channels'] == 2) && ($audioFiles->list[$sourceindex]->channels == 1))
            {
              $tempname2 = $this->getTempWavName();
              $this->convertWithSox($config['sox'] . " \"$tempname1\" -c2 \"$tempname2\"");
              $this->checkFile($tempname2);
              $this->rmFile($tempname1);
              $this->encodeWithOgg($config['oggenc'] . " -b $bitrate -m $bitrate -M $bitrate --resample $samplerate -o \"$target\" \"$tempname2\"");
              $this->checkFile($target);
              $this->rmFile($tempname2);
            }
          else
            {
              if (($config['audioFormats'][$index]['channels'] == 1) && ($audioFiles->list[$sourceindex]->channels == 2))
                $addparam = "--downmix";
              $this->encodeWithOgg($config['oggenc'] . " -b $bitrate -m $bitrate -M $bitrate --resample $samplerate $addparam -o \"$target\" \"$tempname1\"");
              $this->checkFile($target);
              $this->rmFile($tempname1);
            }
        }
      else
        {
          raiseError("No rule found to convert " . $this->getFormatFilename($index) . "!");
        }

      $this->checkFile($target);
      debug('conversion finished', $target);

      return $target;
    }

} // end class sotf_AudioCheck

?>
Return current item: StreamOnTheFly