#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct ID3V2
{
char tag[3];
char version;
char subVersion;
char flag;
char size[4];
};
const int bitrateIndex[16][5] = {
{
0,
0,
0,
0,
0
},
{
32,
32,
32,
32,
8
},
{
64,
48,
40,
48,
16
},
{
96,
56,
48,
56,
24
},
{
128,
64,
56,
64,
32
},
{
160,
80,
64,
80,
40
},
{
192,
96,
80,
96,
48
},
{
224,
112,
96,
112,
56
},
{
256,
128,
112,
128,
64
},
{
288,
160,
128,
144,
80
},
{
320,
192,
160,
160,
96
},
{
352,
224,
192,
176,
112
},
{
384,
256,
224,
192,
128
},
{
416,
320,
256,
224,
144
},
{
448,
384,
320,
256,
160
},
{
0,
0,
0,
0,
0
}
};
const int sampleRate[4][3] = {
{
44100,
22050,
11025
},
{
48000,
24000,
12000
},
{
32000,
16000,
8000
},
{
0,
0,
0
}
};
class AudioFile {
public:
AudioFile(char* file) {
this->ifs.open(file, ios::in);
if (!this->ifs.is_open()) {
cout << "cannot open file" << endl;
return;
}
}
void read(void* p, int size) {
this->filePos += size;
ifs.read((char*)p, size);
}
void seek(long pos) {
this->filePos = pos;
ifs.clear();
ifs.seekg(pos);
}
int pos() {
return this->filePos;
}
bool eof() {
return this->ifs.eof();
}
~AudioFile()
{
ifs.close();
}
private:
ifstream ifs;
int filePos = 0;
};
class Analyser {
private:
double sampleCount = 0;
int HEADER_SIZE = 4;
void* frame;
public:
Analyser() {
//this->frame = malloc(this->HEADER_SIZE);
this->frame = new char[this->HEADER_SIZE];
}
int getVersion(unsigned short info) {
return (info & 0xF00) >> 11;
}
int getLayer(unsigned short info) {
switch ((info & 0x600) >> 9) {
case 1:
return 3;
case 2:
return 2;
case 3:
return 1;
}
}
int parseSize(char size[4]) {
int a = size[0] * 2097152;
int b = size[1] * 16384;
int c = size[2] * 128;
int d = size[3];
return a + b + c + d;
}
int getSampleRate(int frequency) {
return (frequency & 0x0F) >> 2;
}
int translateSampleRate(int version, int y) {
return sampleRate[y][version - 1];
}
int getBitrate(unsigned char bit) {
return (bit & 0xF0) >> 4;
}
int translateBitrate(int version, int layer, int y) {
int x = -1;
switch (version)
{
case 1:
x = layer - 1;
break;
case 2:
if (layer == 1)
x = 3;
else
x = 4;
default:
printf("cannot determine bitrate x !");
return -1;
}
return bitrateIndex[y][x];
}
int getPadding(char rawPad) {
return (rawPad & 0x0F) >> 1;
}
int calFrameSize(int layer, int bitRate, int sampleRate, int padding) {
if (layer == 1)
return (12000 * bitRate / sampleRate + padding) * 4;
return 144000 * bitRate / sampleRate + padding;
}
int getDuration() {
return this->sampleCount;
}
bool calDuration(AudioFile* af) {
af->read(this->frame, this->HEADER_SIZE);
if (this->frame == NULL)
throw "NULL pointer exception!";
auto info = *(unsigned short*)(this->frame);
if ((info & 0xF0FF) != 0xF0FF)return false;
if (af->eof()) return false;
auto version = this->getVersion(info);
auto layer = this->getLayer(info);
auto bi = this->translateBitrate(version, layer, this->getBitrate(*(char*)((char*)this->frame + 2)));
auto pad = this->getPadding(*((char*)this->frame + 2));
auto sample = this->translateSampleRate(version, this->getSampleRate(*((char*)this->frame + 2)));
auto frameSize = this->calFrameSize(layer, bi, sample, pad);
this->sampleCount += (layer == 1 ? 384 : 1152) / (double)sample;
auto next = af->pos() + frameSize - this->HEADER_SIZE;
//cout << this->sampleCount << " --> " << sample << " -->" << " next: " << next << endl;
af->seek(next);
return true;
}
};
int main(int argc, char** argv) {
const char* au = { "C:/Users/djatm/Music/all out of love.mp3" };
AudioFile* af = new AudioFile((char*)au);
int id3Size = sizeof(ID3V2);
ID3V2* id3v2 = (ID3V2*)malloc(id3Size);
af->read(id3v2, id3Size);
if (id3v2 == NULL) {
return;
}
if (id3v2->tag[0] != 'I' || id3v2->tag[1] != 'D' || id3v2->tag[2] != '3') {
delete af;
cout << "cannot parse tag!" << endl;
return;
}
Analyser* an = new Analyser();
int headerEnd = an->parseSize(id3v2->size) + 10;
af->seek(headerEnd);
while (an->calDuration(af));
cout << "duration: " << an->getDuration() << endl;
delete af;
return 0;
}
如有不足還望指出!!!