#include "stdafx.h"

using namespace bitreader_helper;

static unsigned extract_header_bits(const t_uint8 p_header[4],unsigned p_base,unsigned p_bits)
{
	assert(p_base+p_bits<=32);
	return extract_bits(p_header,p_base,p_bits);
}

namespace {

	class header_parser
	{
	public:
		header_parser(const t_uint8 p_header[4]) : m_bitptr(0) 
		{
			memcpy(m_header,p_header,4);
		}
		unsigned read(unsigned p_bits)
		{
			unsigned ret = extract_header_bits(m_header,m_bitptr,p_bits);
			m_bitptr += p_bits;
			return ret;
		}
	private:
		t_uint8 m_header[4];
		unsigned m_bitptr;
	};
}

typedef t_uint16 uint16;

static const uint16 bitrate_table_l1v1[16]  = {  0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448,  0};
static const uint16 bitrate_table_l2v1[16]  = {  0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384,  0};
static const uint16 bitrate_table_l3v1[16]  = {  0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,  0};
static const uint16 bitrate_table_l1v2[16]  = {  0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,  0};
static const uint16 bitrate_table_l23v2[16] = {  0,  8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,  0};
static const uint16 sample_rate_table[] = {11025,12000,8000};

unsigned mp3_utils::QueryMPEGFrameSize(const t_uint8 p_header[4])
{
	TMPEGFrameInfo info;
	if (!ParseMPEGFrameHeader(info,p_header)) return 0;
	return info.m_bytes;
}

bool mp3_utils::ParseMPEGFrameHeader(TMPEGFrameInfo & p_info,const t_uint8 p_header[4])
{
	enum {MPEG_LAYER_1 = 3, MPEG_LAYER_2 = 2, MPEG_LAYER_3 = 1};
	enum {_MPEG_1 = 3, _MPEG_2 = 2, _MPEG_25 = 0};

	header_parser parser(p_header);
	if (parser.read(11) != 0x7FF) return false;
	unsigned mpeg_version = parser.read(2);
	unsigned layer = parser.read(2);
	unsigned protection = parser.read(1);
	unsigned bitrate_index = parser.read(4);
	unsigned sample_rate_index = parser.read(2);
	if (sample_rate_index == 11) return false;//reserved
	unsigned paddingbit = parser.read(1);
	int paddingdelta = 0;
	parser.read(1);//private
	unsigned channel_mode = parser.read(2);
	parser.read(2);//channel_mode_extension
	parser.read(1);//copyright
	parser.read(1);//original
	parser.read(2);//emphasis

	unsigned bitrate = 0,sample_rate = 0;

	switch(layer)
	{
	default:
		return false;
	case MPEG_LAYER_3:
		paddingdelta = paddingbit ? 1 : 0;
		
		p_info.m_layer = 3;
		switch(mpeg_version)
		{
		case _MPEG_1:
			p_info.m_duration = 1152;
			bitrate = bitrate_table_l3v1[bitrate_index];
			break;
		case _MPEG_2:
		case _MPEG_25:
			p_info.m_duration = 576;
			bitrate = bitrate_table_l23v2[bitrate_index];
			break;
		default:
			return false;
		}
	
		break;
	case MPEG_LAYER_2:
		paddingdelta = paddingbit ? 1 : 0;
		p_info.m_duration = 1152;
		p_info.m_layer = 2;
		switch(mpeg_version)
		{
		case _MPEG_1:
			bitrate = bitrate_table_l2v1[bitrate_index];
			break;
		case _MPEG_2:
		case _MPEG_25:
			bitrate = bitrate_table_l23v2[bitrate_index];
			break;
		default:
			return false;
		}
		break;
	case MPEG_LAYER_1:
		paddingdelta = paddingbit ? 4 : 0;
		p_info.m_duration = 384;
		p_info.m_layer = 1;
		switch(mpeg_version)
		{
		case _MPEG_1:
			bitrate = bitrate_table_l1v1[bitrate_index];
			break;
		case _MPEG_2:
		case _MPEG_25:
			bitrate = bitrate_table_l1v2[bitrate_index];
			break;		
		default:
			return false;
		}
		break;
	}
	if (bitrate == 0) return false;

	sample_rate = sample_rate_table[sample_rate_index];
	if (sample_rate == 0) return false;
	switch(mpeg_version)
	{
	case _MPEG_1:
		sample_rate *= 4;
		p_info.m_mpegversion = MPEG_1;
		break;
	case _MPEG_2:
		sample_rate *= 2;
		p_info.m_mpegversion = MPEG_2;
		break;
	case _MPEG_25:
		p_info.m_mpegversion = MPEG_25;
		break;
	}

	switch(channel_mode)
	{
	case 0:
	case 1:
	case 2:
		p_info.m_channels = 2;
		break;
	case 3:
		p_info.m_channels = 1;
		break;
	}

	p_info.m_channel_mode = channel_mode;

	p_info.m_sample_rate = sample_rate;


	p_info.m_bytes = ( bitrate /*kbps*/ * (1000/8) /* kbps-to-bytes*/ * p_info.m_duration /*samples-per-frame*/ ) / sample_rate + paddingdelta;

	if (p_info.m_layer == 1) p_info.m_bytes &= ~3;

	p_info.m_crc = protection == 0;

	return true;
}

unsigned mp3header::get_samples_per_frame()
{
	mp3_utils::TMPEGFrameInfo fr;
	if (!decode(fr)) return 0;
	return fr.m_duration;
}


bool mp3_utils::IsSameStream(TMPEGFrameInfo const & p_frame1,TMPEGFrameInfo const & p_frame2) {
	return 
		p_frame1.m_channel_mode == p_frame2.m_channel_mode && 
		p_frame1.m_sample_rate == p_frame2.m_sample_rate &&
		p_frame1.m_layer == p_frame2.m_layer &&
		p_frame1.m_mpegversion == p_frame2.m_mpegversion;
}