c++ 計算mp3時長

laremehpe發表於2024-07-13
#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;
}

如有不足還望指出!!!

相關文章