├── .gitignore ├── README.md ├── inc ├── css │ ├── bttn.css │ └── sb.css ├── js │ ├── filter.js │ └── jquery-3.3.1.min.js └── json │ └── soundboard.json ├── index.html ├── node ├── .gitignore ├── config │ ├── config.js │ └── config.json ├── index.html ├── json_filename.js ├── package-lock.json └── package.json ├── php ├── buildJSON_filename.php ├── buildJSON_id3.php ├── config.php ├── filename_version.php ├── getID3 │ ├── extension.cache.dbm.php │ ├── extension.cache.mysql.php │ ├── extension.cache.mysqli.php │ ├── extension.cache.sqlite3.php │ ├── getid3.lib.php │ ├── getid3.php │ ├── module.archive.gzip.php │ ├── module.archive.rar.php │ ├── module.archive.szip.php │ ├── module.archive.tar.php │ ├── module.archive.zip.php │ ├── module.audio-video.asf.php │ ├── module.audio-video.bink.php │ ├── module.audio-video.flv.php │ ├── module.audio-video.matroska.php │ ├── module.audio-video.mpeg.php │ ├── module.audio-video.nsv.php │ ├── module.audio-video.quicktime.php │ ├── module.audio-video.real.php │ ├── module.audio-video.riff.php │ ├── module.audio-video.swf.php │ ├── module.audio-video.ts.php │ ├── module.audio.aa.php │ ├── module.audio.aac.php │ ├── module.audio.ac3.php │ ├── module.audio.amr.php │ ├── module.audio.au.php │ ├── module.audio.avr.php │ ├── module.audio.bonk.php │ ├── module.audio.dsf.php │ ├── module.audio.dss.php │ ├── module.audio.dts.php │ ├── module.audio.flac.php │ ├── module.audio.la.php │ ├── module.audio.lpac.php │ ├── module.audio.midi.php │ ├── module.audio.mod.php │ ├── module.audio.monkey.php │ ├── module.audio.mp3.php │ ├── module.audio.mpc.php │ ├── module.audio.ogg.php │ ├── module.audio.optimfrog.php │ ├── module.audio.rkau.php │ ├── module.audio.shorten.php │ ├── module.audio.tta.php │ ├── module.audio.voc.php │ ├── module.audio.vqf.php │ ├── module.audio.wavpack.php │ ├── module.graphic.bmp.php │ ├── module.graphic.efax.php │ ├── module.graphic.gif.php │ ├── module.graphic.jpg.php │ ├── module.graphic.pcd.php │ ├── module.graphic.png.php │ ├── module.graphic.svg.php │ ├── module.graphic.tiff.php │ ├── module.misc.cue.php │ ├── module.misc.exe.php │ ├── module.misc.iso.php │ ├── module.misc.msoffice.php │ ├── module.misc.par2.php │ ├── module.misc.pdf.php │ ├── module.tag.apetag.php │ ├── module.tag.id3v1.php │ ├── module.tag.id3v2.php │ ├── module.tag.lyrics3.php │ ├── module.tag.xmp.php │ ├── write.apetag.php │ ├── write.id3v1.php │ ├── write.id3v2.php │ ├── write.lyrics3.php │ ├── write.metaflac.php │ ├── write.php │ ├── write.real.php │ └── write.vorbiscomment.php └── index.php └── sounds ├── Ayayayayayayayay!.mp3 ├── Constipated.mp3 ├── Do-do-do-do.mp3 ├── Floridians-Dumb-as-dirt.mp3 ├── Frosty-Nads[Q].mp3 ├── Happy-Birthday.mp3 ├── Happy-Purim.mp3 ├── I-Don't-Believe-It.mp3 ├── Idiotic-jerk.mp3 ├── Laugh-Bird.mp3 ├── Laugh-Brooke.mp3 ├── Laugh-Gilbert.mp3 ├── Laugh-Goofy.mp3 ├── Laugh-Montage.mp3 ├── Loan-me-50-Dollars.mp3 ├── No.mp3 ├── Only-in-the-Banana-Republic.mp3 ├── Ya-mon!.mp3 ├── Yank-it-baby.mp3 └── Yeeeeeeesss.mp3 /.gitignore: -------------------------------------------------------------------------------- 1 | sounds/ 2 | .vscode/ 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Single Page Soundboard 2 | 3 | This code creates a Soundboard on a single HTML page that loads a JSON file. It uses Javascript, JQuery, HTML5, and CSS. The JSON file can be created by hand or built using PHP. I was motivated to create this in 2015 when Soundboard.com ignored my two feature requests. This version is better than Soundboard.com in at least 4 ways: 4 | 5 | 1. Loads way faster. 6 | 1. Tablet and Mobile friendly. 7 | 1. Sorts alphabetically automatically. 8 | 1. You can play multiple drops simultaneously. (optional) 9 | 10 | The original version required PHP. Now you can use PHP to build the JSON file of your Soundboard files, but it is not required. 11 | 12 | ## 2019 Update 13 | 14 | You can now filter the drops with the text input. It will search track titles and artists. 15 | 16 | ## 2020 Update 17 | 18 | I created a [Svelte version](https://github.com/digitalcolony/simple-soundboard-svelte) of the Simple Soundboard. Check it out! 19 | 20 | ## Getting Started 21 | 22 | 1. Create a new folder and place all the MP3 drops you wish to have on the soundboard. For this repo, I've added 20 drops to get you started. 23 | 1. Create or build a JSON file with the sound drops. I'll explain the format in the next section of this README. 24 | 1. Add a link to the JSON file on the Soundboard page. (EX: url: "./inc/json/soundboard.json") 25 | 1. Set the variable **_playOnlyOneSoundAtATime_** to true or false inside the javascript. 26 | 1. Deploy to any web server. 27 | 28 | ## JSON Format 29 | 30 | The Soundboard JSON file is a collection of files. Each file will have a name, duration, and mp3 file path. Duration is not used at this time, but could be in the future. The files do not have to be local. Any valid URL pointing to an MP3 file will work, provided that the host allows direct linking to audio files. 31 | 32 | ```javascript 33 | { 34 | "files": [ 35 | { 36 | "name": "1 to 12 hour - Boca Britany Somers", 37 | "mp3": 38 | "/sounds/1-to-12-hour.mp3" 39 | }, 40 | { 41 | "name": "2 of the Dumbest White Men - Mike Reineri", 42 | "mp3": 43 | "/sounds/2-of-the-Dumbest-White-Men.mp3" 44 | } 45 | ] 46 | } 47 | ``` 48 | 49 | ## Creating the JSON file 50 | 51 | You can create the JSON file a few different ways. 52 | 53 | 1. By hand. With any editor, you can type up your own JSON file that describes your Soundboard. This is perfect if where you want to host the Soundboard does not support server-side code. 54 | 1. With PHP. Included in this repo is PHP code that will build the JSON file for you. PHP is widely supported with web hosts. 55 | 1. NodeJS. The filename version is now completed. The ID3 version is yet to be coded. 56 | 57 | ## Building the JSON with PHP 58 | 59 | There are 2 ways to build the JSON file using PHP. 60 | 61 | 1. Filename: If you want the buttons to draw their names using the filename, use the **buildJSON_filename.php** page for your Simple Soundboard. To display a ? on the button use [Q] in the file name. Example: **why[Q].mp3**. 62 | 1. ID3: If your MP3 files have Titles defined in the ID3 tags, you will want to use the **buildJSON_id3.php** page for your Simple Soundboard. The code will draw the buttons on the soundboard using the getID3 library to read the title and artist. The ID3 title will be used for the button text and the artist will be used for a tooltip on mouseover. 63 | 64 | The ID3 version is the better version to use. If you need a tool to help you edit the ID3 tags of your MP3 files so they all have titles, look into [Mp3Tag](https://www.mp3tag.de/en/). Artist is optional. Drops without an artist will not have a tooltip. 65 | 66 | ## Building the JSON with NodeJS 67 | 68 | 1. Filename: If you want the buttons to draw their names using the filename, use the **json_filename.js** page for your Simple Soundboard. To display a ? on the button use [Q] in the file name. Example: **why[Q].mp3**. From the node/ folder run node json_filename.js. 69 | 70 | 1. ID3: Not coded yet. 71 | 72 | ## Some Ideas For Your Soundboard 73 | 74 | 1. Search for "CSS Button Generator" to create your own custom super cool looking buttons. Place that CSS into the sb.css file. 75 | 1. Set the preload to **none** if you have a lot of drops or the drops are larger files to improve load time. If you have a small number of drops or know that your users all have fast connections, set the preload to **auto**. 76 | 77 | ## Resources 78 | 79 | 1. The ID3 version of the soundboard uses [getID3](https://github.com/JamesHeinrich/getID3/) from James Heinrich. 80 | 1. The ID3 version uses [Ballon.CSS](https://kazzkiq.github.io/balloon.css/) for the mouseover tooltips. 81 | 1. The styling for the soundboard buttons were created with help from [CSS Button Generator](http://css3buttongenerator.com/). 82 | 83 | ## Demo and Sharing 84 | 85 | This is the [Neil Rogers Soundboard](https://neilrogers.org/soundboard/) built using this code. Let me know if you build a Soundboard you would like to share. You can email me (digitalcolony@gmail.com) the link and I'll share it here for others to see. 86 | 87 | ## Future Development 88 | 89 | 1. Create Node.JS ID3 version 90 | 91 | 1. Move JQuery to Vanilla JS 92 | -------------------------------------------------------------------------------- /inc/css/sb.css: -------------------------------------------------------------------------------- 1 | @CHARSET "ISO-8859-1"; 2 | .myButton { 3 | -moz-box-shadow: 3px 4px 0px 0px #9fb4f2; 4 | -webkit-box-shadow: 3px 4px 0px 0px #9fb4f2; 5 | box-shadow: 3px 4px 0px 0px #9fb4f2; 6 | background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #7892c2), color-stop(1, #476e9e)); 7 | background:-moz-linear-gradient(top, #7892c2 5%, #476e9e 100%); 8 | background:-webkit-linear-gradient(top, #7892c2 5%, #476e9e 100%); 9 | background:-o-linear-gradient(top, #7892c2 5%, #476e9e 100%); 10 | background:-ms-linear-gradient(top, #7892c2 5%, #476e9e 100%); 11 | background:linear-gradient(to bottom, #7892c2 5%, #476e9e 100%); 12 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#7892c2', endColorstr='#476e9e',GradientType=0); 13 | background-color:#7892c2; 14 | -moz-border-radius:18px; 15 | -webkit-border-radius:18px; 16 | border-radius:18px; 17 | border:1px solid #4e6096; 18 | display:inline-block; 19 | cursor:pointer; 20 | color:#ffffff; 21 | font-family:arial; 22 | font-size:17px; 23 | padding:7px 25px; 24 | text-decoration:none; 25 | text-shadow:0px 1px 0px #283966; 26 | margin: 3px; 27 | } 28 | .myButton:hover { 29 | background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #476e9e), color-stop(1, #7892c2)); 30 | background:-moz-linear-gradient(top, #476e9e 5%, #7892c2 100%); 31 | background:-webkit-linear-gradient(top, #476e9e 5%, #7892c2 100%); 32 | background:-o-linear-gradient(top, #476e9e 5%, #7892c2 100%); 33 | background:-ms-linear-gradient(top, #476e9e 5%, #7892c2 100%); 34 | background:linear-gradient(to bottom, #476e9e 5%, #7892c2 100%); 35 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#476e9e', endColorstr='#7892c2',GradientType=0); 36 | background-color:#476e9e; 37 | } 38 | .myButton:active { 39 | position:relative; 40 | top:1px; 41 | } 42 | .btn-filter:focus, .btn-filter:active:focus, .btn-filter.active:focus{ 43 | outline:none; 44 | box-shadow:none; 45 | } 46 | -------------------------------------------------------------------------------- /inc/js/filter.js: -------------------------------------------------------------------------------- 1 | const filterInput = document.getElementById("filterInput"); 2 | const filterClearBtn = document.getElementById("filterClearBtn"); 3 | 4 | document.addEventListener("DOMContentLoaded", function(event) { 5 | registerEventListeners(); 6 | }); 7 | 8 | function registerEventListeners() { 9 | filterInput.addEventListener("input", filterDrops); 10 | filterClearBtn.addEventListener("click", filterClear); 11 | } 12 | 13 | function filterClear(e){ 14 | filterInput.value = ""; 15 | filterInput.dispatchEvent(new Event("input")); 16 | } 17 | 18 | function filterDrops(e) { 19 | const text = e.target.value.toLowerCase(); 20 | document.querySelectorAll(".myButton").forEach(function(drop) { 21 | const title = drop.firstChild.textContent; 22 | let artist = drop.getAttribute("data-balloon"); 23 | if (artist === null) { 24 | artist = ""; 25 | } 26 | // Search both title and artist 27 | const track = title + artist; 28 | 29 | if (track.toLowerCase().indexOf(text) != -1) { 30 | drop.style.display = "inline"; 31 | } else { 32 | drop.style.display = "none"; 33 | } 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /inc/json/soundboard.json: -------------------------------------------------------------------------------- 1 | {"files":[{"name":"Ayayayayayayayay!","artist":"","mp3":"\/sounds\/Ayayayayayayayay!.mp3"},{"name":"Bird laugh","artist":"Glen Hill","mp3":"\/sounds\/Laugh-Bird.mp3"},{"name":"Do do do do","artist":"","mp3":"\/sounds\/Do-do-do-do.mp3"},{"name":"Floridians dumb as dirt","artist":"Jennifer Rehm","mp3":"\/sounds\/Floridians-Dumb-as-dirt.mp3"},{"name":"Frosty Nads","artist":"Jennifer Rehm","mp3":"\/sounds\/Frosty-Nads[Q].mp3"},{"name":"Happy Birthday","artist":"","mp3":"\/sounds\/Happy-Birthday.mp3"},{"name":"Happy Purim","artist":"Thanks for Calling Lady","mp3":"\/sounds\/Happy-Purim.mp3"},{"name":"I Don't Believe It","artist":"Jim Mandich","mp3":"\/sounds\/I-Don't-Believe-It.mp3"},{"name":"I Don't Do my Job as Well When I'm Constipated","artist":"","mp3":"\/sounds\/Constipated.mp3"},{"name":"Idiotic jerk","artist":"Old Boat Dude","mp3":"\/sounds\/Idiotic-jerk.mp3"},{"name":"Laugh Brooke","artist":"Brooke Daniels","mp3":"\/sounds\/Laugh-Brooke.mp3"},{"name":"Laugh Gilbert","artist":"Gilbert Solomon","mp3":"\/sounds\/Laugh-Gilbert.mp3"},{"name":"Laugh Goofy","artist":"","mp3":"\/sounds\/Laugh-Goofy.mp3"},{"name":"Laugh Montage","artist":"Gilbert\/Brooke Daniels","mp3":"\/sounds\/Laugh-Montage.mp3"},{"name":"Loan me 50 Dollars","artist":"Larry King","mp3":"\/sounds\/Loan-me-50-Dollars.mp3"},{"name":"No","artist":"Drew Michaels","mp3":"\/sounds\/No.mp3"},{"name":"Only in the Banana Republic","artist":"Jim Mandich","mp3":"\/sounds\/Only-in-the-Banana-Republic.mp3"},{"name":"Ya mon!","artist":"","mp3":"\/sounds\/Ya-mon!.mp3"},{"name":"Yank it baby","artist":"Jennifer Rehm","mp3":"\/sounds\/Yank-it-baby.mp3"},{"name":"Yeeeeeeesss","artist":"Jim Mandich","mp3":"\/sounds\/Yeeeeeeesss.mp3"}]} -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple Soundboard 8 | 9 | 10 | 11 | 17 | 22 | 23 | 24 | 28 | 33 | 34 | 35 | 36 | 64 | 65 |
66 | 67 | 130 | 131 |

132 | View the 133 | Simple Soundboard Repo 136 | on GitHub. 137 |

138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /node/config/config.js: -------------------------------------------------------------------------------- 1 | var env = process.env.NODE_ENV || "soundboard"; 2 | 3 | if (env === "soundboard") { 4 | const config = require("./config.json"); 5 | let envConfig = config[env]; 6 | 7 | Object.keys(envConfig).forEach(key => { 8 | process.env[key] = envConfig[key]; 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /node/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "soundboard": { 3 | "MP3_DIRECTORY": "../sounds/", 4 | "JSON_FILENAME": "../inc/json/soundboard.json", 5 | "SOUNDBOARD_PAGE": "/" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /node/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Build Simple Soundboard JSON File (Node) 8 | 9 | 10 | 11 |

Build Simple Soundboard JSON File (Node)

12 | 13 |

Update the config/config.js before building your JSON file.

14 | 15 |

File Name Version

16 |

Use [Q] for ? and spaces will be replaced with dashes.

17 | node json_filename.js 18 | 19 |

ID3 Version

20 |

21 | Use a tool such as MP3Tag to 22 | update the ID3 fields of your MP3s. 23 |

24 |

ID3 version is not coded yet.

25 | 26 | 27 | -------------------------------------------------------------------------------- /node/json_filename.js: -------------------------------------------------------------------------------- 1 | require("./config/config"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | String.prototype.replaceAll = function(search, replace) { 6 | if (replace === undefined) { 7 | return this.toString(); 8 | } 9 | return this.split(search).join(replace); 10 | }; 11 | 12 | var obj = { 13 | files: [] 14 | }; 15 | const getSoundFileName = val => { 16 | let soundname = path.parse(val).name; 17 | soundname = soundname.replaceAll("[Q]", "?").replaceAll("-", " "); 18 | return soundname; 19 | }; 20 | 21 | fs.readdirSync(process.env["MP3_DIRECTORY"]).forEach(file => { 22 | obj.files.push({ 23 | name: getSoundFileName(file), 24 | artist: "", // artist is not built with filename version 25 | mp3: process.env["MP3_DIRECTORY"] + file.toString() 26 | }); 27 | }); 28 | 29 | const json = JSON.stringify(obj); 30 | fs.writeFile(process.env["JSON_FILENAME"], json, "utf8", function(err) { 31 | if (err) { 32 | throw err; 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /node/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "inherits": { 8 | "version": "2.0.3", 9 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 10 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 11 | }, 12 | "path": { 13 | "version": "0.12.7", 14 | "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", 15 | "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", 16 | "requires": { 17 | "process": "^0.11.1", 18 | "util": "^0.10.3" 19 | } 20 | }, 21 | "process": { 22 | "version": "0.11.10", 23 | "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 24 | "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" 25 | }, 26 | "util": { 27 | "version": "0.10.4", 28 | "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", 29 | "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", 30 | "requires": { 31 | "inherits": "2.0.3" 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node", 3 | "version": "1.0.0", 4 | "description": "Build a JSON file for the Simple Soundboard", 5 | "main": "json_filename.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "path": "^0.12.7" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /php/buildJSON_filename.php: -------------------------------------------------------------------------------- 1 | MP3_DIRECTORY; 4 | $json_file = $configs->JSON_FILENAME; 5 | 6 | $files = array(); 7 | $sounds = array(); 8 | 9 | // confirm directory exists 10 | if (is_dir($mp3directory)) { 11 | // build an array of MP3 files 12 | $it = new RecursiveDirectoryIterator(realpath($mp3directory)); 13 | 14 | foreach(new RecursiveIteratorIterator($it) as $fileinfo) { 15 | if ($fileinfo->isFile()) { 16 | $extension = $fileinfo->getExtension(); 17 | if($extension == "mp3"){ 18 | $file_title = $fileinfo->getFilename(); 19 | $file_title = str_replace(".mp3","",$file_title); 20 | $file_title = str_replace("[Q]","?", $file_title); 21 | $file_title = str_replace("-"," ", $file_title); 22 | $file_artist = ""; 23 | 24 | $file_name = $mp3directory . $fileinfo->getFilename(); 25 | // Remove .. from path for Soundboard loading 26 | $file_name = str_replace("../","/", $file_name); 27 | 28 | $sounds[] = array('name'=> $file_title, 29 | 'artist'=> $file_artist, 30 | 'mp3'=> $file_name); 31 | } 32 | } 33 | } 34 | if(count($sounds) == 0){ 35 | echo "
ERROR: There are no MP3 files in the [". $mp3directory ."] directory. Add one or more and try again."; 36 | exit(); 37 | } 38 | } else { 39 | echo "ERROR: Directory defined [". $mp3directory ."] is either undefined or does not exist."; 40 | exit(); 41 | } 42 | // sort drops alphabetically 43 | sort($sounds); 44 | $files['files'] = $sounds; 45 | $fp = fopen($json_file, 'w'); 46 | fwrite($fp, json_encode($files)); 47 | fclose($fp); 48 | ?> 49 | 50 | 51 | 52 | 53 | 54 | 55 | Simple Soundboard JSON Built 56 | 57 | 58 |

Simple Soundboard JSON Built successfully!

59 |

Visit your Soundboard.

60 | 61 | -------------------------------------------------------------------------------- /php/buildJSON_id3.php: -------------------------------------------------------------------------------- 1 | MP3_DIRECTORY; 4 | $json_file = $configs->JSON_FILENAME; 5 | 6 | $files = array(); 7 | $sounds = array(); 8 | require_once('getID3/getid3.php'); 9 | $getid3_engine = new getID3; 10 | // confirm directory exists 11 | if (is_dir($mp3directory)) { 12 | // build an array of MP3 files 13 | $it = new RecursiveDirectoryIterator(realpath($mp3directory)); 14 | 15 | foreach(new RecursiveIteratorIterator($it) as $fileinfo) { 16 | if ($fileinfo->isFile()) { 17 | $extension = $fileinfo->getExtension(); 18 | if($extension == "mp3"){ 19 | $id3_info = $getid3_engine->analyze($fileinfo); 20 | getid3_lib::CopyTagsToComments($id3_info); 21 | $file_title = htmlentities(!empty($id3_info['comments_html']['title']) ? 22 | implode('
', $id3_info['comments_html']['title']) : ""); 23 | $file_artist = htmlentities(!empty($id3_info['comments_html']['artist']) ? 24 | implode('
', $id3_info['comments_html']['artist']) : ""); 25 | // Not sure why, but getting a double quote to display right required a double decode. 26 | $file_title = html_entity_decode(html_entity_decode($file_title)); 27 | $file_artist = html_entity_decode(html_entity_decode($file_artist)); 28 | $file_name = $mp3directory . $fileinfo->getFilename(); 29 | // Remove .. from path for Soundboard loading 30 | $file_name = str_replace("../","/", $file_name); 31 | if($file_title == ""){ 32 | $file_title = str_replace(".mp3","",$file_name); 33 | } 34 | 35 | $sounds[] = array('name'=> $file_title, 36 | 'artist'=> $file_artist, 37 | 'mp3'=> $file_name); 38 | } 39 | } 40 | } 41 | if(count($sounds) == 0){ 42 | echo "
ERROR: There are no MP3 files in the [". $mp3directory ."] directory. Add one or more and try again."; 43 | exit(); 44 | } 45 | } else { 46 | echo "ERROR: Directory defined [". $mp3directory ."] is either undefined or does not exist."; 47 | exit(); 48 | } 49 | // sort drops alphabetically 50 | sort($sounds); 51 | $files['files'] = $sounds; 52 | $fp = fopen($json_file, 'w'); 53 | fwrite($fp, json_encode($files)); 54 | fclose($fp); 55 | ?> 56 | 57 | 58 | 59 | 60 | 61 | 62 | Simple Soundboard JSON Built 63 | 64 | 65 |

Simple Soundboard JSON Built successfully!

66 |

Visit your Soundboard.

67 | 68 |

69 | 70 | -------------------------------------------------------------------------------- /php/config.php: -------------------------------------------------------------------------------- 1 | '../sounds/', 4 | 'JSON_FILENAME' => '../inc/json/soundboard.json', 5 | 'SOUNDBOARD_PAGE' => '/', 6 | 'TIME_ZONE' => 'America/Los_Angeles' 7 | ); 8 | ?> 9 | 10 | -------------------------------------------------------------------------------- /php/filename_version.php: -------------------------------------------------------------------------------- 1 | MP3_DIRECTORY; 4 | $mp3 = array(); 5 | // confirm directory exists 6 | if (file_exists($mp3directory)) { 7 | // build an array of MP3 files 8 | $directory = new DirectoryIterator($mp3directory); 9 | foreach ($directory as $fileinfo) { 10 | if ($fileinfo->isFile()) { 11 | $extension = $fileinfo->getExtension(); 12 | if($extension == "mp3"){ 13 | $mp3[] = $fileinfo->getFilename(); 14 | } 15 | } 16 | } 17 | if(count($mp3) == 0){ 18 | echo "ERROR: There are no MP3 files in the [". $mp3directory ."] directory. Add one or more and try again."; 19 | exit(); 20 | } 21 | } else { 22 | echo "ERROR: Directory defined [". $mp3directory ."] does not exist."; 23 | exit(); 24 | } 25 | // sort drops alphabetically 26 | sort($mp3); 27 | ?> 28 | 29 | 30 | 31 | 32 | <?php echo($configs->PAGE_TITLE); ?> 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 |
44 | 45 | 69 | 70 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /php/getID3/extension.cache.dbm.php: -------------------------------------------------------------------------------- 1 | // 4 | // available at http://getid3.sourceforge.net // 5 | // or http://www.getid3.org // 6 | // also https://github.com/JamesHeinrich/getID3 // 7 | ///////////////////////////////////////////////////////////////// 8 | // // 9 | // extension.cache.dbm.php - part of getID3() // 10 | // Please see readme.txt for more information // 11 | // /// 12 | ///////////////////////////////////////////////////////////////// 13 | // // 14 | // This extension written by Allan Hansen // 15 | // /// 16 | ///////////////////////////////////////////////////////////////// 17 | 18 | 19 | /** 20 | * This is a caching extension for getID3(). It works the exact same 21 | * way as the getID3 class, but return cached information very fast 22 | * 23 | * Example: 24 | * 25 | * Normal getID3 usage (example): 26 | * 27 | * require_once 'getid3/getid3.php'; 28 | * $getID3 = new getID3; 29 | * $getID3->encoding = 'UTF-8'; 30 | * $info1 = $getID3->analyze('file1.flac'); 31 | * $info2 = $getID3->analyze('file2.wv'); 32 | * 33 | * getID3_cached usage: 34 | * 35 | * require_once 'getid3/getid3.php'; 36 | * require_once 'getid3/getid3/extension.cache.dbm.php'; 37 | * $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm', 38 | * '/tmp/getid3_cache.lock'); 39 | * $getID3->encoding = 'UTF-8'; 40 | * $info1 = $getID3->analyze('file1.flac'); 41 | * $info2 = $getID3->analyze('file2.wv'); 42 | * 43 | * 44 | * Supported Cache Types 45 | * 46 | * SQL Databases: (use extension.cache.mysql) 47 | * 48 | * cache_type cache_options 49 | * ------------------------------------------------------------------- 50 | * mysql host, database, username, password 51 | * 52 | * 53 | * DBM-Style Databases: (this extension) 54 | * 55 | * cache_type cache_options 56 | * ------------------------------------------------------------------- 57 | * gdbm dbm_filename, lock_filename 58 | * ndbm dbm_filename, lock_filename 59 | * db2 dbm_filename, lock_filename 60 | * db3 dbm_filename, lock_filename 61 | * db4 dbm_filename, lock_filename (PHP5 required) 62 | * 63 | * PHP must have write access to both dbm_filename and lock_filename. 64 | * 65 | * 66 | * Recommended Cache Types 67 | * 68 | * Infrequent updates, many reads any DBM 69 | * Frequent updates mysql 70 | */ 71 | 72 | 73 | class getID3_cached_dbm extends getID3 74 | { 75 | 76 | // public: constructor - see top of this file for cache type and cache_options 77 | public function __construct($cache_type, $dbm_filename, $lock_filename) { 78 | 79 | // Check for dba extension 80 | if (!extension_loaded('dba')) { 81 | throw new Exception('PHP is not compiled with dba support, required to use DBM style cache.'); 82 | } 83 | 84 | // Check for specific dba driver 85 | if (!function_exists('dba_handlers') || !in_array($cache_type, dba_handlers())) { 86 | throw new Exception('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.'); 87 | } 88 | 89 | // Create lock file if needed 90 | if (!file_exists($lock_filename)) { 91 | if (!touch($lock_filename)) { 92 | throw new Exception('failed to create lock file: '.$lock_filename); 93 | } 94 | } 95 | 96 | // Open lock file for writing 97 | if (!is_writeable($lock_filename)) { 98 | throw new Exception('lock file: '.$lock_filename.' is not writable'); 99 | } 100 | $this->lock = fopen($lock_filename, 'w'); 101 | 102 | // Acquire exclusive write lock to lock file 103 | flock($this->lock, LOCK_EX); 104 | 105 | // Create dbm-file if needed 106 | if (!file_exists($dbm_filename)) { 107 | if (!touch($dbm_filename)) { 108 | throw new Exception('failed to create dbm file: '.$dbm_filename); 109 | } 110 | } 111 | 112 | // Try to open dbm file for writing 113 | $this->dba = dba_open($dbm_filename, 'w', $cache_type); 114 | if (!$this->dba) { 115 | 116 | // Failed - create new dbm file 117 | $this->dba = dba_open($dbm_filename, 'n', $cache_type); 118 | 119 | if (!$this->dba) { 120 | throw new Exception('failed to create dbm file: '.$dbm_filename); 121 | } 122 | 123 | // Insert getID3 version number 124 | dba_insert(getID3::VERSION, getID3::VERSION, $this->dba); 125 | } 126 | 127 | // Init misc values 128 | $this->cache_type = $cache_type; 129 | $this->dbm_filename = $dbm_filename; 130 | 131 | // Register destructor 132 | register_shutdown_function(array($this, '__destruct')); 133 | 134 | // Check version number and clear cache if changed 135 | if (dba_fetch(getID3::VERSION, $this->dba) != getID3::VERSION) { 136 | $this->clear_cache(); 137 | } 138 | 139 | parent::__construct(); 140 | } 141 | 142 | 143 | 144 | // public: destructor 145 | public function __destruct() { 146 | 147 | // Close dbm file 148 | dba_close($this->dba); 149 | 150 | // Release exclusive lock 151 | flock($this->lock, LOCK_UN); 152 | 153 | // Close lock file 154 | fclose($this->lock); 155 | } 156 | 157 | 158 | 159 | // public: clear cache 160 | public function clear_cache() { 161 | 162 | // Close dbm file 163 | dba_close($this->dba); 164 | 165 | // Create new dbm file 166 | $this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type); 167 | 168 | if (!$this->dba) { 169 | throw new Exception('failed to clear cache/recreate dbm file: '.$this->dbm_filename); 170 | } 171 | 172 | // Insert getID3 version number 173 | dba_insert(getID3::VERSION, getID3::VERSION, $this->dba); 174 | 175 | // Re-register shutdown function 176 | register_shutdown_function(array($this, '__destruct')); 177 | } 178 | 179 | 180 | 181 | // public: analyze file 182 | public function analyze($filename) { 183 | 184 | if (file_exists($filename)) { 185 | 186 | // Calc key filename::mod_time::size - should be unique 187 | $key = $filename.'::'.filemtime($filename).'::'.filesize($filename); 188 | 189 | // Loopup key 190 | $result = dba_fetch($key, $this->dba); 191 | 192 | // Hit 193 | if ($result !== false) { 194 | return unserialize($result); 195 | } 196 | } 197 | 198 | // Miss 199 | $result = parent::analyze($filename); 200 | 201 | // Save result 202 | if (file_exists($filename)) { 203 | dba_insert($key, serialize($result), $this->dba); 204 | } 205 | 206 | return $result; 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /php/getID3/extension.cache.mysql.php: -------------------------------------------------------------------------------- 1 | // 4 | // available at http://getid3.sourceforge.net // 5 | // or http://www.getid3.org // 6 | // also https://github.com/JamesHeinrich/getID3 // 7 | ///////////////////////////////////////////////////////////////// 8 | // // 9 | // extension.cache.mysql.php - part of getID3() // 10 | // Please see readme.txt for more information // 11 | // /// 12 | ///////////////////////////////////////////////////////////////// 13 | // // 14 | // This extension written by Allan Hansen // 15 | // Table name mod by Carlo Capocasa // 16 | // /// 17 | ///////////////////////////////////////////////////////////////// 18 | 19 | 20 | /** 21 | * This is a caching extension for getID3(). It works the exact same 22 | * way as the getID3 class, but return cached information very fast 23 | * 24 | * Example: (see also demo.cache.mysql.php in /demo/) 25 | * 26 | * Normal getID3 usage (example): 27 | * 28 | * require_once 'getid3/getid3.php'; 29 | * $getID3 = new getID3; 30 | * $getID3->encoding = 'UTF-8'; 31 | * $info1 = $getID3->analyze('file1.flac'); 32 | * $info2 = $getID3->analyze('file2.wv'); 33 | * 34 | * getID3_cached usage: 35 | * 36 | * require_once 'getid3/getid3.php'; 37 | * require_once 'getid3/getid3/extension.cache.mysql.php'; 38 | * // 5th parameter (tablename) is optional, default is 'getid3_cache' 39 | * $getID3 = new getID3_cached_mysql('localhost', 'database', 'username', 'password', 'tablename'); 40 | * $getID3->encoding = 'UTF-8'; 41 | * $info1 = $getID3->analyze('file1.flac'); 42 | * $info2 = $getID3->analyze('file2.wv'); 43 | * 44 | * 45 | * Supported Cache Types (this extension) 46 | * 47 | * SQL Databases: 48 | * 49 | * cache_type cache_options 50 | * ------------------------------------------------------------------- 51 | * mysql host, database, username, password 52 | * 53 | * 54 | * DBM-Style Databases: (use extension.cache.dbm) 55 | * 56 | * cache_type cache_options 57 | * ------------------------------------------------------------------- 58 | * gdbm dbm_filename, lock_filename 59 | * ndbm dbm_filename, lock_filename 60 | * db2 dbm_filename, lock_filename 61 | * db3 dbm_filename, lock_filename 62 | * db4 dbm_filename, lock_filename (PHP5 required) 63 | * 64 | * PHP must have write access to both dbm_filename and lock_filename. 65 | * 66 | * 67 | * Recommended Cache Types 68 | * 69 | * Infrequent updates, many reads any DBM 70 | * Frequent updates mysql 71 | */ 72 | 73 | 74 | class getID3_cached_mysql extends getID3 75 | { 76 | 77 | // private vars 78 | private $cursor; 79 | private $connection; 80 | 81 | 82 | // public: constructor - see top of this file for cache type and cache_options 83 | public function __construct($host, $database, $username, $password, $table='getid3_cache') { 84 | 85 | // Check for mysql support 86 | if (!function_exists('mysql_pconnect')) { 87 | throw new Exception('PHP not compiled with mysql support.'); 88 | } 89 | 90 | // Connect to database 91 | $this->connection = mysql_pconnect($host, $username, $password); 92 | if (!$this->connection) { 93 | throw new Exception('mysql_pconnect() failed - check permissions and spelling.'); 94 | } 95 | 96 | // Select database 97 | if (!mysql_select_db($database, $this->connection)) { 98 | throw new Exception('Cannot use database '.$database); 99 | } 100 | 101 | // Set table 102 | $this->table = $table; 103 | 104 | // Create cache table if not exists 105 | $this->create_table(); 106 | 107 | // Check version number and clear cache if changed 108 | $version = ''; 109 | $SQLquery = 'SELECT `value`'; 110 | $SQLquery .= ' FROM `'.mysql_real_escape_string($this->table).'`'; 111 | $SQLquery .= ' WHERE (`filename` = \''.mysql_real_escape_string(getID3::VERSION).'\')'; 112 | $SQLquery .= ' AND (`filesize` = -1)'; 113 | $SQLquery .= ' AND (`filetime` = -1)'; 114 | $SQLquery .= ' AND (`analyzetime` = -1)'; 115 | if ($this->cursor = mysql_query($SQLquery, $this->connection)) { 116 | list($version) = mysql_fetch_array($this->cursor); 117 | } 118 | if ($version != getID3::VERSION) { 119 | $this->clear_cache(); 120 | } 121 | 122 | parent::__construct(); 123 | } 124 | 125 | 126 | 127 | // public: clear cache 128 | public function clear_cache() { 129 | 130 | $this->cursor = mysql_query('DELETE FROM `'.mysql_real_escape_string($this->table).'`', $this->connection); 131 | $this->cursor = mysql_query('INSERT INTO `'.mysql_real_escape_string($this->table).'` VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')', $this->connection); 132 | } 133 | 134 | 135 | 136 | // public: analyze file 137 | public function analyze($filename, $filesize=null, $original_filename='') { 138 | 139 | if (file_exists($filename)) { 140 | 141 | // Short-hands 142 | $filetime = filemtime($filename); 143 | $filesize = filesize($filename); 144 | 145 | // Lookup file 146 | $SQLquery = 'SELECT `value`'; 147 | $SQLquery .= ' FROM `'.mysql_real_escape_string($this->table).'`'; 148 | $SQLquery .= ' WHERE (`filename` = \''.mysql_real_escape_string($filename).'\')'; 149 | $SQLquery .= ' AND (`filesize` = \''.mysql_real_escape_string($filesize).'\')'; 150 | $SQLquery .= ' AND (`filetime` = \''.mysql_real_escape_string($filetime).'\')'; 151 | $this->cursor = mysql_query($SQLquery, $this->connection); 152 | if (mysql_num_rows($this->cursor) > 0) { 153 | // Hit 154 | list($result) = mysql_fetch_array($this->cursor); 155 | return unserialize(base64_decode($result)); 156 | } 157 | } 158 | 159 | // Miss 160 | $analysis = parent::analyze($filename, $filesize, $original_filename); 161 | 162 | // Save result 163 | if (file_exists($filename)) { 164 | $SQLquery = 'INSERT INTO `'.mysql_real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('; 165 | $SQLquery .= '\''.mysql_real_escape_string($filename).'\''; 166 | $SQLquery .= ', \''.mysql_real_escape_string($filesize).'\''; 167 | $SQLquery .= ', \''.mysql_real_escape_string($filetime).'\''; 168 | $SQLquery .= ', \''.mysql_real_escape_string(time() ).'\''; 169 | $SQLquery .= ', \''.mysql_real_escape_string(base64_encode(serialize($analysis))).'\')'; 170 | $this->cursor = mysql_query($SQLquery, $this->connection); 171 | } 172 | return $analysis; 173 | } 174 | 175 | 176 | 177 | // private: (re)create sql table 178 | private function create_table($drop=false) { 179 | 180 | $SQLquery = 'CREATE TABLE IF NOT EXISTS `'.mysql_real_escape_string($this->table).'` ('; 181 | $SQLquery .= '`filename` VARCHAR(990) NOT NULL DEFAULT \'\''; 182 | $SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\''; 183 | $SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\''; 184 | $SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\''; 185 | $SQLquery .= ', `value` LONGTEXT NOT NULL'; 186 | $SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM CHARACTER SET=latin1 COLLATE=latin1_general_ci'; 187 | $this->cursor = mysql_query($SQLquery, $this->connection); 188 | echo mysql_error($this->connection); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /php/getID3/extension.cache.mysqli.php: -------------------------------------------------------------------------------- 1 | // 4 | // available at http://getid3.sourceforge.net // 5 | // or http://www.getid3.org // 6 | // also https://github.com/JamesHeinrich/getID3 // 7 | ///////////////////////////////////////////////////////////////// 8 | // // 9 | // extension.cache.mysqli.php - part of getID3() // 10 | // Please see readme.txt for more information // 11 | // /// 12 | ///////////////////////////////////////////////////////////////// 13 | // // 14 | // This extension written by Allan Hansen // 15 | // Table name mod by Carlo Capocasa // 16 | // /// 17 | ///////////////////////////////////////////////////////////////// 18 | 19 | 20 | /** 21 | * This is a caching extension for getID3(). It works the exact same 22 | * way as the getID3 class, but return cached information very fast 23 | * 24 | * Example: (see also demo.cache.mysql.php in /demo/) 25 | * 26 | * Normal getID3 usage (example): 27 | * 28 | * require_once 'getid3/getid3.php'; 29 | * $getID3 = new getID3; 30 | * $getID3->encoding = 'UTF-8'; 31 | * $info1 = $getID3->analyze('file1.flac'); 32 | * $info2 = $getID3->analyze('file2.wv'); 33 | * 34 | * getID3_cached usage: 35 | * 36 | * require_once 'getid3/getid3.php'; 37 | * require_once 'getid3/getid3/extension.cache.mysqli.php'; 38 | * // 5th parameter (tablename) is optional, default is 'getid3_cache' 39 | * $getID3 = new getID3_cached_mysqli('localhost', 'database', 'username', 'password', 'tablename'); 40 | * $getID3->encoding = 'UTF-8'; 41 | * $info1 = $getID3->analyze('file1.flac'); 42 | * $info2 = $getID3->analyze('file2.wv'); 43 | * 44 | * 45 | * Supported Cache Types (this extension) 46 | * 47 | * SQL Databases: 48 | * 49 | * cache_type cache_options 50 | * ------------------------------------------------------------------- 51 | * mysqli host, database, username, password 52 | * 53 | * 54 | * DBM-Style Databases: (use extension.cache.dbm) 55 | * 56 | * cache_type cache_options 57 | * ------------------------------------------------------------------- 58 | * gdbm dbm_filename, lock_filename 59 | * ndbm dbm_filename, lock_filename 60 | * db2 dbm_filename, lock_filename 61 | * db3 dbm_filename, lock_filename 62 | * db4 dbm_filename, lock_filename (PHP5 required) 63 | * 64 | * PHP must have write access to both dbm_filename and lock_filename. 65 | * 66 | * 67 | * Recommended Cache Types 68 | * 69 | * Infrequent updates, many reads any DBM 70 | * Frequent updates mysqli 71 | */ 72 | 73 | class getID3_cached_mysqli extends getID3 74 | { 75 | // private vars 76 | private $mysqli; 77 | private $cursor; 78 | 79 | 80 | // public: constructor - see top of this file for cache type and cache_options 81 | public function __construct($host, $database, $username, $password, $table='getid3_cache') { 82 | 83 | // Check for mysqli support 84 | if (!function_exists('mysqli_connect')) { 85 | throw new Exception('PHP not compiled with mysqli support.'); 86 | } 87 | 88 | // Connect to database 89 | $this->mysqli = new mysqli($host, $username, $password); 90 | if (!$this->mysqli) { 91 | throw new Exception('mysqli_connect() failed - check permissions and spelling.'); 92 | } 93 | 94 | // Select database 95 | if (!$this->mysqli->select_db($database)) { 96 | throw new Exception('Cannot use database '.$database); 97 | } 98 | 99 | // Set table 100 | $this->table = $table; 101 | 102 | // Create cache table if not exists 103 | $this->create_table(); 104 | 105 | // Check version number and clear cache if changed 106 | $version = ''; 107 | $SQLquery = 'SELECT `value`'; 108 | $SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`'; 109 | $SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string(getID3::VERSION).'\')'; 110 | $SQLquery .= ' AND (`filesize` = -1)'; 111 | $SQLquery .= ' AND (`filetime` = -1)'; 112 | $SQLquery .= ' AND (`analyzetime` = -1)'; 113 | if ($this->cursor = $this->mysqli->query($SQLquery)) { 114 | list($version) = $this->cursor->fetch_array(); 115 | } 116 | if ($version != getID3::VERSION) { 117 | $this->clear_cache(); 118 | } 119 | 120 | parent::__construct(); 121 | } 122 | 123 | 124 | // public: clear cache 125 | public function clear_cache() { 126 | $this->mysqli->query('DELETE FROM `'.$this->mysqli->real_escape_string($this->table).'`'); 127 | $this->mysqli->query('INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')'); 128 | } 129 | 130 | 131 | // public: analyze file 132 | public function analyze($filename, $filesize=null, $original_filename='') { 133 | 134 | if (file_exists($filename)) { 135 | 136 | // Short-hands 137 | $filetime = filemtime($filename); 138 | $filesize = filesize($filename); 139 | 140 | // Lookup file 141 | $SQLquery = 'SELECT `value`'; 142 | $SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`'; 143 | $SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string($filename).'\')'; 144 | $SQLquery .= ' AND (`filesize` = \''.$this->mysqli->real_escape_string($filesize).'\')'; 145 | $SQLquery .= ' AND (`filetime` = \''.$this->mysqli->real_escape_string($filetime).'\')'; 146 | $this->cursor = $this->mysqli->query($SQLquery); 147 | if ($this->cursor->num_rows > 0) { 148 | // Hit 149 | list($result) = $this->cursor->fetch_array(); 150 | return unserialize(base64_decode($result)); 151 | } 152 | } 153 | 154 | // Miss 155 | $analysis = parent::analyze($filename, $filesize, $original_filename); 156 | 157 | // Save result 158 | if (file_exists($filename)) { 159 | $SQLquery = 'INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('; 160 | $SQLquery .= '\''.$this->mysqli->real_escape_string($filename).'\''; 161 | $SQLquery .= ', \''.$this->mysqli->real_escape_string($filesize).'\''; 162 | $SQLquery .= ', \''.$this->mysqli->real_escape_string($filetime).'\''; 163 | $SQLquery .= ', \''.$this->mysqli->real_escape_string(time() ).'\''; 164 | $SQLquery .= ', \''.$this->mysqli->real_escape_string(base64_encode(serialize($analysis))).'\')'; 165 | $this->cursor = $this->mysqli->query($SQLquery); 166 | } 167 | return $analysis; 168 | } 169 | 170 | 171 | // private: (re)create mysqli table 172 | private function create_table($drop=false) { 173 | $SQLquery = 'CREATE TABLE IF NOT EXISTS `'.$this->mysqli->real_escape_string($this->table).'` ('; 174 | $SQLquery .= '`filename` VARCHAR(990) NOT NULL DEFAULT \'\''; 175 | $SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\''; 176 | $SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\''; 177 | $SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\''; 178 | $SQLquery .= ', `value` LONGTEXT NOT NULL'; 179 | $SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`)) ENGINE=MyISAM CHARACTER SET=latin1 COLLATE=latin1_general_ci'; 180 | $this->cursor = $this->mysqli->query($SQLquery); 181 | echo $this->mysqli->error; 182 | } 183 | } -------------------------------------------------------------------------------- /php/getID3/module.archive.rar.php: -------------------------------------------------------------------------------- 1 | // 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.archive.rar.php // 12 | // module for analyzing RAR files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_rar extends getid3_handler 19 | { 20 | 21 | public $option_use_rar_extension = false; 22 | 23 | public function Analyze() { 24 | $info = &$this->getid3->info; 25 | 26 | $info['fileformat'] = 'rar'; 27 | 28 | if ($this->option_use_rar_extension === true) { 29 | if (function_exists('rar_open')) { 30 | if ($rp = rar_open($info['filenamepath'])) { 31 | $info['rar']['files'] = array(); 32 | $entries = rar_list($rp); 33 | foreach ($entries as $entry) { 34 | $info['rar']['files'] = getid3_lib::array_merge_clobber($info['rar']['files'], getid3_lib::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize())); 35 | } 36 | rar_close($rp); 37 | return true; 38 | } else { 39 | $this->error('failed to rar_open('.$info['filename'].')'); 40 | } 41 | } else { 42 | $this->error('RAR support does not appear to be available in this PHP installation'); 43 | } 44 | } else { 45 | $this->error('PHP-RAR processing has been disabled (set $getid3_rar->option_use_rar_extension=true to enable)'); 46 | } 47 | return false; 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /php/getID3/module.archive.szip.php: -------------------------------------------------------------------------------- 1 | // 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.archive.szip.php // 12 | // module for analyzing SZIP compressed files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_szip extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $SZIPHeader = $this->fread(6); 26 | if (substr($SZIPHeader, 0, 4) != "SZ\x0A\x04") { 27 | $this->error('Expecting "53 5A 0A 04" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($SZIPHeader, 0, 4)).'"'); 28 | return false; 29 | } 30 | $info['fileformat'] = 'szip'; 31 | $info['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1)); 32 | $info['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1)); 33 | $this->error('SZIP parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 34 | return false; 35 | 36 | while (!$this->feof()) { 37 | $NextBlockID = $this->fread(2); 38 | switch ($NextBlockID) { 39 | case 'SZ': 40 | // Note that szip files can be concatenated, this has the same effect as 41 | // concatenating the files. this also means that global header blocks 42 | // might be present between directory/data blocks. 43 | $this->fseek(4, SEEK_CUR); 44 | break; 45 | 46 | case 'BH': 47 | $BHheaderbytes = getid3_lib::BigEndian2Int($this->fread(3)); 48 | $BHheaderdata = $this->fread($BHheaderbytes); 49 | $BHheaderoffset = 0; 50 | while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) { 51 | //filename as \0 terminated string (empty string indicates end) 52 | //owner as \0 terminated string (empty is same as last file) 53 | //group as \0 terminated string (empty is same as last file) 54 | //3 byte filelength in this block 55 | //2 byte access flags 56 | //4 byte creation time (like in unix) 57 | //4 byte modification time (like in unix) 58 | //4 byte access time (like in unix) 59 | 60 | $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00")); 61 | $BHheaderoffset += (strlen($BHdataArray['filename']) + 1); 62 | 63 | $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00")); 64 | $BHheaderoffset += (strlen($BHdataArray['owner']) + 1); 65 | 66 | $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00")); 67 | $BHheaderoffset += (strlen($BHdataArray['group']) + 1); 68 | 69 | $BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3)); 70 | $BHheaderoffset += 3; 71 | 72 | $BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2)); 73 | $BHheaderoffset += 2; 74 | 75 | $BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); 76 | $BHheaderoffset += 4; 77 | 78 | $BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); 79 | $BHheaderoffset += 4; 80 | 81 | $BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); 82 | $BHheaderoffset += 4; 83 | 84 | $info['szip']['BH'][] = $BHdataArray; 85 | } 86 | break; 87 | 88 | default: 89 | break 2; 90 | } 91 | } 92 | 93 | return true; 94 | 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /php/getID3/module.archive.tar.php: -------------------------------------------------------------------------------- 1 | // 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.archive.tar.php // 12 | // module for analyzing TAR files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | // // 17 | // Module originally written by // 18 | // Mike Mozolin // 19 | // // 20 | ///////////////////////////////////////////////////////////////// 21 | 22 | 23 | class getid3_tar extends getid3_handler 24 | { 25 | 26 | public function Analyze() { 27 | $info = &$this->getid3->info; 28 | 29 | $info['fileformat'] = 'tar'; 30 | $info['tar']['files'] = array(); 31 | 32 | $unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155prefix'; 33 | $null_512k = str_repeat("\x00", 512); // end-of-file marker 34 | 35 | $this->fseek(0); 36 | while (!feof($this->getid3->fp)) { 37 | $buffer = $this->fread(512); 38 | if (strlen($buffer) < 512) { 39 | break; 40 | } 41 | 42 | // check the block 43 | $checksum = 0; 44 | for ($i = 0; $i < 148; $i++) { 45 | $checksum += ord($buffer{$i}); 46 | } 47 | for ($i = 148; $i < 156; $i++) { 48 | $checksum += ord(' '); 49 | } 50 | for ($i = 156; $i < 512; $i++) { 51 | $checksum += ord($buffer{$i}); 52 | } 53 | $attr = unpack($unpack_header, $buffer); 54 | $name = (isset($attr['fname'] ) ? trim($attr['fname'] ) : ''); 55 | $mode = octdec(isset($attr['mode'] ) ? trim($attr['mode'] ) : ''); 56 | $uid = octdec(isset($attr['uid'] ) ? trim($attr['uid'] ) : ''); 57 | $gid = octdec(isset($attr['gid'] ) ? trim($attr['gid'] ) : ''); 58 | $size = octdec(isset($attr['size'] ) ? trim($attr['size'] ) : ''); 59 | $mtime = octdec(isset($attr['mtime'] ) ? trim($attr['mtime'] ) : ''); 60 | $chksum = octdec(isset($attr['chksum'] ) ? trim($attr['chksum'] ) : ''); 61 | $typflag = (isset($attr['typflag']) ? trim($attr['typflag']) : ''); 62 | $lnkname = (isset($attr['lnkname']) ? trim($attr['lnkname']) : ''); 63 | $magic = (isset($attr['magic'] ) ? trim($attr['magic'] ) : ''); 64 | $ver = (isset($attr['ver'] ) ? trim($attr['ver'] ) : ''); 65 | $uname = (isset($attr['uname'] ) ? trim($attr['uname'] ) : ''); 66 | $gname = (isset($attr['gname'] ) ? trim($attr['gname'] ) : ''); 67 | $devmaj = octdec(isset($attr['devmaj'] ) ? trim($attr['devmaj'] ) : ''); 68 | $devmin = octdec(isset($attr['devmin'] ) ? trim($attr['devmin'] ) : ''); 69 | $prefix = (isset($attr['prefix'] ) ? trim($attr['prefix'] ) : ''); 70 | if (($checksum == 256) && ($chksum == 0)) { 71 | // EOF Found 72 | break; 73 | } 74 | if ($prefix) { 75 | $name = $prefix.'/'.$name; 76 | } 77 | if ((preg_match('#/$#', $name)) && !$name) { 78 | $typeflag = 5; 79 | } 80 | if ($buffer == $null_512k) { 81 | // it's the end of the tar-file... 82 | break; 83 | } 84 | 85 | // Read to the next chunk 86 | $this->fseek($size, SEEK_CUR); 87 | 88 | $diff = $size % 512; 89 | if ($diff != 0) { 90 | // Padding, throw away 91 | $this->fseek((512 - $diff), SEEK_CUR); 92 | } 93 | // Protect against tar-files with garbage at the end 94 | if ($name == '') { 95 | break; 96 | } 97 | $info['tar']['file_details'][$name] = array ( 98 | 'name' => $name, 99 | 'mode_raw' => $mode, 100 | 'mode' => self::display_perms($mode), 101 | 'uid' => $uid, 102 | 'gid' => $gid, 103 | 'size' => $size, 104 | 'mtime' => $mtime, 105 | 'chksum' => $chksum, 106 | 'typeflag' => self::get_flag_type($typflag), 107 | 'linkname' => $lnkname, 108 | 'magic' => $magic, 109 | 'version' => $ver, 110 | 'uname' => $uname, 111 | 'gname' => $gname, 112 | 'devmajor' => $devmaj, 113 | 'devminor' => $devmin 114 | ); 115 | $info['tar']['files'] = getid3_lib::array_merge_clobber($info['tar']['files'], getid3_lib::CreateDeepArray($info['tar']['file_details'][$name]['name'], '/', $size)); 116 | } 117 | return true; 118 | } 119 | 120 | // Parses the file mode to file permissions 121 | public function display_perms($mode) { 122 | // Determine Type 123 | if ($mode & 0x1000) $type='p'; // FIFO pipe 124 | elseif ($mode & 0x2000) $type='c'; // Character special 125 | elseif ($mode & 0x4000) $type='d'; // Directory 126 | elseif ($mode & 0x6000) $type='b'; // Block special 127 | elseif ($mode & 0x8000) $type='-'; // Regular 128 | elseif ($mode & 0xA000) $type='l'; // Symbolic Link 129 | elseif ($mode & 0xC000) $type='s'; // Socket 130 | else $type='u'; // UNKNOWN 131 | 132 | // Determine permissions 133 | $owner['read'] = (($mode & 00400) ? 'r' : '-'); 134 | $owner['write'] = (($mode & 00200) ? 'w' : '-'); 135 | $owner['execute'] = (($mode & 00100) ? 'x' : '-'); 136 | $group['read'] = (($mode & 00040) ? 'r' : '-'); 137 | $group['write'] = (($mode & 00020) ? 'w' : '-'); 138 | $group['execute'] = (($mode & 00010) ? 'x' : '-'); 139 | $world['read'] = (($mode & 00004) ? 'r' : '-'); 140 | $world['write'] = (($mode & 00002) ? 'w' : '-'); 141 | $world['execute'] = (($mode & 00001) ? 'x' : '-'); 142 | 143 | // Adjust for SUID, SGID and sticky bit 144 | if ($mode & 0x800) $owner['execute'] = ($owner['execute'] == 'x') ? 's' : 'S'; 145 | if ($mode & 0x400) $group['execute'] = ($group['execute'] == 'x') ? 's' : 'S'; 146 | if ($mode & 0x200) $world['execute'] = ($world['execute'] == 'x') ? 't' : 'T'; 147 | 148 | $s = sprintf('%1s', $type); 149 | $s .= sprintf('%1s%1s%1s', $owner['read'], $owner['write'], $owner['execute']); 150 | $s .= sprintf('%1s%1s%1s', $group['read'], $group['write'], $group['execute']); 151 | $s .= sprintf('%1s%1s%1s'."\n", $world['read'], $world['write'], $world['execute']); 152 | return $s; 153 | } 154 | 155 | // Converts the file type 156 | public function get_flag_type($typflag) { 157 | static $flag_types = array( 158 | '0' => 'LF_NORMAL', 159 | '1' => 'LF_LINK', 160 | '2' => 'LF_SYNLINK', 161 | '3' => 'LF_CHR', 162 | '4' => 'LF_BLK', 163 | '5' => 'LF_DIR', 164 | '6' => 'LF_FIFO', 165 | '7' => 'LF_CONFIG', 166 | 'D' => 'LF_DUMPDIR', 167 | 'K' => 'LF_LONGLINK', 168 | 'L' => 'LF_LONGNAME', 169 | 'M' => 'LF_MULTIVOL', 170 | 'N' => 'LF_NAMES', 171 | 'S' => 'LF_SPARSE', 172 | 'V' => 'LF_VOLHDR' 173 | ); 174 | return (isset($flag_types[$typflag]) ? $flag_types[$typflag] : ''); 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /php/getID3/module.audio-video.bink.php: -------------------------------------------------------------------------------- 1 | // 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.bink.php // 12 | // module for analyzing Bink or Smacker audio-video files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_bink extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->error('Bink / Smacker files not properly processed by this version of getID3() ['.$this->getid3->version().']'); 25 | 26 | $this->fseek($info['avdataoffset']); 27 | $fileTypeID = $this->fread(3); 28 | switch ($fileTypeID) { 29 | case 'BIK': 30 | return $this->ParseBink(); 31 | break; 32 | 33 | case 'SMK': 34 | return $this->ParseSmacker(); 35 | break; 36 | 37 | default: 38 | $this->error('Expecting "BIK" or "SMK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($fileTypeID).'"'); 39 | return false; 40 | break; 41 | } 42 | 43 | return true; 44 | 45 | } 46 | 47 | public function ParseBink() { 48 | $info = &$this->getid3->info; 49 | $info['fileformat'] = 'bink'; 50 | $info['video']['dataformat'] = 'bink'; 51 | 52 | $fileData = 'BIK'.$this->fread(13); 53 | 54 | $info['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4)); 55 | $info['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2)); 56 | 57 | if (($info['avdataend'] - $info['avdataoffset']) != ($info['bink']['data_size'] + 8)) { 58 | $this->error('Probably truncated file: expecting '.$info['bink']['data_size'].' bytes, found '.($info['avdataend'] - $info['avdataoffset'])); 59 | } 60 | 61 | return true; 62 | } 63 | 64 | public function ParseSmacker() { 65 | $info = &$this->getid3->info; 66 | $info['fileformat'] = 'smacker'; 67 | $info['video']['dataformat'] = 'smacker'; 68 | 69 | return true; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /php/getID3/module.audio-video.mpeg.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/php/getID3/module.audio-video.mpeg.php -------------------------------------------------------------------------------- /php/getID3/module.audio-video.swf.php: -------------------------------------------------------------------------------- 1 | // 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-video.swf.php // 12 | // module for analyzing Shockwave Flash files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_swf extends getid3_handler 19 | { 20 | public $ReturnAllTagData = false; 21 | 22 | public function Analyze() { 23 | $info = &$this->getid3->info; 24 | 25 | $info['fileformat'] = 'swf'; 26 | $info['video']['dataformat'] = 'swf'; 27 | 28 | // http://www.openswf.org/spec/SWFfileformat.html 29 | 30 | $this->fseek($info['avdataoffset']); 31 | 32 | $SWFfileData = $this->fread($info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data 33 | 34 | $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3); 35 | switch ($info['swf']['header']['signature']) { 36 | case 'FWS': 37 | $info['swf']['header']['compressed'] = false; 38 | break; 39 | 40 | case 'CWS': 41 | $info['swf']['header']['compressed'] = true; 42 | break; 43 | 44 | default: 45 | $this->error('Expecting "FWS" or "CWS" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['swf']['header']['signature']).'"'); 46 | unset($info['swf']); 47 | unset($info['fileformat']); 48 | return false; 49 | break; 50 | } 51 | $info['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1)); 52 | $info['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4)); 53 | 54 | if ($info['swf']['header']['compressed']) { 55 | $SWFHead = substr($SWFfileData, 0, 8); 56 | $SWFfileData = substr($SWFfileData, 8); 57 | if ($decompressed = @gzuncompress($SWFfileData)) { 58 | $SWFfileData = $SWFHead.$decompressed; 59 | } else { 60 | $this->error('Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($info['swf']['header']['length'] - 8).' bytes uncompressed)'); 61 | return false; 62 | } 63 | } 64 | 65 | $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3; 66 | $FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8); 67 | $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x07), 3, '0', STR_PAD_LEFT); 68 | for ($i = 1; $i < $FrameSizeDataLength; $i++) { 69 | $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT); 70 | } 71 | list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1)); 72 | $info['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2); 73 | $info['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2); 74 | 75 | // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm 76 | // Next in the header is the frame rate, which is kind of weird. 77 | // It is supposed to be stored as a 16bit integer, but the first byte 78 | // (or last depending on how you look at it) is completely ignored. 79 | // Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps. 80 | 81 | // Byte at (8 + $FrameSizeDataLength) is always zero and ignored 82 | $info['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1)); 83 | $info['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2)); 84 | 85 | $info['video']['frame_rate'] = $info['swf']['header']['frame_rate']; 86 | $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20)); 87 | $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20)); 88 | $info['video']['pixel_aspect_ratio'] = (float) 1; 89 | 90 | if (($info['swf']['header']['frame_count'] > 0) && ($info['swf']['header']['frame_rate'] > 0)) { 91 | $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate']; 92 | } 93 | //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'
'; 94 | 95 | 96 | // SWF tags 97 | 98 | $CurrentOffset = 12 + $FrameSizeDataLength; 99 | $SWFdataLength = strlen($SWFfileData); 100 | 101 | while ($CurrentOffset < $SWFdataLength) { 102 | //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'
'; 103 | 104 | $TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2)); 105 | $TagID = ($TagIDTagLength & 0xFFFC) >> 6; 106 | $TagLength = ($TagIDTagLength & 0x003F); 107 | $CurrentOffset += 2; 108 | if ($TagLength == 0x3F) { 109 | $TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4)); 110 | $CurrentOffset += 4; 111 | } 112 | 113 | unset($TagData); 114 | $TagData['offset'] = $CurrentOffset; 115 | $TagData['size'] = $TagLength; 116 | $TagData['id'] = $TagID; 117 | $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength); 118 | switch ($TagID) { 119 | case 0: // end of movie 120 | break 2; 121 | 122 | case 9: // Set background color 123 | //$info['swf']['tags'][] = $TagData; 124 | $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT)); 125 | break; 126 | 127 | default: 128 | if ($this->ReturnAllTagData) { 129 | $info['swf']['tags'][] = $TagData; 130 | } 131 | break; 132 | } 133 | 134 | $CurrentOffset += $TagLength; 135 | } 136 | 137 | return true; 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /php/getID3/module.audio-video.ts.php: -------------------------------------------------------------------------------- 1 | // 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-video.ts.php // 12 | // module for analyzing MPEG Transport Stream (.ts) files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_ts extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $TSheader = $this->fread(19); 26 | $magic = "\x47"; 27 | if (substr($TSheader, 0, 1) != $magic) { 28 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($TSheader, 0, 1)).' instead.'); 29 | return false; 30 | } 31 | $info['fileformat'] = 'ts'; 32 | 33 | // http://en.wikipedia.org/wiki/.ts 34 | 35 | $offset = 0; 36 | $info['ts']['packet']['sync'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 1)); $offset += 1; 37 | $pid_flags_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 2)); $offset += 2; 38 | $SAC_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 1)); $offset += 1; 39 | $info['ts']['packet']['flags']['transport_error_indicator'] = (bool) ($pid_flags_raw & 0x8000); // Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error 40 | $info['ts']['packet']['flags']['payload_unit_start_indicator'] = (bool) ($pid_flags_raw & 0x4000); // 1 means start of PES data or PSI otherwise zero only. 41 | $info['ts']['packet']['flags']['transport_high_priority'] = (bool) ($pid_flags_raw & 0x2000); // 1 means higher priority than other packets with the same PID. 42 | $info['ts']['packet']['packet_id'] = ($pid_flags_raw & 0x1FFF) >> 0; 43 | 44 | $info['ts']['packet']['raw']['scrambling_control'] = ($SAC_raw & 0xC0) >> 6; 45 | $info['ts']['packet']['flags']['adaption_field_exists'] = (bool) ($SAC_raw & 0x20); 46 | $info['ts']['packet']['flags']['payload_exists'] = (bool) ($SAC_raw & 0x10); 47 | $info['ts']['packet']['continuity_counter'] = ($SAC_raw & 0x0F) >> 0; // Incremented only when a payload is present 48 | $info['ts']['packet']['scrambling_control'] = $this->TSscramblingControlLookup($info['ts']['packet']['raw']['scrambling_control']); 49 | 50 | if ($info['ts']['packet']['flags']['adaption_field_exists']) { 51 | $AdaptionField_raw = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 2)); $offset += 2; 52 | $info['ts']['packet']['adaption']['field_length'] = ($AdaptionField_raw & 0xFF00) >> 8; // Number of bytes in the adaptation field immediately following this byte 53 | $info['ts']['packet']['adaption']['flags']['discontinuity'] = (bool) ($AdaptionField_raw & 0x0080); // Set to 1 if current TS packet is in a discontinuity state with respect to either the continuity counter or the program clock reference 54 | $info['ts']['packet']['adaption']['flags']['random_access'] = (bool) ($AdaptionField_raw & 0x0040); // Set to 1 if the PES packet in this TS packet starts a video/audio sequence 55 | $info['ts']['packet']['adaption']['flags']['high_priority'] = (bool) ($AdaptionField_raw & 0x0020); // 1 = higher priority 56 | $info['ts']['packet']['adaption']['flags']['pcr'] = (bool) ($AdaptionField_raw & 0x0010); // 1 means adaptation field does contain a PCR field 57 | $info['ts']['packet']['adaption']['flags']['opcr'] = (bool) ($AdaptionField_raw & 0x0008); // 1 means adaptation field does contain an OPCR field 58 | $info['ts']['packet']['adaption']['flags']['splice_point'] = (bool) ($AdaptionField_raw & 0x0004); // 1 means presence of splice countdown field in adaptation field 59 | $info['ts']['packet']['adaption']['flags']['private_data'] = (bool) ($AdaptionField_raw & 0x0002); // 1 means presence of private data bytes in adaptation field 60 | $info['ts']['packet']['adaption']['flags']['extension'] = (bool) ($AdaptionField_raw & 0x0001); // 1 means presence of adaptation field extension 61 | if ($info['ts']['packet']['adaption']['flags']['pcr']) { 62 | $info['ts']['packet']['adaption']['raw']['pcr'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 6)); $offset += 6; 63 | } 64 | if ($info['ts']['packet']['adaption']['flags']['opcr']) { 65 | $info['ts']['packet']['adaption']['raw']['opcr'] = getid3_lib::BigEndian2Int(substr($TSheader, $offset, 6)); $offset += 6; 66 | } 67 | } 68 | 69 | $this->error('MPEG Transport Stream (.ts) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 70 | return false; 71 | 72 | } 73 | 74 | 75 | public function TSscramblingControlLookup($raw) { 76 | $TSscramblingControlLookup = array(0x00=>'not scrambled', 0x01=>'reserved', 0x02=>'scrambled, even key', 0x03=>'scrambled, odd key'); 77 | return (isset($TSscramblingControlLookup[$raw]) ? $TSscramblingControlLookup[$raw] : 'invalid'); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /php/getID3/module.audio.aa.php: -------------------------------------------------------------------------------- 1 | // 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.aa.php // 12 | // module for analyzing Audible Audiobook files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_aa extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $AAheader = $this->fread(8); 26 | 27 | $magic = "\x57\x90\x75\x36"; 28 | if (substr($AAheader, 4, 4) != $magic) { 29 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AAheader, 4, 4)).'"'); 30 | return false; 31 | } 32 | 33 | // shortcut 34 | $info['aa'] = array(); 35 | $thisfile_aa = &$info['aa']; 36 | 37 | $info['fileformat'] = 'aa'; 38 | $info['audio']['dataformat'] = 'aa'; 39 | $this->error('Audible Audiobook (.aa) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 40 | return false; 41 | $info['audio']['bitrate_mode'] = 'cbr'; // is it? 42 | $thisfile_aa['encoding'] = 'ISO-8859-1'; 43 | 44 | $thisfile_aa['filesize'] = getid3_lib::BigEndian2Int(substr($AUheader, 0, 4)); 45 | if ($thisfile_aa['filesize'] > ($info['avdataend'] - $info['avdataoffset'])) { 46 | $this->warning('Possible truncated file - expecting "'.$thisfile_aa['filesize'].'" bytes of data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"'); 47 | } 48 | 49 | $info['audio']['bits_per_sample'] = 16; // is it? 50 | $info['audio']['sample_rate'] = $thisfile_aa['sample_rate']; 51 | $info['audio']['channels'] = $thisfile_aa['channels']; 52 | 53 | //$info['playtime_seconds'] = 0; 54 | //$info['audio']['bitrate'] = 0; 55 | 56 | return true; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /php/getID3/module.audio.ac3.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/php/getID3/module.audio.ac3.php -------------------------------------------------------------------------------- /php/getID3/module.audio.amr.php: -------------------------------------------------------------------------------- 1 | // 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.aa.php // 12 | // module for analyzing Audible Audiobook files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_amr extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $AMRheader = $this->fread(6); 26 | 27 | $magic = '#!AMR'."\x0A"; 28 | if (substr($AMRheader, 0, 6) != $magic) { 29 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AMRheader, 0, 6)).'"'); 30 | return false; 31 | } 32 | 33 | // shortcut 34 | $info['amr'] = array(); 35 | $thisfile_amr = &$info['amr']; 36 | 37 | $info['fileformat'] = 'amr'; 38 | $info['audio']['dataformat'] = 'amr'; 39 | $info['audio']['bitrate_mode'] = 'vbr'; // within a small predefined range: 4.75kbps to 12.2kbps 40 | $info['audio']['bits_per_sample'] = 13; // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 200–3400 Hz" 41 | $info['audio']['sample_rate'] = 8000; // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 200–3400 Hz" 42 | $info['audio']['channels'] = 1; 43 | $thisfile_amr['frame_mode_count'] = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0, 7=>0); 44 | 45 | $buffer = ''; 46 | do { 47 | if ((strlen($buffer) < $this->getid3->fread_buffer_size()) && !feof($this->getid3->fp)) { 48 | $buffer .= $this->fread($this->getid3->fread_buffer_size() * 2); 49 | } 50 | $AMR_frame_header = ord(substr($buffer, 0, 1)); 51 | $codec_mode_request = ($AMR_frame_header & 0x78) >> 3; // The 2nd bit through 5th bit (counting the most significant bit as the first bit) comprise the CMR (Codec Mode Request), values 0-7 being valid for AMR. The top bit of the CMR can actually be ignored, though it is used when AMR forms RTP payloads. The lower 3-bits of the header are reserved and are not used. Viewing the header from most significant bit to least significant bit, the encoding is XCCCCXXX, where Xs are reserved (typically 0) and the Cs are the CMR. 52 | if ($codec_mode_request > 7) { 53 | break; 54 | } 55 | $thisfile_amr['frame_mode_count'][$codec_mode_request]++; 56 | $buffer = substr($buffer, $this->amr_mode_bytes_per_frame($codec_mode_request)); 57 | } while (strlen($buffer) > 0); 58 | 59 | $info['playtime_seconds'] = array_sum($thisfile_amr['frame_mode_count']) * 0.020; // each frame contain 160 samples and is 20 milliseconds long 60 | $info['audio']['bitrate'] = (8 * ($info['avdataend'] - $info['avdataoffset'])) / $info['playtime_seconds']; // bitrate could be calculated from average bitrate by distributation of frame types. That would give effective audio bitrate, this gives overall file bitrate which will be a little bit higher since every frame will waste 8 bits for header, plus a few bits for octet padding 61 | $info['bitrate'] = $info['audio']['bitrate']; 62 | 63 | return true; 64 | } 65 | 66 | 67 | public function amr_mode_bitrate($key) { 68 | static $amr_mode_bitrate = array( 69 | 0 => 4750, 70 | 1 => 5150, 71 | 2 => 5900, 72 | 3 => 6700, 73 | 4 => 7400, 74 | 5 => 7950, 75 | 6 => 10200, 76 | 7 => 12200, 77 | ); 78 | return (isset($amr_mode_bitrate[$key]) ? $amr_mode_bitrate[$key] : false); 79 | } 80 | 81 | public function amr_mode_bytes_per_frame($key) { 82 | static $amr_mode_bitrate = array( 83 | 0 => 13, // 1-byte frame header + 95 bits [padded to: 12 bytes] audio data 84 | 1 => 14, // 1-byte frame header + 103 bits [padded to: 13 bytes] audio data 85 | 2 => 16, // 1-byte frame header + 118 bits [padded to: 15 bytes] audio data 86 | 3 => 18, // 1-byte frame header + 134 bits [padded to: 17 bytes] audio data 87 | 4 => 20, // 1-byte frame header + 148 bits [padded to: 19 bytes] audio data 88 | 5 => 21, // 1-byte frame header + 159 bits [padded to: 20 bytes] audio data 89 | 6 => 27, // 1-byte frame header + 204 bits [padded to: 26 bytes] audio data 90 | 7 => 32, // 1-byte frame header + 244 bits [padded to: 31 bytes] audio data 91 | ); 92 | return (isset($amr_mode_bitrate[$key]) ? $amr_mode_bitrate[$key] : false); 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /php/getID3/module.audio.au.php: -------------------------------------------------------------------------------- 1 | // 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.au.php // 12 | // module for analyzing AU files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_au extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $AUheader = $this->fread(8); 26 | 27 | $magic = '.snd'; 28 | if (substr($AUheader, 0, 4) != $magic) { 29 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" (".snd") at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($AUheader, 0, 4)).'"'); 30 | return false; 31 | } 32 | 33 | // shortcut 34 | $info['au'] = array(); 35 | $thisfile_au = &$info['au']; 36 | 37 | $info['fileformat'] = 'au'; 38 | $info['audio']['dataformat'] = 'au'; 39 | $info['audio']['bitrate_mode'] = 'cbr'; 40 | $thisfile_au['encoding'] = 'ISO-8859-1'; 41 | 42 | $thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4)); 43 | $AUheader .= $this->fread($thisfile_au['header_length'] - 8); 44 | $info['avdataoffset'] += $thisfile_au['header_length']; 45 | 46 | $thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4)); 47 | $thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4)); 48 | $thisfile_au['sample_rate'] = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4)); 49 | $thisfile_au['channels'] = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4)); 50 | $thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24)); 51 | 52 | $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']); 53 | $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']); 54 | if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) { 55 | $info['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample']; 56 | } else { 57 | unset($thisfile_au['bits_per_sample']); 58 | } 59 | 60 | $info['audio']['sample_rate'] = $thisfile_au['sample_rate']; 61 | $info['audio']['channels'] = $thisfile_au['channels']; 62 | 63 | if (($info['avdataoffset'] + $thisfile_au['data_size']) > $info['avdataend']) { 64 | $this->warning('Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' bytes"'); 65 | } 66 | 67 | $info['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8)); 68 | $info['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $info['playtime_seconds']; 69 | 70 | return true; 71 | } 72 | 73 | public function AUdataFormatNameLookup($id) { 74 | static $AUdataFormatNameLookup = array( 75 | 0 => 'unspecified format', 76 | 1 => '8-bit mu-law', 77 | 2 => '8-bit linear', 78 | 3 => '16-bit linear', 79 | 4 => '24-bit linear', 80 | 5 => '32-bit linear', 81 | 6 => 'floating-point', 82 | 7 => 'double-precision float', 83 | 8 => 'fragmented sampled data', 84 | 9 => 'SUN_FORMAT_NESTED', 85 | 10 => 'DSP program', 86 | 11 => '8-bit fixed-point', 87 | 12 => '16-bit fixed-point', 88 | 13 => '24-bit fixed-point', 89 | 14 => '32-bit fixed-point', 90 | 91 | 16 => 'non-audio display data', 92 | 17 => 'SND_FORMAT_MULAW_SQUELCH', 93 | 18 => '16-bit linear with emphasis', 94 | 19 => '16-bit linear with compression', 95 | 20 => '16-bit linear with emphasis + compression', 96 | 21 => 'Music Kit DSP commands', 97 | 22 => 'SND_FORMAT_DSP_COMMANDS_SAMPLES', 98 | 23 => 'CCITT g.721 4-bit ADPCM', 99 | 24 => 'CCITT g.722 ADPCM', 100 | 25 => 'CCITT g.723 3-bit ADPCM', 101 | 26 => 'CCITT g.723 5-bit ADPCM', 102 | 27 => 'A-Law 8-bit' 103 | ); 104 | return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false); 105 | } 106 | 107 | public function AUdataFormatBitsPerSampleLookup($id) { 108 | static $AUdataFormatBitsPerSampleLookup = array( 109 | 1 => 8, 110 | 2 => 8, 111 | 3 => 16, 112 | 4 => 24, 113 | 5 => 32, 114 | 6 => 32, 115 | 7 => 64, 116 | 117 | 11 => 8, 118 | 12 => 16, 119 | 13 => 24, 120 | 14 => 32, 121 | 122 | 18 => 16, 123 | 19 => 16, 124 | 20 => 16, 125 | 126 | 23 => 16, 127 | 128 | 25 => 16, 129 | 26 => 16, 130 | 27 => 8 131 | ); 132 | return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false); 133 | } 134 | 135 | public function AUdataFormatUsedBitsPerSampleLookup($id) { 136 | static $AUdataFormatUsedBitsPerSampleLookup = array( 137 | 1 => 8, 138 | 2 => 8, 139 | 3 => 16, 140 | 4 => 24, 141 | 5 => 32, 142 | 6 => 32, 143 | 7 => 64, 144 | 145 | 11 => 8, 146 | 12 => 16, 147 | 13 => 24, 148 | 14 => 32, 149 | 150 | 18 => 16, 151 | 19 => 16, 152 | 20 => 16, 153 | 154 | 23 => 4, 155 | 156 | 25 => 3, 157 | 26 => 5, 158 | 27 => 8, 159 | ); 160 | return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /php/getID3/module.audio.avr.php: -------------------------------------------------------------------------------- 1 | // 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.avr.php // 12 | // module for analyzing AVR Audio files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_avr extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | // http://cui.unige.ch/OSG/info/AudioFormats/ap11.html 25 | // http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html 26 | // offset type length name comments 27 | // --------------------------------------------------------------------- 28 | // 0 char 4 ID format ID == "2BIT" 29 | // 4 char 8 name sample name (unused space filled with 0) 30 | // 12 short 1 mono/stereo 0=mono, -1 (0xFFFF)=stereo 31 | // With stereo, samples are alternated, 32 | // the first voice is the left : 33 | // (LRLRLRLRLRLRLRLRLR...) 34 | // 14 short 1 resolution 8, 12 or 16 (bits) 35 | // 16 short 1 signed or not 0=unsigned, -1 (0xFFFF)=signed 36 | // 18 short 1 loop or not 0=no loop, -1 (0xFFFF)=loop on 37 | // 20 short 1 MIDI note 0xFFnn, where 0 <= nn <= 127 38 | // 0xFFFF means "no MIDI note defined" 39 | // 22 byte 1 Replay speed Frequence in the Replay software 40 | // 0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz, 41 | // 3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz 42 | // 6=43.885 Khz, 7=47.261 Khz 43 | // -1 (0xFF)=no defined Frequence 44 | // 23 byte 3 sample rate in Hertz 45 | // 26 long 1 size in bytes (2 * bytes in stereo) 46 | // 30 long 1 loop begin 0 for no loop 47 | // 34 long 1 loop size equal to 'size' for no loop 48 | // 38 short 2 Reserved, MIDI keyboard split */ 49 | // 40 short 2 Reserved, sample compression */ 50 | // 42 short 2 Reserved */ 51 | // 44 char 20; Additional filename space, used if (name[7] != 0) 52 | // 64 byte 64 user data 53 | // 128 bytes ? sample data (12 bits samples are coded on 16 bits: 54 | // 0000 xxxx xxxx xxxx) 55 | // --------------------------------------------------------------------- 56 | 57 | // Note that all values are in motorola (big-endian) format, and that long is 58 | // assumed to be 4 bytes, and short 2 bytes. 59 | // When reading the samples, you should handle both signed and unsigned data, 60 | // and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert 61 | // 8-bit data between signed/unsigned just add 127 to the sample values. 62 | // Simularly for 16-bit data you should add 32769 63 | 64 | $info['fileformat'] = 'avr'; 65 | 66 | $this->fseek($info['avdataoffset']); 67 | $AVRheader = $this->fread(128); 68 | 69 | $info['avr']['raw']['magic'] = substr($AVRheader, 0, 4); 70 | $magic = '2BIT'; 71 | if ($info['avr']['raw']['magic'] != $magic) { 72 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['avr']['raw']['magic']).'"'); 73 | unset($info['fileformat']); 74 | unset($info['avr']); 75 | return false; 76 | } 77 | $info['avdataoffset'] += 128; 78 | 79 | $info['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8)); 80 | $info['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2)); 81 | $info['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2)); 82 | $info['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2)); 83 | $info['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2)); 84 | $info['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2)); 85 | $info['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1)); 86 | $info['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3)); 87 | $info['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4)); 88 | $info['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4)); 89 | $info['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4)); 90 | $info['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2)); 91 | $info['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2)); 92 | $info['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2)); 93 | $info['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20)); 94 | $info['avr']['comment'] = rtrim(substr($AVRheader, 64, 64)); 95 | 96 | $info['avr']['flags']['stereo'] = (($info['avr']['raw']['mono'] == 0) ? false : true); 97 | $info['avr']['flags']['signed'] = (($info['avr']['raw']['signed'] == 0) ? false : true); 98 | $info['avr']['flags']['loop'] = (($info['avr']['raw']['loop'] == 0) ? false : true); 99 | 100 | $info['avr']['midi_notes'] = array(); 101 | if (($info['avr']['raw']['midi'] & 0xFF00) != 0xFF00) { 102 | $info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0xFF00) >> 8; 103 | } 104 | if (($info['avr']['raw']['midi'] & 0x00FF) != 0x00FF) { 105 | $info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0x00FF); 106 | } 107 | 108 | if (($info['avdataend'] - $info['avdataoffset']) != ($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 1 : 2))) { 109 | $this->warning('Probable truncated file: expecting '.($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset'])); 110 | } 111 | 112 | $info['audio']['dataformat'] = 'avr'; 113 | $info['audio']['lossless'] = true; 114 | $info['audio']['bitrate_mode'] = 'cbr'; 115 | $info['audio']['bits_per_sample'] = $info['avr']['bits_per_sample']; 116 | $info['audio']['sample_rate'] = $info['avr']['sample_rate']; 117 | $info['audio']['channels'] = ($info['avr']['flags']['stereo'] ? 2 : 1); 118 | $info['playtime_seconds'] = ($info['avr']['sample_length'] / $info['audio']['channels']) / $info['avr']['sample_rate']; 119 | $info['audio']['bitrate'] = ($info['avr']['sample_length'] * (($info['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $info['playtime_seconds']; 120 | 121 | 122 | return true; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /php/getID3/module.audio.dsf.php: -------------------------------------------------------------------------------- 1 | // 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.dsf.php // 12 | // module for analyzing dsf/DSF Audio files // 13 | // dependencies: module.tag.id3v2.php // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); 18 | 19 | class getid3_dsf extends getid3_handler 20 | { 21 | 22 | public function Analyze() { 23 | $info = &$this->getid3->info; 24 | 25 | $info['fileformat'] = 'dsf'; 26 | $info['audio']['dataformat'] = 'dsf'; 27 | $info['audio']['lossless'] = true; 28 | $info['audio']['bitrate_mode'] = 'cbr'; 29 | 30 | $this->fseek($info['avdataoffset']); 31 | $dsfheader = $this->fread(28 + 12); 32 | 33 | $headeroffset = 0; 34 | $info['dsf']['dsd']['magic'] = substr($dsfheader, $headeroffset, 4); 35 | $headeroffset += 4; 36 | $magic = 'DSD '; 37 | if ($info['dsf']['dsd']['magic'] != $magic) { 38 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['dsf']['dsd']['magic']).'"'); 39 | unset($info['fileformat']); 40 | unset($info['audio']); 41 | unset($info['dsf']); 42 | return false; 43 | } 44 | $info['dsf']['dsd']['dsd_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // should be 28 45 | $headeroffset += 8; 46 | $info['dsf']['dsd']['dsf_file_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); 47 | $headeroffset += 8; 48 | $info['dsf']['dsd']['meta_chunk_offset'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); 49 | $headeroffset += 8; 50 | 51 | 52 | $info['dsf']['fmt']['magic'] = substr($dsfheader, $headeroffset, 4); 53 | $headeroffset += 4; 54 | $magic = 'fmt '; 55 | if ($info['dsf']['fmt']['magic'] != $magic) { 56 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib::PrintHexBytes($info['dsf']['fmt']['magic']).'"'); 57 | return false; 58 | } 59 | $info['dsf']['fmt']['fmt_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); // usually 52 bytes 60 | $headeroffset += 8; 61 | $dsfheader .= $this->fread($info['dsf']['fmt']['fmt_chunk_size'] - 12 + 12); // we have already read the entire DSD chunk, plus 12 bytes of FMT. We now want to read the size of FMT, plus 12 bytes into the next chunk to get magic and size. 62 | if (strlen($dsfheader) != ($info['dsf']['dsd']['dsd_chunk_size'] + $info['dsf']['fmt']['fmt_chunk_size'] + 12)) { 63 | $this->error('Expecting '.($info['dsf']['dsd']['dsd_chunk_size'] + $info['dsf']['fmt']['fmt_chunk_size']).' bytes header, found '.strlen($dsfheader).' bytes'); 64 | return false; 65 | } 66 | $info['dsf']['fmt']['format_version'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "1" 67 | $headeroffset += 4; 68 | $info['dsf']['fmt']['format_id'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // usually "0" = "DSD Raw" 69 | $headeroffset += 4; 70 | $info['dsf']['fmt']['channel_type_id'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 71 | $headeroffset += 4; 72 | $info['dsf']['fmt']['channels'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 73 | $headeroffset += 4; 74 | $info['dsf']['fmt']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 75 | $headeroffset += 4; 76 | $info['dsf']['fmt']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 77 | $headeroffset += 4; 78 | $info['dsf']['fmt']['sample_count'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); 79 | $headeroffset += 8; 80 | $info['dsf']['fmt']['channel_block_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); 81 | $headeroffset += 4; 82 | $info['dsf']['fmt']['reserved'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 4)); // zero-filled 83 | $headeroffset += 4; 84 | 85 | 86 | $info['dsf']['data']['magic'] = substr($dsfheader, $headeroffset, 4); 87 | $headeroffset += 4; 88 | $magic = 'data'; 89 | if ($info['dsf']['data']['magic'] != $magic) { 90 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$headeroffset.', found "'.getid3_lib::PrintHexBytes($info['dsf']['data']['magic']).'"'); 91 | return false; 92 | } 93 | $info['dsf']['data']['data_chunk_size'] = getid3_lib::LittleEndian2Int(substr($dsfheader, $headeroffset, 8)); 94 | $headeroffset += 8; 95 | $info['avdataoffset'] = $headeroffset; 96 | $info['avdataend'] = $info['avdataoffset'] + $info['dsf']['data']['data_chunk_size']; 97 | 98 | 99 | if ($info['dsf']['dsd']['meta_chunk_offset'] > 0) { 100 | $getid3_id3v2 = new getid3_id3v2($this->getid3); 101 | $getid3_id3v2->StartingOffset = $info['dsf']['dsd']['meta_chunk_offset']; 102 | $getid3_id3v2->Analyze(); 103 | unset($getid3_id3v2); 104 | } 105 | 106 | 107 | $info['dsf']['fmt']['channel_type'] = $this->DSFchannelTypeLookup($info['dsf']['fmt']['channel_type_id']); 108 | $info['audio']['channelmode'] = $info['dsf']['fmt']['channel_type']; 109 | $info['audio']['bits_per_sample'] = $info['dsf']['fmt']['bits_per_sample']; 110 | $info['audio']['sample_rate'] = $info['dsf']['fmt']['sample_rate']; 111 | $info['audio']['channels'] = $info['dsf']['fmt']['channels']; 112 | $info['audio']['bitrate'] = $info['audio']['bits_per_sample'] * $info['audio']['sample_rate'] * $info['audio']['channels']; 113 | $info['playtime_seconds'] = ($info['dsf']['data']['data_chunk_size'] * 8) / $info['audio']['bitrate']; 114 | 115 | return true; 116 | } 117 | 118 | 119 | public static function DSFchannelTypeLookup($channel_type_id) { 120 | static $DSFchannelTypeLookup = array( 121 | // interleaving order: 122 | 1 => 'mono', // 1: Mono 123 | 2 => 'stereo', // 1: Front-Left; 2: Front-Right 124 | 3 => '3-channel', // 1: Front-Left; 2: Front-Right; 3: Center 125 | 4 => 'quad', // 1: Front-Left; 2: Front-Right; 3: Back-Left; 4: Back-Right 126 | 5 => '4-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency 127 | 6 => '5-channel', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Back-Left 5: Back-Right 128 | 7 => '5.1', // 1: Front-Left; 2: Front-Right; 3: Center; 4: Low-Frequency; 5: Back-Left; 6: Back-Right 129 | ); 130 | return (isset($DSFchannelTypeLookup[$channel_type_id]) ? $DSFchannelTypeLookup[$channel_type_id] : ''); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /php/getID3/module.audio.dss.php: -------------------------------------------------------------------------------- 1 | // 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.dss.php // 12 | // module for analyzing Digital Speech Standard (DSS) files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_dss extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $DSSheader = $this->fread(1540); 26 | 27 | if (!preg_match('#^[\\x02-\\x06]ds[s2]#', $DSSheader)) { 28 | $this->error('Expecting "[02-06] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"'); 29 | return false; 30 | } 31 | 32 | // some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm 33 | $info['encoding'] = 'ISO-8859-1'; // not certain, but assumed 34 | $info['dss'] = array(); 35 | 36 | $info['fileformat'] = 'dss'; 37 | $info['mime_type'] = 'audio/x-'.substr($DSSheader, 1, 3); // "audio/x-dss" or "audio/x-ds2" 38 | $info['audio']['dataformat'] = substr($DSSheader, 1, 3); // "dss" or "ds2" 39 | $info['audio']['bitrate_mode'] = 'cbr'; 40 | 41 | $info['dss']['version'] = ord(substr($DSSheader, 0, 1)); 42 | $info['dss']['hardware'] = trim(substr($DSSheader, 12, 16)); // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400" 43 | $info['dss']['unknown1'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 28, 4)); 44 | // 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen 45 | $info['dss']['date_create_unix'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12)); 46 | $info['dss']['date_complete_unix'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12)); 47 | $info['dss']['playtime_sec'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2)); // approximate file playtime in HHMMSS 48 | if ($info['dss']['version'] <= 3) { 49 | $info['dss']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 512, 4)); // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512 50 | $info['dss']['priority'] = ord(substr($DSSheader, 793, 1)); 51 | $info['dss']['comments'] = trim(substr($DSSheader, 798, 100)); 52 | $info['dss']['sample_rate_index'] = ord(substr($DSSheader, 1538, 1)); // this isn't certain, this may or may not be where the sample rate info is stored, but it seems consistent on my small selection of sample files 53 | $info['audio']['sample_rate'] = $this->DSSsampleRateLookup($info['dss']['sample_rate_index']); 54 | } else { 55 | $this->getid3->warning('DSS above version 3 not fully supported in this version of getID3. Any additional documentation or format specifications would be welcome. This file is version '.$info['dss']['version']); 56 | } 57 | 58 | $info['audio']['bits_per_sample'] = 16; // maybe, maybe not -- most compressed audio formats don't have a fixed bits-per-sample value, but this is a reasonable approximation 59 | $info['audio']['channels'] = 1; 60 | 61 | if (!empty($info['dss']['playtime_ms']) && (floor($info['dss']['playtime_ms'] / 1000) == $info['dss']['playtime_sec'])) { // *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check 62 | $info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000; 63 | } else { 64 | $info['playtime_seconds'] = $info['dss']['playtime_sec']; 65 | if (!empty($info['dss']['playtime_ms'])) { 66 | $this->getid3->warning('playtime_ms ('.number_format($info['dss']['playtime_ms'] / 1000, 3).') does not match playtime_sec ('.number_format($info['dss']['playtime_sec']).') - using playtime_sec value'); 67 | } 68 | } 69 | $info['audio']['bitrate'] = ($info['filesize'] * 8) / $info['playtime_seconds']; 70 | 71 | return true; 72 | } 73 | 74 | public function DSSdateStringToUnixDate($datestring) { 75 | $y = substr($datestring, 0, 2); 76 | $m = substr($datestring, 2, 2); 77 | $d = substr($datestring, 4, 2); 78 | $h = substr($datestring, 6, 2); 79 | $i = substr($datestring, 8, 2); 80 | $s = substr($datestring, 10, 2); 81 | $y += (($y < 95) ? 2000 : 1900); 82 | return mktime($h, $i, $s, $m, $d, $y); 83 | } 84 | 85 | public function DSSsampleRateLookup($sample_rate_index) { 86 | static $dssSampleRateLookup = array( 87 | 0x0A => 16000, 88 | 0x0C => 11025, 89 | 0x0D => 12000, 90 | 0x15 => 8000, 91 | ); 92 | if (!array_key_exists($sample_rate_index, $dssSampleRateLookup)) { 93 | $this->getid3->warning('unknown sample_rate_index: 0x'.strtoupper(dechex($sample_rate_index))); 94 | return false; 95 | } 96 | return $dssSampleRateLookup[$sample_rate_index]; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /php/getID3/module.audio.lpac.php: -------------------------------------------------------------------------------- 1 | // 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.lpac.php // 12 | // module for analyzing LPAC Audio files // 13 | // dependencies: module.audio-video.riff.php // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); 18 | 19 | class getid3_lpac extends getid3_handler 20 | { 21 | 22 | public function Analyze() { 23 | $info = &$this->getid3->info; 24 | 25 | $this->fseek($info['avdataoffset']); 26 | $LPACheader = $this->fread(14); 27 | if (substr($LPACheader, 0, 4) != 'LPAC') { 28 | $this->error('Expected "LPAC" at offset '.$info['avdataoffset'].', found "'.$StreamMarker.'"'); 29 | return false; 30 | } 31 | $info['avdataoffset'] += 14; 32 | 33 | $info['fileformat'] = 'lpac'; 34 | $info['audio']['dataformat'] = 'lpac'; 35 | $info['audio']['lossless'] = true; 36 | $info['audio']['bitrate_mode'] = 'vbr'; 37 | 38 | $info['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1)); 39 | $flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1)); 40 | $info['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4)); 41 | $flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4)); 42 | 43 | $info['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40); 44 | $info['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04); 45 | $info['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02); 46 | $info['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01); 47 | 48 | if ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) { 49 | $this->warning('24-bit and 16-bit flags cannot both be set'); 50 | } 51 | 52 | $info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000); 53 | $info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000); 54 | $info['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256; 55 | $info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000); 56 | $info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000); 57 | $info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000); 58 | $info['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8; 59 | $info['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F); 60 | 61 | if ($info['lpac']['flags']['fast_compress'] && ($info['lpac']['max_prediction_order'] != 3)) { 62 | $this->warning('max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$info['lpac']['max_prediction_order'].'"'); 63 | } 64 | switch ($info['lpac']['file_version']) { 65 | case 6: 66 | if ($info['lpac']['flags']['adaptive_quantization']) { 67 | $this->warning('adaptive_quantization expected to be false in LPAC file stucture v6, actually true'); 68 | } 69 | if ($info['lpac']['quantization'] != 20) { 70 | $this->warning('Quantization expected to be 20 in LPAC file stucture v6, actually '.$info['lpac']['flags']['Q']); 71 | } 72 | break; 73 | 74 | default: 75 | //$this->warning('This version of getID3() ['.$this->getid3->version().'] only supports LPAC file format version 6, this file is version '.$info['lpac']['file_version'].' - please report to info@getid3.org'); 76 | break; 77 | } 78 | 79 | $getid3_temp = new getID3(); 80 | $getid3_temp->openfile($this->getid3->filename); 81 | $getid3_temp->info = $info; 82 | $getid3_riff = new getid3_riff($getid3_temp); 83 | $getid3_riff->Analyze(); 84 | $info['avdataoffset'] = $getid3_temp->info['avdataoffset']; 85 | $info['riff'] = $getid3_temp->info['riff']; 86 | $info['error'] = $getid3_temp->info['error']; 87 | $info['warning'] = $getid3_temp->info['warning']; 88 | $info['lpac']['comments']['comment'] = $getid3_temp->info['comments']; 89 | $info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate']; 90 | unset($getid3_temp, $getid3_riff); 91 | 92 | $info['audio']['channels'] = ($info['lpac']['flags']['stereo'] ? 2 : 1); 93 | 94 | if ($info['lpac']['flags']['24_bit']) { 95 | $info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample']; 96 | } elseif ($info['lpac']['flags']['16_bit']) { 97 | $info['audio']['bits_per_sample'] = 16; 98 | } else { 99 | $info['audio']['bits_per_sample'] = 8; 100 | } 101 | 102 | if ($info['lpac']['flags']['fast_compress']) { 103 | // fast 104 | $info['audio']['encoder_options'] = '-1'; 105 | } else { 106 | switch ($info['lpac']['max_prediction_order']) { 107 | case 20: // simple 108 | $info['audio']['encoder_options'] = '-2'; 109 | break; 110 | case 30: // medium 111 | $info['audio']['encoder_options'] = '-3'; 112 | break; 113 | case 40: // high 114 | $info['audio']['encoder_options'] = '-4'; 115 | break; 116 | case 60: // extrahigh 117 | $info['audio']['encoder_options'] = '-5'; 118 | break; 119 | } 120 | } 121 | 122 | $info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate']; 123 | $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; 124 | 125 | return true; 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /php/getID3/module.audio.mod.php: -------------------------------------------------------------------------------- 1 | // 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.mod.php // 12 | // module for analyzing MOD Audio files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_mod extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | $this->fseek($info['avdataoffset']); 24 | $fileheader = $this->fread(1088); 25 | if (preg_match('#^IMPM#', $fileheader)) { 26 | return $this->getITheaderFilepointer(); 27 | } elseif (preg_match('#^Extended Module#', $fileheader)) { 28 | return $this->getXMheaderFilepointer(); 29 | } elseif (preg_match('#^.{44}SCRM#', $fileheader)) { 30 | return $this->getS3MheaderFilepointer(); 31 | } elseif (preg_match('#^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)#', $fileheader)) { 32 | return $this->getMODheaderFilepointer(); 33 | } 34 | $this->error('This is not a known type of MOD file'); 35 | return false; 36 | } 37 | 38 | 39 | public function getMODheaderFilepointer() { 40 | $info = &$this->getid3->info; 41 | $this->fseek($info['avdataoffset'] + 1080); 42 | $FormatID = $this->fread(4); 43 | if (!preg_match('#^(M.K.|[5-9]CHN|[1-3][0-9]CH)$#', $FormatID)) { 44 | $this->error('This is not a known type of MOD file'); 45 | return false; 46 | } 47 | 48 | $info['fileformat'] = 'mod'; 49 | 50 | $this->error('MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 51 | return false; 52 | } 53 | 54 | public function getXMheaderFilepointer() { 55 | $info = &$this->getid3->info; 56 | $this->fseek($info['avdataoffset']); 57 | $FormatID = $this->fread(15); 58 | if (!preg_match('#^Extended Module$#', $FormatID)) { 59 | $this->error('This is not a known type of XM-MOD file'); 60 | return false; 61 | } 62 | 63 | $info['fileformat'] = 'xm'; 64 | 65 | $this->error('XM-MOD parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 66 | return false; 67 | } 68 | 69 | public function getS3MheaderFilepointer() { 70 | $info = &$this->getid3->info; 71 | $this->fseek($info['avdataoffset'] + 44); 72 | $FormatID = $this->fread(4); 73 | if (!preg_match('#^SCRM$#', $FormatID)) { 74 | $this->error('This is not a ScreamTracker MOD file'); 75 | return false; 76 | } 77 | 78 | $info['fileformat'] = 's3m'; 79 | 80 | $this->error('ScreamTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 81 | return false; 82 | } 83 | 84 | public function getITheaderFilepointer() { 85 | $info = &$this->getid3->info; 86 | $this->fseek($info['avdataoffset']); 87 | $FormatID = $this->fread(4); 88 | if (!preg_match('#^IMPM$#', $FormatID)) { 89 | $this->error('This is not an ImpulseTracker MOD file'); 90 | return false; 91 | } 92 | 93 | $info['fileformat'] = 'it'; 94 | 95 | $this->error('ImpulseTracker parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 96 | return false; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /php/getID3/module.audio.rkau.php: -------------------------------------------------------------------------------- 1 | // 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.shorten.php // 12 | // module for analyzing Shorten Audio files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_rkau extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $RKAUHeader = $this->fread(20); 26 | $magic = 'RKA'; 27 | if (substr($RKAUHeader, 0, 3) != $magic) { 28 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($RKAUHeader, 0, 3)).'"'); 29 | return false; 30 | } 31 | 32 | $info['fileformat'] = 'rkau'; 33 | $info['audio']['dataformat'] = 'rkau'; 34 | $info['audio']['bitrate_mode'] = 'vbr'; 35 | 36 | $info['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1)); 37 | $info['rkau']['version'] = '1.'.str_pad($info['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT); 38 | if (($info['rkau']['version'] > 1.07) || ($info['rkau']['version'] < 1.06)) { 39 | $this->error('This version of getID3() ['.$this->getid3->version().'] can only parse RKAU files v1.06 and 1.07 (this file is v'.$info['rkau']['version'].')'); 40 | unset($info['rkau']); 41 | return false; 42 | } 43 | 44 | $info['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4)); 45 | $info['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4)); 46 | $info['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1)); 47 | $info['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1)); 48 | 49 | $info['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1)); 50 | $this->RKAUqualityLookup($info['rkau']); 51 | 52 | $info['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1)); 53 | $info['rkau']['flags']['joint_stereo'] = (bool) (!($info['rkau']['raw']['flags'] & 0x01)); 54 | $info['rkau']['flags']['streaming'] = (bool) ($info['rkau']['raw']['flags'] & 0x02); 55 | $info['rkau']['flags']['vrq_lossy_mode'] = (bool) ($info['rkau']['raw']['flags'] & 0x04); 56 | 57 | if ($info['rkau']['flags']['streaming']) { 58 | $info['avdataoffset'] += 20; 59 | $info['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4)); 60 | } else { 61 | $info['avdataoffset'] += 16; 62 | $info['rkau']['compressed_bytes'] = $info['avdataend'] - $info['avdataoffset'] - 1; 63 | } 64 | // Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes, 65 | // sometimes it's more, sometimes less. No idea why(?) 66 | 67 | $info['audio']['lossless'] = $info['rkau']['lossless']; 68 | $info['audio']['channels'] = $info['rkau']['channels']; 69 | $info['audio']['bits_per_sample'] = $info['rkau']['bits_per_sample']; 70 | $info['audio']['sample_rate'] = $info['rkau']['sample_rate']; 71 | 72 | $info['playtime_seconds'] = $info['rkau']['source_bytes'] / ($info['rkau']['sample_rate'] * $info['rkau']['channels'] * ($info['rkau']['bits_per_sample'] / 8)); 73 | $info['audio']['bitrate'] = ($info['rkau']['compressed_bytes'] * 8) / $info['playtime_seconds']; 74 | 75 | return true; 76 | 77 | } 78 | 79 | 80 | public function RKAUqualityLookup(&$RKAUdata) { 81 | $level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4; 82 | $quality = $RKAUdata['raw']['quality'] & 0x0F; 83 | 84 | $RKAUdata['lossless'] = (($quality == 0) ? true : false); 85 | $RKAUdata['compression_level'] = $level + 1; 86 | if (!$RKAUdata['lossless']) { 87 | $RKAUdata['quality_setting'] = $quality; 88 | } 89 | 90 | return true; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /php/getID3/module.audio.shorten.php: -------------------------------------------------------------------------------- 1 | // 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.shorten.php // 12 | // module for analyzing Shorten Audio files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_shorten extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | 26 | $ShortenHeader = $this->fread(8); 27 | $magic = 'ajkg'; 28 | if (substr($ShortenHeader, 0, 4) != $magic) { 29 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($ShortenHeader, 0, 4)).'"'); 30 | return false; 31 | } 32 | $info['fileformat'] = 'shn'; 33 | $info['audio']['dataformat'] = 'shn'; 34 | $info['audio']['lossless'] = true; 35 | $info['audio']['bitrate_mode'] = 'vbr'; 36 | 37 | $info['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1)); 38 | 39 | $this->fseek($info['avdataend'] - 12); 40 | $SeekTableSignatureTest = $this->fread(12); 41 | $info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK'); 42 | if ($info['shn']['seektable']['present']) { 43 | $info['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4)); 44 | $info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length']; 45 | $this->fseek($info['shn']['seektable']['offset']); 46 | $SeekTableMagic = $this->fread(4); 47 | $magic = 'SEEK'; 48 | if ($SeekTableMagic != $magic) { 49 | 50 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['shn']['seektable']['offset'].', found "'.getid3_lib::PrintHexBytes($SeekTableMagic).'"'); 51 | return false; 52 | 53 | } else { 54 | 55 | // typedef struct tag_TSeekEntry 56 | // { 57 | // unsigned long SampleNumber; 58 | // unsigned long SHNFileByteOffset; 59 | // unsigned long SHNLastBufferReadPosition; 60 | // unsigned short SHNByteGet; 61 | // unsigned short SHNBufferOffset; 62 | // unsigned short SHNFileBitOffset; 63 | // unsigned long SHNGBuffer; 64 | // unsigned short SHNBitShift; 65 | // long CBuf0[3]; 66 | // long CBuf1[3]; 67 | // long Offset0[4]; 68 | // long Offset1[4]; 69 | // }TSeekEntry; 70 | 71 | $SeekTableData = $this->fread($info['shn']['seektable']['length'] - 16); 72 | $info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80); 73 | //$info['shn']['seektable']['entries'] = array(); 74 | //$SeekTableOffset = 0; 75 | //for ($i = 0; $i < $info['shn']['seektable']['entry_count']; $i++) { 76 | // $SeekTableEntry['sample_number'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); 77 | // $SeekTableOffset += 4; 78 | // $SeekTableEntry['shn_file_byte_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); 79 | // $SeekTableOffset += 4; 80 | // $SeekTableEntry['shn_last_buffer_read_position'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); 81 | // $SeekTableOffset += 4; 82 | // $SeekTableEntry['shn_byte_get'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); 83 | // $SeekTableOffset += 2; 84 | // $SeekTableEntry['shn_buffer_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); 85 | // $SeekTableOffset += 2; 86 | // $SeekTableEntry['shn_file_bit_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); 87 | // $SeekTableOffset += 2; 88 | // $SeekTableEntry['shn_gbuffer'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); 89 | // $SeekTableOffset += 4; 90 | // $SeekTableEntry['shn_bit_shift'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); 91 | // $SeekTableOffset += 2; 92 | // for ($j = 0; $j < 3; $j++) { 93 | // $SeekTableEntry['cbuf0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); 94 | // $SeekTableOffset += 4; 95 | // } 96 | // for ($j = 0; $j < 3; $j++) { 97 | // $SeekTableEntry['cbuf1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); 98 | // $SeekTableOffset += 4; 99 | // } 100 | // for ($j = 0; $j < 4; $j++) { 101 | // $SeekTableEntry['offset0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); 102 | // $SeekTableOffset += 4; 103 | // } 104 | // for ($j = 0; $j < 4; $j++) { 105 | // $SeekTableEntry['offset1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); 106 | // $SeekTableOffset += 4; 107 | // } 108 | // 109 | // $info['shn']['seektable']['entries'][] = $SeekTableEntry; 110 | //} 111 | 112 | } 113 | 114 | } 115 | 116 | if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { 117 | $this->error('PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files'); 118 | return false; 119 | } 120 | 121 | if (GETID3_OS_ISWINDOWS) { 122 | 123 | $RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe'); 124 | foreach ($RequiredFiles as $required_file) { 125 | if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { 126 | $this->error(GETID3_HELPERAPPSDIR.$required_file.' does not exist'); 127 | return false; 128 | } 129 | } 130 | $commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$info['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 64'; 131 | $commandline = str_replace('/', '\\', $commandline); 132 | 133 | } else { 134 | 135 | static $shorten_present; 136 | if (!isset($shorten_present)) { 137 | $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`; 138 | } 139 | if (!$shorten_present) { 140 | $this->error('shorten binary was not found in path or /usr/local/bin'); 141 | return false; 142 | } 143 | $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($info['filenamepath']).' - | head -c 64'; 144 | 145 | } 146 | 147 | $output = `$commandline`; 148 | 149 | if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) { 150 | 151 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); 152 | 153 | $fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4)); 154 | $DecodedWAVFORMATEX = getid3_riff::parseWAVEFORMATex(substr($output, 20, $fmt_size)); 155 | $info['audio']['channels'] = $DecodedWAVFORMATEX['channels']; 156 | $info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample']; 157 | $info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate']; 158 | 159 | if (substr($output, 20 + $fmt_size, 4) == 'data') { 160 | 161 | $info['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec']; 162 | 163 | } else { 164 | 165 | $this->error('shorten failed to decode DATA chunk to expected location, cannot determine playtime'); 166 | return false; 167 | 168 | } 169 | 170 | $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8; 171 | 172 | } else { 173 | 174 | $this->error('shorten failed to decode file to WAV for parsing'); 175 | return false; 176 | 177 | } 178 | 179 | return true; 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /php/getID3/module.audio.tta.php: -------------------------------------------------------------------------------- 1 | // 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.tta.php // 12 | // module for analyzing TTA Audio files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_tta extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $info['fileformat'] = 'tta'; 25 | $info['audio']['dataformat'] = 'tta'; 26 | $info['audio']['lossless'] = true; 27 | $info['audio']['bitrate_mode'] = 'vbr'; 28 | 29 | $this->fseek($info['avdataoffset']); 30 | $ttaheader = $this->fread(26); 31 | 32 | $info['tta']['magic'] = substr($ttaheader, 0, 3); 33 | $magic = 'TTA'; 34 | if ($info['tta']['magic'] != $magic) { 35 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['tta']['magic']).'"'); 36 | unset($info['fileformat']); 37 | unset($info['audio']); 38 | unset($info['tta']); 39 | return false; 40 | } 41 | 42 | switch ($ttaheader{3}) { 43 | case "\x01": // TTA v1.x 44 | case "\x02": // TTA v1.x 45 | case "\x03": // TTA v1.x 46 | // "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year." 47 | $info['tta']['major_version'] = 1; 48 | $info['avdataoffset'] += 16; 49 | 50 | $info['tta']['compression_level'] = ord($ttaheader{3}); 51 | $info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); 52 | $info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); 53 | $info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4)); 54 | $info['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); 55 | 56 | $info['audio']['encoder_options'] = '-e'.$info['tta']['compression_level']; 57 | $info['playtime_seconds'] = $info['tta']['samples_per_channel'] / $info['tta']['sample_rate']; 58 | break; 59 | 60 | case '2': // TTA v2.x 61 | // "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4." 62 | $info['tta']['major_version'] = 2; 63 | $info['avdataoffset'] += 20; 64 | 65 | $info['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); 66 | $info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); 67 | $info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); 68 | $info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2)); 69 | $info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); 70 | $info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4)); 71 | 72 | $info['audio']['encoder_options'] = '-e'.$info['tta']['compression_level']; 73 | $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate']; 74 | break; 75 | 76 | case '1': // TTA v3.x 77 | // "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher." 78 | $info['tta']['major_version'] = 3; 79 | $info['avdataoffset'] += 26; 80 | 81 | $info['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::wFormatTagLookup() 82 | $info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); 83 | $info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); 84 | $info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4)); 85 | $info['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4)); 86 | $info['tta']['crc32_footer'] = substr($ttaheader, 18, 4); 87 | $info['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4)); 88 | 89 | $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate']; 90 | break; 91 | 92 | default: 93 | $this->error('This version of getID3() ['.$this->getid3->version().'] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3}); 94 | return false; 95 | break; 96 | } 97 | 98 | $info['audio']['encoder'] = 'TTA v'.$info['tta']['major_version']; 99 | $info['audio']['bits_per_sample'] = $info['tta']['bits_per_sample']; 100 | $info['audio']['sample_rate'] = $info['tta']['sample_rate']; 101 | $info['audio']['channels'] = $info['tta']['channels']; 102 | $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; 103 | 104 | return true; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /php/getID3/module.audio.voc.php: -------------------------------------------------------------------------------- 1 | // 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.voc.php // 12 | // module for analyzing Creative VOC Audio files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_voc extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $OriginalAVdataOffset = $info['avdataoffset']; 25 | $this->fseek($info['avdataoffset']); 26 | $VOCheader = $this->fread(26); 27 | 28 | $magic = 'Creative Voice File'; 29 | if (substr($VOCheader, 0, 19) != $magic) { 30 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($VOCheader, 0, 19)).'"'); 31 | return false; 32 | } 33 | 34 | // shortcuts 35 | $thisfile_audio = &$info['audio']; 36 | $info['voc'] = array(); 37 | $thisfile_voc = &$info['voc']; 38 | 39 | $info['fileformat'] = 'voc'; 40 | $thisfile_audio['dataformat'] = 'voc'; 41 | $thisfile_audio['bitrate_mode'] = 'cbr'; 42 | $thisfile_audio['lossless'] = true; 43 | $thisfile_audio['channels'] = 1; // might be overriden below 44 | $thisfile_audio['bits_per_sample'] = 8; // might be overriden below 45 | 46 | // byte # Description 47 | // ------ ------------------------------------------ 48 | // 00-12 'Creative Voice File' 49 | // 13 1A (eof to abort printing of file) 50 | // 14-15 Offset of first datablock in .voc file (std 1A 00 in Intel Notation) 51 | // 16-17 Version number (minor,major) (VOC-HDR puts 0A 01) 52 | // 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11) 53 | 54 | $thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2)); 55 | $thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1)); 56 | $thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1)); 57 | 58 | do { 59 | 60 | $BlockOffset = $this->ftell(); 61 | $BlockData = $this->fread(4); 62 | $BlockType = ord($BlockData{0}); 63 | $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3)); 64 | $ThisBlock = array(); 65 | 66 | getid3_lib::safe_inc($thisfile_voc['blocktypes'][$BlockType], 1); 67 | switch ($BlockType) { 68 | case 0: // Terminator 69 | // do nothing, we'll break out of the loop down below 70 | break; 71 | 72 | case 1: // Sound data 73 | $BlockData .= $this->fread(2); 74 | if ($info['avdataoffset'] <= $OriginalAVdataOffset) { 75 | $info['avdataoffset'] = $this->ftell(); 76 | } 77 | $this->fseek($BlockSize - 2, SEEK_CUR); 78 | 79 | $ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1)); 80 | $ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1)); 81 | 82 | $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']); 83 | if ($ThisBlock['compression_type'] <= 3) { 84 | $thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name'])); 85 | } 86 | 87 | // Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available) 88 | if (empty($thisfile_audio['sample_rate'])) { 89 | // SR byte = 256 - (1000000 / sample_rate) 90 | $thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']); 91 | } 92 | break; 93 | 94 | case 2: // Sound continue 95 | case 3: // Silence 96 | case 4: // Marker 97 | case 6: // Repeat 98 | case 7: // End repeat 99 | // nothing useful, just skip 100 | $this->fseek($BlockSize, SEEK_CUR); 101 | break; 102 | 103 | case 8: // Extended 104 | $BlockData .= $this->fread(4); 105 | 106 | //00-01 Time Constant: 107 | // Mono: 65536 - (256000000 / sample_rate) 108 | // Stereo: 65536 - (256000000 / (sample_rate * 2)) 109 | $ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2)); 110 | $ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1)); 111 | $ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1)); 112 | 113 | $thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1); 114 | $thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']); 115 | break; 116 | 117 | case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit 118 | $BlockData .= $this->fread(12); 119 | if ($info['avdataoffset'] <= $OriginalAVdataOffset) { 120 | $info['avdataoffset'] = $this->ftell(); 121 | } 122 | $this->fseek($BlockSize - 12, SEEK_CUR); 123 | 124 | $ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); 125 | $ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1)); 126 | $ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1)); 127 | $ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2)); 128 | 129 | $ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']); 130 | if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) { 131 | $thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']); 132 | } 133 | 134 | $thisfile_audio['sample_rate'] = $ThisBlock['sample_rate']; 135 | $thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample']; 136 | $thisfile_audio['channels'] = $ThisBlock['channels']; 137 | break; 138 | 139 | default: 140 | $this->warning('Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset); 141 | $this->fseek($BlockSize, SEEK_CUR); 142 | break; 143 | } 144 | 145 | if (!empty($ThisBlock)) { 146 | $ThisBlock['block_offset'] = $BlockOffset; 147 | $ThisBlock['block_size'] = $BlockSize; 148 | $ThisBlock['block_type_id'] = $BlockType; 149 | $thisfile_voc['blocks'][] = $ThisBlock; 150 | } 151 | 152 | } while (!feof($this->getid3->fp) && ($BlockType != 0)); 153 | 154 | // Terminator block doesn't have size field, so seek back 3 spaces 155 | $this->fseek(-3, SEEK_CUR); 156 | 157 | ksort($thisfile_voc['blocktypes']); 158 | 159 | if (!empty($thisfile_voc['compressed_bits_per_sample'])) { 160 | $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']); 161 | $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; 162 | } 163 | 164 | return true; 165 | } 166 | 167 | public function VOCcompressionTypeLookup($index) { 168 | static $VOCcompressionTypeLookup = array( 169 | 0 => '8-bit', 170 | 1 => '4-bit', 171 | 2 => '2.6-bit', 172 | 3 => '2-bit' 173 | ); 174 | return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels'); 175 | } 176 | 177 | public function VOCwFormatLookup($index) { 178 | static $VOCwFormatLookup = array( 179 | 0x0000 => '8-bit unsigned PCM', 180 | 0x0001 => 'Creative 8-bit to 4-bit ADPCM', 181 | 0x0002 => 'Creative 8-bit to 3-bit ADPCM', 182 | 0x0003 => 'Creative 8-bit to 2-bit ADPCM', 183 | 0x0004 => '16-bit signed PCM', 184 | 0x0006 => 'CCITT a-Law', 185 | 0x0007 => 'CCITT u-Law', 186 | 0x2000 => 'Creative 16-bit to 4-bit ADPCM' 187 | ); 188 | return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); 189 | } 190 | 191 | public function VOCwFormatActualBitsPerSampleLookup($index) { 192 | static $VOCwFormatLookup = array( 193 | 0x0000 => 8, 194 | 0x0001 => 4, 195 | 0x0002 => 3, 196 | 0x0003 => 2, 197 | 0x0004 => 16, 198 | 0x0006 => 8, 199 | 0x0007 => 8, 200 | 0x2000 => 4 201 | ); 202 | return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /php/getID3/module.audio.vqf.php: -------------------------------------------------------------------------------- 1 | // 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.vqf.php // 12 | // module for analyzing VQF audio files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_vqf extends getid3_handler 19 | { 20 | public function Analyze() { 21 | $info = &$this->getid3->info; 22 | 23 | // based loosely on code from TTwinVQ by Jurgen Faul 24 | // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html 25 | 26 | $info['fileformat'] = 'vqf'; 27 | $info['audio']['dataformat'] = 'vqf'; 28 | $info['audio']['bitrate_mode'] = 'cbr'; 29 | $info['audio']['lossless'] = false; 30 | 31 | // shortcut 32 | $info['vqf']['raw'] = array(); 33 | $thisfile_vqf = &$info['vqf']; 34 | $thisfile_vqf_raw = &$thisfile_vqf['raw']; 35 | 36 | $this->fseek($info['avdataoffset']); 37 | $VQFheaderData = $this->fread(16); 38 | 39 | $offset = 0; 40 | $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4); 41 | $magic = 'TWIN'; 42 | if ($thisfile_vqf_raw['header_tag'] != $magic) { 43 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($thisfile_vqf_raw['header_tag']).'"'); 44 | unset($info['vqf']); 45 | unset($info['fileformat']); 46 | return false; 47 | } 48 | $offset += 4; 49 | $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8); 50 | $offset += 8; 51 | $thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4)); 52 | $offset += 4; 53 | 54 | while ($this->ftell() < $info['avdataend']) { 55 | 56 | $ChunkBaseOffset = $this->ftell(); 57 | $chunkoffset = 0; 58 | $ChunkData = $this->fread(8); 59 | $ChunkName = substr($ChunkData, $chunkoffset, 4); 60 | if ($ChunkName == 'DATA') { 61 | $info['avdataoffset'] = $ChunkBaseOffset; 62 | break; 63 | } 64 | $chunkoffset += 4; 65 | $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); 66 | $chunkoffset += 4; 67 | if ($ChunkSize > ($info['avdataend'] - $this->ftell())) { 68 | $this->error('Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset); 69 | break; 70 | } 71 | if ($ChunkSize > 0) { 72 | $ChunkData .= $this->fread($ChunkSize); 73 | } 74 | 75 | switch ($ChunkName) { 76 | case 'COMM': 77 | // shortcut 78 | $thisfile_vqf['COMM'] = array(); 79 | $thisfile_vqf_COMM = &$thisfile_vqf['COMM']; 80 | 81 | $thisfile_vqf_COMM['channel_mode'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); 82 | $chunkoffset += 4; 83 | $thisfile_vqf_COMM['bitrate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); 84 | $chunkoffset += 4; 85 | $thisfile_vqf_COMM['sample_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); 86 | $chunkoffset += 4; 87 | $thisfile_vqf_COMM['security_level'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); 88 | $chunkoffset += 4; 89 | 90 | $info['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1; 91 | $info['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']); 92 | $info['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000; 93 | $info['audio']['encoder_options'] = 'CBR' . ceil($info['audio']['bitrate']/1000); 94 | 95 | if ($info['audio']['bitrate'] == 0) { 96 | $this->error('Corrupt VQF file: bitrate_audio == zero'); 97 | return false; 98 | } 99 | break; 100 | 101 | case 'NAME': 102 | case 'AUTH': 103 | case '(c) ': 104 | case 'FILE': 105 | case 'COMT': 106 | case 'ALBM': 107 | $thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8)); 108 | break; 109 | 110 | case 'DSIZ': 111 | $thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4)); 112 | break; 113 | 114 | default: 115 | $this->warning('Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset); 116 | break; 117 | } 118 | } 119 | 120 | $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']; 121 | 122 | if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($info['avdataend'] - $info['avdataoffset'] - strlen('DATA'))))) { 123 | switch ($thisfile_vqf['DSIZ']) { 124 | case 0: 125 | case 1: 126 | $this->warning('Invalid DSIZ value "'.$thisfile_vqf['DSIZ'].'". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v'.($thisfile_vqf['DSIZ'] + 1).'.0'); 127 | $info['audio']['encoder'] = 'Ahead Nero'; 128 | break; 129 | 130 | default: 131 | $this->warning('Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($info['avdataend'] - $info['avdataoffset'] - strlen('DATA'))); 132 | break; 133 | } 134 | } 135 | 136 | return true; 137 | } 138 | 139 | public function VQFchannelFrequencyLookup($frequencyid) { 140 | static $VQFchannelFrequencyLookup = array( 141 | 11 => 11025, 142 | 22 => 22050, 143 | 44 => 44100 144 | ); 145 | return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000); 146 | } 147 | 148 | public function VQFcommentNiceNameLookup($shortname) { 149 | static $VQFcommentNiceNameLookup = array( 150 | 'NAME' => 'title', 151 | 'AUTH' => 'artist', 152 | '(c) ' => 'copyright', 153 | 'FILE' => 'filename', 154 | 'COMT' => 'comment', 155 | 'ALBM' => 'album' 156 | ); 157 | return (isset($VQFcommentNiceNameLookup[$shortname]) ? $VQFcommentNiceNameLookup[$shortname] : $shortname); 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /php/getID3/module.graphic.efax.php: -------------------------------------------------------------------------------- 1 | // 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.archive.efax.php // 12 | // module for analyzing eFax files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_efax extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $efaxheader = $this->fread(1024); 26 | 27 | $info['efax']['header']['magic'] = substr($efaxheader, 0, 2); 28 | if ($info['efax']['header']['magic'] != "\xDC\xFE") { 29 | $this->error('Invalid eFax byte order identifier (expecting DC FE, found '.getid3_lib::PrintHexBytes($info['efax']['header']['magic']).') at offset '.$info['avdataoffset']); 30 | return false; 31 | } 32 | $info['fileformat'] = 'efax'; 33 | 34 | $info['efax']['header']['filesize'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 2, 4)); 35 | if ($info['efax']['header']['filesize'] != $info['filesize']) { 36 | $this->error('Probable '.(($info['efax']['header']['filesize'] > $info['filesize']) ? 'truncated' : 'corrupt').' file, expecting '.$info['efax']['header']['filesize'].' bytes, found '.$info['filesize'].' bytes'); 37 | } 38 | $info['efax']['header']['software1'] = rtrim(substr($efaxheader, 26, 32), "\x00"); 39 | $info['efax']['header']['software2'] = rtrim(substr($efaxheader, 58, 32), "\x00"); 40 | $info['efax']['header']['software3'] = rtrim(substr($efaxheader, 90, 32), "\x00"); 41 | 42 | $info['efax']['header']['pages'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 198, 2)); 43 | $info['efax']['header']['data_bytes'] = getid3_lib::LittleEndian2Int(substr($efaxheader, 202, 4)); 44 | 45 | $this->error('eFax parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 46 | return false; 47 | 48 | return true; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /php/getID3/module.graphic.pcd.php: -------------------------------------------------------------------------------- 1 | // 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.graphic.pcd.php // 12 | // module for analyzing PhotoCD (PCD) Image files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_pcd extends getid3_handler 19 | { 20 | public $ExtractData = 0; 21 | 22 | public function Analyze() { 23 | $info = &$this->getid3->info; 24 | 25 | $info['fileformat'] = 'pcd'; 26 | $info['video']['dataformat'] = 'pcd'; 27 | $info['video']['lossless'] = false; 28 | 29 | 30 | $this->fseek($info['avdataoffset'] + 72); 31 | 32 | $PCDflags = $this->fread(1); 33 | $PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false); 34 | 35 | 36 | if ($PCDisVertical) { 37 | $info['video']['resolution_x'] = 3072; 38 | $info['video']['resolution_y'] = 2048; 39 | } else { 40 | $info['video']['resolution_x'] = 2048; 41 | $info['video']['resolution_y'] = 3072; 42 | } 43 | 44 | 45 | if ($this->ExtractData > 3) { 46 | 47 | $this->error('Cannot extract PSD image data for detail levels above BASE (level-3) because encrypted with Kodak-proprietary compression/encryption.'); 48 | 49 | } elseif ($this->ExtractData > 0) { 50 | 51 | $PCD_levels[1] = array( 192, 128, 0x02000); // BASE/16 52 | $PCD_levels[2] = array( 384, 256, 0x0B800); // BASE/4 53 | $PCD_levels[3] = array( 768, 512, 0x30000); // BASE 54 | //$PCD_levels[4] = array(1536, 1024, ??); // BASE*4 - encrypted with Kodak-proprietary compression/encryption 55 | //$PCD_levels[5] = array(3072, 2048, ??); // BASE*16 - encrypted with Kodak-proprietary compression/encryption 56 | //$PCD_levels[6] = array(6144, 4096, ??); // BASE*64 - encrypted with Kodak-proprietary compression/encryption; PhotoCD-Pro only 57 | 58 | list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3]; 59 | 60 | $this->fseek($info['avdataoffset'] + $PCD_dataOffset); 61 | 62 | for ($y = 0; $y < $PCD_height; $y += 2) { 63 | // The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h. 64 | // To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each 65 | // consisting of ‘w’ bytes, where ‘w’ is the width of the image-subtype. The first ‘w’ bytes and 66 | // the first half of the third ‘w’ bytes contain data for the first RGB-line, the second ‘w’ bytes 67 | // and the second half of the third ‘w’ bytes contain data for a second RGB-line. 68 | 69 | $PCD_data_Y1 = $this->fread($PCD_width); 70 | $PCD_data_Y2 = $this->fread($PCD_width); 71 | $PCD_data_Cb = $this->fread(intval(round($PCD_width / 2))); 72 | $PCD_data_Cr = $this->fread(intval(round($PCD_width / 2))); 73 | 74 | for ($x = 0; $x < $PCD_width; $x++) { 75 | if ($PCDisVertical) { 76 | $info['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); 77 | $info['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); 78 | } else { 79 | $info['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); 80 | $info['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); 81 | } 82 | } 83 | } 84 | 85 | // Example for plotting extracted data 86 | //getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true); 87 | //if ($PCDisVertical) { 88 | // $BMPinfo['resolution_x'] = $PCD_height; 89 | // $BMPinfo['resolution_y'] = $PCD_width; 90 | //} else { 91 | // $BMPinfo['resolution_x'] = $PCD_width; 92 | // $BMPinfo['resolution_y'] = $PCD_height; 93 | //} 94 | //$BMPinfo['bmp']['data'] = $info['pcd']['data']; 95 | //getid3_bmp::PlotBMP($BMPinfo); 96 | //exit; 97 | 98 | } 99 | 100 | } 101 | 102 | public function YCbCr2RGB($Y, $Cb, $Cr) { 103 | static $YCbCr_constants = array(); 104 | if (empty($YCbCr_constants)) { 105 | $YCbCr_constants['red']['Y'] = 0.0054980 * 256; 106 | $YCbCr_constants['red']['Cb'] = 0.0000000 * 256; 107 | $YCbCr_constants['red']['Cr'] = 0.0051681 * 256; 108 | $YCbCr_constants['green']['Y'] = 0.0054980 * 256; 109 | $YCbCr_constants['green']['Cb'] = -0.0015446 * 256; 110 | $YCbCr_constants['green']['Cr'] = -0.0026325 * 256; 111 | $YCbCr_constants['blue']['Y'] = 0.0054980 * 256; 112 | $YCbCr_constants['blue']['Cb'] = 0.0079533 * 256; 113 | $YCbCr_constants['blue']['Cr'] = 0.0000000 * 256; 114 | } 115 | 116 | $RGBcolor = array('red'=>0, 'green'=>0, 'blue'=>0); 117 | foreach ($RGBcolor as $rgbname => $dummy) { 118 | $RGBcolor[$rgbname] = max(0, 119 | min(255, 120 | intval( 121 | round( 122 | ($YCbCr_constants[$rgbname]['Y'] * $Y) + 123 | ($YCbCr_constants[$rgbname]['Cb'] * ($Cb - 156)) + 124 | ($YCbCr_constants[$rgbname]['Cr'] * ($Cr - 137)) 125 | ) 126 | ) 127 | ) 128 | ); 129 | } 130 | return (($RGBcolor['red'] * 65536) + ($RGBcolor['green'] * 256) + $RGBcolor['blue']); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /php/getID3/module.graphic.svg.php: -------------------------------------------------------------------------------- 1 | // 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.graphic.svg.php // 12 | // module for analyzing SVG Image files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_svg extends getid3_handler 19 | { 20 | 21 | 22 | public function Analyze() { 23 | $info = &$this->getid3->info; 24 | 25 | $this->fseek($info['avdataoffset']); 26 | 27 | $SVGheader = $this->fread(4096); 28 | if (preg_match('#\<\?xml([^\>]+)\?\>#i', $SVGheader, $matches)) { 29 | $info['svg']['xml']['raw'] = $matches; 30 | } 31 | if (preg_match('#\<\!DOCTYPE([^\>]+)\>#i', $SVGheader, $matches)) { 32 | $info['svg']['doctype']['raw'] = $matches; 33 | } 34 | if (preg_match('#\]+)\>#i', $SVGheader, $matches)) { 35 | $info['svg']['svg']['raw'] = $matches; 36 | } 37 | if (isset($info['svg']['svg']['raw'])) { 38 | 39 | $sections_to_fix = array('xml', 'doctype', 'svg'); 40 | foreach ($sections_to_fix as $section_to_fix) { 41 | if (!isset($info['svg'][$section_to_fix])) { 42 | continue; 43 | } 44 | $section_data = array(); 45 | while (preg_match('/ "([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) { 46 | $section_data[] = $matches[1]; 47 | $info['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $info['svg'][$section_to_fix]['raw'][1]); 48 | } 49 | while (preg_match('/([^\s]+)="([^"]+)"/', $info['svg'][$section_to_fix]['raw'][1], $matches)) { 50 | $section_data[] = $matches[0]; 51 | $info['svg'][$section_to_fix]['raw'][1] = str_replace($matches[0], '', $info['svg'][$section_to_fix]['raw'][1]); 52 | } 53 | $section_data = array_merge($section_data, preg_split('/[\s,]+/', $info['svg'][$section_to_fix]['raw'][1])); 54 | foreach ($section_data as $keyvaluepair) { 55 | $keyvaluepair = trim($keyvaluepair); 56 | if ($keyvaluepair) { 57 | $keyvalueexploded = explode('=', $keyvaluepair); 58 | $key = (isset($keyvalueexploded[0]) ? $keyvalueexploded[0] : ''); 59 | $value = (isset($keyvalueexploded[1]) ? $keyvalueexploded[1] : ''); 60 | $info['svg'][$section_to_fix]['sections'][$key] = trim($value, '"'); 61 | } 62 | } 63 | } 64 | 65 | $info['fileformat'] = 'svg'; 66 | $info['video']['dataformat'] = 'svg'; 67 | $info['video']['lossless'] = true; 68 | //$info['video']['bits_per_sample'] = 24; 69 | $info['video']['pixel_aspect_ratio'] = (float) 1; 70 | 71 | if (!empty($info['svg']['svg']['sections']['width'])) { 72 | $info['svg']['width'] = intval($info['svg']['svg']['sections']['width']); 73 | } 74 | if (!empty($info['svg']['svg']['sections']['height'])) { 75 | $info['svg']['height'] = intval($info['svg']['svg']['sections']['height']); 76 | } 77 | if (!empty($info['svg']['svg']['sections']['version'])) { 78 | $info['svg']['version'] = $info['svg']['svg']['sections']['version']; 79 | } 80 | if (!isset($info['svg']['version']) && isset($info['svg']['doctype']['sections'])) { 81 | foreach ($info['svg']['doctype']['sections'] as $key => $value) { 82 | if (preg_match('#//W3C//DTD SVG ([0-9\.]+)//#i', $key, $matches)) { 83 | $info['svg']['version'] = $matches[1]; 84 | break; 85 | } 86 | } 87 | } 88 | 89 | if (!empty($info['svg']['width'])) { 90 | $info['video']['resolution_x'] = $info['svg']['width']; 91 | } 92 | if (!empty($info['svg']['height'])) { 93 | $info['video']['resolution_y'] = $info['svg']['height']; 94 | } 95 | 96 | return true; 97 | } 98 | $this->error('Did not find expected tag'); 99 | return false; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /php/getID3/module.graphic.tiff.php: -------------------------------------------------------------------------------- 1 | // 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.archive.tiff.php // 12 | // module for analyzing TIFF files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_tiff extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $TIFFheader = $this->fread(4); 26 | 27 | switch (substr($TIFFheader, 0, 2)) { 28 | case 'II': 29 | $info['tiff']['byte_order'] = 'Intel'; 30 | break; 31 | case 'MM': 32 | $info['tiff']['byte_order'] = 'Motorola'; 33 | break; 34 | default: 35 | $this->error('Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$info['avdataoffset']); 36 | return false; 37 | break; 38 | } 39 | 40 | $info['fileformat'] = 'tiff'; 41 | $info['video']['dataformat'] = 'tiff'; 42 | $info['video']['lossless'] = true; 43 | $info['tiff']['ifd'] = array(); 44 | $CurrentIFD = array(); 45 | 46 | $FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8); 47 | 48 | $nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']); 49 | 50 | while ($nextIFDoffset > 0) { 51 | 52 | $CurrentIFD['offset'] = $nextIFDoffset; 53 | 54 | $this->fseek($info['avdataoffset'] + $nextIFDoffset); 55 | $CurrentIFD['fieldcount'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']); 56 | 57 | for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) { 58 | $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']); 59 | $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']); 60 | $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']); 61 | $CurrentIFD['fields'][$i]['raw']['offset'] = $this->fread(4); 62 | 63 | switch ($CurrentIFD['fields'][$i]['raw']['type']) { 64 | case 1: // BYTE An 8-bit unsigned integer. 65 | if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { 66 | $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $info['tiff']['byte_order']); 67 | } else { 68 | $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); 69 | } 70 | break; 71 | 72 | case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null. 73 | if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { 74 | $CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3); 75 | } else { 76 | $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); 77 | } 78 | break; 79 | 80 | case 3: // SHORT A 16-bit (2-byte) unsigned integer. 81 | if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) { 82 | $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $info['tiff']['byte_order']); 83 | } else { 84 | $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); 85 | } 86 | break; 87 | 88 | case 4: // LONG A 32-bit (4-byte) unsigned integer. 89 | if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) { 90 | $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); 91 | } else { 92 | $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); 93 | } 94 | break; 95 | 96 | case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator. 97 | break; 98 | } 99 | } 100 | 101 | $info['tiff']['ifd'][] = $CurrentIFD; 102 | $CurrentIFD = array(); 103 | $nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']); 104 | 105 | } 106 | 107 | foreach ($info['tiff']['ifd'] as $IFDid => $IFDarray) { 108 | foreach ($IFDarray['fields'] as $key => $fieldarray) { 109 | switch ($fieldarray['raw']['tag']) { 110 | case 256: // ImageWidth 111 | case 257: // ImageLength 112 | case 258: // BitsPerSample 113 | case 259: // Compression 114 | if (!isset($fieldarray['value'])) { 115 | $this->fseek($fieldarray['offset']); 116 | $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); 117 | 118 | } 119 | break; 120 | 121 | case 270: // ImageDescription 122 | case 271: // Make 123 | case 272: // Model 124 | case 305: // Software 125 | case 306: // DateTime 126 | case 315: // Artist 127 | case 316: // HostComputer 128 | if (isset($fieldarray['value'])) { 129 | $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value']; 130 | } else { 131 | $this->fseek($fieldarray['offset']); 132 | $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); 133 | 134 | } 135 | break; 136 | } 137 | switch ($fieldarray['raw']['tag']) { 138 | case 256: // ImageWidth 139 | $info['video']['resolution_x'] = $fieldarray['value']; 140 | break; 141 | 142 | case 257: // ImageLength 143 | $info['video']['resolution_y'] = $fieldarray['value']; 144 | break; 145 | 146 | case 258: // BitsPerSample 147 | if (isset($fieldarray['value'])) { 148 | $info['video']['bits_per_sample'] = $fieldarray['value']; 149 | } else { 150 | $info['video']['bits_per_sample'] = 0; 151 | for ($i = 0; $i < $fieldarray['raw']['length']; $i++) { 152 | $info['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $info['tiff']['byte_order']); 153 | } 154 | } 155 | break; 156 | 157 | case 259: // Compression 158 | $info['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']); 159 | break; 160 | 161 | case 270: // ImageDescription 162 | case 271: // Make 163 | case 272: // Model 164 | case 305: // Software 165 | case 306: // DateTime 166 | case 315: // Artist 167 | case 316: // HostComputer 168 | $TIFFcommentName = $this->TIFFcommentName($fieldarray['raw']['tag']); 169 | if (isset($info['tiff']['comments'][$TIFFcommentName])) { 170 | $info['tiff']['comments'][$TIFFcommentName][] = $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']; 171 | } else { 172 | $info['tiff']['comments'][$TIFFcommentName] = array($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']); 173 | } 174 | break; 175 | 176 | default: 177 | break; 178 | } 179 | } 180 | } 181 | 182 | return true; 183 | } 184 | 185 | 186 | public function TIFFendian2Int($bytestring, $byteorder) { 187 | if ($byteorder == 'Intel') { 188 | return getid3_lib::LittleEndian2Int($bytestring); 189 | } elseif ($byteorder == 'Motorola') { 190 | return getid3_lib::BigEndian2Int($bytestring); 191 | } 192 | return false; 193 | } 194 | 195 | public function TIFFcompressionMethod($id) { 196 | static $TIFFcompressionMethod = array(); 197 | if (empty($TIFFcompressionMethod)) { 198 | $TIFFcompressionMethod = array( 199 | 1 => 'Uncompressed', 200 | 2 => 'Huffman', 201 | 3 => 'Fax - CCITT 3', 202 | 5 => 'LZW', 203 | 32773 => 'PackBits', 204 | ); 205 | } 206 | return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')'); 207 | } 208 | 209 | public function TIFFcommentName($id) { 210 | static $TIFFcommentName = array(); 211 | if (empty($TIFFcommentName)) { 212 | $TIFFcommentName = array( 213 | 270 => 'imagedescription', 214 | 271 => 'make', 215 | 272 => 'model', 216 | 305 => 'software', 217 | 306 => 'datetime', 218 | 315 => 'artist', 219 | 316 => 'hostcomputer', 220 | ); 221 | } 222 | return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')'); 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /php/getID3/module.misc.exe.php: -------------------------------------------------------------------------------- 1 | // 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.misc.exe.php // 12 | // module for analyzing EXE files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_exe extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $EXEheader = $this->fread(28); 26 | 27 | $magic = 'MZ'; 28 | if (substr($EXEheader, 0, 2) != $magic) { 29 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($EXEheader, 0, 2)).'"'); 30 | return false; 31 | } 32 | 33 | $info['fileformat'] = 'exe'; 34 | $info['exe']['mz']['magic'] = 'MZ'; 35 | 36 | $info['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2)); 37 | $info['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2)); 38 | $info['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2)); 39 | $info['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2)); 40 | $info['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2)); 41 | $info['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2)); 42 | $info['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2)); 43 | $info['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2)); 44 | $info['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2)); 45 | $info['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4)); 46 | $info['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2)); 47 | $info['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2)); 48 | 49 | $info['exe']['mz']['byte_size'] = (($info['exe']['mz']['raw']['page_count'] - 1)) * 512 + $info['exe']['mz']['raw']['last_page_size']; 50 | $info['exe']['mz']['header_size'] = $info['exe']['mz']['raw']['header_paragraphs'] * 16; 51 | $info['exe']['mz']['memory_minimum'] = $info['exe']['mz']['raw']['min_memory_paragraphs'] * 16; 52 | $info['exe']['mz']['memory_recommended'] = $info['exe']['mz']['raw']['max_memory_paragraphs'] * 16; 53 | 54 | $this->error('EXE parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 55 | return false; 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /php/getID3/module.misc.msoffice.php: -------------------------------------------------------------------------------- 1 | // 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.misc.msoffice.php // 12 | // module for analyzing MS Office (.doc, .xls, etc) files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_msoffice extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $this->fseek($info['avdataoffset']); 25 | $DOCFILEheader = $this->fread(8); 26 | $magic = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"; 27 | if (substr($DOCFILEheader, 0, 8) != $magic) { 28 | $this->error('Expecting "'.getid3_lib::PrintHexBytes($magic).'" at '.$info['avdataoffset'].', found '.getid3_lib::PrintHexBytes(substr($DOCFILEheader, 0, 8)).' instead.'); 29 | return false; 30 | } 31 | $info['fileformat'] = 'msoffice'; 32 | 33 | $this->error('MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 34 | return false; 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /php/getID3/module.misc.par2.php: -------------------------------------------------------------------------------- 1 | // 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.misc.par2.php // 12 | // module for analyzing PAR2 files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_par2 extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $info['fileformat'] = 'par2'; 25 | 26 | $this->error('PAR2 parsing not enabled in this version of getID3()'); 27 | return false; 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /php/getID3/module.misc.pdf.php: -------------------------------------------------------------------------------- 1 | // 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.misc.pdf.php // 12 | // module for analyzing PDF files // 13 | // dependencies: NONE // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_pdf extends getid3_handler 19 | { 20 | 21 | public function Analyze() { 22 | $info = &$this->getid3->info; 23 | 24 | $info['fileformat'] = 'pdf'; 25 | 26 | $this->error('PDF parsing not enabled in this version of getID3() ['.$this->getid3->version().']'); 27 | return false; 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /php/getID3/write.apetag.php: -------------------------------------------------------------------------------- 1 | // 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 | // write.apetag.php // 12 | // module for writing APE tags // 13 | // dependencies: module.tag.apetag.php // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true); 19 | 20 | class getid3_write_apetag 21 | { 22 | 23 | public $filename; 24 | public $tag_data; 25 | public $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data 26 | public $warnings = array(); // any non-critical errors will be stored here 27 | public $errors = array(); // any critical errors will be stored here 28 | 29 | public function __construct() { 30 | return true; 31 | } 32 | 33 | public function WriteAPEtag() { 34 | // NOTE: All data passed to this function must be UTF-8 format 35 | 36 | $getID3 = new getID3; 37 | $ThisFileInfo = $getID3->analyze($this->filename); 38 | 39 | if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) { 40 | if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) { 41 | // Current APE tag between Lyrics3 and ID3v1/EOF 42 | // This break Lyrics3 functionality 43 | if (!$this->DeleteAPEtag()) { 44 | return false; 45 | } 46 | $ThisFileInfo = $getID3->analyze($this->filename); 47 | } 48 | } 49 | 50 | if ($this->always_preserve_replaygain) { 51 | $ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain'); 52 | foreach ($ReplayGainTagsToPreserve as $rg_key) { 53 | if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) { 54 | $this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]; 55 | } 56 | } 57 | } 58 | 59 | if ($APEtag = $this->GenerateAPEtag()) { 60 | if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { 61 | $oldignoreuserabort = ignore_user_abort(true); 62 | flock($fp, LOCK_EX); 63 | 64 | $PostAPEdataOffset = $ThisFileInfo['avdataend']; 65 | if (isset($ThisFileInfo['ape']['tag_offset_end'])) { 66 | $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']); 67 | } 68 | if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) { 69 | $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']); 70 | } 71 | fseek($fp, $PostAPEdataOffset); 72 | $PostAPEdata = ''; 73 | if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) { 74 | $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset); 75 | } 76 | 77 | fseek($fp, $PostAPEdataOffset); 78 | if (isset($ThisFileInfo['ape']['tag_offset_start'])) { 79 | fseek($fp, $ThisFileInfo['ape']['tag_offset_start']); 80 | } 81 | ftruncate($fp, ftell($fp)); 82 | fwrite($fp, $APEtag, strlen($APEtag)); 83 | if (!empty($PostAPEdata)) { 84 | fwrite($fp, $PostAPEdata, strlen($PostAPEdata)); 85 | } 86 | flock($fp, LOCK_UN); 87 | fclose($fp); 88 | ignore_user_abort($oldignoreuserabort); 89 | return true; 90 | } 91 | } 92 | return false; 93 | } 94 | 95 | public function DeleteAPEtag() { 96 | $getID3 = new getID3; 97 | $ThisFileInfo = $getID3->analyze($this->filename); 98 | if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) { 99 | if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { 100 | 101 | flock($fp, LOCK_EX); 102 | $oldignoreuserabort = ignore_user_abort(true); 103 | 104 | fseek($fp, $ThisFileInfo['ape']['tag_offset_end']); 105 | $DataAfterAPE = ''; 106 | if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) { 107 | $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']); 108 | } 109 | 110 | ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']); 111 | fseek($fp, $ThisFileInfo['ape']['tag_offset_start']); 112 | 113 | if (!empty($DataAfterAPE)) { 114 | fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE)); 115 | } 116 | 117 | flock($fp, LOCK_UN); 118 | fclose($fp); 119 | ignore_user_abort($oldignoreuserabort); 120 | 121 | return true; 122 | } 123 | return false; 124 | } 125 | return true; 126 | } 127 | 128 | 129 | public function GenerateAPEtag() { 130 | // NOTE: All data passed to this function must be UTF-8 format 131 | 132 | $items = array(); 133 | if (!is_array($this->tag_data)) { 134 | return false; 135 | } 136 | foreach ($this->tag_data as $key => $arrayofvalues) { 137 | if (!is_array($arrayofvalues)) { 138 | return false; 139 | } 140 | 141 | $valuestring = ''; 142 | foreach ($arrayofvalues as $value) { 143 | $valuestring .= str_replace("\x00", '', $value)."\x00"; 144 | } 145 | $valuestring = rtrim($valuestring, "\x00"); 146 | 147 | // Length of the assigned value in bytes 148 | $tagitem = getid3_lib::LittleEndian2String(strlen($valuestring), 4); 149 | 150 | //$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false); 151 | $tagitem .= "\x00\x00\x00\x00"; 152 | 153 | $tagitem .= $this->CleanAPEtagItemKey($key)."\x00"; 154 | $tagitem .= $valuestring; 155 | 156 | $items[] = $tagitem; 157 | 158 | } 159 | 160 | return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false); 161 | } 162 | 163 | public function GenerateAPEtagHeaderFooter(&$items, $isheader=false) { 164 | $tagdatalength = 0; 165 | foreach ($items as $itemdata) { 166 | $tagdatalength += strlen($itemdata); 167 | } 168 | 169 | $APEheader = 'APETAGEX'; 170 | $APEheader .= getid3_lib::LittleEndian2String(2000, 4); 171 | $APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4); 172 | $APEheader .= getid3_lib::LittleEndian2String(count($items), 4); 173 | $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false); 174 | $APEheader .= str_repeat("\x00", 8); 175 | 176 | return $APEheader; 177 | } 178 | 179 | public function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) { 180 | $APEtagFlags = array_fill(0, 4, 0); 181 | if ($header) { 182 | $APEtagFlags[0] |= 0x80; // Tag contains a header 183 | } 184 | if (!$footer) { 185 | $APEtagFlags[0] |= 0x40; // Tag contains no footer 186 | } 187 | if ($isheader) { 188 | $APEtagFlags[0] |= 0x20; // This is the header, not the footer 189 | } 190 | 191 | // 0: Item contains text information coded in UTF-8 192 | // 1: Item contains binary information °) 193 | // 2: Item is a locator of external stored information °°) 194 | // 3: reserved 195 | $APEtagFlags[3] |= ($encodingid << 1); 196 | 197 | if ($readonly) { 198 | $APEtagFlags[3] |= 0x01; // Tag or Item is Read Only 199 | } 200 | 201 | return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]); 202 | } 203 | 204 | public function CleanAPEtagItemKey($itemkey) { 205 | $itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey); 206 | 207 | // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html 208 | switch (strtoupper($itemkey)) { 209 | case 'EAN/UPC': 210 | case 'ISBN': 211 | case 'LC': 212 | case 'ISRC': 213 | $itemkey = strtoupper($itemkey); 214 | break; 215 | 216 | default: 217 | $itemkey = ucwords($itemkey); 218 | break; 219 | } 220 | return $itemkey; 221 | 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /php/getID3/write.id3v1.php: -------------------------------------------------------------------------------- 1 | // 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 | // write.id3v1.php // 12 | // module for writing ID3v1 tags // 13 | // dependencies: module.tag.id3v1.php // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); 18 | 19 | class getid3_write_id3v1 20 | { 21 | public $filename; 22 | public $filesize; 23 | public $tag_data; 24 | public $warnings = array(); // any non-critical errors will be stored here 25 | public $errors = array(); // any critical errors will be stored here 26 | 27 | public function __construct() { 28 | return true; 29 | } 30 | 31 | public function WriteID3v1() { 32 | // File MUST be writeable - CHMOD(646) at least 33 | if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) { 34 | $this->setRealFileSize(); 35 | if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) { 36 | $this->errors[] = 'Unable to WriteID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; 37 | return false; 38 | } 39 | if ($fp_source = fopen($this->filename, 'r+b')) { 40 | fseek($fp_source, -128, SEEK_END); 41 | if (fread($fp_source, 3) == 'TAG') { 42 | fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag 43 | } else { 44 | fseek($fp_source, 0, SEEK_END); // append new ID3v1 tag 45 | } 46 | $this->tag_data['track'] = (isset($this->tag_data['track']) ? $this->tag_data['track'] : (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : (isset($this->tag_data['tracknumber']) ? $this->tag_data['tracknumber'] : ''))); 47 | 48 | $new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag( 49 | (isset($this->tag_data['title'] ) ? $this->tag_data['title'] : ''), 50 | (isset($this->tag_data['artist'] ) ? $this->tag_data['artist'] : ''), 51 | (isset($this->tag_data['album'] ) ? $this->tag_data['album'] : ''), 52 | (isset($this->tag_data['year'] ) ? $this->tag_data['year'] : ''), 53 | (isset($this->tag_data['genreid']) ? $this->tag_data['genreid'] : ''), 54 | (isset($this->tag_data['comment']) ? $this->tag_data['comment'] : ''), 55 | (isset($this->tag_data['track'] ) ? $this->tag_data['track'] : '')); 56 | fwrite($fp_source, $new_id3v1_tag_data, 128); 57 | fclose($fp_source); 58 | return true; 59 | 60 | } else { 61 | $this->errors[] = 'Could not fopen('.$this->filename.', "r+b")'; 62 | return false; 63 | } 64 | } 65 | $this->errors[] = 'File is not writeable: '.$this->filename; 66 | return false; 67 | } 68 | 69 | public function FixID3v1Padding() { 70 | // ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces 71 | // This function rewrites the ID3v1 tag with correct padding 72 | 73 | // Initialize getID3 engine 74 | $getID3 = new getID3; 75 | $getID3->option_tag_id3v2 = false; 76 | $getID3->option_tag_apetag = false; 77 | $getID3->option_tags_html = false; 78 | $getID3->option_extra_info = false; 79 | $getID3->option_tag_id3v1 = true; 80 | $ThisFileInfo = $getID3->analyze($this->filename); 81 | if (isset($ThisFileInfo['tags']['id3v1'])) { 82 | foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) { 83 | $id3v1data[$key] = implode(',', $value); 84 | } 85 | $this->tag_data = $id3v1data; 86 | return $this->WriteID3v1(); 87 | } 88 | return false; 89 | } 90 | 91 | public function RemoveID3v1() { 92 | // File MUST be writeable - CHMOD(646) at least 93 | if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) { 94 | $this->setRealFileSize(); 95 | if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) { 96 | $this->errors[] = 'Unable to RemoveID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; 97 | return false; 98 | } 99 | if ($fp_source = fopen($this->filename, 'r+b')) { 100 | 101 | fseek($fp_source, -128, SEEK_END); 102 | if (fread($fp_source, 3) == 'TAG') { 103 | ftruncate($fp_source, $this->filesize - 128); 104 | } else { 105 | // no ID3v1 tag to begin with - do nothing 106 | } 107 | fclose($fp_source); 108 | return true; 109 | 110 | } else { 111 | $this->errors[] = 'Could not fopen('.$this->filename.', "r+b")'; 112 | } 113 | } else { 114 | $this->errors[] = $this->filename.' is not writeable'; 115 | } 116 | return false; 117 | } 118 | 119 | public function setRealFileSize() { 120 | if (PHP_INT_MAX > 2147483647) { 121 | $this->filesize = filesize($this->filename); 122 | return true; 123 | } 124 | // 32-bit PHP will not return correct values for filesize() if file is >=2GB 125 | // but getID3->analyze() has workarounds to get actual filesize 126 | $getID3 = new getID3; 127 | $getID3->option_tag_id3v1 = false; 128 | $getID3->option_tag_id3v2 = false; 129 | $getID3->option_tag_apetag = false; 130 | $getID3->option_tags_html = false; 131 | $getID3->option_extra_info = false; 132 | $ThisFileInfo = $getID3->analyze($this->filename); 133 | $this->filesize = $ThisFileInfo['filesize']; 134 | return true; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /php/getID3/write.lyrics3.php: -------------------------------------------------------------------------------- 1 | // 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 | // write.lyrics3.php // 12 | // module for writing Lyrics3 tags // 13 | // dependencies: module.tag.lyrics3.php // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_write_lyrics3 19 | { 20 | public $filename; 21 | public $tag_data; 22 | //public $lyrics3_version = 2; // 1 or 2 23 | public $warnings = array(); // any non-critical errors will be stored here 24 | public $errors = array(); // any critical errors will be stored here 25 | 26 | public function __construct() { 27 | return true; 28 | } 29 | 30 | public function WriteLyrics3() { 31 | $this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3'; 32 | return false; 33 | } 34 | public function DeleteLyrics3() { 35 | // Initialize getID3 engine 36 | $getID3 = new getID3; 37 | $ThisFileInfo = $getID3->analyze($this->filename); 38 | if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) { 39 | if (is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) { 40 | 41 | flock($fp, LOCK_EX); 42 | $oldignoreuserabort = ignore_user_abort(true); 43 | 44 | fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end']); 45 | $DataAfterLyrics3 = ''; 46 | if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) { 47 | $DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']); 48 | } 49 | 50 | ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']); 51 | 52 | if (!empty($DataAfterLyrics3)) { 53 | fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start']); 54 | fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3)); 55 | } 56 | 57 | flock($fp, LOCK_UN); 58 | fclose($fp); 59 | ignore_user_abort($oldignoreuserabort); 60 | 61 | return true; 62 | 63 | } else { 64 | $this->errors[] = 'Cannot fopen('.$this->filename.', "a+b")'; 65 | return false; 66 | } 67 | } 68 | // no Lyrics3 present 69 | return true; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /php/getID3/write.metaflac.php: -------------------------------------------------------------------------------- 1 | // 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 | // write.metaflac.php // 12 | // module for writing metaflac tags // 13 | // dependencies: /helperapps/metaflac.exe // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_write_metaflac 19 | { 20 | 21 | public $filename; 22 | public $tag_data; 23 | public $warnings = array(); // any non-critical errors will be stored here 24 | public $errors = array(); // any critical errors will be stored here 25 | 26 | public function __construct() { 27 | return true; 28 | } 29 | 30 | public function WriteMetaFLAC() { 31 | 32 | if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { 33 | $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written'; 34 | return false; 35 | } 36 | 37 | // Create file with new comments 38 | $tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3'); 39 | if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) { 40 | foreach ($this->tag_data as $key => $value) { 41 | foreach ($value as $commentdata) { 42 | fwrite($fpcomments, $this->CleanmetaflacName($key).'='.$commentdata."\n"); 43 | } 44 | } 45 | fclose($fpcomments); 46 | 47 | } else { 48 | $this->errors[] = 'failed to open temporary tags file, tags not written - fopen("'.$tempcommentsfilename.'", "wb")'; 49 | return false; 50 | } 51 | 52 | $oldignoreuserabort = ignore_user_abort(true); 53 | if (GETID3_OS_ISWINDOWS) { 54 | 55 | if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) { 56 | //$commandline = '"'.GETID3_HELPERAPPSDIR.'metaflac.exe" --no-utf8-convert --remove-all-tags --import-tags-from="'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"'; 57 | // metaflac works fine if you copy-paste the above commandline into a command prompt, 58 | // but refuses to work with `backtick` if there are "doublequotes" present around BOTH 59 | // the metaflac pathname and the target filename. For whatever reason...?? 60 | // The solution is simply ensure that the metaflac pathname has no spaces, 61 | // and therefore does not need to be quoted 62 | 63 | // On top of that, if error messages are not always captured properly under Windows 64 | // To at least see if there was a problem, compare file modification timestamps before and after writing 65 | clearstatcache(); 66 | $timestampbeforewriting = filemtime($this->filename); 67 | 68 | $commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1'; 69 | $metaflacError = `$commandline`; 70 | 71 | if (empty($metaflacError)) { 72 | clearstatcache(); 73 | if ($timestampbeforewriting == filemtime($this->filename)) { 74 | $metaflacError = 'File modification timestamp has not changed - it looks like the tags were not written'; 75 | } 76 | } 77 | } else { 78 | $metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR; 79 | } 80 | 81 | } else { 82 | 83 | // It's simpler on *nix 84 | $commandline = 'metaflac --no-utf8-convert --remove-all-tags --import-tags-from='.escapeshellarg($tempcommentsfilename).' '.escapeshellarg($this->filename).' 2>&1'; 85 | $metaflacError = `$commandline`; 86 | 87 | } 88 | 89 | // Remove temporary comments file 90 | unlink($tempcommentsfilename); 91 | ignore_user_abort($oldignoreuserabort); 92 | 93 | if (!empty($metaflacError)) { 94 | 95 | $this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError; 96 | return false; 97 | 98 | } 99 | 100 | return true; 101 | } 102 | 103 | 104 | public function DeleteMetaFLAC() { 105 | 106 | if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { 107 | $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted'; 108 | return false; 109 | } 110 | 111 | $oldignoreuserabort = ignore_user_abort(true); 112 | if (GETID3_OS_ISWINDOWS) { 113 | 114 | if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) { 115 | // To at least see if there was a problem, compare file modification timestamps before and after writing 116 | clearstatcache(); 117 | $timestampbeforewriting = filemtime($this->filename); 118 | 119 | $commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --remove-all-tags "'.$this->filename.'" 2>&1'; 120 | $metaflacError = `$commandline`; 121 | 122 | if (empty($metaflacError)) { 123 | clearstatcache(); 124 | if ($timestampbeforewriting == filemtime($this->filename)) { 125 | $metaflacError = 'File modification timestamp has not changed - it looks like the tags were not deleted'; 126 | } 127 | } 128 | } else { 129 | $metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR; 130 | } 131 | 132 | } else { 133 | 134 | // It's simpler on *nix 135 | $commandline = 'metaflac --remove-all-tags "'.$this->filename.'" 2>&1'; 136 | $metaflacError = `$commandline`; 137 | 138 | } 139 | 140 | ignore_user_abort($oldignoreuserabort); 141 | 142 | if (!empty($metaflacError)) { 143 | $this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError; 144 | return false; 145 | } 146 | return true; 147 | } 148 | 149 | 150 | public function CleanmetaflacName($originalcommentname) { 151 | // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. 152 | // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through 153 | // 0x7A inclusive (a-z). 154 | 155 | // replace invalid chars with a space, return uppercase text 156 | // Thanks Chris Bolt for improving this function 157 | // note: *reg_replace() replaces nulls with empty string (not space) 158 | return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname))); 159 | 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /php/getID3/write.vorbiscomment.php: -------------------------------------------------------------------------------- 1 | // 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 | // write.vorbiscomment.php // 12 | // module for writing VorbisComment tags // 13 | // dependencies: /helperapps/vorbiscomment.exe // 14 | // /// 15 | ///////////////////////////////////////////////////////////////// 16 | 17 | 18 | class getid3_write_vorbiscomment 19 | { 20 | 21 | public $filename; 22 | public $tag_data; 23 | public $warnings = array(); // any non-critical errors will be stored here 24 | public $errors = array(); // any critical errors will be stored here 25 | 26 | public function __construct() { 27 | return true; 28 | } 29 | 30 | public function WriteVorbisComment() { 31 | 32 | if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { 33 | $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written'; 34 | return false; 35 | } 36 | 37 | // Create file with new comments 38 | $tempcommentsfilename = tempnam(GETID3_TEMP_DIR, 'getID3'); 39 | if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) { 40 | 41 | foreach ($this->tag_data as $key => $value) { 42 | foreach ($value as $commentdata) { 43 | fwrite($fpcomments, $this->CleanVorbisCommentName($key).'='.$commentdata."\n"); 44 | } 45 | } 46 | fclose($fpcomments); 47 | 48 | } else { 49 | $this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written'; 50 | return false; 51 | } 52 | 53 | $oldignoreuserabort = ignore_user_abort(true); 54 | if (GETID3_OS_ISWINDOWS) { 55 | 56 | if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) { 57 | //$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"'; 58 | // vorbiscomment works fine if you copy-paste the above commandline into a command prompt, 59 | // but refuses to work with `backtick` if there are "doublequotes" present around BOTH 60 | // the metaflac pathname and the target filename. For whatever reason...?? 61 | // The solution is simply ensure that the metaflac pathname has no spaces, 62 | // and therefore does not need to be quoted 63 | 64 | // On top of that, if error messages are not always captured properly under Windows 65 | // To at least see if there was a problem, compare file modification timestamps before and after writing 66 | clearstatcache(); 67 | $timestampbeforewriting = filemtime($this->filename); 68 | 69 | $commandline = GETID3_HELPERAPPSDIR.'vorbiscomment.exe -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1'; 70 | $VorbiscommentError = `$commandline`; 71 | 72 | if (empty($VorbiscommentError)) { 73 | clearstatcache(); 74 | if ($timestampbeforewriting == filemtime($this->filename)) { 75 | $VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written'; 76 | } 77 | } 78 | } else { 79 | $VorbiscommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR; 80 | } 81 | 82 | } else { 83 | 84 | $commandline = 'vorbiscomment -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1'; 85 | $VorbiscommentError = `$commandline`; 86 | 87 | } 88 | 89 | // Remove temporary comments file 90 | unlink($tempcommentsfilename); 91 | ignore_user_abort($oldignoreuserabort); 92 | 93 | if (!empty($VorbiscommentError)) { 94 | 95 | $this->errors[] = 'system call to vorbiscomment failed with message: '."\n\n".$VorbiscommentError; 96 | return false; 97 | 98 | } 99 | 100 | return true; 101 | } 102 | 103 | public function DeleteVorbisComment() { 104 | $this->tag_data = array(array()); 105 | return $this->WriteVorbisComment(); 106 | } 107 | 108 | public function CleanVorbisCommentName($originalcommentname) { 109 | // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. 110 | // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through 111 | // 0x7A inclusive (a-z). 112 | 113 | // replace invalid chars with a space, return uppercase text 114 | // Thanks Chris Bolt for improving this function 115 | // note: *reg_replace() replaces nulls with empty string (not space) 116 | return strtoupper(preg_replace('#[^ -<>-}]#', ' ', str_replace("\x00", ' ', $originalcommentname))); 117 | 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /php/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Build Simple Soundboard JSON File 8 | 9 | 10 |

Build Simple Soundboard JSON File

11 |
    12 |
  • File Name Version - Use [Q] for ? and dashes will be replaced with spaces.
  • 13 |
  • ID3 Version - Use a tool such as MP3Tag to update the ID3 fields of your MP3s.
  • 14 |
15 | MP3_DIRECTORY; 18 | $json_file = $configs->JSON_FILENAME; 19 | date_default_timezone_set($configs->TIME_ZONE); 20 | 21 | if(is_dir($mp3directory)){ 22 | echo("

MP3 Directory [".$mp3directory."] exists.

"); 23 | if(is_file($json_file)){ 24 | echo("

JSON File [".$json_file."] was last built at ".date ("F d Y H:i:s", filemtime($json_file)).".

"); 25 | } else { 26 | echo("

JSON File [".$json_file."] has not been created.

"); 27 | } 28 | } else { 29 | echo("

MP3 Directory [".$mp3directory."] DOES NOT exist.

"); 30 | } 31 | ?> 32 | 33 | -------------------------------------------------------------------------------- /sounds/Ayayayayayayayay!.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Ayayayayayayayay!.mp3 -------------------------------------------------------------------------------- /sounds/Constipated.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Constipated.mp3 -------------------------------------------------------------------------------- /sounds/Do-do-do-do.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Do-do-do-do.mp3 -------------------------------------------------------------------------------- /sounds/Floridians-Dumb-as-dirt.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Floridians-Dumb-as-dirt.mp3 -------------------------------------------------------------------------------- /sounds/Frosty-Nads[Q].mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Frosty-Nads[Q].mp3 -------------------------------------------------------------------------------- /sounds/Happy-Birthday.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Happy-Birthday.mp3 -------------------------------------------------------------------------------- /sounds/Happy-Purim.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Happy-Purim.mp3 -------------------------------------------------------------------------------- /sounds/I-Don't-Believe-It.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/I-Don't-Believe-It.mp3 -------------------------------------------------------------------------------- /sounds/Idiotic-jerk.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Idiotic-jerk.mp3 -------------------------------------------------------------------------------- /sounds/Laugh-Bird.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Laugh-Bird.mp3 -------------------------------------------------------------------------------- /sounds/Laugh-Brooke.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Laugh-Brooke.mp3 -------------------------------------------------------------------------------- /sounds/Laugh-Gilbert.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Laugh-Gilbert.mp3 -------------------------------------------------------------------------------- /sounds/Laugh-Goofy.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Laugh-Goofy.mp3 -------------------------------------------------------------------------------- /sounds/Laugh-Montage.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Laugh-Montage.mp3 -------------------------------------------------------------------------------- /sounds/Loan-me-50-Dollars.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Loan-me-50-Dollars.mp3 -------------------------------------------------------------------------------- /sounds/No.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/No.mp3 -------------------------------------------------------------------------------- /sounds/Only-in-the-Banana-Republic.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Only-in-the-Banana-Republic.mp3 -------------------------------------------------------------------------------- /sounds/Ya-mon!.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Ya-mon!.mp3 -------------------------------------------------------------------------------- /sounds/Yank-it-baby.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Yank-it-baby.mp3 -------------------------------------------------------------------------------- /sounds/Yeeeeeeesss.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digitalcolony/Simple-Soundboard/0b5afb02b611623362e0020a93fdf4088762b849/sounds/Yeeeeeeesss.mp3 --------------------------------------------------------------------------------