/* * mp3stat - scan mp3 for time and other bitrate info built with some code * from mp3check. * Copyright (c) 2002 by Ed Sweetman * mp3check - check mp3 file for consistency and print infos * Copyright (C) 1998 by Johannes Overmann */ #include #include #include #include #include #include "mp3stat.h" using namespace std; int mp3::min_valid = 6; int mp3::FREEFORMAT = 0; int mp3::FORBIDDEN = -1; uint mp3::CONST_MASK = 0xffffffff; int mp3::layer_tab[4] = { 0, 3, 2, 1 }; int mp3::bitrate1_tab[16][3] = { {FREEFORMAT, FREEFORMAT, FREEFORMAT}, {32, 32, 32}, {64, 48, 40}, {96, 56, 48}, {128, 64, 56}, {160, 80, 64}, {192, 96, 80}, {224, 112, 96}, {256, 128, 112}, {288, 160, 128}, {320, 192, 160}, {352, 224, 192}, {384, 256, 224}, {416, 320, 256}, {448, 384, 320}, {FORBIDDEN, FORBIDDEN, FORBIDDEN} }; int mp3::bitrate2_tab[16][3] = { {FREEFORMAT, FREEFORMAT, FREEFORMAT}, {32, 8, 8}, {48, 16, 16}, {56, 24, 24}, {64, 32, 32}, {80, 40, 40}, {96, 48, 48}, {112, 56, 56}, {128, 64, 64}, {144, 80, 80}, {160, 96, 96}, {176, 112, 112}, {192, 128, 128}, {224, 144, 144}, {256, 160, 160}, {FORBIDDEN, FORBIDDEN, FORBIDDEN} }; double mp3::sampd1_tab[4] = { 44.1, 48.0, 32.0, 0.0 }; int mp3::samp_1_tab[4] = { 44100, 48000, 32000, 50000 }; double mp3::sampd2_tab[4] = { 22.05, 24.0, 16.0, 0.0 }; int mp3::samp_2_tab[4] = { 22050, 24000, 16000, 50000 }; // get header from pointer mp3::Header mp3::get_header (const uchar * p) { Header h; uchar *q = (uchar *) & h; q[0] = p[3]; q[1] = p[2]; q[2] = p[1]; q[3] = p[0]; return h; } // set header to pointer void mp3::set_header (uchar * p, Header h) { uchar *q = (uchar *) & h; p[0] = q[3]; p[1] = q[2]; p[2] = q[1]; p[3] = q[0]; } // return length of frame in bytes int mp3::frame_length (Header h) { if (h.ID) { return ((((h.layer () == 1) ? 48000 : 144000) * h.bitrate ()) / h.samp_int_rate ()) + h.padding_bit; } else { return ((((h.layer () == 1) ? 24000 : 72000) * h.bitrate ()) / h.samp_int_rate ()) + h.padding_bit; } } //Detects the next valid header int mp3::find_next_header (const uchar * p, int len, int min_valid) { int i; const uchar *q = p; const uchar *t; int rest, k, l; Header h, h2; for (i = 0; i < len - 3; i++, q++) { if (*q == 255) { h = get_header (q); l = frame_length (h); if (h.isValid () && (l >= 21)) { t = q + l; rest = len - i - l; for (k = 1; (k < min_valid) && (rest >= 4); k++) { h2 = get_header (t); if (!h2.isValid ()) break; if (!h2.sameConstant (h)) break; l = frame_length (h2); if (l < 21) break; t += l; rest -= l; } if (k == min_valid) return i; } } } return -1; // not found } // Major function ...scans mp3 and gathers info as it does it void mp3::scan_mp3 (uchar * p, int len, statistic * mp3info2) { int start = find_next_header (p, len, min_valid); int rest = len; int l, s; if (start >= 0) { // check whole file rest -= start; p += start; Header head = get_header (p); l = frame_length (head); p += l; rest -= l; start += l; while (rest >= 4) { Header h = get_header (p); mp3info2->addBit(h.bitrate()); if (!(h.isValid () && (frame_length (h) >= 21))) { // invalid header, search for next valid header s = find_next_header (p, rest, min_valid); if (s < 0) break; // error: junk at eof // skips invalid bytes p += s; rest -= s; start += s; } else { head = h; // skip to next frame l = frame_length (h); p += l; rest -= l; start += l; } } } } // mmaps mp3 calls scan_mp3 which returns filled status structure mp3info void mp3::statfile (statistic * mp3info2) { //create stat accessor struct stat buf; int len; int fd; uchar *p; uchar *free_p; stat (mp3info2->getName().c_str(), &buf); len = buf.st_size; mp3info2->setSize((double) len / 1024); // open file fd = open (mp3info2->getName().c_str(), O_RDONLY); if (fd == -1) { return; } // mmap file p = (uchar *) mmap (0, len, PROT_READ, MAP_SHARED, fd, 0); free_p = p; //this should be left here, scan_mp3 is modifying p if (p == (uchar *) - 1) { return; } scan_mp3 (p, len, mp3info2); // unmap file and close if (munmap ((char *) free_p, len)) { return; } close (fd); } extern "C" input* createi() { return new mp3; } extern "C" void destroyi(input* tempIn) { delete tempIn; }