Working MP3 decoder!

This commit is contained in:
casey 2016-05-02 03:03:41 -07:00
parent 5207bb36de
commit 8cd06ea886
13 changed files with 281 additions and 303 deletions

View File

@ -1,20 +1,72 @@
#include "StdAfx.h"
#include <math.h>
#include <iostream>
#include "mp3decoder.h"
MP3Decoder::MP3Decoder() :
m_hFile(NULL),
m_pDecoder(NULL)
{
static bool splitFrame(musik::core::io::IDataStream *dataStream, Frame &fr) {
unsigned char headerbuf[4] = { 0, 0, 0, 0 };
unsigned char crcbuf[2];
unsigned char sideInfoBuffer[32];
unsigned long bytesRead;
unsigned long errorCount = 0;
unsigned long currentOffset = dataStream->Position();
if (dataStream->Read(headerbuf, 4) != 4) {
return false;
}
while (!fr.m_Header.Load(headerbuf)) {
headerbuf[0] = headerbuf[1];
headerbuf[1] = headerbuf[2];
headerbuf[2] = headerbuf[3];
if (dataStream->Read(&headerbuf[3], 1) != 1) {
return false;
}
if (errorCount++ >= fr.m_Header.GetDataSize()) {
fr.m_Header.Reset();
}
currentOffset++;
}
if (fr.m_Header.IsCRC()) {
if (dataStream->Read(crcbuf, 2) != 2) {
return false;
}
fr.m_CRC.Load(crcbuf);
}
if (fr.m_Header.GetLayer() == LAYER3) {
unsigned count = fr.m_Header.GetSideInfoSize();
if (dataStream->Read(sideInfoBuffer, count) != count) {
return false;
}
if (!fr.m_SI.Load(&fr.m_Header, sideInfoBuffer)) {
return false;
}
}
unsigned frameBytes = fr.m_Header.GetDataSize();
if (dataStream->Read(fr.m_Data, frameBytes) != frameBytes) {
return false;
}
return true;
}
MP3Decoder::~MP3Decoder(void)
{
Close();
MP3Decoder::MP3Decoder()
: m_pDecoder(NULL) {
}
unsigned long MP3Decoder::GetID3HeaderLength(unsigned char * buffer)
{
MP3Decoder::~MP3Decoder() {
}
unsigned long MP3Decoder::GetID3HeaderLength(unsigned char * buffer) {
unsigned char VerMajor;
unsigned char VerMinor;
unsigned char Flags;
@ -22,7 +74,7 @@ unsigned long MP3Decoder::GetID3HeaderLength(unsigned char * buffer)
if( (toupper(buffer[0]) == 'I') &&
(toupper(buffer[1]) == 'D') &&
(toupper(buffer[2]) == '3') )
(toupper(buffer[2]) == '3'))
{
VerMajor = buffer[3];
VerMinor = buffer[4];
@ -34,50 +86,55 @@ unsigned long MP3Decoder::GetID3HeaderLength(unsigned char * buffer)
return(Length);
}
return(0);
return 0;
}
bool MP3Decoder::GetXingHeader(unsigned char * XingBuffer)
{
#define FRAMES_FLAG 0x0001
#define BYTES_FLAG 0x0002
#define TOC_FLAG 0x0004
#define VBR_SCALE_FLAG 0x0008
#define GET_INT32BE(b) (i = (b[0] << 24) | (b[1] << 16) | b[2] << 8 | b[3], b += 4, i)
bool MP3Decoder::GetXingHeader(unsigned char * xingBuffer) {
#define FRAMES_FLAG 0x0001
#define BYTES_FLAG 0x0002
#define TOC_FLAG 0x0004
#define VBR_SCALE_FLAG 0x0008
#define GET_INT32BE(b) (i = (b[0] << 24) | (b[1] << 16) | b[2] << 8 | b[3], b += 4, i) /* windows only? */
unsigned long i;
m_bXingValid = false;
if(strncmp((char *)XingBuffer, "Xing", 4))
if(strncmp((char *)XingBuffer, "Info", 4))
if (strncmp((char *) xingBuffer, "Xing", 4)) {
if (strncmp((char *)xingBuffer, "Info", 4)) {
return false;
}
}
XingBuffer += 4;
xingBuffer += 4;
unsigned long HeadFlags = GET_INT32BE(XingBuffer);
unsigned long headFlags = GET_INT32BE(xingBuffer);
m_NumFrames = 0;
if(HeadFlags & FRAMES_FLAG)
m_NumFrames = GET_INT32BE(XingBuffer);
if(m_NumFrames < 1)
if (headFlags & FRAMES_FLAG) {
m_NumFrames = GET_INT32BE(xingBuffer);
}
if (m_NumFrames < 1) {
return false;
}
m_StreamDataLength = 0;
if(HeadFlags & BYTES_FLAG)
m_StreamDataLength = GET_INT32BE(XingBuffer);
if (headFlags & BYTES_FLAG) {
m_StreamDataLength = GET_INT32BE(xingBuffer);
}
if(HeadFlags & TOC_FLAG)
{
for (i = 0; i < 100; i++)
m_TOC[i] = XingBuffer[i];
XingBuffer += 100;
if (headFlags & TOC_FLAG) {
for (i = 0; i < 100; i++) {
m_TOC[i] = xingBuffer[i];
}
xingBuffer += 100;
}
m_VbrScale = -1;
if(HeadFlags & VBR_SCALE_FLAG)
{
m_VbrScale = GET_INT32BE(XingBuffer);
if (headFlags & VBR_SCALE_FLAG) {
m_VbrScale = GET_INT32BE(xingBuffer);
}
m_bXingValid = true;
@ -85,37 +142,35 @@ bool MP3Decoder::GetXingHeader(unsigned char * XingBuffer)
return true;
}
bool MP3Decoder::GetStreamData(void)
{
bool MP3Decoder::GetStreamData() {
unsigned char tbuf[11];
unsigned long bytesread;
Frame fr;
ReadFile(m_hFile, tbuf, 10, &bytesread, NULL);
this->dataStream->Read(tbuf, 10);
m_ID3v2Length = GetID3HeaderLength(tbuf);
SetFilePointer(m_hFile, m_ID3v2Length, NULL, FILE_BEGIN);
this->dataStream->SetPosition(m_ID3v2Length);
if(m_Splitter.Process(m_hFile, fr))
{
if(splitFrame(this->dataStream, fr)) {
unsigned char * pHeader = fr.m_Data;
if(!GetXingHeader(pHeader))
{
// analyse file here
SetFilePointer(m_hFile, m_ID3v2Length, NULL, FILE_BEGIN);
// just guesstimate the number of frames!
this->dataStream->SetPosition(m_ID3v2Length);
// just guesstimate the number of frames!
/* DANGEROUS -- ASSUMES FINITE LENGTH*/
m_StreamDataLength = this->dataStream->Filesize() - m_ID3v2Length;
m_StreamDataLength = GetFileSize(m_hFile, NULL) - m_ID3v2Length;
// TODO: check for ID3 TAG at the end of the file and subtract
// also remove the size of this current header
m_StreamDataLength -= fr.m_Header.GetTotalFrameSize();
m_NumFrames = m_StreamDataLength / fr.m_Header.GetTotalFrameSize();
}
else
{
if(m_bXingValid == false)
LogConsoleMessage(TEXT("MP3 Decoder"), TEXT("Mp3 has Xing header but it is invalid."));
else {
if (m_bXingValid == false) {
std::cout << "Mp3Decoder.cpp: Mp3 has Xing header but it is invalid.";
}
}
double bs[3] = { 384.0, 1152.0, 1152.0 };
@ -123,8 +178,9 @@ bool MP3Decoder::GetStreamData(void)
m_StreamLengthMS = TimePerFrame * m_NumFrames;
if(fr.m_Header.GetMpegVersion() != MPEG1)
if (fr.m_Header.GetMpegVersion() != MPEG1) {
m_StreamLengthMS /= 2;
}
m_SampleRate = fr.m_Header.GetSampleFrequency();
m_NumChannels = fr.m_Header.GetChannels();
@ -137,78 +193,29 @@ bool MP3Decoder::GetStreamData(void)
return false;
}
bool MP3Decoder::Open(const utfchar* sourcePath)
{
m_LastLayer = -1;
m_hFile = CreateFile( sourcePath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if(m_hFile == INVALID_HANDLE_VALUE)
{
m_hFile = NULL;
return false;
}
this->sourcePath = sourcePath;
bool MP3Decoder::Open(musik::core::io::IDataStream *dataStream) {
this->dataStream = dataStream;
this->m_LastLayer = -1;
return GetStreamData();
}
bool MP3Decoder::Close(void)
{
if(m_pDecoder)
{
delete m_pDecoder;
m_pDecoder = NULL;
}
if(m_hFile)
{
CloseHandle(m_hFile);
m_hFile = NULL;
}
return false;
}
void MP3Decoder::Destroy(void)
{
void MP3Decoder::Destroy(void) {
delete this;
}
bool MP3Decoder::GetFormat(unsigned long * SampleRate, unsigned long * Channels)
{
*SampleRate = m_SampleRate;
*Channels = m_NumChannels;
#define MP3_BUFFER_FLOAT_ALLOWANCE 2304 /* why? */
return true;
}
bool MP3Decoder::GetBuffer(IBuffer *buffer) {
buffer->SetChannels(this->m_NumChannels);
buffer->SetSamples(MP3_BUFFER_FLOAT_ALLOWANCE / buffer->Channels());
buffer->SetSampleRate(this->m_SampleRate);
bool MP3Decoder::SetState(unsigned long State)
{
return true;
}
bool MP3Decoder::GetBuffer(float ** ppBuffer, unsigned long * NumSamples)
{
static float SampleBuffer[2304];
if(m_Splitter.Process(m_hFile, m_Frame))
{
if((m_Frame.m_Header.GetLayer() != m_LastLayer) || (m_pDecoder == NULL))
{
switch(m_Frame.m_Header.GetLayer())
{
case LAYER3:
{
if(m_pDecoder)
{
if (splitFrame(this->dataStream, m_Frame)) {
/* bail if the mpeg layer is incorrect*/
if ((m_Frame.m_Header.GetLayer() != m_LastLayer) || (m_pDecoder == NULL)) {
switch (m_Frame.m_Header.GetLayer()) {
case LAYER3: {
if (m_pDecoder) {
delete m_pDecoder;
m_pDecoder = NULL;
}
@ -217,73 +224,61 @@ bool MP3Decoder::GetBuffer(float ** ppBuffer, unsigned long * NumSamples)
}
break;
default:
LogConsoleMessage(TEXT("MP3 Decoder"), TEXT("Unsupported Layer (Only Layer 3 supported)."));
default: {
LogConsoleMessage(
L"MP3 Decoder",
L"Unsupported Layer (Only Layer 3 supported).");
return false;
break;
}
}
}
if(!m_pDecoder->ProcessFrame(&m_Frame, SampleBuffer, NumSamples))
{
*ppBuffer = NULL;
*NumSamples = 0;
unsigned long bufferCount = 0;
if (!m_pDecoder->ProcessFrame(&m_Frame, buffer->BufferPointer(), &bufferCount)) {
m_Frame.m_Header.Reset();
return true;
}
*ppBuffer = SampleBuffer;
return(true);
}
return false;
}
}
buffer->SetSamples(bufferCount / buffer->Channels());
}
bool MP3Decoder::GetLength(unsigned long * MS)
{
*MS = m_StreamLengthMS;
return true;
}
bool MP3Decoder::SetPosition(unsigned long * MS)
{
unsigned long newMS = *MS;
float percent = 100.00f * ((float)newMS / (float)m_StreamLengthMS);
double MP3Decoder::SetPosition(double seconds, double totalLength) {
float milliseconds = (float) seconds * 1000.0f;
float percent = 100.00f * ((float) milliseconds / (float) m_StreamLengthMS);
unsigned long offset;
if(m_bXingValid)
if (m_bXingValid)
{
/* interpolate in TOC to get file seek point in bytes */
int a;
int a = min(percent, 99);
float fa, fb, fx;
a = min(percent, 99);
fa = m_TOC[a];
if (a < 99)
if (a < 99) {
fb = m_TOC[a + 1];
else
}
else {
fb = 256;
}
fx = fa + (fb - fa) * (percent - a);
offset = (1.0f / 256.0f) * fx * m_StreamDataLength;
}
else
{
offset = (float)m_StreamDataLength * (float)(percent/ 100.0f) ;
else {
offset = (float) m_StreamDataLength * (float)(percent/ 100.0f) ;
}
SetFilePointer(m_hFile, offset + m_ID3v2Length, NULL, FILE_BEGIN);
this->dataStream->SetPosition(offset + m_ID3v2Length);
bool result = splitFrame(this->dataStream, m_Frame);
m_Splitter.Process(m_hFile, m_Frame);
if(m_pDecoder)
{
if (m_pDecoder) {
delete m_pDecoder;
m_pDecoder = NULL;
}
return true;
return result ? seconds : -1;
}

View File

@ -1,57 +1,46 @@
#pragma once
#include <core/audio/IAudioSource.h>
#include <core/sdk/IDecoder.h>
#include "FrameSplitter.h"
#include "Layer3Decoder.h"
using namespace musik::core::audio;
class MP3Decoder : public IAudioSource
class MP3Decoder : public IDecoder
{
public:
MP3Decoder();
~MP3Decoder();
bool Open(musik::core::io::IDataStream *dataStream);
double SetPosition(double seconds, double totalLength);
bool GetBuffer(IBuffer *buffer);
void Destroy();
protected:
CFrameSplitter m_Splitter;
IMPEGDecoder *m_pDecoder;
Frame m_Frame;
IMPEGDecoder * m_pDecoder;
HANDLE m_hFile;
unsigned long m_LastLayer;
unsigned long m_SampleRate;
unsigned long m_NumChannels;
unsigned long m_ID3v2Length;
unsigned long m_StreamLengthMS;
unsigned long m_NumFrames;
unsigned long m_StreamDataLength;
unsigned long m_VbrScale;
unsigned char m_TOC[100];
bool m_bXingValid;
unsigned long GetID3HeaderLength(unsigned char * buffer);
bool GetXingHeader(unsigned char * XingBuffer);
bool GetStreamData(void);
public:
MP3Decoder(void);
~MP3Decoder(void);
bool Open(const utfchar* sourcePath);
bool Close(void);
void Destroy(void);
bool GetLength(unsigned long * MS);
bool SetPosition(unsigned long * MS);
bool SetState(unsigned long State);
bool GetFormat(unsigned long * SampleRate, unsigned long * Channels);
bool GetBuffer(float ** ppBuffer, unsigned long * NumSamples);
const utfchar* GetSource() const { return sourcePath.c_str(); };
bool GetStreamData();
private:
utfstring sourcePath;
musik::core::io::IDataStream *dataStream;
// Stubs
void LogConsoleMessage(LPTSTR szModuleName, LPTSTR szMessage) {}; // TODO: replace with sigslot

View File

@ -36,42 +36,36 @@
#include "boost/algorithm/string.hpp"
#include "boost/filesystem.hpp"
#include "MP3SourceSupplier.h"
#include "Mp3DecoderFactory.h"
#include "MP3Decoder.h"
#include <string>
MP3SourceSupplier::MP3SourceSupplier()
{
Mp3DecoderFactory::Mp3DecoderFactory() {
}
MP3SourceSupplier::~MP3SourceSupplier()
{
Mp3DecoderFactory::~Mp3DecoderFactory() {
}
void MP3SourceSupplier::Destroy()
{
void Mp3DecoderFactory::Destroy() {
delete this;
}
IAudioSource* MP3SourceSupplier::CreateAudioSource()
{
IDecoder* Mp3DecoderFactory::CreateDecoder() {
return new MP3Decoder();
}
bool MP3SourceSupplier::CanHandle(const utfchar* source) const
bool Mp3DecoderFactory::CanHandle(const char* source) const
{
using namespace boost::filesystem;
using namespace boost::algorithm;
wpath sourcepath(source);
if (!is_regular(sourcepath))
return false;
if (to_lower_copy(extension(sourcepath)) != TEXT(".mp3"))
return false;
std::string str(source);
if (str.find("mp3") != std::string::npos ||
str.find("audio/mpeg3") != std::string::npos ||
str.find("audio/x-mpeg-3") != std::string::npos ||
str.find("audio/mp3") != std::string::npos)
{
return true;
}
return false;
}

View File

@ -33,16 +33,17 @@
#pragma once
#include <core\audio\IAudioSource.h>
#include <core\sdk\IDecoderFactory.h>
using namespace musik::core::audio;
class MP3SourceSupplier : public IAudioSourceSupplier
class Mp3DecoderFactory : public IDecoderFactory
{
public: MP3SourceSupplier();
public: ~MP3SourceSupplier();
public:
Mp3DecoderFactory();
~Mp3DecoderFactory();
public: IAudioSource* CreateAudioSource();
public: void Destroy();
public: bool CanHandle(const utfchar* source) const;
IDecoder* CreateDecoder();
void Destroy();
bool CanHandle(const char* type) const;
};

View File

@ -53,7 +53,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>.;../..;../../3rdparty/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.;../..;../../3rdparty/include;../../../../boost_1_60_0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
@ -65,7 +65,7 @@
</ClCompile>
<Link>
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../../3rdparty/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>../../3rdparty/lib;../../../../boost_1_60_0/lib32-msvc-14.0;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
@ -94,7 +94,7 @@
<ClCompile Include="SideInfo.cpp" />
<ClCompile Include="MP3Decoder.cpp" />
<ClCompile Include="mp3decoder_plugin.cpp" />
<ClCompile Include="MP3SourceSupplier.cpp" />
<ClCompile Include="Mp3DecoderFactory.cpp" />
<ClCompile Include="stdafx.cpp" />
</ItemGroup>
<ItemGroup>
@ -110,7 +110,7 @@
<ClInclude Include="resource.h" />
<ClInclude Include="SideInfo.h" />
<ClInclude Include="MP3Decoder.h" />
<ClInclude Include="MP3SourceSupplier.h" />
<ClInclude Include="Mp3DecoderFactory.h" />
<ClInclude Include="stdafx.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -35,31 +35,26 @@
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <core/sdk/IPlugin.h>
#include <core/sdk/IDecoder.h>
#include "Mp3DecoderFactory.h"
#include "core/IPlugin.h"
#include "MP3SourceSupplier.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
return true;
}
class MP3DecoderPlugin : public musik::core::IPlugin
{
void Destroy() { delete this; };
const utfchar* Name() { return TEXT("MP3 decoder"); };
const utfchar* Version() { return TEXT("1"); };
const utfchar* Author() { return TEXT("Björn Olievier"); };
void Destroy() { delete this; }
const char* Name() { return "MP3 decoder"; };
const char* Version() { return "1"; };
const char* Author() { return "Björn Olievier"; };
};
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin()
{
extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin() {
return new MP3DecoderPlugin();
}
extern "C" __declspec(dllexport) IAudioSourceSupplier* CreateAudioSourceSupplier()
{
return new MP3SourceSupplier();
extern "C" __declspec(dllexport) IDecoderFactory* GetDecoderFactory() {
return new Mp3DecoderFactory();
}

View File

@ -51,22 +51,23 @@ IDecoder* OggDecoderFactory::CreateDecoder() {
}
bool OggDecoderFactory::CanHandle(const char* type) const {
if (type) {
std::string typeString(type);
if (typeString.find("ogg") != std::string::npos) {
return true;
}
if (typeString.find("oga") != std::string::npos) {
return true;
}
if (typeString.find("audio/ogg") != std::string::npos) {
return true;
}
if (typeString.find("audio/vorbis") != std::string::npos) {
return true;
}
}
return false;
}

View File

@ -81,6 +81,8 @@ void WaveOut::SetVolume(double volume) {
void WaveOut::Stop() {
boost::recursive_mutex::scoped_lock lock(this->outputDeviceMutex);
/* reset waveout first. if we don't do this, it seems like it'll still
try to send events to the thread, and fail with a runtime exception. */
if (this->waveHandle != NULL) {
waveOutReset(this->waveHandle);
}
@ -88,7 +90,7 @@ void WaveOut::Stop() {
/* stop the thread so nothing else is processed */
this->StopWaveOutThread();
/* reset will free the buffers, close deallocs */
/* dealloc the handle, we'll create a new one later if we need to... */
if (this->waveHandle != NULL) {
waveOutClose(this->waveHandle);
this->waveHandle = NULL;

View File

@ -148,7 +148,7 @@ void Buffer::CopyFormat(BufferPtr fromBuffer) {
void Buffer::ResizeBuffer() {
long requiredBufferSize = this->sampleSize * this->channels;
if (requiredBufferSize > this->internalBufferSize) {
if(this->buffer) {
if (this->buffer) {
delete this->buffer;
this->buffer = NULL;
}

View File

@ -84,7 +84,7 @@ namespace musik { namespace core { namespace io {
///\returns
///how much has acctually been read
//////////////////////////////////////////
virtual PositionType Read(void* buffer, PositionType readBytes) = 0;
virtual PositionType Read(void *buffer, PositionType readBytes) = 0;
//////////////////////////////////////////
///\brief

View File

@ -61,7 +61,7 @@ class IDecoder{
///\returns
///The actual position set
//////////////////////////////////////////
virtual double SetPosition(double seconds,double totalLength) = 0;
virtual double SetPosition(double seconds, double totalLength) = 0;
//////////////////////////////////////////
///\brief
@ -70,7 +70,7 @@ class IDecoder{
///\returns
///false is there is nothing left
//////////////////////////////////////////
virtual bool GetBuffer(IBuffer *buffer) = 0; // return false to signal that we are done decoding.
virtual bool GetBuffer(IBuffer *buffer) = 0;
//////////////////////////////////////////
///\brief

View File

@ -65,7 +65,6 @@ class IDecoderFactory{
///The "type" can either be a file extension or a mimetype
//////////////////////////////////////////
virtual bool CanHandle(const char* type) const = 0;
};
//////////////////////////////////////////////////////////////////////////////

View File

@ -85,6 +85,8 @@ void ConsoleUI::Run()
{
std::string command;
transport.SetVolume(0.1);
while (!this->shouldQuit)
{
this->PrintCommands();