File indexing completed on 2024-12-22 05:33:12

0001 <?php
0002 /////////////////////////////////////////////////////////////////
0003 /// getID3() by James Heinrich <info@getid3.org>               //
0004 //  available at http://getid3.sourceforge.net                 //
0005 //            or http://www.getid3.org                         //
0006 //          also https://github.com/JamesHeinrich/getID3       //
0007 /////////////////////////////////////////////////////////////////
0008 // See readme.txt for more details                             //
0009 /////////////////////////////////////////////////////////////////
0010 //                                                             //
0011 // module.audio.dss.php                                        //
0012 // module for analyzing Digital Speech Standard (DSS) files    //
0013 // dependencies: NONE                                          //
0014 //                                                            ///
0015 /////////////////////////////////////////////////////////////////
0016 
0017 
0018 class getid3_dss extends getid3_handler
0019 {
0020 
0021   public function Analyze() {
0022     $info = &$this->getid3->info;
0023 
0024     $this->fseek($info['avdataoffset']);
0025     $DSSheader  = $this->fread(1540);
0026 
0027     if (!preg_match('#^(\x02|\x03)ds[s2]#', $DSSheader)) {
0028       $info['error'][] = 'Expecting "[02-03] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"';
0029       return false;
0030     }
0031 
0032     // some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
0033     $info['encoding']              = 'ISO-8859-1'; // not certain, but assumed
0034     $info['dss'] = array();
0035 
0036     $info['fileformat']            = 'dss';
0037     $info['mime_type']             = 'audio/x-'.substr($DSSheader, 1, 3); // "audio/x-dss" or "audio/x-ds2"
0038     $info['audio']['dataformat']   =            substr($DSSheader, 1, 3); //         "dss" or         "ds2"
0039     $info['audio']['bitrate_mode'] = 'cbr';
0040 
0041     $info['dss']['version']           =                            ord(substr($DSSheader,    0,   1));
0042     $info['dss']['hardware']          =                           trim(substr($DSSheader,   12,  16)); // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400"
0043     $info['dss']['unknown1']          =   getid3_lib::LittleEndian2Int(substr($DSSheader,   28,   4));
0044     // 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen
0045     $info['dss']['date_create']       = $this->DSSdateStringToUnixDate(substr($DSSheader,   38,  12));
0046     $info['dss']['date_complete']     = $this->DSSdateStringToUnixDate(substr($DSSheader,   50,  12));
0047     $info['dss']['playtime_sec']      = intval((substr($DSSheader,  62, 2) * 3600) + (substr($DSSheader,  64, 2) * 60) + substr($DSSheader,  66, 2)); // approximate file playtime in HHMMSS
0048     $info['dss']['playtime_ms']       =   getid3_lib::LittleEndian2Int(substr($DSSheader,  512,   4)); // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
0049     $info['dss']['priority']          =                            ord(substr($DSSheader,  793,   1));
0050     $info['dss']['comments']          =                           trim(substr($DSSheader,  798, 100));
0051     $info['dss']['sample_rate_index'] =                            ord(substr($DSSheader, 1538,   1));  // this isn't certain, this may or may not be where the sample rate info is stored, but it seems consistent on my small selection of sample files
0052 
0053     $info['audio']['bits_per_sample']  = 16; // maybe, maybe not -- most compressed audio formats don't have a fixed bits-per-sample value, but this is a reasonable approximation
0054     $info['audio']['sample_rate']      = $this->DSSsampleRateLookup($info['dss']['sample_rate_index']);
0055     $info['audio']['channels']         = 1;
0056 
0057     $info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000;
0058     if (floor($info['dss']['playtime_ms'] / 1000) != $info['dss']['playtime_sec']) {
0059       // *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check
0060       $info['playtime_seconds'] = $info['dss']['playtime_sec'];
0061       $this->getid3->warning('playtime_ms ('.number_format($info['dss']['playtime_ms'] / 1000, 3).') does not match playtime_sec ('.number_format($info['dss']['playtime_sec']).') - using playtime_sec value');
0062     }
0063     $info['audio']['bitrate'] = ($info['filesize'] * 8) / $info['playtime_seconds'];
0064 
0065     return true;
0066   }
0067 
0068   public function DSSdateStringToUnixDate($datestring) {
0069     $y = substr($datestring,  0, 2);
0070     $m = substr($datestring,  2, 2);
0071     $d = substr($datestring,  4, 2);
0072     $h = substr($datestring,  6, 2);
0073     $i = substr($datestring,  8, 2);
0074     $s = substr($datestring, 10, 2);
0075     $y += (($y < 95) ? 2000 : 1900);
0076     return mktime($h, $i, $s, $m, $d, $y);
0077   }
0078 
0079   public function DSSsampleRateLookup($sample_rate_index) {
0080     static $dssSampleRateLookup = array(
0081       0x0A => 16000,
0082       0x0C => 11025,
0083       0x0D => 12000,
0084       0x15 =>  8000,
0085     );
0086     if (!array_key_exists($sample_rate_index, $dssSampleRateLookup)) {
0087       $this->getid3->warning('unknown sample_rate_index: '.$sample_rate_index);
0088       return false;
0089     }
0090     return $dssSampleRateLookup[$sample_rate_index];
0091   }
0092 
0093 }