getid3_id3v1

The WordPress Core getid3 id3v1 class.

Defined (1)

The class is defined in the following location(s).

/wp-includes/ID3/module.tag.id3v1.php  
  1. class getid3_id3v1 extends getid3_handler 
  2.  
  3. public function Analyze() { 
  4. $info = &$this->getid3->info; 
  5.  
  6. if (!getid3_lib::intValueSupported($info['filesize'])) { 
  7. $info['warning'][] = 'Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; 
  8. return false; 
  9.  
  10. $this->fseek(-256, SEEK_END); 
  11. $preid3v1 = $this->fread(128); 
  12. $id3v1tag = $this->fread(128); 
  13.  
  14. if (substr($id3v1tag, 0, 3) == 'TAG') { 
  15.  
  16. $info['avdataend'] = $info['filesize'] - 128; 
  17.  
  18. $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30)); 
  19. $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30)); 
  20. $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30)); 
  21. $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4)); 
  22. $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them 
  23. $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1)); 
  24.  
  25. // If second-last byte of comment field is null and last byte of comment field is non-null 
  26. // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number 
  27. if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) { 
  28. $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1)); 
  29. $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28); 
  30. $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']); 
  31.  
  32. $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']); 
  33. if (!empty($ParsedID3v1['genre'])) { 
  34. unset($ParsedID3v1['genreid']); 
  35. if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || ($ParsedID3v1['genre'] == 'Unknown'))) { 
  36. unset($ParsedID3v1['genre']); 
  37.  
  38. foreach ($ParsedID3v1 as $key => $value) { 
  39. $ParsedID3v1['comments'][$key][0] = $value; 
  40.  
  41. // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces 
  42. $GoodFormatID3v1tag = $this->GenerateID3v1Tag( 
  43. $ParsedID3v1['title'],  
  44. $ParsedID3v1['artist'],  
  45. $ParsedID3v1['album'],  
  46. $ParsedID3v1['year'],  
  47. (isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),  
  48. $ParsedID3v1['comment'],  
  49. (!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : '')); 
  50. $ParsedID3v1['padding_valid'] = true; 
  51. if ($id3v1tag !== $GoodFormatID3v1tag) { 
  52. $ParsedID3v1['padding_valid'] = false; 
  53. $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding'; 
  54.  
  55. $ParsedID3v1['tag_offset_end'] = $info['filesize']; 
  56. $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128; 
  57.  
  58. $info['id3v1'] = $ParsedID3v1; 
  59.  
  60. if (substr($preid3v1, 0, 3) == 'TAG') { 
  61. // The way iTunes handles tags is, well, brain-damaged. 
  62. // It completely ignores v1 if ID3v2 is present. 
  63. // This goes as far as adding a new v1 tag *even if there already is one* 
  64.  
  65. // A suspected double-ID3v1 tag has been detected, but it could be that 
  66. // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag 
  67. if (substr($preid3v1, 96, 8) == 'APETAGEX') { 
  68. // an APE tag footer was found before the last ID3v1, assume false "TAG" synch 
  69. } elseif (substr($preid3v1, 119, 6) == 'LYRICS') { 
  70. // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch 
  71. } else { 
  72. // APE and Lyrics3 footers not found - assume double ID3v1 
  73. $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes'; 
  74. $info['avdataend'] -= 128; 
  75.  
  76. return true; 
  77.  
  78. public static function cutfield($str) { 
  79. return trim(substr($str, 0, strcspn($str, "\x00"))); 
  80.  
  81. public static function ArrayOfGenres($allowSCMPXextended=false) { 
  82. static $GenreLookup = array( 
  83. 0 => 'Blues',  
  84. 1 => 'Classic Rock',  
  85. 2 => 'Country',  
  86. 3 => 'Dance',  
  87. 4 => 'Disco',  
  88. 5 => 'Funk',  
  89. 6 => 'Grunge',  
  90. 7 => 'Hip-Hop',  
  91. 8 => 'Jazz',  
  92. 9 => 'Metal',  
  93. 10 => 'New Age',  
  94. 11 => 'Oldies',  
  95. 12 => 'Other',  
  96. 13 => 'Pop',  
  97. 14 => 'R&B',  
  98. 15 => 'Rap',  
  99. 16 => 'Reggae',  
  100. 17 => 'Rock',  
  101. 18 => 'Techno',  
  102. 19 => 'Industrial',  
  103. 20 => 'Alternative',  
  104. 21 => 'Ska',  
  105. 22 => 'Death Metal',  
  106. 23 => 'Pranks',  
  107. 24 => 'Soundtrack',  
  108. 25 => 'Euro-Techno',  
  109. 26 => 'Ambient',  
  110. 27 => 'Trip-Hop',  
  111. 28 => 'Vocal',  
  112. 29 => 'Jazz+Funk',  
  113. 30 => 'Fusion',  
  114. 31 => 'Trance',  
  115. 32 => 'Classical',  
  116. 33 => 'Instrumental',  
  117. 34 => 'Acid',  
  118. 35 => 'House',  
  119. 36 => 'Game',  
  120. 37 => 'Sound Clip',  
  121. 38 => 'Gospel',  
  122. 39 => 'Noise',  
  123. 40 => 'Alt. Rock',  
  124. 41 => 'Bass',  
  125. 42 => 'Soul',  
  126. 43 => 'Punk',  
  127. 44 => 'Space',  
  128. 45 => 'Meditative',  
  129. 46 => 'Instrumental Pop',  
  130. 47 => 'Instrumental Rock',  
  131. 48 => 'Ethnic',  
  132. 49 => 'Gothic',  
  133. 50 => 'Darkwave',  
  134. 51 => 'Techno-Industrial',  
  135. 52 => 'Electronic',  
  136. 53 => 'Pop-Folk',  
  137. 54 => 'Eurodance',  
  138. 55 => 'Dream',  
  139. 56 => 'Southern Rock',  
  140. 57 => 'Comedy',  
  141. 58 => 'Cult',  
  142. 59 => 'Gangsta Rap',  
  143. 60 => 'Top 40',  
  144. 61 => 'Christian Rap',  
  145. 62 => 'Pop/Funk',  
  146. 63 => 'Jungle',  
  147. 64 => 'Native American',  
  148. 65 => 'Cabaret',  
  149. 66 => 'New Wave',  
  150. 67 => 'Psychedelic',  
  151. 68 => 'Rave',  
  152. 69 => 'Showtunes',  
  153. 70 => 'Trailer',  
  154. 71 => 'Lo-Fi',  
  155. 72 => 'Tribal',  
  156. 73 => 'Acid Punk',  
  157. 74 => 'Acid Jazz',  
  158. 75 => 'Polka',  
  159. 76 => 'Retro',  
  160. 77 => 'Musical',  
  161. 78 => 'Rock & Roll',  
  162. 79 => 'Hard Rock',  
  163. 80 => 'Folk',  
  164. 81 => 'Folk/Rock',  
  165. 82 => 'National Folk',  
  166. 83 => 'Swing',  
  167. 84 => 'Fast-Fusion',  
  168. 85 => 'Bebob',  
  169. 86 => 'Latin',  
  170. 87 => 'Revival',  
  171. 88 => 'Celtic',  
  172. 89 => 'Bluegrass',  
  173. 90 => 'Avantgarde',  
  174. 91 => 'Gothic Rock',  
  175. 92 => 'Progressive Rock',  
  176. 93 => 'Psychedelic Rock',  
  177. 94 => 'Symphonic Rock',  
  178. 95 => 'Slow Rock',  
  179. 96 => 'Big Band',  
  180. 97 => 'Chorus',  
  181. 98 => 'Easy Listening',  
  182. 99 => 'Acoustic',  
  183. 100 => 'Humour',  
  184. 101 => 'Speech',  
  185. 102 => 'Chanson',  
  186. 103 => 'Opera',  
  187. 104 => 'Chamber Music',  
  188. 105 => 'Sonata',  
  189. 106 => 'Symphony',  
  190. 107 => 'Booty Bass',  
  191. 108 => 'Primus',  
  192. 109 => 'Porn Groove',  
  193. 110 => 'Satire',  
  194. 111 => 'Slow Jam',  
  195. 112 => 'Club',  
  196. 113 => 'Tango',  
  197. 114 => 'Samba',  
  198. 115 => 'Folklore',  
  199. 116 => 'Ballad',  
  200. 117 => 'Power Ballad',  
  201. 118 => 'Rhythmic Soul',  
  202. 119 => 'Freestyle',  
  203. 120 => 'Duet',  
  204. 121 => 'Punk Rock',  
  205. 122 => 'Drum Solo',  
  206. 123 => 'A Cappella',  
  207. 124 => 'Euro-House',  
  208. 125 => 'Dance Hall',  
  209. 126 => 'Goa',  
  210. 127 => 'Drum & Bass',  
  211. 128 => 'Club-House',  
  212. 129 => 'Hardcore',  
  213. 130 => 'Terror',  
  214. 131 => 'Indie',  
  215. 132 => 'BritPop',  
  216. 133 => 'Negerpunk',  
  217. 134 => 'Polsk Punk',  
  218. 135 => 'Beat',  
  219. 136 => 'Christian Gangsta Rap',  
  220. 137 => 'Heavy Metal',  
  221. 138 => 'Black Metal',  
  222. 139 => 'Crossover',  
  223. 140 => 'Contemporary Christian',  
  224. 141 => 'Christian Rock',  
  225. 142 => 'Merengue',  
  226. 143 => 'Salsa',  
  227. 144 => 'Thrash Metal',  
  228. 145 => 'Anime',  
  229. 146 => 'JPop',  
  230. 147 => 'Synthpop',  
  231.  
  232. 255 => 'Unknown',  
  233.  
  234. 'CR' => 'Cover',  
  235. 'RX' => 'Remix' 
  236. ); 
  237.  
  238. static $GenreLookupSCMPX = array(); 
  239. if ($allowSCMPXextended && empty($GenreLookupSCMPX)) { 
  240. $GenreLookupSCMPX = $GenreLookup; 
  241. // http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended 
  242. // Extended ID3v1 genres invented by SCMPX 
  243. // Note that 255 "Japanese Anime" conflicts with standard "Unknown" 
  244. $GenreLookupSCMPX[240] = 'Sacred'; 
  245. $GenreLookupSCMPX[241] = 'Northern Europe'; 
  246. $GenreLookupSCMPX[242] = 'Irish & Scottish'; 
  247. $GenreLookupSCMPX[243] = 'Scotland'; 
  248. $GenreLookupSCMPX[244] = 'Ethnic Europe'; 
  249. $GenreLookupSCMPX[245] = 'Enka'; 
  250. $GenreLookupSCMPX[246] = 'Children\'s Song'; 
  251. $GenreLookupSCMPX[247] = 'Japanese Sky'; 
  252. $GenreLookupSCMPX[248] = 'Japanese Heavy Rock'; 
  253. $GenreLookupSCMPX[249] = 'Japanese Doom Rock'; 
  254. $GenreLookupSCMPX[250] = 'Japanese J-POP'; 
  255. $GenreLookupSCMPX[251] = 'Japanese Seiyu'; 
  256. $GenreLookupSCMPX[252] = 'Japanese Ambient Techno'; 
  257. $GenreLookupSCMPX[253] = 'Japanese Moemoe'; 
  258. $GenreLookupSCMPX[254] = 'Japanese Tokusatsu'; 
  259. //$GenreLookupSCMPX[255] = 'Japanese Anime'; 
  260.  
  261. return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup); 
  262.  
  263. public static function LookupGenreName($genreid, $allowSCMPXextended=true) { 
  264. switch ($genreid) { 
  265. case 'RX': 
  266. case 'CR': 
  267. break; 
  268. default: 
  269. if (!is_numeric($genreid)) { 
  270. return false; 
  271. $genreid = intval($genreid); // to handle 3 or '3' or '03' 
  272. break; 
  273. $GenreLookup = self::ArrayOfGenres($allowSCMPXextended); 
  274. return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false); 
  275.  
  276. public static function LookupGenreID($genre, $allowSCMPXextended=false) { 
  277. $GenreLookup = self::ArrayOfGenres($allowSCMPXextended); 
  278. $LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre)); 
  279. foreach ($GenreLookup as $key => $value) { 
  280. if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) { 
  281. return $key; 
  282. return false; 
  283.  
  284. public static function StandardiseID3v1GenreName($OriginalGenre) { 
  285. if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) { 
  286. return self::LookupGenreName($GenreID); 
  287. return $OriginalGenre; 
  288.  
  289. public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') { 
  290. $ID3v1Tag = 'TAG'; 
  291. $ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT); 
  292. $ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT); 
  293. $ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT); 
  294. $ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT); 
  295. if (!empty($track) && ($track > 0) && ($track <= 255)) { 
  296. $ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT); 
  297. $ID3v1Tag .= "\x00"; 
  298. if (gettype($track) == 'string') { 
  299. $track = (int) $track; 
  300. $ID3v1Tag .= chr($track); 
  301. } else { 
  302. $ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT); 
  303. if (($genreid < 0) || ($genreid > 147)) { 
  304. $genreid = 255; // 'unknown' genre 
  305. switch (gettype($genreid)) { 
  306. case 'string': 
  307. case 'integer': 
  308. $ID3v1Tag .= chr(intval($genreid)); 
  309. break; 
  310. default: 
  311. $ID3v1Tag .= chr(255); // 'unknown' genre 
  312. break; 
  313.  
  314. return $ID3v1Tag; 
  315.