Вход Регистрация
Файл: my.net.ru/system/libs/getid3/module.audio.dsdiff.php
Строк: 650
<?php

/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org>               //
//  available at https://github.com/JamesHeinrich/getID3       //
//            or https://www.getid3.org                        //
//            or http://getid3.sourceforge.net                 //
//  see readme.txt for more details                            //
/////////////////////////////////////////////////////////////////
//                                                             //
// module.audio.dsdiff.php                                     //
// module for analyzing Direct Stream Digital Interchange      //
// File Format (DSDIFF) files                                  //
// dependencies: NONE                                          //
//                                                            ///
/////////////////////////////////////////////////////////////////

if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
    
exit;
}

class 
getid3_dsdiff extends getid3_handler
{
    
/**
     * @return bool
     */
    
public function Analyze() {
        
$info = &$this->getid3->info;

        
$this->fseek($info['avdataoffset']);
        
$DSDIFFheader $this->fread(4);

        
// https://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
        
if (substr($DSDIFFheader04) != 'FRM8') {
            
$this->error('Expecting "FRM8" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSDIFFheader04)).'"');
            return 
false;
        }
        unset(
$DSDIFFheader);
        
$this->fseek($info['avdataoffset']);

        
$info['encoding']                 = 'ISO-8859-1'// not certain, but assumed
        
$info['fileformat']               = 'dsdiff';
        
$info['mime_type']                = 'audio/dsd';
        
$info['audio']['dataformat']      = 'dsdiff';
        
$info['audio']['bitrate_mode']    = 'cbr';
        
$info['audio']['bits_per_sample'] = 1;

        
$info['dsdiff'] = array();
        while (!
$this->feof() && ($ChunkHeader $this->fread(12))) {
            if (
strlen($ChunkHeader) < 12) {
                
$this->error('Expecting chunk header at offset '.$thisChunk['offset'].', found insufficient data in file, aborting parsing');
                break;
            }
            
$thisChunk = array();
            
$thisChunk['offset'] = $this->ftell() - 12;
            
$thisChunk['name'] = substr($ChunkHeader04);
            if (!
preg_match('#^[\x21-\x7E]+ *$#'$thisChunk['name'])) {
                
// "a concatenation of four printable ASCII characters in the range ' ' (space, 0x20) through '~'(0x7E). Space (0x20) cannot precede printing characters; trailing spaces are allowed."
                
$this->error('Invalid chunk name "'.$thisChunk['name'].'" ('.getid3_lib::PrintHexBytes($thisChunk['name']).') at offset '.$thisChunk['offset'].', aborting parsing');
            }
            
$thisChunk['size'] = getid3_lib::BigEndian2Int(substr($ChunkHeader48));
            
$datasize $thisChunk['size'] + ($thisChunk['size'] % 2); // "If the data is an odd number of bytes in length, a pad byte must be added at the end. The pad byte is not included in ckDataSize."

            
switch ($thisChunk['name']) {
                case 
'FRM8':
                    
$thisChunk['form_type'] = $this->fread(4);
                    if (
$thisChunk['form_type'] != 'DSD ') {
                        
$this->error('Expecting "DSD " at offset '.($this->ftell() - 4).', found "'.getid3_lib::PrintHexBytes($thisChunk['form_type']).'", aborting parsing');
                        break 
2;
                    }
                    
// do nothing further, prevent skipping subchunks
                    
break;
                case 
'PROP'// PROPerty chunk
                    
$thisChunk['prop_type'] = $this->fread(4);
                    if (
$thisChunk['prop_type'] != 'SND ') {
                        
$this->error('Expecting "SND " at offset '.($this->ftell() - 4).', found "'.getid3_lib::PrintHexBytes($thisChunk['prop_type']).'", aborting parsing');
                        break 
2;
                    }
                    
// do nothing further, prevent skipping subchunks
                    
break;
                case 
'DIIN'// eDIted master INformation chunk
                    // do nothing, just prevent skipping subchunks
                    
break;

                case 
'FVER'// Format VERsion chunk
                    
if ($thisChunk['size'] == 4) {
                        
$FVER $this->fread(4);
                        
$info['dsdiff']['format_version'] = ord($FVER[0]).'.'.ord($FVER[1]).'.'.ord($FVER[2]).'.'.ord($FVER[3]);
                        unset(
$FVER);
                    } else {
                        
$this->warning('Expecting "FVER" chunk to be 4 bytes, found '.$thisChunk['size'].' bytes, skipping chunk');
                        
$this->fseek($datasizeSEEK_CUR);
                    }
                    break;
                case 
'FS  '// sample rate chunk
                    
if ($thisChunk['size'] == 4) {
                        
$info['dsdiff']['sample_rate'] = getid3_lib::BigEndian2Int($this->fread(4));
                        
$info['audio']['sample_rate'] = $info['dsdiff']['sample_rate'];
                    } else {
                        
$this->warning('Expecting "FVER" chunk to be 4 bytes, found '.$thisChunk['size'].' bytes, skipping chunk');
                        
$this->fseek($datasizeSEEK_CUR);
                    }
                    break;
                case 
'CHNL'// CHaNneLs chunk
                    
$thisChunk['num_channels'] = getid3_lib::BigEndian2Int($this->fread(2));
                    if (
$thisChunk['num_channels'] == 0) {
                        
$this->warning('channel count should be greater than zero, skipping chunk');
                        
$this->fseek($datasize 2SEEK_CUR);
                    }
                    for (
$i 0$i $thisChunk['num_channels']; $i++) {
                        
$thisChunk['channels'][$i] = $this->fread(4);
                    }
                    
$info['audio']['channels'] = $thisChunk['num_channels'];
                    break;
                case 
'CMPR'// CoMPRession type chunk
                    
$thisChunk['compression_type'] = $this->fread(4);
                    
$info['audio']['dataformat'] = trim($thisChunk['compression_type']);
                    
$humanReadableByteLength getid3_lib::BigEndian2Int($this->fread(1));
                    
$thisChunk['compression_name'] = $this->fread($humanReadableByteLength);
                    if ((
$humanReadableByteLength 2) == 0) {
                        
// need to seek to multiple of 2 bytes, human-readable string length is only one byte long so if the string is an even number of bytes we need to seek past a padding byte after the string
                        
$this->fseek(1SEEK_CUR);
                    }
                    unset(
$humanReadableByteLength);
                    break;
                case 
'ABSS'// ABSolute Start time chunk
                    
$ABSS $this->fread(8);
                    
$info['dsdiff']['absolute_start_time']['hours']   = getid3_lib::BigEndian2Int(substr($ABSS02));
                    
$info['dsdiff']['absolute_start_time']['minutes'] = getid3_lib::BigEndian2Int(substr($ABSS21));
                    
$info['dsdiff']['absolute_start_time']['seconds'] = getid3_lib::BigEndian2Int(substr($ABSS31));
                    
$info['dsdiff']['absolute_start_time']['samples'] = getid3_lib::BigEndian2Int(substr($ABSS44));
                    unset(
$ABSS);
                    break;
                case 
'LSCO'// LoudSpeaker COnfiguration chunk
                    // 0 = 2-channel stereo set-up
                    // 3 = 5-channel set-up according to ITU-R BS.775-1 [ITU]
                    // 4 = 6-channel set-up, 5-channel set-up according to ITU-R BS.775-1 [ITU], plus additional Low Frequency Enhancement (LFE) loudspeaker. Also known as "5.1 configuration"
                    // 65535 = Undefined channel set-up
                    
$thisChunk['loundspeaker_config_id'] = getid3_lib::BigEndian2Int($this->fread(2));
                    break;
                case 
'COMT'// COMmenTs chunk
                    
$thisChunk['num_comments'] = getid3_lib::BigEndian2Int($this->fread(2));
                    for (
$i 0$i $thisChunk['num_comments']; $i++) {
                        
$thisComment = array();
                        
$COMT $this->fread(14);
                        
$thisComment['creation_year']   = getid3_lib::BigEndian2Int(substr($COMT,  02));
                        
$thisComment['creation_month']  = getid3_lib::BigEndian2Int(substr($COMT,  21));
                        
$thisComment['creation_day']    = getid3_lib::BigEndian2Int(substr($COMT,  31));
                        
$thisComment['creation_hour']   = getid3_lib::BigEndian2Int(substr($COMT,  41));
                        
$thisComment['creation_minute'] = getid3_lib::BigEndian2Int(substr($COMT,  51));
                        
$thisComment['comment_type_id'] = getid3_lib::BigEndian2Int(substr($COMT,  62));
                        
$thisComment['comment_ref_id']  = getid3_lib::BigEndian2Int(substr($COMT,  82));
                        
$thisComment['string_length']   = getid3_lib::BigEndian2Int(substr($COMT104));
                        
$thisComment['comment_text'] = $this->fread($thisComment['string_length']);
                        if (
$thisComment['string_length'] % 2) {
                            
// commentText[] is the description of the Comment. This text must be padded with a byte at the end, if needed, to make it an even number of bytes long. This pad byte, if present, is not included in count.
                            
$this->fseek(1SEEK_CUR);
                        }
                        
$thisComment['comment_type']      = $this->DSDIFFcmtType($thisComment['comment_type_id']);
                        
$thisComment['comment_reference'] = $this->DSDIFFcmtRef($thisComment['comment_type_id'], $thisComment['comment_ref_id']);
                        
$thisComment['creation_unix'] = mktime($thisComment['creation_hour'], $thisComment['creation_minute'], 0$thisComment['creation_month'], $thisComment['creation_day'], $thisComment['creation_year']);
                        
$thisChunk['comments'][$i] = $thisComment;

                        
$commentkey = ($thisComment['comment_reference'] ?: 'comment');
                        
$info['dsdiff']['comments'][$commentkey][] = $thisComment['comment_text'];
                        unset(
$thisComment);
                    }
                    break;
                case 
'MARK'// MARKer chunk
                    
$MARK $this->fread(22);
                    
$thisChunk['marker_hours']   = getid3_lib::BigEndian2Int(substr($MARK,  02));
                    
$thisChunk['marker_minutes'] = getid3_lib::BigEndian2Int(substr($MARK,  21));
                    
$thisChunk['marker_seconds'] = getid3_lib::BigEndian2Int(substr($MARK,  31));
                    
$thisChunk['marker_samples'] = getid3_lib::BigEndian2Int(substr($MARK,  44));
                    
$thisChunk['marker_offset']  = getid3_lib::BigEndian2Int(substr($MARK,  84));
                    
$thisChunk['marker_type_id'] = getid3_lib::BigEndian2Int(substr($MARK122));
                    
$thisChunk['marker_channel'] = getid3_lib::BigEndian2Int(substr($MARK142));
                    
$thisChunk['marker_flagraw'] = getid3_lib::BigEndian2Int(substr($MARK162));
                    
$thisChunk['string_length']  = getid3_lib::BigEndian2Int(substr($MARK184));
                    
$thisChunk['description'] = ($thisChunk['string_length'] ? $this->fread($thisChunk['string_length']) : '');
                    if (
$thisChunk['string_length'] % 2) {
                        
// markerText[] is the description of the marker. This text must be padded with a byte at the end, if needed, to make it an even number of bytes long. This pad byte, if present, is not included in count.
                        
$this->fseek(1SEEK_CUR);
                    }
                    
$thisChunk['marker_type'] = $this->DSDIFFmarkType($thisChunk['marker_type_id']);
                    unset(
$MARK);
                    break;
                case 
'DIAR'// artist chunk
                
case 'DITI'// title chunk
                    
$thisChunk['string_length']  = getid3_lib::BigEndian2Int($this->fread(4));
                    
$thisChunk['description'] = ($thisChunk['string_length'] ? $this->fread($thisChunk['string_length']) : '');
                    if (
$thisChunk['string_length'] % 2) {
                        
// This text must be padded with a byte at the end, if needed, to make it an even number of bytes long. This pad byte, if present, is not included in count.
                        
$this->fseek(1SEEK_CUR);
                    }

                    if (
$commentkey = (($thisChunk['name'] == 'DIAR') ? 'artist' : (($thisChunk['name'] == 'DITI') ? 'title' ''))) {
                        @
$info['dsdiff']['comments'][$commentkey][] = $thisChunk['description'];
                    }
                    break;
                case 
'EMID'// Edited Master ID chunk
                    
if ($thisChunk['size']) {
                        
$thisChunk['identifier'] = $this->fread($thisChunk['size']);
                    }
                    break;

                case 
'ID3 ':
                    
$endOfID3v2 $this->ftell() + $datasize// we will need to reset the filepointer after parsing ID3v2

                    
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php'__FILE__true);
                    
$getid3_temp = new getID3();
                    
$getid3_temp->openfile($this->getid3->filenamenull$this->getid3->fp);
                    
$getid3_id3v2 = new getid3_id3v2($getid3_temp);
                    
$getid3_id3v2->StartingOffset $this->ftell();
                    if (
$thisChunk['valid'] = $getid3_id3v2->Analyze()) {
                        
$info['id3v2'] = $getid3_temp->info['id3v2'];
                    }
                    unset(
$getid3_temp$getid3_id3v2);

                    
$this->fseek($endOfID3v2);
                    break;

                case 
'DSD '// DSD sound data chunk
                
case 'DST '// DST sound data chunk
                    // actual audio data, we're not interested, skip
                    
$this->fseek($datasizeSEEK_CUR);
                    break;
                default:
                    
$this->warning('Unhandled chunk "'.$thisChunk['name'].'"');
                    
$this->fseek($datasizeSEEK_CUR);
                    break;
            }

            @
$info['dsdiff']['chunks'][] = $thisChunk;
            
//break;
        
}
        if (empty(
$info['audio']['bitrate']) && !empty($info['audio']['channels']) && !empty($info['audio']['sample_rate']) && !empty($info['audio']['bits_per_sample'])) {
            
$info['audio']['bitrate'] = $info['audio']['bits_per_sample'] * $info['audio']['sample_rate'] * $info['audio']['channels'];
        }

        return 
true;
    }

    
/**
     * @param int $cmtType
     *
     * @return string
     */
    
public static function DSDIFFcmtType($cmtType) {
        static 
$DSDIFFcmtType = array(
            
=> 'General (album) Comment',
            
=> 'Channel Comment',
            
=> 'Sound Source',
            
=> 'File History',
        );
        return (isset(
$DSDIFFcmtType[$cmtType]) ? $DSDIFFcmtType[$cmtType] : 'reserved');
    }

    
/**
     * @param int $cmtType
     * @param int $cmtRef
     *
     * @return string
     */
    
public static function DSDIFFcmtRef($cmtType$cmtRef) {
        static 
$DSDIFFcmtRef = array(
            
=> array(  // Sound Source
                
=> 'DSD recording',
                
=> 'Analogue recording',
                
=> 'PCM recording',
            ),
            
=> array( // File History
                
=> 'comment',   // General Remark
                
=> 'encodeby',  // Name of the operator
                
=> 'encoder',   // Name or type of the creating machine
                
=> 'timezone',  // Time zone information
                
=> 'revision',  // Revision of the file
            
),
        );
        switch (
$cmtType) {
            case 
0:
                
// If the comment type is General Comment the comment reference must be 0
                
return '';
            case 
1:
                
// If the comment type is Channel Comment, the comment reference defines the channel number to which the comment belongs
                
return ($cmtRef 'channel '.$cmtRef 'all channels');
            case 
2:
            case 
3:
                return (isset(
$DSDIFFcmtRef[$cmtType][$cmtRef]) ? $DSDIFFcmtRef[$cmtType][$cmtRef] : 'reserved');
        }
        return 
'unsupported $cmtType='.$cmtType;
    }

    
/**
     * @param int $cmtType
     *
     * @return string
     */
    
public static function DSDIFFmarkType($markType) {
        static 
$DSDIFFmarkType = array(
            
=> 'TrackStart',   // Entry point for a Track start
            
=> 'TrackStop',    // Entry point for ending a Track
            
=> 'ProgramStart'// Start point of 2-channel or multi-channel area
            
=> 'Obsolete',     //
            
=> 'Index',        // Entry point of an Index
        
);
        return (isset(
$DSDIFFmarkType[$markType]) ? $DSDIFFmarkType[$markType] : 'reserved');
    }

}
Онлайн: 1
Реклама