Overview

Packages

  • admin
  • classes
    • media
  • CodeIgniter
    • Libraries
  • core
  • functions
  • JSMin
  • None
  • OpenID
  • PHP
  • PHPMailer
  • plugins
    • admin
    • development
    • feed
    • mail
    • media
    • misc
    • seo
    • spam
    • uploader
    • users
    • zenpage
    • zenphoto
      • news
  • Services
    • JSON

Classes

  • _zp_captcha
  • _zp_HTML_cache
  • admin_approval
  • Album
  • AlbumBase
  • AlbumZip
  • AMFReader
  • AMFStream
  • AnyFile
  • AnyFile_Options
  • Auth_OpenID
  • Auth_OpenID_AlreadySigned
  • Auth_OpenID_AssociateRequest
  • Auth_OpenID_Association
  • Auth_OpenID_AuthRequest
  • Auth_OpenID_AX
  • Auth_OpenID_AX_AttrInfo
  • Auth_OpenID_AX_Error
  • Auth_OpenID_AX_FetchRequest
  • Auth_OpenID_AX_FetchResponse
  • Auth_OpenID_AX_KeyValueMessage
  • Auth_OpenID_AX_Message
  • Auth_OpenID_AX_StoreRequest
  • Auth_OpenID_AX_StoreResponse
  • Auth_OpenID_BcMathWrapper
  • Auth_OpenID_CancelResponse
  • Auth_OpenID_CheckAuthRequest
  • Auth_OpenID_CheckIDRequest
  • Auth_OpenID_Consumer
  • Auth_OpenID_ConsumerResponse
  • Auth_OpenID_CryptUtil
  • Auth_OpenID_DatabaseConnection
  • Auth_OpenID_Decoder
  • Auth_OpenID_DiffieHellman
  • Auth_OpenID_DiffieHellmanSHA1ConsumerSession
  • Auth_OpenID_DiffieHellmanSHA1ServerSession
  • Auth_OpenID_DiffieHellmanSHA256ConsumerSession
  • Auth_OpenID_DiffieHellmanSHA256ServerSession
  • Auth_OpenID_DumbStore
  • Auth_OpenID_Encoder
  • Auth_OpenID_EncodingError
  • Auth_OpenID_Extension
  • Auth_OpenID_FailureResponse
  • Auth_OpenID_FileStore
  • Auth_OpenID_GenericConsumer
  • Auth_OpenID_GmpMathWrapper
  • Auth_OpenID_KVForm
  • Auth_OpenID_MalformedReturnURL
  • Auth_OpenID_MalformedTrustRoot
  • Auth_OpenID_Mapping
  • Auth_OpenID_MathLibrary
  • Auth_OpenID_MDB2Store
  • Auth_OpenID_MemcachedStore
  • Auth_OpenID_Message
  • Auth_OpenID_MySQLStore
  • Auth_OpenID_NamespaceMap
  • Auth_OpenID_NoReturnToError
  • Auth_OpenID_OpenIDStore
  • Auth_OpenID_PAPE_Request
  • Auth_OpenID_PAPE_Response
  • Auth_OpenID_Parse
  • Auth_OpenID_PlainTextConsumerSession
  • Auth_OpenID_PlainTextServerSession
  • Auth_OpenID_PostgreSQLStore
  • Auth_OpenID_Request
  • Auth_OpenID_Server
  • Auth_OpenID_ServerError
  • Auth_OpenID_ServerErrorContainer
  • Auth_OpenID_ServerRequest
  • Auth_OpenID_ServerResponse
  • Auth_OpenID_ServiceEndpoint
  • Auth_OpenID_ServiceEndpointLoader
  • Auth_OpenID_SessionNegotiator
  • Auth_OpenID_SetupNeededResponse
  • Auth_OpenID_Signatory
  • Auth_OpenID_SigningEncoder
  • Auth_OpenID_SQLiteStore
  • Auth_OpenID_SQLStore
  • Auth_OpenID_SRegBase
  • Auth_OpenID_SRegRequest
  • Auth_OpenID_SRegResponse
  • Auth_OpenID_SuccessResponse
  • Auth_OpenID_TrustRoot
  • Auth_OpenID_TypeURIMismatch
  • Auth_OpenID_UntrustedReturnURL
  • Auth_OpenID_WebResponse
  • Auth_Yadis_Discovery
  • Auth_Yadis_DiscoveryResult
  • Auth_Yadis_dom
  • Auth_Yadis_domxml
  • Auth_Yadis_HTTPFetcher
  • Auth_Yadis_HTTPResponse
  • Auth_Yadis_Manager
  • Auth_Yadis_ManagerLoader
  • Auth_Yadis_ParanoidHTTPFetcher
  • Auth_Yadis_ParseHTML
  • Auth_Yadis_PHPSession
  • Auth_Yadis_PlainHTTPFetcher
  • Auth_Yadis_ProxyResolver
  • Auth_Yadis_Service
  • Auth_Yadis_SessionLoader
  • Auth_Yadis_XMLParser
  • Auth_Yadis_XRDS
  • Auth_Yadis_Yadis
  • auto_backup
  • AVCSequenceParameterSetReader
  • bxslider
  • cacheManager
  • cacheManagerFeed
  • CI_jsmin
  • CI_load
  • cloneZenphoto
  • codeIgniter_kludge
  • colorbox
  • Comment
  • comment_form
  • contactformOptions
  • crop_image
  • cycle
  • defaultCodeblocks
  • deprecated_functions
  • DownloadList
  • dynamic_locale
  • dynamicAlbum
  • elFinder_options
  • email_new_user
  • exampleMacros
  • external_auth
  • ExternalFeed
  • externalFeed_options
  • favorites
  • favoritesOptions
  • federated_logon
  • feed
  • fieldExtender
  • flag_thumbnail
  • Gallery
  • galleryArticles
  • getID3
  • getid3_aac
  • getid3_apetag
  • getid3_flv
  • getid3_handler
  • getid3_id3v1
  • getid3_id3v2
  • getid3_lib
  • getid3_lyrics3
  • getid3_mp3
  • getid3_mpeg
  • getid3_quicktime
  • getid3_swf
  • GoogleMap
  • Googlemaps
  • googleVerifyOptions
  • hitcounter
  • HTML
  • htmlmetatags
  • http_auth
  • Image
  • image_effects
  • internal_deprecations
  • ipBlocker
  • jcarousel
  • jPlayer
  • jplayer_options
  • jquery_rating
  • JSMin
  • lib_GD_Options
  • lib_Imagick_Options
  • lib_NoGraphics
  • MediaObject
  • menu_manager
  • MergedRSS
  • MergedRSSOptions
  • mobile
  • Mobile_Detect
  • mobileTheme
  • multipleLayoutOptions
  • null_seo
  • OAuthConsumer
  • OAuthDataStore
  • OAuthRequest
  • OAuthServer
  • OAuthSignatureMethod
  • OAuthSignatureMethod_HMAC_SHA1
  • OAuthSignatureMethod_PLAINTEXT
  • OAuthSignatureMethod_RSA_SHA1
  • OAuthToken
  • OAuthUtil
  • pagedThumbsNav
  • pagedthumbsOptions
  • PclZip
  • PersistentObject
  • PHPMailer
  • PlainText
  • POP3
  • print_album_menu
  • pseudoPlayer
  • publishContent
  • quota_manager
  • reCaptcha
  • ReCaptchaResponse
  • register_user
  • rewriteRules
  • rewriteTokens
  • RSS
  • RSS_internal_deprecations
  • rss_options
  • search_statistics
  • SearchEngine
  • security_logger
  • seo_locale
  • Services_JSON
  • Services_JSON_Error
  • setupexternalFeed
  • setupRSS
  • show_not_loggedin
  • sitemap
  • slideshow
  • SMTP
  • static_html_cache
  • tagsuggest
  • TextObject
  • TextObject_internal_deprecations
  • TextObject_Options
  • ThemeObject
  • themeSwitcher
  • tinymce4Options
  • tinyURL
  • Transientimage
  • tweet
  • TwitterOAuth
  • UploadHandler
  • user_expiry
  • user_groups
  • user_logout_options
  • userAddressFields
  • utf8
  • Video
  • Video_internal_deprecations
  • VideoObject_Options
  • viewer_size_image_options
  • WEBdocs
  • WEBdocs_Options
  • xmpMetadata
  • Zenpage
  • Zenpage_internal_deprecations
  • ZenpageCategory
  • zenpagecms
  • ZenpageItems
  • ZenpageNews
  • ZenpagePage
  • ZenpageRoot
  • Zenphoto_Administrator
  • Zenphoto_Authority
  • zenphoto_org_news
  • zenphoto_seo
  • zenphotoDonate
  • ZipStream
  • zp_PHPMailer
  • zpCaptcha
  • zpFunctions
  • zpLegacySpam
  • zpMutex
  • zpSimpleSpam
  • zpTrivialSpam

Exceptions

  • BadFunctionCallException
  • BadMethodCallException
  • Exception
  • getid3_exception
  • JSMin_UnterminatedCommentException
  • JSMin_UnterminatedRegExpException
  • JSMin_UnterminatedStringException
  • LogicException
  • OAuthExcept
  • phpmailerException

Functions

  • __autoload
  • _escape_xref
  • _recaptcha_aes_encrypt
  • _recaptcha_aes_pad
  • _recaptcha_http_post
  • _recaptcha_mailhide_email_parts
  • _recaptcha_mailhide_urlbase64
  • _recaptcha_qsencode
  • accessAllAlbums
  • add_context
  • addalbumsToDatabase
  • addCategoriesToDatabase
  • addGeoCoord
  • addItem
  • addPagesToDatabase
  • addPluginScript
  • addPluginType
  • addSubalbumMenus
  • admin_album_list
  • admin_securityChecks
  • admin_showupdate
  • adminPageNav
  • adminToolbox
  • albumNumber
  • applyMacros
  • Auth_OpenID_arrangeByType
  • Auth_OpenID_AX_checkAlias
  • Auth_OpenID_AX_toTypeURIs
  • Auth_OpenID_bestMatchingService
  • Auth_OpenID_checkFieldName
  • Auth_OpenID_checkSessionType
  • Auth_OpenID_checkTimestamp
  • Auth_OpenID_detectMathLibrary
  • Auth_OpenID_discover
  • Auth_OpenID_discoverURI
  • Auth_OpenID_discoverWithoutYadis
  • Auth_OpenID_discoverWithYadis
  • Auth_OpenID_discoverXRI
  • Auth_OpenID_extractReturnURL
  • Auth_OpenID_findOPLocalIdentifier
  • Auth_OpenID_getAllAssociationTypes
  • Auth_OpenID_getAllowedReturnURLs
  • Auth_OpenID_getAuthorityPattern
  • Auth_OpenID_getAvailableSessionTypes
  • Auth_OpenID_getDefaultAssociationOrder
  • Auth_OpenID_getDefaultGen
  • Auth_OpenID_getDefaultMod
  • Auth_OpenID_getDefaultNegotiator
  • Auth_OpenID_getEncodedPattern
  • Auth_OpenID_getEncryptedNegotiator
  • Auth_OpenID_getEscapeRE
  • Auth_OpenID_getMathLib
  • Auth_OpenID_getOnlyEncryptedOrder
  • Auth_OpenID_getOpenIDConsumerTypeURIs
  • Auth_OpenID_getOpenIDTypeName
  • Auth_OpenID_getOpenIDTypeURIs
  • Auth_OpenID_getOPOrUserServices
  • Auth_OpenID_getSecretSize
  • Auth_OpenID_getSessionTypes
  • Auth_OpenID_getSupportedAssociationTypes
  • Auth_OpenID_getUnreserved
  • Auth_OpenID_getURIPattern
  • Auth_OpenID_getURLIllegalCharRE
  • Auth_OpenID_HMACSHA1
  • Auth_OpenID_HMACSHA256
  • Auth_OpenID_include_init
  • Auth_OpenID_isError
  • Auth_OpenID_isOpenID1
  • Auth_OpenID_legacy_discover
  • Auth_OpenID_makeOpenIDEndpoints
  • Auth_OpenID_math_extensions
  • Auth_OpenID_mkNonce
  • Auth_OpenID_noMathSupport
  • Auth_OpenID_pct_encoded_replace
  • Auth_OpenID_pct_encoded_replace_unreserved
  • Auth_OpenID_registerNamespaceAlias
  • Auth_OpenID_remove_dot_segments
  • Auth_OpenID_removeNamespaceAlias
  • Auth_OpenID_returnToMatches
  • Auth_OpenID_setNoMathSupport
  • Auth_OpenID_SHA1
  • Auth_OpenID_SHA256
  • Auth_OpenID_splitNonce
  • Auth_OpenID_supportsSReg
  • Auth_OpenID_urinorm
  • Auth_OpenID_verifyReturnTo
  • Auth_Yadis_array_scramble
  • Auth_Yadis_escapeForIRI
  • Auth_Yadis_getCanonicalID
  • Auth_Yadis_getDefaultProxy
  • Auth_Yadis_getEscapeRE
  • Auth_Yadis_getIPrivateChars
  • Auth_Yadis_getNSMap
  • Auth_Yadis_getServiceEndpoints
  • Auth_Yadis_getSupportedExtensions
  • Auth_Yadis_getUCSChars
  • Auth_Yadis_getXMLParser
  • Auth_Yadis_getXRDExpiration
  • Auth_Yadis_getXrefRE
  • Auth_Yadis_getXRIAuthorities
  • Auth_Yadis_identifierScheme
  • Auth_Yadis_iriToURI
  • Auth_Yadis_pct_escape_unicode
  • Auth_Yadis_providerIsAuthoritative
  • Auth_Yadis_rootAuthority
  • Auth_Yadis_setDefaultParser
  • Auth_Yadis_startswith
  • Auth_Yadis_toIRINormal
  • Auth_Yadis_toURINormal
  • Auth_Yadis_XRI
  • Auth_Yadis_XRIAppendArgs
  • authorSelector
  • build_query
  • build_url
  • bulkActionRedirect
  • bulkTags
  • byteConvert
  • cacheImage
  • checkAccess
  • checkAlbumimagesort
  • checkAlbumParentid
  • checkAlbumPassword
  • checkChosenItemStatus
  • checkChosenMenuset
  • checked
  • checkFolder
  • checkForEmptyTitle
  • checkForGuest
  • checkForPage
  • checkForPassword
  • checkForUpdate
  • checkHitcounterDisplay
  • checkIfChecked
  • checkIfLockedNews
  • checkIfLockedPage
  • checkIfNew
  • checkInstall
  • checkLayoutUseForImages
  • checkNewsAccess
  • checkNewsCategoryPassword
  • checkObjectsThumb
  • checkPagePassword
  • checkPageValidity
  • checkParentLayouts
  • checkPublishDates
  • checkRequiredField
  • checkSelectedAlbum
  • checkSignature
  • cleanAlbum
  • cleanHTML
  • clearSitemapCache
  • clonedFrom
  • codeblocktabsJS
  • comment_form_addComment
  • comment_form_handle_comment
  • comment_form_PaginationJS
  • comment_form_postcomment
  • comment_form_print10Most
  • comment_form_visualEditor
  • commentFormUseCaptcha
  • commentReply
  • commentsAllowed
  • consolidatedEditMessages
  • copyLayoutSelection
  • copyThemeDirectory
  • countArticles
  • countCombiNews
  • createMenuIfNotExists
  • createRelatedItemsResultArray
  • cron_starter
  • currentRelativeURL
  • customOptions
  • dateDiff
  • datepickerJS
  • dateTimeConvert
  • db_affected_rows
  • db_close
  • db_collation
  • db_connect
  • db_count
  • db_create
  • db_create_table
  • db_error
  • db_fetch_assoc
  • db_fetch_row
  • db_free_result
  • db_getSQLmode
  • db_insert_id
  • db_LIKE_escape
  • db_list_fields
  • db_name
  • db_num_rows
  • db_permissions
  • db_quote
  • db_setSQLmode
  • db_show
  • db_software
  • db_table_update
  • db_truncate_table
  • debug404
  • debugLog
  • debugLogBacktrace
  • debugLogVar
  • defaultCodeblocks_codebox
  • deleteArticle
  • deleteCategory
  • deleteItem
  • deleteLayoutSelection
  • deletePage
  • deleteThemeDirectory
  • detect_fetcher
  • detect_math
  • detect_query_corruption
  • detect_random
  • detect_stores
  • detect_xml
  • dircopy
  • displayError
  • doIncludes
  • elFinder_admin_tabs
  • elFinder_tinymce
  • enableExtension
  • escape
  • executeRSS
  • exitZP
  • exposeZenPhotoInformations
  • extensionEnabled
  • fetchComments
  • filesystemToInternal
  • filter_extractReturnURL
  • filter_MatchesAnyOpenIDConsumerType
  • filter_MatchesAnyOpenIDType
  • filterImageQuery
  • fix_path_redirect
  • formatList
  • fullText
  • galleryAlbumsPerPage
  • genAlbumList
  • generateCaptcha
  • generateLanguageList
  • generateListFromArray
  • generateListFromFiles
  • generateRadiobuttonsFromArray
  • generateSitemapCacheFile
  • generateSitemapIndexCacheFile
  • generateUnorderedListFromArray
  • get_AnyFile_suffixes
  • get_context
  • get_filterScript
  • get_instance
  • get_language_string
  • getAdminThumb
  • getAlbumArray
  • getAlbumBreadcrumb
  • getAlbumBreadcrumbAdmin
  • getAlbumCustomData
  • getAlbumData
  • getAlbumDate
  • getAlbumDesc
  • getAlbumFolder
  • getAlbumGeodata
  • getAlbumId
  • getAlbumInherited
  • getAlbumLinkURL
  • getAlbumLocation
  • getAlbumPage
  • getAlbumPlace
  • getAlbumStatistic
  • getAlbumThumb
  • getAlbumTitle
  • getAlbumURL
  • getAllAccessibleAlbums
  • getAllAlbums
  • getAllArticleDates
  • getAllCategories
  • getAllDates
  • getAllowedTags
  • getAllSubAlbumIDs
  • getAllSubalbums
  • getAllTagsCount
  • getAllTagsFromAlbum
  • getAllTagsFromAlbum_multi_unique
  • getAllTagsFromZenpage
  • getAllTagsUnique
  • getAllTranslations
  • getAnnotatedAlbumTitle
  • getAnnotatedImageTitle
  • getArticles
  • getAuthor
  • getBare
  • getBareAlbumDesc
  • getBareAlbumTitle
  • getBareGalleryDesc
  • getBareGalleryTitle
  • getBareImageDesc
  • getBareImageTitle
  • getBareNewsAlbumTitle
  • getBareNewsTitle
  • getBarePageTitle
  • getCategory
  • getCategoryID
  • getCategoryLink
  • getCategoryParentID
  • getCategorySortOrder
  • getCategoryTitle
  • getCheckboxState
  • getCodeblock
  • getCombiNews
  • getCommentAddress
  • getCommentAuthorEmail
  • getCommentAuthorLink
  • getCommentAuthorName
  • getCommentAuthorSite
  • getCommentBody
  • getCommentCount
  • getCommentDate
  • getCommentDateTime
  • getCommentErrors
  • getCommentsAllowed
  • getCommentStored
  • getCommentTime
  • getConsumer
  • getContentShorten
  • getCurrentMenuItem
  • getCurrentNewsArchive
  • getCurrentNewsCategory
  • getCurrentNewsCategoryID
  • getCurrentNewsCategoryParentID
  • getCurrentNewsPage
  • getCurrentPage
  • getCurrentTheme
  • getCustomAlbumThumb
  • getCustomAlbumThumbMaxSpace
  • getCustomImageURL
  • getCustomPageURL
  • getCustomSizedImageMaxSpace
  • getCustomSizedImageThumbMaxSpace
  • getDefaultHeight
  • getDefaultSizedImage
  • getDefaultWidth
  • getDownloadLink
  • getdownloadList
  • getDownloadURL
  • getE
  • getEnabledPlugins
  • getExpiryDatePost
  • getFavoritesURL
  • getField
  • getFirstImageURL
  • getFullHeight
  • getFullImageURL
  • getFullNewsImage
  • getFullWidth
  • getGalleryDesc
  • getGalleryIndexURL
  • getGalleryTitle
  • getGeoCoord
  • getHeadTitle
  • getHitcounter
  • getImageArgs
  • getImageCacheFilename
  • getImageCachePostfix
  • getImageCity
  • getImageCountry
  • getImageCustomData
  • getImageData
  • getImageDate
  • getImageDesc
  • getImageEXIFData
  • getImageGeodata
  • getImageID
  • getImageLinkURL
  • getImageLocation
  • getImageMetaData
  • getImageParameters
  • getImageProcessorURI
  • getImageProcessorURIFromCacheName
  • getImageRotation
  • getImageSortOrder
  • getImageState
  • getImageStatistic
  • getImageThumb
  • getImageTitle
  • getImageURI
  • getImageURL
  • getItem
  • getItemByID
  • getItemTitleAndURL
  • getjPlayerSkinCSS
  • getjPlayerSkins
  • getLanguageArray
  • getLanguageFlag
  • getLastImageURL
  • getLatestComments
  • getLatestNews
  • getLatestZenpageComments
  • getLayout
  • getLayoutSelector
  • getLink
  • getLinkHTML
  • getLogTabs
  • getMacros
  • getMainSiteName
  • getMainSiteURL
  • getManagedAlbumList
  • getMaxSpaceContainer
  • getMenuFromLink
  • getMenuItemChilds
  • getMenuItems
  • getMenumanagerPredicessor
  • getMenumanagerSuccessor
  • getMenuSetSelector
  • getMenuVisibility
  • getMimeString
  • getNestedAlbumList
  • getNewsAdminOption
  • getNewsAdminOptionPath
  • getNewsAlbumName
  • getNewsAlbumTitle
  • getNewsAlbumURL
  • getNewsArchivePath
  • getNewsArchiveURL
  • getNewsAuthor
  • getNewsCategories
  • getNewsCategoryCustomData
  • getNewsCategoryDesc
  • getNewsCategoryPath
  • getNewsCategoryURL
  • getNewsContent
  • getNewsContentShorten
  • getNewsCustomData
  • getNewsDate
  • getNewsExtraContent
  • getNewsID
  • getNewsImageTags
  • getNewsIndexURL
  • getNewsLink
  • getNewsPagesStatistic
  • getNewsPathNav
  • getNewsReadMore
  • getNewsTitle
  • getNewsTitleLink
  • getNewsTitlePath
  • getNewsType
  • getNewsURL
  • getNewsVideoContent
  • getNextAlbum
  • getNextAlbumURL
  • getNextImageThumb
  • getNextImageURL
  • getNextNewsPageURL
  • getNextNewsURL
  • getNextPageURL
  • getNextPrevNews
  • getNotViewableAlbums
  • getNotViewableImages
  • getNumAlbums
  • getNumAllSubalbums
  • getNumImages
  • getNumNews
  • getNumPages
  • getNumSubalbums
  • getOpenIDURL
  • getOption
  • getOptionFromDB
  • getOptionList
  • getPageAuthor
  • getPageContent
  • getPageCustomData
  • getPageDate
  • getPageExtraContent
  • getPageID
  • getPageLastChangeDate
  • getPageLinkPath
  • getPageLinkURL
  • getPageNavList
  • getPageNumURL
  • getPageParentID
  • getPageRedirect
  • getPages
  • getPageSelector
  • getPageSortorder
  • getPageTitle
  • getPageTitleLink
  • getPageURL
  • getParentAlbums
  • getParentAlbumsAdmin
  • getParentBreadcrumb
  • getParentItems
  • getParentMenuItems
  • getParentNewsCategories
  • getParentPages
  • getPasswordProtectImage
  • getPHPFiles
  • getPlugin
  • getPluginFiles
  • getPluginTabs
  • getPrevAlbum
  • getPrevAlbumURL
  • getPrevImageThumb
  • getPrevImageURL
  • getPrevNewsPageURL
  • getPrevNewsURL
  • getPrevPageURL
  • getProtectedImageURL
  • getRandomImages
  • getRandomImagesAlbum
  • getRating
  • getRelatedItems
  • getRequestURI
  • getReturnTo
  • getRSSHeaderLink
  • getRSSLink
  • getScheme
  • getSearchDate
  • getSearchURL
  • getSearchWords
  • getSelectedLayout
  • getSerializedArray
  • getSetClause
  • getSiteHomeURL
  • getSitemapAlbumList
  • getSitemapAlbums
  • getSitemapGoogleImageVideoExtras
  • getSitemapGoogleLoopIndex
  • getSitemapImages
  • getSitemapIndexLinks
  • getSitemapZenpageNewsArticles
  • getSitemapZenpageNewsCategories
  • getSitemapZenpageNewsIndex
  • getSitemapZenpagePages
  • getSizeCustomImage
  • getSizeDefaultImage
  • getSizeDefaultThumb
  • getSizedImageURL
  • getSizeFullImage
  • getStore
  • getSubCategories
  • getSubtabs
  • getSuffix
  • getTagCountByAccess
  • getTagOrder
  • getTags
  • gettext_pl
  • gettext_th
  • getTheme
  • getThemeFiles
  • getThemeOption
  • getTimezones
  • getTinyMCE4ConfigFiles
  • getTitle
  • getTotalArticles
  • getTotalImagesIn
  • getTotalNewsPages
  • getTotalPages
  • getTrustRoot
  • getUnprotectedImageURL
  • getUrAlbum
  • getURL
  • getUserIP
  • getUserLocale
  • getVersion
  • getViewerImageSize
  • getWatermarkParam
  • getWatermarkPath
  • getWatermarks
  • getWhereClause
  • getXSRFToken
  • getZenpageHitcounter
  • getZenpageRSSHeaderLink
  • getZenpageRSSLink
  • getZenpageStatistic
  • googleVerifyHead
  • handleSearchParms
  • hasDynamicAlbumSuffix
  • hasNextImage
  • hasNextPage
  • hasPrevImage
  • hasPrevPage
  • hitcounter
  • html_decode
  • html_encode
  • html_encodeTagged
  • httpsRedirect
  • httpUploadHandler
  • httpUploadHandler_admin_tabs
  • i18nSetLocale
  • imageBlurGD
  • imageDebug
  • imageError
  • imageNumber
  • imgSrcURI
  • in_context
  • inNewsCategory
  • inProtectedNewsCategory
  • installSignature
  • instrument
  • inSubNewsCategoryOf
  • internalToFilesystem
  • inventMenuItem
  • iptc_make_tag
  • is_AdminEditPage
  • is_connected
  • is_GalleryNewsType
  • is_News
  • is_NewsArchive
  • is_NewsArticle
  • is_NewsCategory
  • is_NewsPage
  • is_NewsType
  • is_Pages
  • is_valid_email_zp
  • is_valid_image
  • is_valid_other_type
  • is_zip
  • isAlbumClass
  • isAlbumPage
  • isArchive
  • isHandledAlbum
  • isImageClass
  • isImagePage
  • isImagePhoto
  • isImageVideo
  • isLandscape
  • isMyAlbum
  • isMyNews
  • isMyPage
  • isolate
  • isProtectedAlbum
  • isProtectedNewsCategory
  • isProtectedPage
  • isSubNewsCategoryOf
  • isValidURL
  • jQueryUpload_head
  • jQueryUpload_headers
  • jQueryUploadHandler
  • jQueryUploadHandler_admin_tabs
  • js_encode
  • json_decode
  • json_encode
  • kses
  • kses_array_lc
  • kses_attr
  • kses_bad_protocol
  • kses_bad_protocol_once
  • kses_bad_protocol_once2
  • kses_check_attr_val
  • kses_decode_entities
  • kses_hair
  • kses_hook
  • kses_html_error
  • kses_js_entities
  • kses_no_null
  • kses_normalize_entities
  • kses_normalize_entities2
  • kses_split
  • kses_split2
  • kses_stripslashes
  • kses_version
  • ksesProcess
  • layoutSelector
  • layoutSelector_album
  • listDBUses
  • listDirectoryFiles
  • listUses
  • load_zenpage_news
  • load_zenpage_pages
  • loadLocalOptions
  • log_message
  • lookupSortKey
  • macro_admin_tabs
  • macroList_show
  • makeAlbumCurrent
  • makeImageCurrent
  • makeSpecialImageName
  • markRelease_button
  • mb_strlen
  • mb_strpos
  • mb_strrpos
  • mb_strtolower
  • mb_strtoupper
  • mb_substr
  • mb_substr_count
  • menu_admin_toolbox_global
  • menu_tabs
  • minDiff
  • mkdir_recursive
  • my_truncate_string
  • myts_date
  • newAlbum
  • newImage
  • next_album
  • next_comment
  • next_image
  • next_news
  • next_page
  • ngettext_pl
  • ngettext_th
  • normalizeColumns
  • omsAdditions
  • openedForComments
  • parse_query
  • parse_size
  • parseAllowedTags
  • parseHttpAcceptLanguage
  • passAlbums
  • passImages
  • pathurlencode
  • PclZipUtilCopyBlock
  • PclZipUtilOptionText
  • PclZipUtilPathInclusion
  • PclZipUtilPathReduction
  • PclZipUtilRename
  • PclZipUtilTranslateWinPath
  • PHPMailerAutoload
  • populateManagedObjectsList
  • postAlbumSort
  • postIndexDecode
  • postIndexEncode
  • prefix
  • prepareAlbumPage
  • prepareCustomPage
  • prepareImagePage
  • prepareIndexPage
  • print404status
  • print_language_string_list
  • printAddToFavorites
  • printAdminFooter
  • printAdminHeader
  • printAdminRightsTable
  • printAdminToolbox
  • printAlbumBreadcrumb
  • printAlbumButtons
  • printAlbumCustomData
  • printAlbumData
  • printAlbumDate
  • printAlbumDesc
  • printAlbumEditForm
  • printAlbumEditRow
  • printAlbumLegend
  • printAlbumLink
  • printAlbumLocation
  • printAlbumMap
  • printAlbumMenu
  • printAlbumMenuJump
  • printAlbumMenuList
  • printAlbumMenuListAlbum
  • printAlbumPlace
  • printAlbumRating
  • printAlbumsSelector
  • printAlbumStatistic
  • printAlbumStatisticItem
  • printAlbumThumbImage
  • printAlbumTitle
  • printAlbumURL
  • printAlbumZip
  • printAllDates
  • printAllNewsCategories
  • printAllTags
  • printAllTagsAs
  • printAllTagsFromAlbum
  • printAllTagsFromZenpage
  • printAnnotatedAlbumTitle
  • printAnnotatedImageTitle
  • printArticleCategories
  • printArticleDatesDropdown
  • printArticlesPerPageDropdown
  • printBareAlbumDesc
  • printBareAlbumTitle
  • printBareGalleryDesc
  • printBareGalleryTitle
  • printBareImageDesc
  • printBareImageTitle
  • printBareNewsTitle
  • printBarePageTitle
  • printBulkActions
  • printCaptcha
  • printCategoriesStatistic
  • printCategoryCheckboxListEntry
  • printCategoryDropdown
  • printCategoryListSortableTable
  • printCategorySelection
  • printCodeblock
  • printCodeblockEdit
  • printCommentAuthorLink
  • printCommentErrors
  • printCommentForm
  • printContactForm
  • printCurrentNewsArchive
  • printCurrentNewsCategory
  • printCustomAlbumThumbImage
  • printCustomAlbumThumbMaxSpace
  • printCustomMenu
  • printCustomPageSelector
  • printCustomPageURL
  • printCustomSizedImage
  • printCustomSizedImageMaxHeight
  • printCustomSizedImageMaxSpace
  • printCustomSizedImageThumbMaxSpace
  • printDefaultSizedImage
  • printDownloadAlbumZipURL
  • printDownloadLink
  • printDownloadLinkAlbumZip
  • printdownloadList
  • printDownloadURL
  • printEditable
  • printEditCommentLink
  • printEditDropdown
  • printExpired
  • printFavoritesLink
  • printFavoritesURL
  • printField
  • printGalleryDesc
  • printGalleryIndexURL
  • printGalleryTitle
  • printGoogleMap
  • printHeadTitle
  • printHomeLink
  • printImageCustomData
  • printImageData
  • printImageDate
  • printImageDesc
  • printImageDiv
  • printImageEXIFData
  • printImageID
  • printImageLink
  • printImageMap
  • printImageMetadata
  • printImageRating
  • printImageSortOrder
  • printImageStatistic
  • printImageThumb
  • printImageTitle
  • printImageURL
  • printItemEditLink
  • printItemsList
  • printItemsListTable
  • printItemStatusDropdown
  • printjCarouselThumbNav
  • printjPlayerPlaylist
  • printLanguageSelector
  • printLatestAlbums
  • printLatestComments
  • printLatestImages
  • printLatestImagesByDate
  • printLatestImagesByMtime
  • printLatestNews
  • printLatestUpdatedAlbums
  • printLatestZenpageComments
  • printLink
  • printLinkHTML
  • printLogoAndLinks
  • printManagedObjects
  • printMenuemanagerPageList
  • printMenuemanagerPageListWithNav
  • printMenumanagerBreadcrumb
  • printMenumanagerNextLink
  • printMenumanagerPrevLink
  • printMostPopularItems
  • printMostRatedAlbums
  • printMostRatedImages
  • printMostRatedItems
  • printNestedAlbumsList
  • printNestedItemsList
  • printNestedMenu
  • printNews
  • printNewsArchive
  • printNewsAuthor
  • printNewsCategories
  • printNewsCategoryCustomData
  • printNewsCategoryDesc
  • printNewsCategoryURL
  • printNewsContent
  • printNewsCustomData
  • printNewsDate
  • printNewsExtraContent
  • printNewsImageTags
  • printNewsIndexURL
  • printNewsLink
  • printNewsPageList
  • printNewsPageListWithNav
  • printNewsReadMoreLink
  • printNewsStatistic
  • printNewsTitle
  • printNewsTitleLink
  • printNewsURL
  • printNextNewsLink
  • printNextNewsPageLink
  • printNextPageLink
  • printNextPageURL
  • printPageArticleTags
  • printPageAuthor
  • printPageContent
  • printPageCustomData
  • printPageDate
  • printPagedThumbsNav
  • printPageExtraContent
  • printPageID
  • printPageLastChangeDate
  • printPageLinkURL
  • printPageList
  • printPageListWithNav
  • printPageMenu
  • printPageNav
  • printPageSelector
  • printPagesListTable
  • printPagesStatistic
  • printPageTitle
  • printPageTitleLink
  • printPageURL
  • printParentBreadcrumb
  • printParentPagesBreadcrumb
  • printPasswordForm
  • printPopularAlbums
  • printPopularImages
  • printPreloadScript
  • printPrevNewsLink
  • printPrevNewsPageLink
  • printPrevPageLink
  • printPrevPageURL
  • printPublished
  • printPublishIconLink
  • printRandomImages
  • printRating
  • printRegisterURL
  • printRegistrationForm
  • printRelatedItems
  • printRSSHeaderLink
  • printRSSLink
  • printSearchBreadcrumb
  • printSearchForm
  • printSiteHomeURL
  • printSizedImageLink
  • printSizedImageURL
  • printSlideShow
  • printSlideShowJS
  • printSlideShowLink
  • printSortableHead
  • printSortOrderDropdown
  • printSubPagesExcerpts
  • printSubtabs
  • printTabs
  • printTags
  • printThumbNav
  • printTopRatedAlbums
  • printTopRatedImages
  • printTopRatedItems
  • printUnpublishedDropdown
  • printUserLogin_out
  • printUserSizeImage
  • printUserSizeSelector
  • printVersion
  • printZenJavascripts
  • printZenpageIconLegend
  • printZenpageItemsBreadcrumb
  • printZenpageNewsCategorySelector
  • printZenpagePagesSelector
  • printZenpageRSSHeaderLink
  • printZenpageRSSLink
  • printZenpageStatistic
  • printZenphotoLink
  • process_language_string_save
  • processAlbumBulkActions
  • processAlbumEdit
  • processCodeblockSave
  • processCommentBulkActions
  • processCredentials
  • processCustomOptionSave
  • processEditSelection
  • processExpired
  • processImageBulkActions
  • processImageEdit
  • processManagedObjects
  • processMenuBulkActions
  • processOrder
  • processRights
  • processTags
  • processZenpageBulkActions
  • propSizes
  • publishItem
  • purgeOption
  • query
  • query_full_array
  • query_single_row
  • rc4
  • read_exif_data_protected
  • readTags
  • recaptcha_check_answer
  • recaptcha_get_html
  • recaptcha_get_signup_url
  • recaptcha_mailhide_html
  • recaptcha_mailhide_url
  • reconfigureAction
  • reconfigureCS
  • reconfigurePage
  • recordMissing
  • rem_context
  • removeParentAlbumNames
  • resetCurrentAlbum
  • restore_context
  • reveal
  • rewrite_get_album_image
  • rewrite_path
  • rewrite_path_zenpage
  • RSS_Channel
  • RSS_Retrieve
  • RSS_Tags
  • rulesList
  • run
  • safe_fnmatch
  • safe_glob
  • sanitize
  • sanitize_numeric
  • sanitize_path
  • sanitize_script
  • sanitize_string
  • sanitizeRedirect
  • save_context
  • saveLayoutSelection
  • saveZenphotoLayoutSelection
  • search_quote
  • secureServer
  • seo_cleanup_button
  • seoFriendly
  • seoFriendlyJS
  • set_context
  • setAlbumCustomData
  • setAlbumSubtabs
  • setImageCustomData
  • setMainDomain
  • setOption
  • setOptionDefault
  • setPluginDomain
  • setThemeColumns
  • setThemeDomain
  • setThemeOption
  • setThemeOptionDefault
  • setupAllowedMaps
  • setupCurrentLocale
  • setupDomain
  • setupTheme
  • shortenContent
  • showOrNotShowField
  • shuffle_assoc
  • signatureChange
  • site_upgrade_button
  • site_upgrade_status
  • sitemap_echonl
  • sitemap_getChangefreq
  • sitemap_getDateformat
  • sitemap_getDBLimit
  • sitemap_getISO8601Date
  • skipScheduledPublishing
  • sortByKey
  • sortByMultilingual
  • sortMultiArray
  • standardScripts
  • standardThemeOptions
  • stickyNews
  • storeConfig
  • storeTags
  • stripSuffix
  • submenuOf
  • switchLog
  • tagSelector
  • tagSuggestJS
  • tagSuggestJS_admin
  • tagSuggestJS_frontend
  • themeIsEditable
  • themeSetup
  • timezoneDiff
  • tinymce4ConfigJS
  • truncate_string
  • unpublishedZenphotoItemCheck
  • unpublishSubalbums
  • unQuote
  • unzip
  • updateArticle
  • updateCacheName
  • updateCategory
  • updateConfigItem
  • updateItemSortorder
  • updateItemsSortorder
  • updateMenuItem
  • updatePage
  • upload_extra
  • upload_form
  • upload_head
  • user_mailing_list_button
  • validateLocale
  • wordpress_import_button
  • wp_prefix
  • wp_query_full_array
  • wpimport_TryAgainError
  • XSRFdefender
  • XSRFToken
  • zenJavascript
  • zenpageAlbumImage
  • zenpageBulkActionMessage
  • zenpageHitcounter
  • zenpageJSCSS
  • zenpageOpenedForComments
  • zenpagePublish
  • zenphoto_PHPMailer
  • zenphoto_sendmail
  • zenPhotoTheme
  • zp_apply_filter
  • zp_clearCookie
  • zp_colorAllocate
  • zp_cookieEncode
  • zp_copyCanvas
  • zp_createImage
  • zp_drawRectangle
  • zp_error
  • zp_filter_slot
  • zp_filter_unique_id
  • zp_getCookie
  • zp_getFonts
  • zp_graphicsLibInfo
  • zp_handle_password
  • zp_handle_password_single
  • zp_has_filter
  • zp_image_types
  • zp_imageCanRotate
  • zp_imageColorTransparent
  • zp_imageDims
  • zp_imageFill
  • zp_imageFontHeight
  • zp_imageFontWidth
  • zp_imageFromString
  • zp_imageGet
  • zp_imageGray
  • zp_imageHeight
  • zp_imageIPTC
  • zp_imageKill
  • zp_imageLoadFont
  • zp_imageMerge
  • zp_imageOutput
  • zp_imageResizeAlpha
  • zp_imageUnsharpMask
  • zp_imageWidth
  • zp_load_album
  • zp_load_gallery
  • zp_load_image
  • zp_load_page
  • zp_load_request
  • zp_load_search
  • zp_loggedin
  • zp_mail
  • zp_register_filter
  • zp_remove_filter
  • zp_resampleImage
  • zp_rotateImage
  • zp_session_start
  • zp_setCookie
  • zp_writeString
  • zpErrorHandler
  • zpFormattedDate
  • zpRewriteURL
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
   1: <?php
   2: /////////////////////////////////////////////////////////////////
   3: /// getID3() by James Heinrich <info@getid3.org>               //
   4: //  available at http://getid3.sourceforge.net                 //
   5: //            or http://www.getid3.org                         //
   6: //          also https://github.com/JamesHeinrich/getID3       //
   7: /////////////////////////////////////////////////////////////////
   8: // See readme.txt for more details                             //
   9: /////////////////////////////////////////////////////////////////
  10: //                                                             //
  11: // module.audio.mp3.php                                        //
  12: // module for analyzing MP3 files                              //
  13: // dependencies: NONE                                          //
  14: //                                                            ///
  15: /////////////////////////////////////////////////////////////////
  16: 
  17: 
  18: // number of frames to scan to determine if MPEG-audio sequence is valid
  19: // Lower this number to 5-20 for faster scanning
  20: // Increase this number to 50+ for most accurate detection of valid VBR/CBR
  21: // mpeg-audio streams
  22: define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
  23: 
  24: 
  25: class getid3_mp3 extends getid3_handler
  26: {
  27: 
  28:     public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
  29: 
  30:     public function Analyze() {
  31:         $info = &$this->getid3->info;
  32: 
  33:         $initialOffset = $info['avdataoffset'];
  34: 
  35:         if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
  36:             if ($this->allow_bruteforce) {
  37:                 $info['error'][] = 'Rescanning file in BruteForce mode';
  38:                 $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
  39:             }
  40:         }
  41: 
  42: 
  43:         if (isset($info['mpeg']['audio']['bitrate_mode'])) {
  44:             $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
  45:         }
  46: 
  47:         if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
  48: 
  49:             $synchoffsetwarning = 'Unknown data before synch ';
  50:             if (isset($info['id3v2']['headerlength'])) {
  51:                 $synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
  52:             } elseif ($initialOffset > 0) {
  53:                 $synchoffsetwarning .= '(should be at '.$initialOffset.', ';
  54:             } else {
  55:                 $synchoffsetwarning .= '(should be at beginning of file, ';
  56:             }
  57:             $synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
  58:             if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
  59: 
  60:                 if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
  61: 
  62:                     $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
  63:                     $info['audio']['codec'] = 'LAME';
  64:                     $CurrentDataLAMEversionString = 'LAME3.';
  65: 
  66:                 } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
  67: 
  68:                     $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
  69:                     $info['audio']['codec'] = 'LAME';
  70:                     $CurrentDataLAMEversionString = 'LAME3.';
  71: 
  72:                 }
  73: 
  74:             }
  75:             $info['warning'][] = $synchoffsetwarning;
  76: 
  77:         }
  78: 
  79:         if (isset($info['mpeg']['audio']['LAME'])) {
  80:             $info['audio']['codec'] = 'LAME';
  81:             if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
  82:                 $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
  83:             } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
  84:                 $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
  85:             }
  86:         }
  87: 
  88:         $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
  89:         if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
  90:             // a version number of LAME that does not end with a number like "LAME3.92"
  91:             // or with a closing parenthesis like "LAME3.88 (alpha)"
  92:             // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
  93: 
  94:             // not sure what the actual last frame length will be, but will be less than or equal to 1441
  95:             $PossiblyLongerLAMEversion_FrameLength = 1441;
  96: 
  97:             // Not sure what version of LAME this is - look in padding of last frame for longer version string
  98:             $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
  99:             $this->fseek($PossibleLAMEversionStringOffset);
 100:             $PossiblyLongerLAMEversion_Data = $this->fread($PossiblyLongerLAMEversion_FrameLength);
 101:             switch (substr($CurrentDataLAMEversionString, -1)) {
 102:                 case 'a':
 103:                 case 'b':
 104:                     // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
 105:                     // need to trim off "a" to match longer string
 106:                     $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
 107:                     break;
 108:             }
 109:             if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
 110:                 if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
 111:                     $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3"  "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
 112:                     if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
 113:                         $info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
 114:                     }
 115:                 }
 116:             }
 117:         }
 118:         if (!empty($info['audio']['encoder'])) {
 119:             $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
 120:         }
 121: 
 122:         switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
 123:             case 1:
 124:             case 2:
 125:                 $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
 126:                 break;
 127:         }
 128:         if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
 129:             switch ($info['audio']['dataformat']) {
 130:                 case 'mp1':
 131:                 case 'mp2':
 132:                 case 'mp3':
 133:                     $info['fileformat'] = $info['audio']['dataformat'];
 134:                     break;
 135: 
 136:                 default:
 137:                     $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
 138:                     break;
 139:             }
 140:         }
 141: 
 142:         if (empty($info['fileformat'])) {
 143:             unset($info['fileformat']);
 144:             unset($info['audio']['bitrate_mode']);
 145:             unset($info['avdataoffset']);
 146:             unset($info['avdataend']);
 147:             return false;
 148:         }
 149: 
 150:         $info['mime_type']         = 'audio/mpeg';
 151:         $info['audio']['lossless'] = false;
 152: 
 153:         // Calculate playtime
 154:         if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
 155:             $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
 156:         }
 157: 
 158:         $info['audio']['encoder_options'] = $this->GuessEncoderOptions();
 159: 
 160:         return true;
 161:     }
 162: 
 163: 
 164:     public function GuessEncoderOptions() {
 165:         // shortcuts
 166:         $info = &$this->getid3->info;
 167:         if (!empty($info['mpeg']['audio'])) {
 168:             $thisfile_mpeg_audio = &$info['mpeg']['audio'];
 169:             if (!empty($thisfile_mpeg_audio['LAME'])) {
 170:                 $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
 171:             }
 172:         }
 173: 
 174:         $encoder_options = '';
 175:         static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
 176: 
 177:         if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
 178: 
 179:             $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
 180: 
 181:         } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
 182: 
 183:             $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
 184: 
 185:         } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
 186: 
 187:             static $KnownEncoderValues = array();
 188:             if (empty($KnownEncoderValues)) {
 189: 
 190:                 //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
 191:                 $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane';        // 3.90,   3.90.1, 3.92
 192:                 $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane';        // 3.90.2, 3.90.3, 3.91
 193:                 $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane';        // 3.94,   3.95
 194:                 $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme';       // 3.90,   3.90.1, 3.92
 195:                 $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme';       // 3.90.2, 3.91
 196:                 $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme';       // 3.90.3
 197:                 $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme';  // 3.90,   3.90.1, 3.92
 198:                 $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme';  // 3.90.2, 3.90.3, 3.91
 199:                 $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard';      // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
 200:                 $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard';      // 3.90.3
 201:                 $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
 202:                 $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
 203:                 $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix';                    // 3.90,   3.90.1, 3.92
 204:                 $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix';                    // 3.90.2, 3.90.3, 3.91
 205:                 $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix';                    // 3.94,   3.95
 206:                 $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium';        // 3.90.3
 207:                 $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium';   // 3.90.3
 208: 
 209:                 $KnownEncoderValues[0xFF][99][1][1][1][2][0]     = '--preset studio';            // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
 210:                 $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio';            // 3.90.3, 3.93.1
 211:                 $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio';            // 3.93
 212:                 $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio';            // 3.94,   3.95
 213:                 $KnownEncoderValues[0xC0][88][1][1][1][2][0]     = '--preset cd';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 214:                 $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd';                // 3.90.3, 3.93.1
 215:                 $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd';                // 3.93
 216:                 $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd';                // 3.94,   3.95
 217:                 $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 218:                 $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi';              // 3.90.3, 3.93,   3.93.1
 219:                 $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi';              // 3.94,   3.95
 220:                 $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 221:                 $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio';             // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 222:                 $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 223:                 $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm';     // 3.90.3, 3.93,   3.93.1
 224:                 $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm';     // 3.94,   3.95
 225:                 $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice';             // 3.90.3, 3.93,   3.93.1
 226:                 $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice';             // 3.94,   3.95
 227:                 $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice';             // 3.94a14
 228:                 $KnownEncoderValues[0x28][65][1][1][0][2][7500]  = '--preset mw-us';             // 3.90,   3.90.1, 3.92
 229:                 $KnownEncoderValues[0x28][65][1][1][0][2][7600]  = '--preset mw-us';             // 3.90.2, 3.91
 230:                 $KnownEncoderValues[0x28][58][2][2][0][2][7000]  = '--preset mw-us';             // 3.90.3, 3.93,   3.93.1
 231:                 $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us';             // 3.94,   3.95
 232:                 $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us';             // 3.94a14
 233:                 $KnownEncoderValues[0x28][57][2][1][0][4][8800]  = '--preset mw-us';             // 3.94a15
 234:                 $KnownEncoderValues[0x18][58][2][2][0][2][4000]  = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
 235:                 $KnownEncoderValues[0x18][58][2][2][0][2][3900]  = '--preset phon+/lw/mw-eu/sw'; // 3.93
 236:                 $KnownEncoderValues[0x18][57][2][1][0][4][5900]  = '--preset phon+/lw/mw-eu/sw'; // 3.94,   3.95
 237:                 $KnownEncoderValues[0x18][57][2][1][0][4][6200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
 238:                 $KnownEncoderValues[0x18][57][2][1][0][4][3200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
 239:                 $KnownEncoderValues[0x10][58][2][2][0][2][3800]  = '--preset phone';             // 3.90.3, 3.93.1
 240:                 $KnownEncoderValues[0x10][58][2][2][0][2][3700]  = '--preset phone';             // 3.93
 241:                 $KnownEncoderValues[0x10][57][2][1][0][4][5600]  = '--preset phone';             // 3.94,   3.95
 242:             }
 243: 
 244:             if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
 245: 
 246:                 $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
 247: 
 248:             } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
 249: 
 250:                 $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
 251: 
 252:             } elseif ($info['audio']['bitrate_mode'] == 'vbr') {
 253: 
 254:                 // http://gabriel.mp3-tech.org/mp3infotag.html
 255:                 // int    Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
 256: 
 257: 
 258:                 $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
 259:                 $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
 260:                 $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
 261: 
 262:             } elseif ($info['audio']['bitrate_mode'] == 'cbr') {
 263: 
 264:                 $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
 265: 
 266:             } else {
 267: 
 268:                 $encoder_options = strtoupper($info['audio']['bitrate_mode']);
 269: 
 270:             }
 271: 
 272:         } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
 273: 
 274:             $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
 275: 
 276:         } elseif (!empty($info['audio']['bitrate'])) {
 277: 
 278:             if ($info['audio']['bitrate_mode'] == 'cbr') {
 279:                 $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
 280:             } else {
 281:                 $encoder_options = strtoupper($info['audio']['bitrate_mode']);
 282:             }
 283: 
 284:         }
 285:         if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
 286:             $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
 287:         }
 288: 
 289:         if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
 290:             $encoder_options .= ' --nogap';
 291:         }
 292: 
 293:         if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
 294:             $ExplodedOptions = explode(' ', $encoder_options, 4);
 295:             if ($ExplodedOptions[0] == '--r3mix') {
 296:                 $ExplodedOptions[1] = 'r3mix';
 297:             }
 298:             switch ($ExplodedOptions[0]) {
 299:                 case '--preset':
 300:                 case '--alt-preset':
 301:                 case '--r3mix':
 302:                     if ($ExplodedOptions[1] == 'fast') {
 303:                         $ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
 304:                     }
 305:                     switch ($ExplodedOptions[1]) {
 306:                         case 'portable':
 307:                         case 'medium':
 308:                         case 'standard':
 309:                         case 'extreme':
 310:                         case 'insane':
 311:                         case 'fast portable':
 312:                         case 'fast medium':
 313:                         case 'fast standard':
 314:                         case 'fast extreme':
 315:                         case 'fast insane':
 316:                         case 'r3mix':
 317:                             static $ExpectedLowpass = array(
 318:                                     'insane|20500'        => 20500,
 319:                                     'insane|20600'        => 20600,  // 3.90.2, 3.90.3, 3.91
 320:                                     'medium|18000'        => 18000,
 321:                                     'fast medium|18000'   => 18000,
 322:                                     'extreme|19500'       => 19500,  // 3.90,   3.90.1, 3.92, 3.95
 323:                                     'extreme|19600'       => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
 324:                                     'fast extreme|19500'  => 19500,  // 3.90,   3.90.1, 3.92, 3.95
 325:                                     'fast extreme|19600'  => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
 326:                                     'standard|19000'      => 19000,
 327:                                     'fast standard|19000' => 19000,
 328:                                     'r3mix|19500'         => 19500,  // 3.90,   3.90.1, 3.92
 329:                                     'r3mix|19600'         => 19600,  // 3.90.2, 3.90.3, 3.91
 330:                                     'r3mix|18000'         => 18000,  // 3.94,   3.95
 331:                                 );
 332:                             if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
 333:                                 $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
 334:                             }
 335:                             break;
 336: 
 337:                         default:
 338:                             break;
 339:                     }
 340:                     break;
 341:             }
 342:         }
 343: 
 344:         if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
 345:             if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
 346:                 $encoder_options .= ' --resample 44100';
 347:             } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
 348:                 $encoder_options .= ' --resample 48000';
 349:             } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
 350:                 switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
 351:                     case 0: // <= 32000
 352:                         // may or may not be same as source frequency - ignore
 353:                         break;
 354:                     case 1: // 44100
 355:                     case 2: // 48000
 356:                     case 3: // 48000+
 357:                         $ExplodedOptions = explode(' ', $encoder_options, 4);
 358:                         switch ($ExplodedOptions[0]) {
 359:                             case '--preset':
 360:                             case '--alt-preset':
 361:                                 switch ($ExplodedOptions[1]) {
 362:                                     case 'fast':
 363:                                     case 'portable':
 364:                                     case 'medium':
 365:                                     case 'standard':
 366:                                     case 'extreme':
 367:                                     case 'insane':
 368:                                         $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
 369:                                         break;
 370: 
 371:                                     default:
 372:                                         static $ExpectedResampledRate = array(
 373:                                                 'phon+/lw/mw-eu/sw|16000' => 16000,
 374:                                                 'mw-us|24000'             => 24000, // 3.95
 375:                                                 'mw-us|32000'             => 32000, // 3.93
 376:                                                 'mw-us|16000'             => 16000, // 3.92
 377:                                                 'phone|16000'             => 16000,
 378:                                                 'phone|11025'             => 11025, // 3.94a15
 379:                                                 'radio|32000'             => 32000, // 3.94a15
 380:                                                 'fm/radio|32000'          => 32000, // 3.92
 381:                                                 'fm|32000'                => 32000, // 3.90
 382:                                                 'voice|32000'             => 32000);
 383:                                         if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
 384:                                             $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
 385:                                         }
 386:                                         break;
 387:                                 }
 388:                                 break;
 389: 
 390:                             case '--r3mix':
 391:                             default:
 392:                                 $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
 393:                                 break;
 394:                         }
 395:                         break;
 396:                 }
 397:             }
 398:         }
 399:         if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
 400:             //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
 401:             $encoder_options = strtoupper($info['audio']['bitrate_mode']);
 402:         }
 403: 
 404:         return $encoder_options;
 405:     }
 406: 
 407: 
 408:     public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
 409:         static $MPEGaudioVersionLookup;
 410:         static $MPEGaudioLayerLookup;
 411:         static $MPEGaudioBitrateLookup;
 412:         static $MPEGaudioFrequencyLookup;
 413:         static $MPEGaudioChannelModeLookup;
 414:         static $MPEGaudioModeExtensionLookup;
 415:         static $MPEGaudioEmphasisLookup;
 416:         if (empty($MPEGaudioVersionLookup)) {
 417:             $MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
 418:             $MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
 419:             $MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
 420:             $MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
 421:             $MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
 422:             $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
 423:             $MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
 424:         }
 425: 
 426:         if ($this->fseek($offset) != 0) {
 427:             $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
 428:             return false;
 429:         }
 430:         //$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
 431:         $headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
 432: 
 433:         // MP3 audio frame structure:
 434:         // $aa $aa $aa $aa [$bb $bb] $cc...
 435:         // where $aa..$aa is the four-byte mpeg-audio header (below)
 436:         // $bb $bb is the optional 2-byte CRC
 437:         // and $cc... is the audio data
 438: 
 439:         $head4 = substr($headerstring, 0, 4);
 440:         static $MPEGaudioHeaderDecodeCache = array();
 441:         if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
 442:             $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
 443:         } else {
 444:             $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
 445:             $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
 446:         }
 447: 
 448:         static $MPEGaudioHeaderValidCache = array();
 449:         if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
 450:             //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true);  // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
 451:             $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
 452:         }
 453: 
 454:         // shortcut
 455:         if (!isset($info['mpeg']['audio'])) {
 456:             $info['mpeg']['audio'] = array();
 457:         }
 458:         $thisfile_mpeg_audio = &$info['mpeg']['audio'];
 459: 
 460: 
 461:         if ($MPEGaudioHeaderValidCache[$head4]) {
 462:             $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
 463:         } else {
 464:             $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset;
 465:             return false;
 466:         }
 467: 
 468:         if (!$FastMPEGheaderScan) {
 469:             $thisfile_mpeg_audio['version']       = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
 470:             $thisfile_mpeg_audio['layer']         = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
 471: 
 472:             $thisfile_mpeg_audio['channelmode']   = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
 473:             $thisfile_mpeg_audio['channels']      = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
 474:             $thisfile_mpeg_audio['sample_rate']   = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
 475:             $thisfile_mpeg_audio['protection']    = !$thisfile_mpeg_audio['raw']['protection'];
 476:             $thisfile_mpeg_audio['private']       = (bool) $thisfile_mpeg_audio['raw']['private'];
 477:             $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
 478:             $thisfile_mpeg_audio['copyright']     = (bool) $thisfile_mpeg_audio['raw']['copyright'];
 479:             $thisfile_mpeg_audio['original']      = (bool) $thisfile_mpeg_audio['raw']['original'];
 480:             $thisfile_mpeg_audio['emphasis']      = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
 481: 
 482:             $info['audio']['channels']    = $thisfile_mpeg_audio['channels'];
 483:             $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
 484: 
 485:             if ($thisfile_mpeg_audio['protection']) {
 486:                 $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
 487:             }
 488:         }
 489: 
 490:         if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
 491:             // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
 492:             $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
 493:             $thisfile_mpeg_audio['raw']['bitrate'] = 0;
 494:         }
 495:         $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
 496:         $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
 497: 
 498:         if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
 499:             // only skip multiple frame check if free-format bitstream found at beginning of file
 500:             // otherwise is quite possibly simply corrupted data
 501:             $recursivesearch = false;
 502:         }
 503: 
 504:         // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
 505:         if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
 506: 
 507:             $info['audio']['dataformat'] = 'mp2';
 508:             switch ($thisfile_mpeg_audio['channelmode']) {
 509: 
 510:                 case 'mono':
 511:                     if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
 512:                         // these are ok
 513:                     } else {
 514:                         $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
 515:                         return false;
 516:                     }
 517:                     break;
 518: 
 519:                 case 'stereo':
 520:                 case 'joint stereo':
 521:                 case 'dual channel':
 522:                     if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
 523:                         // these are ok
 524:                     } else {
 525:                         $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
 526:                         return false;
 527:                     }
 528:                     break;
 529: 
 530:             }
 531: 
 532:         }
 533: 
 534: 
 535:         if ($info['audio']['sample_rate'] > 0) {
 536:             $thisfile_mpeg_audio['framelength'] = self::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']);
 537:         }
 538: 
 539:         $nextframetestoffset = $offset + 1;
 540:         if ($thisfile_mpeg_audio['bitrate'] != 'free') {
 541: 
 542:             $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
 543: 
 544:             if (isset($thisfile_mpeg_audio['framelength'])) {
 545:                 $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
 546:             } else {
 547:                 $info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
 548:                 return false;
 549:             }
 550: 
 551:         }
 552: 
 553:         $ExpectedNumberOfAudioBytes = 0;
 554: 
 555:         ////////////////////////////////////////////////////////////////////////////////////
 556:         // Variable-bitrate headers
 557: 
 558:         if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
 559:             // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
 560:             // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
 561: 
 562:             $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
 563:             $thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
 564:             $info['audio']['codec']                = 'Fraunhofer';
 565: 
 566:             $SideInfoData = substr($headerstring, 4 + 2, 32);
 567: 
 568:             $FraunhoferVBROffset = 36;
 569: 
 570:             $thisfile_mpeg_audio['VBR_encoder_version']     = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  4, 2)); // VbriVersion
 571:             $thisfile_mpeg_audio['VBR_encoder_delay']       = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  6, 2)); // VbriDelay
 572:             $thisfile_mpeg_audio['VBR_quality']             = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  8, 2)); // VbriQuality
 573:             $thisfile_mpeg_audio['VBR_bytes']               = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
 574:             $thisfile_mpeg_audio['VBR_frames']              = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
 575:             $thisfile_mpeg_audio['VBR_seek_offsets']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
 576:             $thisfile_mpeg_audio['VBR_seek_scale']          = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
 577:             $thisfile_mpeg_audio['VBR_entry_bytes']         = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
 578:             $thisfile_mpeg_audio['VBR_entry_frames']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
 579: 
 580:             $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
 581: 
 582:             $previousbyteoffset = $offset;
 583:             for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
 584:                 $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
 585:                 $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
 586:                 $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
 587:                 $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
 588:                 $previousbyteoffset += $Fraunhofer_OffsetN;
 589:             }
 590: 
 591: 
 592:         } else {
 593: 
 594:             // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
 595:             // depending on MPEG layer and number of channels
 596: 
 597:             $VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
 598:             $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
 599: 
 600:             if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
 601:                 // 'Xing' is traditional Xing VBR frame
 602:                 // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
 603:                 // 'Info' *can* legally be used to specify a VBR file as well, however.
 604: 
 605:                 // http://www.multiweb.cz/twoinches/MP3inside.htm
 606:                 //00..03 = "Xing" or "Info"
 607:                 //04..07 = Flags:
 608:                 //  0x01  Frames Flag     set if value for number of frames in file is stored
 609:                 //  0x02  Bytes Flag      set if value for filesize in bytes is stored
 610:                 //  0x04  TOC Flag        set if values for TOC are stored
 611:                 //  0x08  VBR Scale Flag  set if values for VBR scale is stored
 612:                 //08..11  Frames: Number of frames in file (including the first Xing/Info one)
 613:                 //12..15  Bytes:  File length in Bytes
 614:                 //16..115  TOC (Table of Contents):
 615:                 //  Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
 616:                 //  Each Byte has a value according this formula:
 617:                 //  (TOC[i] / 256) * fileLenInBytes
 618:                 //  So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use:
 619:                 //  TOC[(60/240)*100] = TOC[25]
 620:                 //  and corresponding Byte in file is then approximately at:
 621:                 //  (TOC[25]/256) * 5000000
 622:                 //116..119  VBR Scale
 623: 
 624: 
 625:                 // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
 626: //              if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
 627:                     $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
 628:                     $thisfile_mpeg_audio['VBR_method']   = 'Xing';
 629: //              } else {
 630: //                  $ScanAsCBR = true;
 631: //                  $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
 632: //              }
 633: 
 634:                 $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
 635: 
 636:                 $thisfile_mpeg_audio['xing_flags']['frames']    = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
 637:                 $thisfile_mpeg_audio['xing_flags']['bytes']     = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
 638:                 $thisfile_mpeg_audio['xing_flags']['toc']       = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
 639:                 $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
 640: 
 641:                 if ($thisfile_mpeg_audio['xing_flags']['frames']) {
 642:                     $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset +  8, 4));
 643:                     //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
 644:                 }
 645:                 if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
 646:                     $thisfile_mpeg_audio['VBR_bytes']  = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
 647:                 }
 648: 
 649:                 //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
 650:                 //if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
 651:                 if (!empty($thisfile_mpeg_audio['VBR_frames'])) {
 652:                     $used_filesize  = 0;
 653:                     if (!empty($thisfile_mpeg_audio['VBR_bytes'])) {
 654:                         $used_filesize = $thisfile_mpeg_audio['VBR_bytes'];
 655:                     } elseif (!empty($info['filesize'])) {
 656:                         $used_filesize  = $info['filesize'];
 657:                         $used_filesize -= intval(@$info['id3v2']['headerlength']);
 658:                         $used_filesize -= (isset($info['id3v1']) ? 128 : 0);
 659:                         $used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0);
 660:                         $info['warning'][] = 'MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes';
 661:                     }
 662: 
 663:                     $framelengthfloat = $used_filesize / $thisfile_mpeg_audio['VBR_frames'];
 664: 
 665:                     if ($thisfile_mpeg_audio['layer'] == '1') {
 666:                         // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
 667:                         //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
 668:                         $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
 669:                     } else {
 670:                         // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
 671:                         //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
 672:                         $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
 673:                     }
 674:                     $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
 675:                 }
 676: 
 677:                 if ($thisfile_mpeg_audio['xing_flags']['toc']) {
 678:                     $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
 679:                     for ($i = 0; $i < 100; $i++) {
 680:                         $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
 681:                     }
 682:                 }
 683:                 if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
 684:                     $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
 685:                 }
 686: 
 687: 
 688:                 // http://gabriel.mp3-tech.org/mp3infotag.html
 689:                 if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
 690: 
 691:                     // shortcut
 692:                     $thisfile_mpeg_audio['LAME'] = array();
 693:                     $thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];
 694: 
 695: 
 696:                     $thisfile_mpeg_audio_lame['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
 697:                     $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
 698: 
 699:                     if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
 700: 
 701:                         // extra 11 chars are not part of version string when LAMEtag present
 702:                         unset($thisfile_mpeg_audio_lame['long_version']);
 703: 
 704:                         // It the LAME tag was only introduced in LAME v3.90
 705:                         // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
 706: 
 707:                         // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
 708:                         // are assuming a 'Xing' identifier offset of 0x24, which is the case for
 709:                         // MPEG-1 non-mono, but not for other combinations
 710:                         $LAMEtagOffsetContant = $VBRidOffset - 0x24;
 711: 
 712:                         // shortcuts
 713:                         $thisfile_mpeg_audio_lame['RGAD']    = array('track'=>array(), 'album'=>array());
 714:                         $thisfile_mpeg_audio_lame_RGAD       = &$thisfile_mpeg_audio_lame['RGAD'];
 715:                         $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
 716:                         $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
 717:                         $thisfile_mpeg_audio_lame['raw'] = array();
 718:                         $thisfile_mpeg_audio_lame_raw    = &$thisfile_mpeg_audio_lame['raw'];
 719: 
 720:                         // byte $9B  VBR Quality
 721:                         // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
 722:                         // Actually overwrites original Xing bytes
 723:                         unset($thisfile_mpeg_audio['VBR_scale']);
 724:                         $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
 725: 
 726:                         // bytes $9C-$A4  Encoder short VersionString
 727:                         $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
 728: 
 729:                         // byte $A5  Info Tag revision + VBR method
 730:                         $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
 731: 
 732:                         $thisfile_mpeg_audio_lame['tag_revision']   = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
 733:                         $thisfile_mpeg_audio_lame_raw['vbr_method'] =  $LAMEtagRevisionVBRmethod & 0x0F;
 734:                         $thisfile_mpeg_audio_lame['vbr_method']     = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
 735:                         $thisfile_mpeg_audio['bitrate_mode']        = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
 736: 
 737:                         // byte $A6  Lowpass filter value
 738:                         $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
 739: 
 740:                         // bytes $A7-$AE  Replay Gain
 741:                         // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
 742:                         // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
 743:                         if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
 744:                             // LAME 3.94a16 and later - 9.23 fixed point
 745:                             // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
 746:                             $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
 747:                         } else {
 748:                             // LAME 3.94a15 and earlier - 32-bit floating point
 749:                             // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
 750:                             $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
 751:                         }
 752:                         if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
 753:                             unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
 754:                         } else {
 755:                             $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
 756:                         }
 757: 
 758:                         $thisfile_mpeg_audio_lame_raw['RGAD_track']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
 759:                         $thisfile_mpeg_audio_lame_raw['RGAD_album']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
 760: 
 761: 
 762:                         if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
 763: 
 764:                             $thisfile_mpeg_audio_lame_RGAD_track['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
 765:                             $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
 766:                             $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
 767:                             $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] =  $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
 768:                             $thisfile_mpeg_audio_lame_RGAD_track['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
 769:                             $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
 770:                             $thisfile_mpeg_audio_lame_RGAD_track['gain_db']    = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
 771: 
 772:                             if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
 773:                                 $info['replay_gain']['track']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
 774:                             }
 775:                             $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
 776:                             $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
 777:                         } else {
 778:                             unset($thisfile_mpeg_audio_lame_RGAD['track']);
 779:                         }
 780:                         if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
 781: 
 782:                             $thisfile_mpeg_audio_lame_RGAD_album['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
 783:                             $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
 784:                             $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
 785:                             $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
 786:                             $thisfile_mpeg_audio_lame_RGAD_album['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
 787:                             $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
 788:                             $thisfile_mpeg_audio_lame_RGAD_album['gain_db']    = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
 789: 
 790:                             if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
 791:                                 $info['replay_gain']['album']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
 792:                             }
 793:                             $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
 794:                             $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
 795:                         } else {
 796:                             unset($thisfile_mpeg_audio_lame_RGAD['album']);
 797:                         }
 798:                         if (empty($thisfile_mpeg_audio_lame_RGAD)) {
 799:                             unset($thisfile_mpeg_audio_lame['RGAD']);
 800:                         }
 801: 
 802: 
 803:                         // byte $AF  Encoding flags + ATH Type
 804:                         $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
 805:                         $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune']   = (bool) ($EncodingFlagsATHtype & 0x10);
 806:                         $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
 807:                         $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']  = (bool) ($EncodingFlagsATHtype & 0x40);
 808:                         $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']  = (bool) ($EncodingFlagsATHtype & 0x80);
 809:                         $thisfile_mpeg_audio_lame['ath_type']                      =         $EncodingFlagsATHtype & 0x0F;
 810: 
 811:                         // byte $B0  if ABR {specified bitrate} else {minimal bitrate}
 812:                         $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
 813:                         if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
 814:                             $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
 815:                         } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
 816:                             // ignore
 817:                         } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
 818:                             $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
 819:                         }
 820: 
 821:                         // bytes $B1-$B3  Encoder delays
 822:                         $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
 823:                         $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
 824:                         $thisfile_mpeg_audio_lame['end_padding']   =  $EncoderDelays & 0x000FFF;
 825: 
 826:                         // byte $B4  Misc
 827:                         $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
 828:                         $thisfile_mpeg_audio_lame_raw['noise_shaping']       = ($MiscByte & 0x03);
 829:                         $thisfile_mpeg_audio_lame_raw['stereo_mode']         = ($MiscByte & 0x1C) >> 2;
 830:                         $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
 831:                         $thisfile_mpeg_audio_lame_raw['source_sample_freq']  = ($MiscByte & 0xC0) >> 6;
 832:                         $thisfile_mpeg_audio_lame['noise_shaping']       = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
 833:                         $thisfile_mpeg_audio_lame['stereo_mode']         = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
 834:                         $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
 835:                         $thisfile_mpeg_audio_lame['source_sample_freq']  = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
 836: 
 837:                         // byte $B5  MP3 Gain
 838:                         $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
 839:                         $thisfile_mpeg_audio_lame['mp3_gain_db']     = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
 840:                         $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
 841: 
 842:                         // bytes $B6-$B7  Preset and surround info
 843:                         $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
 844:                         // Reserved                                                    = ($PresetSurroundBytes & 0xC000);
 845:                         $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
 846:                         $thisfile_mpeg_audio_lame['surround_info']     = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
 847:                         $thisfile_mpeg_audio_lame['preset_used_id']    = ($PresetSurroundBytes & 0x07FF);
 848:                         $thisfile_mpeg_audio_lame['preset_used']       = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
 849:                         if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
 850:                             $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
 851:                         }
 852:                         if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
 853:                             // this may change if 3.90.4 ever comes out
 854:                             $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
 855:                         }
 856: 
 857:                         // bytes $B8-$BB  MusicLength
 858:                         $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
 859:                         $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
 860: 
 861:                         // bytes $BC-$BD  MusicCRC
 862:                         $thisfile_mpeg_audio_lame['music_crc']    = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
 863: 
 864:                         // bytes $BE-$BF  CRC-16 of Info Tag
 865:                         $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
 866: 
 867: 
 868:                         // LAME CBR
 869:                         if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
 870: 
 871:                             $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
 872:                             $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
 873:                             $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
 874:                             //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
 875:                             //  $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
 876:                             //}
 877: 
 878:                         }
 879: 
 880:                     }
 881:                 }
 882: 
 883:             } else {
 884: 
 885:                 // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
 886:                 $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
 887:                 if ($recursivesearch) {
 888:                     $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
 889:                     if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
 890:                         $recursivesearch = false;
 891:                         $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
 892:                     }
 893:                     if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
 894:                         $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
 895:                     }
 896:                 }
 897: 
 898:             }
 899: 
 900:         }
 901: 
 902:         if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
 903:             if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
 904:                 if ($this->isDependencyFor('matroska') || $this->isDependencyFor('riff')) {
 905:                     // ignore, audio data is broken into chunks so will always be data "missing"
 906:                 }
 907:                 elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
 908:                     $this->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
 909:                 }
 910:                 else {
 911:                     $this->warning('Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)');
 912:                 }
 913:             } else {
 914:                 if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
 915:                 //  $prenullbytefileoffset = $this->ftell();
 916:                 //  $this->fseek($info['avdataend']);
 917:                 //  $PossibleNullByte = $this->fread(1);
 918:                 //  $this->fseek($prenullbytefileoffset);
 919:                 //  if ($PossibleNullByte === "\x00") {
 920:                         $info['avdataend']--;
 921:                 //      $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
 922:                 //  } else {
 923:                 //      $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
 924:                 //  }
 925:                 } else {
 926:                     $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
 927:                 }
 928:             }
 929:         }
 930: 
 931:         if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
 932:             if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
 933:                 $framebytelength = $this->FreeFormatFrameLength($offset, true);
 934:                 if ($framebytelength > 0) {
 935:                     $thisfile_mpeg_audio['framelength'] = $framebytelength;
 936:                     if ($thisfile_mpeg_audio['layer'] == '1') {
 937:                         // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
 938:                         $info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
 939:                     } else {
 940:                         // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
 941:                         $info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
 942:                     }
 943:                 } else {
 944:                     $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
 945:                 }
 946:             }
 947:         }
 948: 
 949:         if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
 950:             switch ($thisfile_mpeg_audio['bitrate_mode']) {
 951:                 case 'vbr':
 952:                 case 'abr':
 953:                     $bytes_per_frame = 1152;
 954:                     if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
 955:                         $bytes_per_frame = 384;
 956:                     } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
 957:                         $bytes_per_frame = 576;
 958:                     }
 959:                     $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0);
 960:                     if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
 961:                         $info['audio']['bitrate']       = $thisfile_mpeg_audio['VBR_bitrate'];
 962:                         $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
 963:                     }
 964:                     break;
 965:             }
 966:         }
 967: 
 968:         // End variable-bitrate headers
 969:         ////////////////////////////////////////////////////////////////////////////////////
 970: 
 971:         if ($recursivesearch) {
 972: 
 973:             if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
 974:                 return false;
 975:             }
 976: 
 977:         }
 978: 
 979: 
 980:         //if (false) {
 981:         //    // experimental side info parsing section - not returning anything useful yet
 982:         //
 983:         //    $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
 984:         //    $SideInfoOffset = 0;
 985:         //
 986:         //    if ($thisfile_mpeg_audio['version'] == '1') {
 987:         //        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
 988:         //            // MPEG-1 (mono)
 989:         //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
 990:         //            $SideInfoOffset += 9;
 991:         //            $SideInfoOffset += 5;
 992:         //        } else {
 993:         //            // MPEG-1 (stereo, joint-stereo, dual-channel)
 994:         //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
 995:         //            $SideInfoOffset += 9;
 996:         //            $SideInfoOffset += 3;
 997:         //        }
 998:         //    } else { // 2 or 2.5
 999:         //        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
1000:         //            // MPEG-2, MPEG-2.5 (mono)
1001:         //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1002:         //            $SideInfoOffset += 8;
1003:         //            $SideInfoOffset += 1;
1004:         //        } else {
1005:         //            // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
1006:         //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1007:         //            $SideInfoOffset += 8;
1008:         //            $SideInfoOffset += 2;
1009:         //        }
1010:         //    }
1011:         //
1012:         //    if ($thisfile_mpeg_audio['version'] == '1') {
1013:         //        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1014:         //            for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
1015:         //                $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1016:         //                $SideInfoOffset += 2;
1017:         //            }
1018:         //        }
1019:         //    }
1020:         //    for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
1021:         //        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1022:         //            $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
1023:         //            $SideInfoOffset += 12;
1024:         //            $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1025:         //            $SideInfoOffset += 9;
1026:         //            $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1027:         //            $SideInfoOffset += 8;
1028:         //            if ($thisfile_mpeg_audio['version'] == '1') {
1029:         //                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1030:         //                $SideInfoOffset += 4;
1031:         //            } else {
1032:         //                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1033:         //                $SideInfoOffset += 9;
1034:         //            }
1035:         //            $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1036:         //            $SideInfoOffset += 1;
1037:         //
1038:         //            if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
1039:         //
1040:         //                $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
1041:         //                $SideInfoOffset += 2;
1042:         //                $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1043:         //                $SideInfoOffset += 1;
1044:         //
1045:         //                for ($region = 0; $region < 2; $region++) {
1046:         //                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1047:         //                    $SideInfoOffset += 5;
1048:         //                }
1049:         //                $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
1050:         //
1051:         //                for ($window = 0; $window < 3; $window++) {
1052:         //                    $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1053:         //                    $SideInfoOffset += 3;
1054:         //                }
1055:         //
1056:         //            } else {
1057:         //
1058:         //                for ($region = 0; $region < 3; $region++) {
1059:         //                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1060:         //                    $SideInfoOffset += 5;
1061:         //                }
1062:         //
1063:         //                $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1064:         //                $SideInfoOffset += 4;
1065:         //                $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1066:         //                $SideInfoOffset += 3;
1067:         //                $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
1068:         //            }
1069:         //
1070:         //            if ($thisfile_mpeg_audio['version'] == '1') {
1071:         //                $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1072:         //                $SideInfoOffset += 1;
1073:         //            }
1074:         //            $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1075:         //            $SideInfoOffset += 1;
1076:         //            $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1077:         //            $SideInfoOffset += 1;
1078:         //        }
1079:         //    }
1080:         //}
1081: 
1082:         return true;
1083:     }
1084: 
1085:     public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
1086:         $info = &$this->getid3->info;
1087:         $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1088:         $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
1089: 
1090:         for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
1091:             // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
1092:             if (($nextframetestoffset + 4) >= $info['avdataend']) {
1093:                 // end of file
1094:                 return true;
1095:             }
1096: 
1097:             $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1098:             if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
1099:                 if ($ScanAsCBR) {
1100:                     // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
1101:                     if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
1102:                         return false;
1103:                     }
1104:                 }
1105: 
1106: 
1107:                 // next frame is OK, get ready to check the one after that
1108:                 if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
1109:                     $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
1110:                 } else {
1111:                     $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
1112:                     return false;
1113:                 }
1114: 
1115:             } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
1116: 
1117:                 // it's not the end of the file, but there's not enough data left for another frame, so assume it's garbage/padding and return OK
1118:                 return true;
1119: 
1120:             } else {
1121: 
1122:                 // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
1123:                 $info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
1124: 
1125:                 return false;
1126:             }
1127:         }
1128:         return true;
1129:     }
1130: 
1131:     public function FreeFormatFrameLength($offset, $deepscan=false) {
1132:         $info = &$this->getid3->info;
1133: 
1134:         $this->fseek($offset);
1135:         $MPEGaudioData = $this->fread(32768);
1136: 
1137:         $SyncPattern1 = substr($MPEGaudioData, 0, 4);
1138:         // may be different pattern due to padding
1139:         $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
1140:         if ($SyncPattern2 === $SyncPattern1) {
1141:             $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
1142:         }
1143: 
1144:         $framelength = false;
1145:         $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
1146:         $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
1147:         if ($framelength1 > 4) {
1148:             $framelength = $framelength1;
1149:         }
1150:         if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1151:             $framelength = $framelength2;
1152:         }
1153:         if (!$framelength) {
1154: 
1155:             // LAME 3.88 has a different value for modeextension on the first frame vs the rest
1156:             $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
1157:             $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
1158: 
1159:             if ($framelength1 > 4) {
1160:                 $framelength = $framelength1;
1161:             }
1162:             if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1163:                 $framelength = $framelength2;
1164:             }
1165:             if (!$framelength) {
1166:                 $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
1167:                 return false;
1168:             } else {
1169:                 $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
1170:                 $info['audio']['codec']   = 'LAME';
1171:                 $info['audio']['encoder'] = 'LAME3.88';
1172:                 $SyncPattern1 = substr($SyncPattern1, 0, 3);
1173:                 $SyncPattern2 = substr($SyncPattern2, 0, 3);
1174:             }
1175:         }
1176: 
1177:         if ($deepscan) {
1178: 
1179:             $ActualFrameLengthValues = array();
1180:             $nextoffset = $offset + $framelength;
1181:             while ($nextoffset < ($info['avdataend'] - 6)) {
1182:                 $this->fseek($nextoffset - 1);
1183:                 $NextSyncPattern = $this->fread(6);
1184:                 if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
1185:                     // good - found where expected
1186:                     $ActualFrameLengthValues[] = $framelength;
1187:                 } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
1188:                     // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
1189:                     $ActualFrameLengthValues[] = ($framelength - 1);
1190:                     $nextoffset--;
1191:                 } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
1192:                     // ok - found one byte later than expected (last frame was padded, first frame wasn't)
1193:                     $ActualFrameLengthValues[] = ($framelength + 1);
1194:                     $nextoffset++;
1195:                 } else {
1196:                     $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
1197:                     return false;
1198:                 }
1199:                 $nextoffset += $framelength;
1200:             }
1201:             if (count($ActualFrameLengthValues) > 0) {
1202:                 $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
1203:             }
1204:         }
1205:         return $framelength;
1206:     }
1207: 
1208:     public function getOnlyMPEGaudioInfoBruteForce() {
1209:         $MPEGaudioHeaderDecodeCache   = array();
1210:         $MPEGaudioHeaderValidCache    = array();
1211:         $MPEGaudioHeaderLengthCache   = array();
1212:         $MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
1213:         $MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
1214:         $MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
1215:         $MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
1216:         $MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
1217:         $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1218:         $MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
1219:         $LongMPEGversionLookup        = array();
1220:         $LongMPEGlayerLookup          = array();
1221:         $LongMPEGbitrateLookup        = array();
1222:         $LongMPEGpaddingLookup        = array();
1223:         $LongMPEGfrequencyLookup      = array();
1224:         $Distribution['bitrate']      = array();
1225:         $Distribution['frequency']    = array();
1226:         $Distribution['layer']        = array();
1227:         $Distribution['version']      = array();
1228:         $Distribution['padding']      = array();
1229: 
1230:         $info = &$this->getid3->info;
1231:         $this->fseek($info['avdataoffset']);
1232: 
1233:         $max_frames_scan = 5000;
1234:         $frames_scanned  = 0;
1235: 
1236:         $previousvalidframe = $info['avdataoffset'];
1237:         while ($this->ftell() < $info['avdataend']) {
1238:             set_time_limit(30);
1239:             $head4 = $this->fread(4);
1240:             if (strlen($head4) < 4) {
1241:                 break;
1242:             }
1243:             if ($head4{0} != "\xFF") {
1244:                 for ($i = 1; $i < 4; $i++) {
1245:                     if ($head4{$i} == "\xFF") {
1246:                         $this->fseek($i - 4, SEEK_CUR);
1247:                         continue 2;
1248:                     }
1249:                 }
1250:                 continue;
1251:             }
1252:             if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
1253:                 $MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
1254:             }
1255:             if (!isset($MPEGaudioHeaderValidCache[$head4])) {
1256:                 $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
1257:             }
1258:             if ($MPEGaudioHeaderValidCache[$head4]) {
1259: 
1260:                 if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
1261:                     $LongMPEGversionLookup[$head4]   = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
1262:                     $LongMPEGlayerLookup[$head4]     = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
1263:                     $LongMPEGbitrateLookup[$head4]   = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
1264:                     $LongMPEGpaddingLookup[$head4]   = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
1265:                     $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
1266:                     $MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
1267:                         $LongMPEGbitrateLookup[$head4],
1268:                         $LongMPEGversionLookup[$head4],
1269:                         $LongMPEGlayerLookup[$head4],
1270:                         $LongMPEGpaddingLookup[$head4],
1271:                         $LongMPEGfrequencyLookup[$head4]);
1272:                 }
1273:                 if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
1274:                     $WhereWeWere = $this->ftell();
1275:                     $this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
1276:                     $next4 = $this->fread(4);
1277:                     if ($next4{0} == "\xFF") {
1278:                         if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
1279:                             $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
1280:                         }
1281:                         if (!isset($MPEGaudioHeaderValidCache[$next4])) {
1282:                             $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
1283:                         }
1284:                         if ($MPEGaudioHeaderValidCache[$next4]) {
1285:                             $this->fseek(-4, SEEK_CUR);
1286: 
1287:                             getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
1288:                             getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
1289:                             getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
1290:                             getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
1291:                             getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
1292:                             if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
1293:                                 $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
1294:                                 $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1295:                                 foreach ($Distribution as $key1 => $value1) {
1296:                                     foreach ($value1 as $key2 => $value2) {
1297:                                         $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
1298:                                     }
1299:                                 }
1300:                                 break;
1301:                             }
1302:                             continue;
1303:                         }
1304:                     }
1305:                     unset($next4);
1306:                     $this->fseek($WhereWeWere - 3);
1307:                 }
1308: 
1309:             }
1310:         }
1311:         foreach ($Distribution as $key => $value) {
1312:             ksort($Distribution[$key], SORT_NUMERIC);
1313:         }
1314:         ksort($Distribution['version'], SORT_STRING);
1315:         $info['mpeg']['audio']['bitrate_distribution']   = $Distribution['bitrate'];
1316:         $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
1317:         $info['mpeg']['audio']['layer_distribution']     = $Distribution['layer'];
1318:         $info['mpeg']['audio']['version_distribution']   = $Distribution['version'];
1319:         $info['mpeg']['audio']['padding_distribution']   = $Distribution['padding'];
1320:         if (count($Distribution['version']) > 1) {
1321:             $info['error'][] = 'Corrupt file - more than one MPEG version detected';
1322:         }
1323:         if (count($Distribution['layer']) > 1) {
1324:             $info['error'][] = 'Corrupt file - more than one MPEG layer detected';
1325:         }
1326:         if (count($Distribution['frequency']) > 1) {
1327:             $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
1328:         }
1329: 
1330: 
1331:         $bittotal = 0;
1332:         foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
1333:             if ($bitratevalue != 'free') {
1334:                 $bittotal += ($bitratevalue * $bitratecount);
1335:             }
1336:         }
1337:         $info['mpeg']['audio']['frame_count']  = array_sum($Distribution['bitrate']);
1338:         if ($info['mpeg']['audio']['frame_count'] == 0) {
1339:             $info['error'][] = 'no MPEG audio frames found';
1340:             return false;
1341:         }
1342:         $info['mpeg']['audio']['bitrate']      = ($bittotal / $info['mpeg']['audio']['frame_count']);
1343:         $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
1344:         $info['mpeg']['audio']['sample_rate']  = getid3_lib::array_max($Distribution['frequency'], true);
1345: 
1346:         $info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
1347:         $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1348:         $info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
1349:         $info['audio']['dataformat']   = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
1350:         $info['fileformat']            = $info['audio']['dataformat'];
1351: 
1352:         return true;
1353:     }
1354: 
1355: 
1356:     public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
1357:         // looks for synch, decodes MPEG audio header
1358: 
1359:         $info = &$this->getid3->info;
1360: 
1361:         static $MPEGaudioVersionLookup;
1362:         static $MPEGaudioLayerLookup;
1363:         static $MPEGaudioBitrateLookup;
1364:         if (empty($MPEGaudioVersionLookup)) {
1365:            $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1366:            $MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
1367:            $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1368: 
1369:         }
1370: 
1371:         $this->fseek($avdataoffset);
1372:         $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
1373:         if ($sync_seek_buffer_size <= 0) {
1374:             $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
1375:             return false;
1376:         }
1377:         $header = $this->fread($sync_seek_buffer_size);
1378:         $sync_seek_buffer_size = strlen($header);
1379:         $SynchSeekOffset = 0;
1380:         while ($SynchSeekOffset < $sync_seek_buffer_size) {
1381:             if ((($avdataoffset + $SynchSeekOffset)  < $info['avdataend']) && !feof($this->getid3->fp)) {
1382: 
1383:                 if ($SynchSeekOffset > $sync_seek_buffer_size) {
1384:                     // if a synch's not found within the first 128k bytes, then give up
1385:                     $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
1386:                     if (isset($info['audio']['bitrate'])) {
1387:                         unset($info['audio']['bitrate']);
1388:                     }
1389:                     if (isset($info['mpeg']['audio'])) {
1390:                         unset($info['mpeg']['audio']);
1391:                     }
1392:                     if (empty($info['mpeg'])) {
1393:                         unset($info['mpeg']);
1394:                     }
1395:                     return false;
1396: 
1397:                 } elseif (feof($this->getid3->fp)) {
1398: 
1399:                     $info['error'][] = 'Could not find valid MPEG audio synch before end of file';
1400:                     if (isset($info['audio']['bitrate'])) {
1401:                         unset($info['audio']['bitrate']);
1402:                     }
1403:                     if (isset($info['mpeg']['audio'])) {
1404:                         unset($info['mpeg']['audio']);
1405:                     }
1406:                     if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
1407:                         unset($info['mpeg']);
1408:                     }
1409:                     return false;
1410:                 }
1411:             }
1412: 
1413:             if (($SynchSeekOffset + 1) >= strlen($header)) {
1414:                 $info['error'][] = 'Could not find valid MPEG synch before end of file';
1415:                 return false;
1416:             }
1417: 
1418:             if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
1419:                 if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
1420:                     $FirstFrameThisfileInfo = $info;
1421:                     $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
1422:                     if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
1423:                         // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1424:                         // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1425:                         unset($FirstFrameThisfileInfo);
1426:                     }
1427:                 }
1428: 
1429:                 $dummy = $info; // only overwrite real data if valid header found
1430:                 if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
1431:                     $info = $dummy;
1432:                     $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
1433:                     switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
1434:                         case '':
1435:                         case 'id3':
1436:                         case 'ape':
1437:                         case 'mp3':
1438:                             $info['fileformat']          = 'mp3';
1439:                             $info['audio']['dataformat'] = 'mp3';
1440:                             break;
1441:                     }
1442:                     if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
1443:                         if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
1444:                             // If there is garbage data between a valid VBR header frame and a sequence
1445:                             // of valid MPEG-audio frames the VBR data is no longer discarded.
1446:                             $info = $FirstFrameThisfileInfo;
1447:                             $info['avdataoffset']        = $FirstFrameAVDataOffset;
1448:                             $info['fileformat']          = 'mp3';
1449:                             $info['audio']['dataformat'] = 'mp3';
1450:                             $dummy                       = $info;
1451:                             unset($dummy['mpeg']['audio']);
1452:                             $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
1453:                             $GarbageOffsetEnd   = $avdataoffset + $SynchSeekOffset;
1454:                             if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
1455:                                 $info = $dummy;
1456:                                 $info['avdataoffset'] = $GarbageOffsetEnd;
1457:                                 $info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
1458:                             } else {
1459:                                 $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
1460:                             }
1461:                         }
1462:                     }
1463:                     if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
1464:                         // VBR file with no VBR header
1465:                         $BitrateHistogram = true;
1466:                     }
1467: 
1468:                     if ($BitrateHistogram) {
1469: 
1470:                         $info['mpeg']['audio']['stereo_distribution']  = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
1471:                         $info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
1472: 
1473:                         if ($info['mpeg']['audio']['version'] == '1') {
1474:                             if ($info['mpeg']['audio']['layer'] == 3) {
1475:                                 $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0);
1476:                             } elseif ($info['mpeg']['audio']['layer'] == 2) {
1477:                                 $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0);
1478:                             } elseif ($info['mpeg']['audio']['layer'] == 1) {
1479:                                 $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0);
1480:                             }
1481:                         } elseif ($info['mpeg']['audio']['layer'] == 1) {
1482:                             $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0);
1483:                         } else {
1484:                             $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0);
1485:                         }
1486: 
1487:                         $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1488:                         $synchstartoffset = $info['avdataoffset'];
1489:                         $this->fseek($info['avdataoffset']);
1490: 
1491:                         // you can play with these numbers:
1492:                         $max_frames_scan  = 50000;
1493:                         $max_scan_segments = 10;
1494: 
1495:                         // don't play with these numbers:
1496:                         $FastMode = false;
1497:                         $SynchErrorsFound = 0;
1498:                         $frames_scanned   = 0;
1499:                         $this_scan_segment = 0;
1500:                         $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
1501:                         $pct_data_scanned = 0;
1502:                         for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
1503:                             $frames_scanned_this_segment = 0;
1504:                             if ($this->ftell() >= $info['avdataend']) {
1505:                                 break;
1506:                             }
1507:                             $scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
1508:                             if ($current_segment > 0) {
1509:                                 $this->fseek($scan_start_offset[$current_segment]);
1510:                                 $buffer_4k = $this->fread(4096);
1511:                                 for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
1512:                                     if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
1513:                                         if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
1514:                                             $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
1515:                                             if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
1516:                                                 $scan_start_offset[$current_segment] += $j;
1517:                                                 break;
1518:                                             }
1519:                                         }
1520:                                     }
1521:                                 }
1522:                             }
1523:                             $synchstartoffset = $scan_start_offset[$current_segment];
1524:                             while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
1525:                                 $FastMode = true;
1526:                                 $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
1527: 
1528:                                 if (empty($dummy['mpeg']['audio']['framelength'])) {
1529:                                     $SynchErrorsFound++;
1530:                                     $synchstartoffset++;
1531:                                 } else {
1532:                                     getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
1533:                                     getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
1534:                                     getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
1535:                                     $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
1536:                                 }
1537:                                 $frames_scanned++;
1538:                                 if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
1539:                                     $this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
1540:                                     if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
1541:                                         // file likely contains < $max_frames_scan, just scan as one segment
1542:                                         $max_scan_segments = 1;
1543:                                         $frames_scan_per_segment = $max_frames_scan;
1544:                                     } else {
1545:                                         $pct_data_scanned += $this_pct_scanned;
1546:                                         break;
1547:                                     }
1548:                                 }
1549:                             }
1550:                         }
1551:                         if ($pct_data_scanned > 0) {
1552:                             $info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1553:                             foreach ($info['mpeg']['audio'] as $key1 => $value1) {
1554:                                 if (!preg_match('#_distribution$#i', $key1)) {
1555:                                     continue;
1556:                                 }
1557:                                 foreach ($value1 as $key2 => $value2) {
1558:                                     $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
1559:                                 }
1560:                             }
1561:                         }
1562: 
1563:                         if ($SynchErrorsFound > 0) {
1564:                             $info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
1565:                             //return false;
1566:                         }
1567: 
1568:                         $bittotal     = 0;
1569:                         $framecounter = 0;
1570:                         foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
1571:                             $framecounter += $bitratecount;
1572:                             if ($bitratevalue != 'free') {
1573:                                 $bittotal += ($bitratevalue * $bitratecount);
1574:                             }
1575:                         }
1576:                         if ($framecounter == 0) {
1577:                             $info['error'][] = 'Corrupt MP3 file: framecounter == zero';
1578:                             return false;
1579:                         }
1580:                         $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
1581:                         $info['mpeg']['audio']['bitrate']     = ($bittotal / $framecounter);
1582: 
1583:                         $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1584: 
1585: 
1586:                         // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1587:                         $distinct_bitrates = 0;
1588:                         foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
1589:                             if ($bitrate_count > 0) {
1590:                                 $distinct_bitrates++;
1591:                             }
1592:                         }
1593:                         if ($distinct_bitrates > 1) {
1594:                             $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
1595:                         } else {
1596:                             $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
1597:                         }
1598:                         $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1599: 
1600:                     }
1601: 
1602:                     break; // exit while()
1603:                 }
1604:             }
1605: 
1606:             $SynchSeekOffset++;
1607:             if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
1608:                 // end of file/data
1609: 
1610:                 if (empty($info['mpeg']['audio'])) {
1611: 
1612:                     $info['error'][] = 'could not find valid MPEG synch before end of file';
1613:                     if (isset($info['audio']['bitrate'])) {
1614:                         unset($info['audio']['bitrate']);
1615:                     }
1616:                     if (isset($info['mpeg']['audio'])) {
1617:                         unset($info['mpeg']['audio']);
1618:                     }
1619:                     if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
1620:                         unset($info['mpeg']);
1621:                     }
1622:                     return false;
1623: 
1624:                 }
1625:                 break;
1626:             }
1627: 
1628:         }
1629:         $info['audio']['channels']        = $info['mpeg']['audio']['channels'];
1630:         $info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
1631:         $info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
1632:         return true;
1633:     }
1634: 
1635: 
1636:     public static function MPEGaudioVersionArray() {
1637:         static $MPEGaudioVersion = array('2.5', false, '2', '1');
1638:         return $MPEGaudioVersion;
1639:     }
1640: 
1641:     public static function MPEGaudioLayerArray() {
1642:         static $MPEGaudioLayer = array(false, 3, 2, 1);
1643:         return $MPEGaudioLayer;
1644:     }
1645: 
1646:     public static function MPEGaudioBitrateArray() {
1647:         static $MPEGaudioBitrate;
1648:         if (empty($MPEGaudioBitrate)) {
1649:             $MPEGaudioBitrate = array (
1650:                 '1'  =>  array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
1651:                                 2 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
1652:                                 3 => array('free', 32000, 40000, 48000,  56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
1653:                                ),
1654: 
1655:                 '2'  =>  array (1 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
1656:                                 2 => array('free',  8000, 16000, 24000,  32000,  40000,  48000,  56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000),
1657:                                )
1658:             );
1659:             $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
1660:             $MPEGaudioBitrate['2.5']  = $MPEGaudioBitrate['2'];
1661:         }
1662:         return $MPEGaudioBitrate;
1663:     }
1664: 
1665:     public static function MPEGaudioFrequencyArray() {
1666:         static $MPEGaudioFrequency;
1667:         if (empty($MPEGaudioFrequency)) {
1668:             $MPEGaudioFrequency = array (
1669:                 '1'   => array(44100, 48000, 32000),
1670:                 '2'   => array(22050, 24000, 16000),
1671:                 '2.5' => array(11025, 12000,  8000)
1672:             );
1673:         }
1674:         return $MPEGaudioFrequency;
1675:     }
1676: 
1677:     public static function MPEGaudioChannelModeArray() {
1678:         static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
1679:         return $MPEGaudioChannelMode;
1680:     }
1681: 
1682:     public static function MPEGaudioModeExtensionArray() {
1683:         static $MPEGaudioModeExtension;
1684:         if (empty($MPEGaudioModeExtension)) {
1685:             $MPEGaudioModeExtension = array (
1686:                 1 => array('4-31', '8-31', '12-31', '16-31'),
1687:                 2 => array('4-31', '8-31', '12-31', '16-31'),
1688:                 3 => array('', 'IS', 'MS', 'IS+MS')
1689:             );
1690:         }
1691:         return $MPEGaudioModeExtension;
1692:     }
1693: 
1694:     public static function MPEGaudioEmphasisArray() {
1695:         static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
1696:         return $MPEGaudioEmphasis;
1697:     }
1698: 
1699:     public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
1700:         return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
1701:     }
1702: 
1703:     public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
1704:         if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
1705:             return false;
1706:         }
1707: 
1708:         static $MPEGaudioVersionLookup;
1709:         static $MPEGaudioLayerLookup;
1710:         static $MPEGaudioBitrateLookup;
1711:         static $MPEGaudioFrequencyLookup;
1712:         static $MPEGaudioChannelModeLookup;
1713:         static $MPEGaudioModeExtensionLookup;
1714:         static $MPEGaudioEmphasisLookup;
1715:         if (empty($MPEGaudioVersionLookup)) {
1716:             $MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
1717:             $MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
1718:             $MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
1719:             $MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
1720:             $MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
1721:             $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1722:             $MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
1723:         }
1724: 
1725:         if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
1726:             $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
1727:         } else {
1728:             echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
1729:             return false;
1730:         }
1731:         if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
1732:             $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
1733:         } else {
1734:             echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
1735:             return false;
1736:         }
1737:         if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
1738:             echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
1739:             if ($rawarray['bitrate'] == 15) {
1740:                 // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
1741:                 // let it go through here otherwise file will not be identified
1742:                 if (!$allowBitrate15) {
1743:                     return false;
1744:                 }
1745:             } else {
1746:                 return false;
1747:             }
1748:         }
1749:         if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
1750:             echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
1751:             return false;
1752:         }
1753:         if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
1754:             echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
1755:             return false;
1756:         }
1757:         if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
1758:             echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
1759:             return false;
1760:         }
1761:         if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
1762:             echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
1763:             return false;
1764:         }
1765:         // These are just either set or not set, you can't mess that up :)
1766:         // $rawarray['protection'];
1767:         // $rawarray['padding'];
1768:         // $rawarray['private'];
1769:         // $rawarray['copyright'];
1770:         // $rawarray['original'];
1771: 
1772:         return true;
1773:     }
1774: 
1775:     public static function MPEGaudioHeaderDecode($Header4Bytes) {
1776:         // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
1777:         // A - Frame sync (all bits set)
1778:         // B - MPEG Audio version ID
1779:         // C - Layer description
1780:         // D - Protection bit
1781:         // E - Bitrate index
1782:         // F - Sampling rate frequency index
1783:         // G - Padding bit
1784:         // H - Private bit
1785:         // I - Channel Mode
1786:         // J - Mode extension (Only if Joint stereo)
1787:         // K - Copyright
1788:         // L - Original
1789:         // M - Emphasis
1790: 
1791:         if (strlen($Header4Bytes) != 4) {
1792:             return false;
1793:         }
1794: 
1795:         $MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
1796:         $MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
1797:         $MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
1798:         $MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
1799:         $MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
1800:         $MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
1801:         $MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
1802:         $MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
1803:         $MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
1804:         $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
1805:         $MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
1806:         $MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
1807:         $MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
1808: 
1809:         return $MPEGrawHeader;
1810:     }
1811: 
1812:     public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
1813:         static $AudioFrameLengthCache = array();
1814: 
1815:         if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
1816:             $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
1817:             if ($bitrate != 'free') {
1818: 
1819:                 if ($version == '1') {
1820: 
1821:                     if ($layer == '1') {
1822: 
1823:                         // For Layer I slot is 32 bits long
1824:                         $FrameLengthCoefficient = 48;
1825:                         $SlotLength = 4;
1826: 
1827:                     } else { // Layer 2 / 3
1828: 
1829:                         // for Layer 2 and Layer 3 slot is 8 bits long.
1830:                         $FrameLengthCoefficient = 144;
1831:                         $SlotLength = 1;
1832: 
1833:                     }
1834: 
1835:                 } else { // MPEG-2 / MPEG-2.5
1836: 
1837:                     if ($layer == '1') {
1838: 
1839:                         // For Layer I slot is 32 bits long
1840:                         $FrameLengthCoefficient = 24;
1841:                         $SlotLength = 4;
1842: 
1843:                     } elseif ($layer == '2') {
1844: 
1845:                         // for Layer 2 and Layer 3 slot is 8 bits long.
1846:                         $FrameLengthCoefficient = 144;
1847:                         $SlotLength = 1;
1848: 
1849:                     } else { // layer 3
1850: 
1851:                         // for Layer 2 and Layer 3 slot is 8 bits long.
1852:                         $FrameLengthCoefficient = 72;
1853:                         $SlotLength = 1;
1854: 
1855:                     }
1856: 
1857:                 }
1858: 
1859:                 // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
1860:                 if ($samplerate > 0) {
1861:                     $NewFramelength  = ($FrameLengthCoefficient * $bitrate) / $samplerate;
1862:                     $NewFramelength  = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
1863:                     if ($padding) {
1864:                         $NewFramelength += $SlotLength;
1865:                     }
1866:                     $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
1867:                 }
1868:             }
1869:         }
1870:         return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
1871:     }
1872: 
1873:     public static function ClosestStandardMP3Bitrate($bit_rate) {
1874:         static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
1875:         static $bit_rate_table = array (0=>'-');
1876:         $round_bit_rate = intval(round($bit_rate, -3));
1877:         if (!isset($bit_rate_table[$round_bit_rate])) {
1878:             if ($round_bit_rate > max($standard_bit_rates)) {
1879:                 $bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate));
1880:             } else {
1881:                 $bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
1882:                 foreach ($standard_bit_rates as $standard_bit_rate) {
1883:                     if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
1884:                         break;
1885:                     }
1886:                     $bit_rate_table[$round_bit_rate] = $standard_bit_rate;
1887:                 }
1888:             }
1889:         }
1890:         return $bit_rate_table[$round_bit_rate];
1891:     }
1892: 
1893:     public static function XingVBRidOffset($version, $channelmode) {
1894:         static $XingVBRidOffsetCache = array();
1895:         if (empty($XingVBRidOffset)) {
1896:             $XingVBRidOffset = array (
1897:                 '1'   => array ('mono'          => 0x15, // 4 + 17 = 21
1898:                                 'stereo'        => 0x24, // 4 + 32 = 36
1899:                                 'joint stereo'  => 0x24,
1900:                                 'dual channel'  => 0x24
1901:                                ),
1902: 
1903:                 '2'   => array ('mono'          => 0x0D, // 4 +  9 = 13
1904:                                 'stereo'        => 0x15, // 4 + 17 = 21
1905:                                 'joint stereo'  => 0x15,
1906:                                 'dual channel'  => 0x15
1907:                                ),
1908: 
1909:                 '2.5' => array ('mono'          => 0x15,
1910:                                 'stereo'        => 0x15,
1911:                                 'joint stereo'  => 0x15,
1912:                                 'dual channel'  => 0x15
1913:                                )
1914:             );
1915:         }
1916:         return $XingVBRidOffset[$version][$channelmode];
1917:     }
1918: 
1919:     public static function LAMEvbrMethodLookup($VBRmethodID) {
1920:         static $LAMEvbrMethodLookup = array(
1921:             0x00 => 'unknown',
1922:             0x01 => 'cbr',
1923:             0x02 => 'abr',
1924:             0x03 => 'vbr-old / vbr-rh',
1925:             0x04 => 'vbr-new / vbr-mtrh',
1926:             0x05 => 'vbr-mt',
1927:             0x06 => 'vbr (full vbr method 4)',
1928:             0x08 => 'cbr (constant bitrate 2 pass)',
1929:             0x09 => 'abr (2 pass)',
1930:             0x0F => 'reserved'
1931:         );
1932:         return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
1933:     }
1934: 
1935:     public static function LAMEmiscStereoModeLookup($StereoModeID) {
1936:         static $LAMEmiscStereoModeLookup = array(
1937:             0 => 'mono',
1938:             1 => 'stereo',
1939:             2 => 'dual mono',
1940:             3 => 'joint stereo',
1941:             4 => 'forced stereo',
1942:             5 => 'auto',
1943:             6 => 'intensity stereo',
1944:             7 => 'other'
1945:         );
1946:         return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
1947:     }
1948: 
1949:     public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
1950:         static $LAMEmiscSourceSampleFrequencyLookup = array(
1951:             0 => '<= 32 kHz',
1952:             1 => '44.1 kHz',
1953:             2 => '48 kHz',
1954:             3 => '> 48kHz'
1955:         );
1956:         return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
1957:     }
1958: 
1959:     public static function LAMEsurroundInfoLookup($SurroundInfoID) {
1960:         static $LAMEsurroundInfoLookup = array(
1961:             0 => 'no surround info',
1962:             1 => 'DPL encoding',
1963:             2 => 'DPL2 encoding',
1964:             3 => 'Ambisonic encoding'
1965:         );
1966:         return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
1967:     }
1968: 
1969:     public static function LAMEpresetUsedLookup($LAMEtag) {
1970: 
1971:         if ($LAMEtag['preset_used_id'] == 0) {
1972:             // no preset used (LAME >=3.93)
1973:             // no preset recorded (LAME <3.93)
1974:             return '';
1975:         }
1976:         $LAMEpresetUsedLookup = array();
1977: 
1978:         /////  THIS PART CANNOT BE STATIC .
1979:         for ($i = 8; $i <= 320; $i++) {
1980:             switch ($LAMEtag['vbr_method']) {
1981:                 case 'cbr':
1982:                     $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
1983:                     break;
1984:                 case 'abr':
1985:                 default: // other VBR modes shouldn't be here(?)
1986:                     $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
1987:                     break;
1988:             }
1989:         }
1990: 
1991:         // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
1992: 
1993:         // named alt-presets
1994:         $LAMEpresetUsedLookup[1000] = '--r3mix';
1995:         $LAMEpresetUsedLookup[1001] = '--alt-preset standard';
1996:         $LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
1997:         $LAMEpresetUsedLookup[1003] = '--alt-preset insane';
1998:         $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
1999:         $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
2000:         $LAMEpresetUsedLookup[1006] = '--alt-preset medium';
2001:         $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
2002: 
2003:         // LAME 3.94 additions/changes
2004:         $LAMEpresetUsedLookup[1010] = '--preset portable';                                                           // 3.94a15 Oct 21 2003
2005:         $LAMEpresetUsedLookup[1015] = '--preset radio';                                                              // 3.94a15 Oct 21 2003
2006: 
2007:         $LAMEpresetUsedLookup[320]  = '--preset insane';                                                             // 3.94a15 Nov 12 2003
2008:         $LAMEpresetUsedLookup[410]  = '-V9';
2009:         $LAMEpresetUsedLookup[420]  = '-V8';
2010:         $LAMEpresetUsedLookup[440]  = '-V6';
2011:         $LAMEpresetUsedLookup[430]  = '--preset radio';                                                              // 3.94a15 Nov 12 2003
2012:         $LAMEpresetUsedLookup[450]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
2013:         $LAMEpresetUsedLookup[460]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
2014:         $LAMEpresetUsedLookup[470]  = '--r3mix';                                                                     // 3.94b1  Dec 18 2003
2015:         $LAMEpresetUsedLookup[480]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
2016:         $LAMEpresetUsedLookup[490]  = '-V1';
2017:         $LAMEpresetUsedLookup[500]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003
2018: 
2019:         return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
2020:     }
2021: 
2022: }
2023: 
Zenphoto doc API documentation generated by ApiGen