1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: local uint32 bitrate , frame_size , sampling_freq , frames_count = 0 ; 51: local quad frame_header_offset , seek_pos , sum_bitrate = 0 ; 52: local short data ; 53: local byte was_bad_sync , id3v1_tag_found = 0 ; 54: local ubyte buf [ 3 ] ; 55: 56: 57: enum < ubyte > ID3_GENRES 58: { 59: Blues , Classic_Rock , Country , Dance , Disco , Funk , Grunge , Hip_Hop , 60: Jazz , Metal , New_Age , Oldies , Other , Pop , R_and_B , Rap , 61: Reggae , Rock , Techno , Industrial , Alternative , Ska , Death_Metal , Pranks , 62: Soundtrack , Euro_Techno , Ambient , Trip_Hop , Vocal , Jazz_Funk , Fusion , Trance , 63: Classical , Instrumental , Acid , House , Game , Sound_Clip , Gospel , Noise , 64: AlternRock , Bass , Soul , Punk , Space , Meditative , Instrumental_Pop , Instrumental_Rock , 65: Ethnic , Gothic , Darkwave , Techno_Industrial , Electronic , Pop_Folk , Eurodance , Dream , 66: Southern_Rock , Comedy , Cult , Gangsta , Top_40 , Christian_Rap , Pop_Funk , Jungle , 67: Native_American , Cabaret , New_Wave , Psychadelic , Rave , Showtunes , Trailer , Lo_Fi , 68: Tribal , Acid_Punk , Acid_Jazz , Polka , Retro , Musical , Rock_n_Roll , Hard_Rock , 69: Folk , Folk_Rock , National_Folk , Swing , Fast_Fusion , Bebob , Latin , Revival , 70: Celtic , Bluegrass , Avantgarde , Gothic_Rock , 71: Progressive_Rock , Psychedelic_Rock , Symphonic_Rock , Slow_Rock , 72: Big_Band , Chorus , Easy_Listening , Acoustic , Humour , Speech , Chanson , Opera , 73: Chamber_Music , Sonata , Symphony , Booty_Bass , Primus , Porn_Groove , Satire , Slow_Jam , 74: Club , Tango , Samba , Folklore , Ballad , Power_Ballad , Rhythmic_Soul , Freestyle , 75: Duet , Punk_Rock , Drum_Solo , A_capella , Euro_House , Dance_Hall , Goa , Drum_and_Bass , 76: Club_House , Hardcore , Terror , Indie , BritPop , Negerpunk , Polsk_Punk , Beat , 77: Christian , Heavy_Metal , Black_Metal , Crossover , 78: Contemporary , Christian_Rock , Merengue , Salsa , Thrash_Metal , Anime , JPop , Synthpop 79: } ; 80: 81: 82: struct ID3v1_TAG 83: { 84: DisplayFormatDecimal ( ) ; 85: 86: SetBackColor ( 0x33BC55 ) ; 87: char id [ 3 ] ; 88: 89: SetBackColor ( 0x48E048 ) ; 90: char title [ 30 ] ; 91: 92: SetBackColor ( 0x5DE45D ) ; 93: char artist [ 30 ] ; 94: 95: SetBackColor ( 0x72E872 ) ; 96: char album [ 30 ] ; 97: 98: SetBackColor ( 0x87EC87 ) ; 99: char year [ 4 ] ; 100: 101: if ( ReadByte ( FTell ( ) + 28 ) == 0 && ReadByte ( FTell ( ) + 29 ) != 0 ) 102: { 103: 104: 105: SetBackColor ( 0x9CF09C ) ; 106: char comment [ 28 ] ; 107: 108: SetBackColor ( 0xB1F4B1 ) ; 109: byte zero ; 110: 111: SetBackColor ( 0xC6F8C6 ) ; 112: ubyte track ; 113: } 114: else 115: { 116: 117: 118: SetBackColor ( 0x9CF09C ) ; 119: char comment [ 30 ] ; 120: } 121: 122: SetBackColor ( 0xDBFCDB ) ; 123: ID3_GENRES genre ; 124: } ; 125: 126: 127: struct ID3v2_HEADER 128: { 129: SetBackColor ( 0x91C4FF ) ; 130: 131: char head [ 3 ] ; 132: 133: DisplayFormatDecimal ( ) ; 134: 135: ubyte ver_major ; 136: ubyte ver_revision ; 137: 138: struct FLAGS { 139: ubyte UNSYNCHRONISATION_USED : 1 ; 140: ubyte EXTENDED_HEADER_PRESENT : 1 ; 141: ubyte EXPERIMENTAL_TAG : 1 ; 142: ubyte : 5 ; 143: } flags ; 144: 145: DisplayFormatHex ( ) ; 146: 147: ubyte size [ 4 ] ; 148: 149: 150: 151: } ; 152: 153: 154: struct ID3v2_EXTENDED_HEADER 155: { 156: SetBackColor ( 0xA1D4FF ) ; 157: 158: DisplayFormatDecimal ( ) ; 159: 160: uint32 size ; 161: 162: uint16 FLAG_CRC_PRESENT : 1 ; 163: uint16 : 15 ; 164: 165: uint32 padding_sz ; 166: 167: if ( FLAG_CRC_PRESENT ) 168: { 169: DisplayFormatHex ( ) ; 170: uint32 crc ; 171: } 172: } ; 173: 174: 175: struct ID3v2_FRAME 176: { 177: char id [ 4 ] ; 178: 179: DisplayFormatDecimal ( ) ; 180: 181: uint32 size ; 182: 183: struct FRAME_FLAGS { 184: uint16 TAG_ALTER_PRESERV : 1 ; 185: uint16 FILE_ALTER_PRESERV : 1 ; 186: uint16 READ_ONLY_FRAME : 1 ; 187: uint16 : 5 ; 188: uint16 COMPRESSED_FRAME : 1 ; 189: uint16 ENCRYPTED_FRAME : 1 ; 190: uint16 GROUP_MEMBER_FRAME : 1 ; 191: uint16 : 5 ; 192: } flags ; 193: 194: if ( id [ 0 ] == 'T' ) 195: { 196: 197: if ( ReadByte ( FTell ( ) ) == 0 && size > 1 ) 198: { 199: byte id_asciiz_str ; 200: char frame_data [ size - 1 ] ; 201: } 202: else 203: char frame_data [ size ] ; 204: } 205: else 206: { 207: DisplayFormatHex ( ) ; 208: ubyte frame_data [ size ] ; 209: } 210: } ; 211: 212: 213: struct ID3v2_TAG 214: { 215: ID3v2_HEADER hdr ; 216: 217: 218: local uint32 tag_sz = hdr . size [ 0 ] ; 219: tag_sz <<= 7 ; 220: tag_sz |= hdr . size [ 1 ] ; 221: tag_sz <<= 7 ; 222: tag_sz |= hdr . size [ 2 ] ; 223: tag_sz <<= 7 ; 224: tag_sz |= hdr . size [ 3 ] ; 225: 226: 227: 228: 229: 230: 231: if ( hdr . ver_major == 0xFF || hdr . ver_revision == 0xFF || 232: hdr . size [ 0 ] >= 0x80 || hdr . size [ 1 ] >= 0x80 || 233: hdr . size [ 2 ] >= 0x80 || hdr . size [ 3 ] >= 0x80 ) 234: { 235: Printf ( "MP3: warning: invalid ID3v2 tag header\n" ) ; 236: } 237: else 238: { 239: if ( hdr . ver_major != 3 || hdr . flags . UNSYNCHRONISATION_USED || hdr . flags . EXPERIMENTAL_TAG ) 240: { 241: Printf ( "MP3: warning: skipping unsupported ID3v2.%d tag\n" , hdr . ver_major ) ; 242: SetBackColor ( 0xA9DCFF ) ; 243: DisplayFormatHex ( ) ; 244: ubyte id3v2_data [ tag_sz ] ; 245: } 246: else 247: { 248: if ( hdr . flags . EXTENDED_HEADER_PRESENT ) 249: ID3v2_EXTENDED_HEADER ext_hdr ; 250: 251: 252: 253: 254: 255: local uint32 frame_color = 0xC9FCFF ; 256: do 257: { 258: SetBackColor ( frame_color ) ; 259: ID3v2_FRAME tf ; 260: frame_color -= 0x20200 ; 261: } 262: while ( FTell ( ) < tag_sz + sizeof ( hdr ) && ReadByte ( FTell ( ) ) != 0 ) ; 263: 264: SetBackColor ( 0x99CCFF ) ; 265: ubyte id3v2_padding [ tag_sz + sizeof ( hdr ) - FTell ( ) ] ; 266: } 267: } 268: } ; 269: 270: 271: 272: 273: 274: struct MPEG_HEADER 275: { 276: SetBackColor ( 0xCC99FF ) ; 277: 278: DisplayFormatHex ( ) ; 279: 280: uint32 frame_sync : 12 ; 281: 282: DisplayFormatDecimal ( ) ; 283: 284: uint32 mpeg_id : 1 ; 285: uint32 layer_id : 2 ; 286: uint32 protection_bit : 1 ; 287: uint32 bitrate_index : 4 ; 288: uint32 frequency_index : 2 ; 289: uint32 padding_bit : 1 ; 290: uint32 private_bit : 1 ; 291: uint32 channel_mode : 2 ; 292: uint32 mode_extension : 2 ; 293: uint32 copyright : 1 ; 294: uint32 original : 1 ; 295: uint32 emphasis : 2 ; 296: 297: if ( protection_bit == 0 ) 298: { 299: DisplayFormatHex ( ) ; 300: uint16 checksum ; 301: } 302: } ; 303: 304: 305: struct MPEG_FRAME 306: { 307: MPEG_HEADER mpeg_hdr ; 308: 309: 310: bitrate = 0 ; 311: 312: 313: if ( mpeg_hdr . frame_sync < 0xFFE || mpeg_hdr . layer_id == 0 || 314: mpeg_hdr . bitrate_index == 0 || mpeg_hdr . bitrate_index == 15 || 315: mpeg_hdr . frequency_index == 3 ) 316: { 317: Printf ( "MP3: warning: invalid MPEG header in frame at offset 0x%X\n" , 318: FTell ( ) - 4 - ( mpeg_hdr . protection_bit == 0 ? 2 : 0 ) ) ; 319: 320: 321: FSeek ( FTell ( ) - 2 ) ; 322: } 323: else 324: { 325: if ( mpeg_hdr . layer_id == 3 ) 326: { 327: bitrate = ( uint32 ) mpeg_hdr . bitrate_index << 5 ; 328: } 329: else 330: { 331: if ( mpeg_hdr . layer_id == 2 ) 332: { 333: bitrate = ( uint32 ) mpeg_hdr . bitrate_index == 1 ? 32 : 334: ( 1 << 5 + ( uint32 ) mpeg_hdr . bitrate_index / 4 ) + 335: ( ( ( uint32 ) mpeg_hdr . bitrate_index & 3 ) << 336: 3 + ( uint32 ) mpeg_hdr . bitrate_index / 4 ) ; 337: } 338: else 339: { 340: if ( mpeg_hdr . mpeg_id == 1 ) 341: { 342: bitrate = ( 1 << 5 + ( ( uint32 ) mpeg_hdr . bitrate_index - 1 ) / 4 ) + 343: ( ( ( uint32 ) mpeg_hdr . bitrate_index - 1 & 3 ) << 344: 3 + ( ( uint32 ) mpeg_hdr . bitrate_index - 1 ) / 4 ) ; 345: } 346: else 347: { 348: bitrate = ( uint32 ) mpeg_hdr . bitrate_index < 4 ? 349: 350: 8 * ( uint32 ) mpeg_hdr . bitrate_index : 351: 352: ( 1 << 4 + ( uint32 ) mpeg_hdr . bitrate_index / 4 ) + 353: ( 354: ( ( uint32 ) mpeg_hdr . bitrate_index & 3 ) == 0 ? 0 : 355: 356: ( ( uint32 ) mpeg_hdr . bitrate_index & 3 ) == 1 ? 357: ( 1 << 4 + ( uint32 ) mpeg_hdr . bitrate_index / 4 ) : 358: 359: ( ( uint32 ) mpeg_hdr . bitrate_index & 3 ) == 2 ? 360: ( 1 << 4 + ( uint32 ) mpeg_hdr . bitrate_index / 4 ) + 361: ( ( 1 << 4 + ( uint32 ) mpeg_hdr . bitrate_index / 4 ) >> 1 ) : 362: 363: ( 1 << 4 + ( uint32 ) mpeg_hdr . bitrate_index / 4 ) - 364: ( ( 1 << 4 + ( uint32 ) mpeg_hdr . bitrate_index / 4 ) >> 2 ) 365: ) ; 366: } 367: } 368: } 369: } 370: 371: if ( bitrate != 0 ) 372: { 373: local uint16 freq [ 3 ] ; 374: freq [ 0 ] = 2205 ; 375: freq [ 1 ] = 2400 ; 376: freq [ 2 ] = 1600 ; 377: 378: sampling_freq = freq [ mpeg_hdr . frequency_index ] ; 379: 380: if ( mpeg_hdr . mpeg_id == 1 ) 381: sampling_freq <<= 1 ; 382: 383: frame_size = ( bitrate * 14400 ) / sampling_freq ; 384: 385: if ( mpeg_hdr . channel_mode == 3 ) 386: frame_size >>= 1 ; 387: 388: frame_size -= 4 + ( mpeg_hdr . protection_bit == 0 ? 2 : 0 ) - mpeg_hdr . padding_bit ; 389: 390: frame_header_offset = FTell ( ) - 4 - ( mpeg_hdr . protection_bit == 0 ? 2 : 0 ) ; 391: 392: 393: if ( FTell ( ) + frame_size > FileSize ( ) ) 394: { 395: Printf ( "MP3: warning: cut MPEG frame at end of file (frame header offset = 0x%LX, data length = %u)\n" , 396: frame_header_offset , frame_size ) ; 397: 398: Printf ( "MP3: file parsing completed!\nMP3: valid MPEG frames found: %d\n" , frames_count ) ; 399: 400: if ( frames_count != 0 ) 401: Printf ( "MP3: average frame bitrate: %d kbit\n" , sum_bitrate / frames_count ) ; 402: 403: return ; 404: } 405: else 406: { 407: DisplayFormatHex ( ) ; 408: SetBackColor ( 0xCCCCFF ) ; 409: ubyte mpeg_frame_data [ frame_size ] ; 410: } 411: 412: sum_bitrate += bitrate ; 413: 414: frames_count ++ ; 415: } 416: } ; 417: 418: 419: 420: 421: 422: BigEndian ( ) ; 423: 424: ReadBytes ( buf , 0 , 3 ) ; 425: 426: if ( ! Strcmp ( buf , "ID3" ) ) 427: { 428: Printf ( "MP3: ID3v2 tag found\n" ) ; 429: ID3v2_TAG id3v2_tag ; 430: } 431: 432: while ( ! FEof ( ) && ! id3v1_tag_found ) 433: { 434: 435: seek_pos = FTell ( ) ; 436: was_bad_sync = 0 ; 437: do 438: { 439: data = ReadShort ( seek_pos ) ; 440: 441: if ( ( uint16 ) data == 0x5441 && ( uchar ) ReadByte ( seek_pos + 2 ) == 0x47 ) 442: id3v1_tag_found = 1 ; 443: 444: if ( ! was_bad_sync && ( uint16 ) data < 0xFFE0 && ! id3v1_tag_found ) 445: { 446: Printf ( "MP3: warning: invalid MPEG frame synchronization at offset 0x%LX\n" , seek_pos ) ; 447: was_bad_sync = 1 ; 448: } 449: 450: seek_pos ++ ; 451: } 452: while ( ( uint16 ) data < 0xFFE0 && seek_pos < ( FileSize ( ) - 1 ) && ! id3v1_tag_found ) ; 453: 454: if ( ( uint16 ) data >= 0xFFE0 || id3v1_tag_found ) 455: { 456: FSeek ( seek_pos - 1 ) ; 457: } 458: else 459: { 460: Printf ( "MP3: file parsing completed!\nMP3: valid MPEG frames found: %d\n" , frames_count ) ; 461: 462: if ( frames_count != 0 ) 463: Printf ( "MP3: average frame bitrate: %d kbit\n" , sum_bitrate / frames_count ) ; 464: 465: return ; 466: } 467: 468: if ( ! id3v1_tag_found ) 469: { 470: MPEG_FRAME mf ; 471: 472: if ( frames_count == 1 && bitrate ) 473: Printf ( "MP3: first found MPEG frame parameters:\nMP3:\t- header ofsset: 0x%LX\nMP3:\t- bitrate: %d kbit\nMP3:\t- MPEG-%d layer %d\nMP3:\t- sampling frequency: %d Hz\nMP3:\t- channel mode: %s\nMP3:\t- CRC protected: %s\n" , 474: frame_header_offset , 475: bitrate , 476: mf . mpeg_hdr . mpeg_id == 1 ? 1 : 2 , 477: mf . mpeg_hdr . layer_id == 1 ? 3 : mf . mpeg_hdr . layer_id == 2 ? 2 : 1 , 478: sampling_freq * 10 , 479: mf . mpeg_hdr . channel_mode == 3 ? "mono" : 480: mf . mpeg_hdr . channel_mode == 0 ? "stereo" : 481: mf . mpeg_hdr . channel_mode == 1 ? "joint stereo" : "dual channel" , 482: mf . mpeg_hdr . protection_bit == 0 ? "Yes" : "No" ) ; 483: } 484: } 485: 486: if ( id3v1_tag_found ) 487: { 488: Printf ( "MP3: ID3v1 tag found\n" ) ; 489: ID3v1_TAG id3v1_tag ; 490: } 491: 492: if ( ! FEof ( ) ) 493: Printf ( "MP3: warning: there is some unknown extra-data after ID3v1 tag at end of file\n" ) ; 494: 495: Printf ( "MP3: file parsing completed!\nMP3: valid MPEG frames found: %d\n" , frames_count ) ; 496: 497: if ( frames_count != 0 ) 498: Printf ( "MP3: average frame bitrate: %d kbit\n" , sum_bitrate / frames_count ) ; 499: tok_eof